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.
This commit is contained in:
Aman Arora 2021-06-18 16:35:10 -04:00
parent 7c3872e6bf
commit de2052af9c
3 changed files with 165 additions and 8 deletions

View File

@ -1630,7 +1630,14 @@ fn borrow(
self.fcx.param_env,
&place_with_id.place,
);
let place = restrict_preicision_for_box(&place, self.capture_clause);
let place_with_id = PlaceWithHirId { place, ..*place_with_id };
debug!(
"borrow after restrictions:(place_with_id={:?}, diag_expr_id={:?}, bk={:?})",
place_with_id, diag_expr_id, bk
);
if !self.capture_information.contains_key(&place_with_id.place) {
self.init_capture_info_for_place(&place_with_id, diag_expr_id);
@ -1654,6 +1661,34 @@ fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::H
}
}
// In case of move closures we don't want to capture derefs on a box.
// This is motivated by:
// 1. We only want to capture data that is on the stack
// 2. One motivatiton for the user to use a box might be to reduce the amount of data that gets
// moved (if size of pointer < size of data). We want to make sure that this optimization that
// the user made is respected.
fn restrict_preicision_for_box(place: &Place<'tcx>, capture_by: hir::CaptureBy) -> Place<'tcx> {
let mut rv = place.clone();
match capture_by {
hir::CaptureBy::Ref => rv,
hir::CaptureBy::Value => {
if ty::TyS::is_box(place.base_ty) {
Place { projections: Vec::new(), ..rv }
} else {
// Either the box is the last access or there is a deref applied on the box
// In either case we want to stop at the box.
let pos = place.projections.iter().position(|proj| ty::TyS::is_box(proj.ty));
match pos {
None => rv,
Some(idx) => {
Place { projections: rv.projections.drain(0..=idx).collect(), ..rv }
}
}
}
}
}
}
/// Truncate projections so that following rules are obeyed by the captured `place`:
/// - No projections are applied to raw pointers, since these require unsafe blocks. We capture
/// them completely.

View File

@ -114,8 +114,9 @@ fn struct_contains_ref_to_another_struct_3() {
fn truncate_box_derefs() {
struct S(i32);
let b = Box::new(S(10));
// Content within the box is moved within the closure
let b = Box::new(S(10));
let c = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
@ -129,6 +130,37 @@ fn truncate_box_derefs() {
};
c();
// Content within the box is used by a shared ref and the box is the root variable
let b = Box::new(S(10));
let c = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
move || {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
println!("{}", b.0);
//~^ NOTE: Capturing b[] -> ByValue
//~| NOTE: Min Capture b[] -> ByValue
};
c();
// Content within the box is used by a shared ref and the box is not the root variable
let b = Box::new(S(10));
let t = (0, b);
let c = #[rustc_capture_analysis]
//~^ ERROR: attributes on expressions are experimental
//~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
move || {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
println!("{}", t.1.0);
//~^ NOTE: Capturing t[(1, 0)] -> ByValue
//~| NOTE: Min Capture t[(1, 0)] -> ByValue
};
}
fn main() {

View File

@ -44,7 +44,25 @@ LL | let mut c = #[rustc_capture_analysis]
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
error[E0658]: attributes on expressions are experimental
--> $DIR/move_closure.rs:119:13
--> $DIR/move_closure.rs:120:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
error[E0658]: attributes on expressions are experimental
--> $DIR/move_closure.rs:137:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
error[E0658]: attributes on expressions are experimental
--> $DIR/move_closure.rs:154:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@ -247,7 +265,7 @@ LL | let _t = t.0.0;
| ^^^^^
error: First Pass analysis includes:
--> $DIR/move_closure.rs:122:5
--> $DIR/move_closure.rs:123:5
|
LL | / move || {
LL | |
@ -259,18 +277,18 @@ LL | | };
| |_____^
|
note: Capturing b[Deref,(0, 0)] -> ByValue
--> $DIR/move_closure.rs:125:18
--> $DIR/move_closure.rs:126:18
|
LL | let _t = b.0;
| ^^^
note: Capturing b[] -> ByValue
--> $DIR/move_closure.rs:125:18
--> $DIR/move_closure.rs:126:18
|
LL | let _t = b.0;
| ^^^
error: Min Capture analysis includes:
--> $DIR/move_closure.rs:122:5
--> $DIR/move_closure.rs:123:5
|
LL | / move || {
LL | |
@ -282,11 +300,83 @@ LL | | };
| |_____^
|
note: Min Capture b[] -> ByValue
--> $DIR/move_closure.rs:125:18
--> $DIR/move_closure.rs:126:18
|
LL | let _t = b.0;
| ^^^
error: aborting due to 18 previous errors; 1 warning emitted
error: First Pass analysis includes:
--> $DIR/move_closure.rs:140:5
|
LL | / move || {
LL | |
LL | |
LL | | println!("{}", b.0);
LL | |
LL | |
LL | | };
| |_____^
|
note: Capturing b[] -> ByValue
--> $DIR/move_closure.rs:143:24
|
LL | println!("{}", b.0);
| ^^^
error: Min Capture analysis includes:
--> $DIR/move_closure.rs:140:5
|
LL | / move || {
LL | |
LL | |
LL | | println!("{}", b.0);
LL | |
LL | |
LL | | };
| |_____^
|
note: Min Capture b[] -> ByValue
--> $DIR/move_closure.rs:143:24
|
LL | println!("{}", b.0);
| ^^^
error: First Pass analysis includes:
--> $DIR/move_closure.rs:157:5
|
LL | / move || {
LL | |
LL | |
LL | | println!("{}", t.1.0);
LL | |
LL | |
LL | | };
| |_____^
|
note: Capturing t[(1, 0)] -> ByValue
--> $DIR/move_closure.rs:160:24
|
LL | println!("{}", t.1.0);
| ^^^^^
error: Min Capture analysis includes:
--> $DIR/move_closure.rs:157:5
|
LL | / move || {
LL | |
LL | |
LL | | println!("{}", t.1.0);
LL | |
LL | |
LL | | };
| |_____^
|
note: Min Capture t[(1, 0)] -> ByValue
--> $DIR/move_closure.rs:160:24
|
LL | println!("{}", t.1.0);
| ^^^^^
error: aborting due to 24 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0658`.