Skip to content

Commit de2052a

Browse files
committed
2229: Capture box completely in move closures
Even if the content from box is used in a sharef-ref context, we capture the box entirerly. This is motivated by: 1) We only capture data that is on the stack. 2) Capturing data from within the box might end up moving more data than the user anticipated.
1 parent 7c3872e commit de2052a

File tree

3 files changed

+165
-8
lines changed

3 files changed

+165
-8
lines changed

compiler/rustc_typeck/src/check/upvar.rs

+35
Original file line numberDiff line numberDiff line change
@@ -1630,7 +1630,14 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
16301630
self.fcx.param_env,
16311631
&place_with_id.place,
16321632
);
1633+
1634+
let place = restrict_preicision_for_box(&place, self.capture_clause);
1635+
16331636
let place_with_id = PlaceWithHirId { place, ..*place_with_id };
1637+
debug!(
1638+
"borrow after restrictions:(place_with_id={:?}, diag_expr_id={:?}, bk={:?})",
1639+
place_with_id, diag_expr_id, bk
1640+
);
16341641

16351642
if !self.capture_information.contains_key(&place_with_id.place) {
16361643
self.init_capture_info_for_place(&place_with_id, diag_expr_id);
@@ -1654,6 +1661,34 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
16541661
}
16551662
}
16561663

