Skip to content

Commit a4f832b

Browse files
committed
Auto merge of #86445 - sexxi-goose:box_fix, r=nikomatsakis
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. Closes rust-lang/project-rfc-2229#50 r? `@nikomatsakis`
2 parents 9cdb2d3 + d37a07f commit a4f832b

File tree

3 files changed

+178
-15
lines changed

3 files changed

+178
-15
lines changed

compiler/rustc_typeck/src/check/upvar.rs

+48-7
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
167167
);
168168
self.log_capture_analysis_first_pass(closure_def_id, &delegate.capture_information, span);
169169

170-
self.compute_min_captures(closure_def_id, delegate.capture_information);
170+
self.compute_min_captures(closure_def_id, capture_clause, delegate.capture_information);
171171

172172
let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id);
173173

@@ -200,7 +200,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
200200
}
201201

202202
// This will update the min captures based on this new fake information.
203-
self.compute_min_captures(closure_def_id, capture_information);
203+
self.compute_min_captures(closure_def_id, capture_clause, capture_information);
204204
}
205205

206206
if let Some(closure_substs) = infer_kind {
@@ -213,7 +213,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
213213
// If we have an origin, store it.
214214
if let Some(origin) = delegate.current_origin.clone() {
215215
let origin = if enable_precise_capture(self.tcx, span) {
216-
(origin.0, restrict_capture_precision(origin.1))
216+
(origin.0, restrict_capture_precision(capture_clause, origin.1))
217217
} else {
218218
(origin.0, Place { projections: vec![], ..origin.1 })
219219
};
@@ -368,6 +368,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
368368
fn compute_min_captures(
369369
&self,
370370
closure_def_id: DefId,
371+
capture_clause: hir::CaptureBy,
371372
capture_information: InferredCaptureInformation<'tcx>,
372373
) {
373374
if capture_information.is_empty() {
@@ -385,7 +386,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
385386
base => bug!("Expected upvar, found={:?}", base),
386387
};
387388

388-
let place = restrict_capture_precision(place);
389+
let place = restrict_capture_precision(capture_clause, place);
389390

390391
let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) {
391392
None => {
@@ -1590,7 +1591,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
15901591
if let PlaceBase::Upvar(_) = place.base {
15911592
// We need to restrict Fake Read precision to avoid fake reading unsafe code,
15921593
// such as deref of a raw pointer.
1593-
let place = restrict_capture_precision(place);
1594+
let place = restrict_capture_precision(self.capture_clause, place);
15941595
let place =
15951596
restrict_repr_packed_field_ref_capture(self.fcx.tcx, self.fcx.param_env, &place);
15961597
self.fake_reads.push((place, cause, diag_expr_id));
@@ -1625,11 +1626,15 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
16251626
place_with_id, diag_expr_id, bk
16261627
);
16271628

1629+
// We only want repr packed restriction to be applied to reading references into a packed
1630+
// struct, and not when the data is being moved. There for we call this method here instead
1631+
// of in `restrict_capture_precision`.
16281632
let place = restrict_repr_packed_field_ref_capture(
16291633
self.fcx.tcx,
16301634
self.fcx.param_env,
16311635
&place_with_id.place,
16321636
);
1637+
16331638
let place_with_id = PlaceWithHirId { place, ..*place_with_id };
16341639

16351640
if !self.capture_information.contains_key(&place_with_id.place) {
@@ -1654,11 +1659,46 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
16541659
}
16551660
}
16561661

1662+
/// Deref of a box isn't captured in move clousres. This is motivated by:
1663+
/// 1. We only want to capture data that is on the stack
1664+
/// 2. One motivation for the user to use a box might be to reduce the amount of data that gets
1665+
/// moved (if size of pointer < size of data). We want to make sure that this optimization that
1666+
/// the user made is respected.
1667+
fn restrict_precision_for_box<'tcx>(
1668+
capture_clause: hir::CaptureBy,
1669+
mut place: Place<'tcx>,
1670+
) -> Place<'tcx> {
1671+
match capture_clause {
1672+
hir::CaptureBy::Ref => {}
1673+
hir::CaptureBy::Value => {
1674+
if ty::TyS::is_box(place.base_ty) {
1675+
place.projections.truncate(0);
1676+
} else {
1677+
// Either the box is the last access or there is a deref applied on the box
1678+
// In either case we want to stop at the box.
1679+
let pos = place.projections.iter().position(|proj| ty::TyS::is_box(proj.ty));
1680+
match pos {
1681+
None => {}
1682+
Some(idx) => {
1683+
place.projections.truncate(idx + 1);
1684+
}
1685+
}
1686+
}
1687+
}
1688+
}
1689+
1690+
place
1691+
}
1692+
16571693
/// Truncate projections so that following rules are obeyed by the captured `place`:
16581694
/// - No projections are applied to raw pointers, since these require unsafe blocks. We capture
16591695
/// them completely.
16601696
/// - No Index projections are captured, since arrays are captured completely.
1661-
fn restrict_capture_precision<'tcx>(mut place: Place<'tcx>) -> Place<'tcx> {
1697+
/// - Deref of a box isn't captured in move clousres.
1698+
fn restrict_capture_precision<'tcx>(
1699+
capture_clause: hir::CaptureBy,
1700+
mut place: Place<'tcx>,
1701+
) -> Place<'tcx> {
16621702
if place.projections.is_empty() {
16631703
// Nothing to do here
16641704
return place;
@@ -1693,7 +1733,8 @@ fn restrict_capture_precision<'tcx>(mut place: Place<'tcx>) -> Place<'tcx> {
16931733

16941734
place.projections.truncate(length);
16951735

1696-
place
1736+
// Dont't capture projections on top of a box in move closures.
1737+
restrict_precision_for_box(capture_clause, place)
16971738
}
16981739

16991740
/// Truncates a place so that the resultant capture doesn't move data out of a reference

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[Deref,(0, 0)] -> 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),Deref,(0, 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[Deref,(0, 0)] -> 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),Deref,(0, 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)