1664+
// In case of move closures we don't want to capture derefs on a box.
1665+
// This is motivated by:
1666+
// 1. We only want to capture data that is on the stack
1667+
// 2. One motivatiton for the user to use a box might be to reduce the amount of data that gets
1668+
// moved (if size of pointer < size of data). We want to make sure that this optimization that
1669+
// the user made is respected.
1670+
fn restrict_preicision_for_box(place: &Place<'tcx>, capture_by: hir::CaptureBy) -> Place<'tcx> {
1671+
let mut rv = place.clone();
1672+
match capture_by {
1673+
hir::CaptureBy::Ref => rv,
1674+
hir::CaptureBy::Value => {
1675+
if ty::TyS::is_box(place.base_ty) {
1676+
Place { projections: Vec::new(), ..rv }
1677+
} else {
1678+
// Either the box is the last access or there is a deref applied on the box
1679+
// In either case we want to stop at the box.
1680+
let pos = place.projections.iter().position(|proj| ty::TyS::is_box(proj.ty));
1681+
match pos {
1682+
None => rv,
1683+
Some(idx) => {
1684+
Place { projections: rv.projections.drain(0..=idx).collect(), ..rv }
1685+
}
1686+
}
1687+
}
1688+
}
1689+
}
1690+
}
1691+
16571692
/// Truncate projections so that following rules are obeyed by the captured `place`:
16581693
/// - No projections are applied to raw pointers, since these require unsafe blocks. We capture
16591694
/// them completely.

src/test/ui/closures/2229_closure_analysis/move_closure.rs

+33-1
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,9 @@ fn struct_contains_ref_to_another_struct_3() {
114114
fn truncate_box_derefs() {
115115
struct S(i32);
116116

117-
let b = Box::new(S(10));
118117

118+
// Content within the box is moved within the closure
119+
let b = Box::new(S(10));
119120
let c = #[rustc_capture_analysis]
120121
//~^ ERROR: attributes on expressions are experimental
121122
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
@@ -129,6 +130,37 @@ fn truncate_box_derefs() {
129130
};
130131

131132
c();
133+
134+
// Content within the box is used by a shared ref and the box is the root variable
135+
let b = Box::new(S(10));
136+
137+
let c = #[rustc_capture_analysis]
138+
//~^ ERROR: attributes on expressions are experimental
139+
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
140+
move || {
141+
//~^ ERROR: First Pass analysis includes:
142+
//~| ERROR: Min Capture analysis includes:
143+
println!("{}", b.0);
144+
//~^ NOTE: Capturing b[] -> ByValue
145+
//~| NOTE: Min Capture b[] -> ByValue
146+
};
147+
148+
c();
149+
150+
// Content within the box is used by a shared ref and the box is not the root variable
151+
let b = Box::new(S(10));
152+
let t = (0, b);
153+
154+
let c = #[rustc_capture_analysis]
155+
//~^ ERROR: attributes on expressions are experimental
156+
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
157+
move || {
158+
//~^ ERROR: First Pass analysis includes:
159+
//~| ERROR: Min Capture analysis includes:
160+
println!("{}", t.1.0);
161+
//~^ NOTE: Capturing t[(1, 0)] -> ByValue
162+
//~| NOTE: Min Capture t[(1, 0)] -> ByValue
163+
};
132164
}
133165

134166
fn main() {

src/test/ui/closures/2229_closure_analysis/move_closure.stderr

+97-7
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,25 @@ LL | let mut c = #[rustc_capture_analysis]
4444
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
4545

4646
error[E0658]: attributes on expressions are experimental
47-
--> $DIR/move_closure.rs:119:13
47+
--> $DIR/move_closure.rs:120:13
48+
|
49+
LL | let c = #[rustc_capture_analysis]
50+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
51+
|
52+
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
53+
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
54+
55+
error[E0658]: attributes on expressions are experimental
56+
--> $DIR/move_closure.rs:137:13
57+
|
58+
LL | let c = #[rustc_capture_analysis]
59+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
60+
|
61+
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
62+
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
63+
64+
error[E0658]: attributes on expressions are experimental
65+
--> $DIR/move_closure.rs:154:13
4866
|
4967
LL | let c = #[rustc_capture_analysis]
5068
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -247,7 +265,7 @@ LL | let _t = t.0.0;
247265
| ^^^^^
248266

249267
error: First Pass analysis includes:
250-
--> $DIR/move_closure.rs:122:5
268+
--> $DIR/move_closure.rs:123:5
251269
|
252270
LL | / move || {
253271
LL | |
@@ -259,18 +277,18 @@ LL | | };
259277
| |_____^
260278
|
261279
note: Capturing b[Deref,(0, 0)] -> ByValue
262-
--> $DIR/move_closure.rs:125:18
280+
--> $DIR/move_closure.rs:126:18
263281
|
264282
LL | let _t = b.0;
265283
| ^^^
266284
note: Capturing b[] -> ByValue
267-
--> $DIR/move_closure.rs:125:18
285+
--> $DIR/move_closure.rs:126:18
268286
|
269287
LL | let _t = b.0;
270288
| ^^^
271289

272290
error: Min Capture analysis includes:
273-
--> $DIR/move_closure.rs:122:5
291+
--> $DIR/move_closure.rs:123:5
274292
|
275293
LL | / move || {
276294
LL | |
@@ -282,11 +300,83 @@ LL | | };
282300
| |_____^
283301
|
284302
note: Min Capture b[] -> ByValue
285-
--> $DIR/move_closure.rs:125:18
303+
--> $DIR/move_closure.rs:126:18
286304
|
287305
LL | let _t = b.0;
288306
| ^^^
289307

290-
error: aborting due to 18 previous errors; 1 warning emitted
308+
error: First Pass analysis includes:
309+
--> $DIR/move_closure.rs:140:5
310+
|
311+
LL | / move || {
312+
LL | |
313+
LL | |
314+
LL | | println!("{}", b.0);
315+
LL | |
316+
LL | |
317+
LL | | };
318+
| |_____^
319+
|
320+
note: Capturing b[] -> ByValue
321+
--> $DIR/move_closure.rs:143:24
322+
|
323+
LL | println!("{}", b.0);
324+
| ^^^
325+
326+
error: Min Capture analysis includes:
327+
--> $DIR/move_closure.rs:140:5
328+
|
329+
LL | / move || {
330+
LL | |
331+
LL | |
332+
LL | | println!("{}", b.0);
333+
LL | |
334+
LL | |
335+
LL | | };
336+
| |_____^
337+
|
338+
note: Min Capture b[] -> ByValue
339+
--> $DIR/move_closure.rs:143:24
340+
|
341+
LL | println!("{}", b.0);
342+
| ^^^
343+
344+
error: First Pass analysis includes:
345+
--> $DIR/move_closure.rs:157:5
346+
|
347+
LL | / move || {
348+
LL | |
349+
LL | |
350+
LL | | println!("{}", t.1.0);
351+
LL | |
352+
LL | |
353+
LL | | };
354+
| |_____^
355+
|
356+
note: Capturing t[(1, 0)] -> ByValue
357+
--> $DIR/move_closure.rs:160:24
358+
|
359+
LL | println!("{}", t.1.0);
360+
| ^^^^^
361+
362+
error: Min Capture analysis includes:
363+
--> $DIR/move_closure.rs:157:5
364+
|
365+
LL | / move || {
366+
LL | |
367+
LL | |
368+
LL | | println!("{}", t.1.0);
369+
LL | |
370+
LL | |
371+
LL | | };
372+
| |_____^
373+
|
374+
note: Min Capture t[(1, 0)] -> ByValue
375+
--> $DIR/move_closure.rs:160:24
376+
|
377+
LL | println!("{}", t.1.0);
378+
| ^^^^^
379+
380+
error: aborting due to 24 previous errors; 1 warning emitted
291381

292382
For more information about this error, try `rustc --explain E0658`.

0 commit comments

Comments
 (0)