1
Fork 0

fixup! 2229: Capture box completely in move closures

fixup! 2229: Capture box completely in move closures
This commit is contained in:
Aman Arora 2021-06-22 01:58:17 -04:00
parent de2052af9c
commit d37a07ffbe
3 changed files with 36 additions and 30 deletions

View file

@ -167,7 +167,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
); );
self.log_capture_analysis_first_pass(closure_def_id, &delegate.capture_information, span); self.log_capture_analysis_first_pass(closure_def_id, &delegate.capture_information, span);
self.compute_min_captures(closure_def_id, delegate.capture_information); self.compute_min_captures(closure_def_id, capture_clause, delegate.capture_information);
let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id); let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id);
@ -200,7 +200,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
// This will update the min captures based on this new fake information. // This will update the min captures based on this new fake information.
self.compute_min_captures(closure_def_id, capture_information); self.compute_min_captures(closure_def_id, capture_clause, capture_information);
} }
if let Some(closure_substs) = infer_kind { if let Some(closure_substs) = infer_kind {
@ -213,7 +213,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// If we have an origin, store it. // If we have an origin, store it.
if let Some(origin) = delegate.current_origin.clone() { if let Some(origin) = delegate.current_origin.clone() {
let origin = if enable_precise_capture(self.tcx, span) { let origin = if enable_precise_capture(self.tcx, span) {
(origin.0, restrict_capture_precision(origin.1)) (origin.0, restrict_capture_precision(capture_clause, origin.1))
} else { } else {
(origin.0, Place { projections: vec![], ..origin.1 }) (origin.0, Place { projections: vec![], ..origin.1 })
}; };
@ -368,6 +368,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn compute_min_captures( fn compute_min_captures(
&self, &self,
closure_def_id: DefId, closure_def_id: DefId,
capture_clause: hir::CaptureBy,
capture_information: InferredCaptureInformation<'tcx>, capture_information: InferredCaptureInformation<'tcx>,
) { ) {
if capture_information.is_empty() { if capture_information.is_empty() {
@ -385,7 +386,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
base => bug!("Expected upvar, found={:?}", base), base => bug!("Expected upvar, found={:?}", base),
}; };
let place = restrict_capture_precision(place); let place = restrict_capture_precision(capture_clause, place);
let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) { let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) {
None => { None => {
@ -1590,7 +1591,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
if let PlaceBase::Upvar(_) = place.base { if let PlaceBase::Upvar(_) = place.base {
// We need to restrict Fake Read precision to avoid fake reading unsafe code, // We need to restrict Fake Read precision to avoid fake reading unsafe code,
// such as deref of a raw pointer. // such as deref of a raw pointer.
let place = restrict_capture_precision(place); let place = restrict_capture_precision(self.capture_clause, place);
let place = let place =
restrict_repr_packed_field_ref_capture(self.fcx.tcx, self.fcx.param_env, &place); restrict_repr_packed_field_ref_capture(self.fcx.tcx, self.fcx.param_env, &place);
self.fake_reads.push((place, cause, diag_expr_id)); self.fake_reads.push((place, cause, diag_expr_id));
@ -1625,19 +1626,16 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
place_with_id, diag_expr_id, bk place_with_id, diag_expr_id, bk
); );
// We only want repr packed restriction to be applied to reading references into a packed
// struct, and not when the data is being moved. There for we call this method here instead
// of in `restrict_capture_precision`.
let place = restrict_repr_packed_field_ref_capture( let place = restrict_repr_packed_field_ref_capture(
self.fcx.tcx, self.fcx.tcx,
self.fcx.param_env, self.fcx.param_env,
&place_with_id.place, &place_with_id.place,
); );
let place = restrict_preicision_for_box(&place, self.capture_clause);
let place_with_id = PlaceWithHirId { place, ..*place_with_id }; 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) { if !self.capture_information.contains_key(&place_with_id.place) {
self.init_capture_info_for_place(&place_with_id, diag_expr_id); self.init_capture_info_for_place(&place_with_id, diag_expr_id);
@ -1661,39 +1659,46 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
} }
} }
// In case of move closures we don't want to capture derefs on a box. /// Deref of a box isn't captured in move clousres. This is motivated by:
// This is motivated by: /// 1. We only want to capture data that is on the stack
// 1. We only want to capture data that is on the stack /// 2. One motivation for the user to use a box might be to reduce the amount of data that gets
// 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
// moved (if size of pointer < size of data). We want to make sure that this optimization that /// the user made is respected.
// the user made is respected. fn restrict_precision_for_box<'tcx>(
fn restrict_preicision_for_box(place: &Place<'tcx>, capture_by: hir::CaptureBy) -> Place<'tcx> { capture_clause: hir::CaptureBy,
let mut rv = place.clone(); mut place: Place<'tcx>,
match capture_by { ) -> Place<'tcx> {
hir::CaptureBy::Ref => rv, match capture_clause {
hir::CaptureBy::Ref => {}
hir::CaptureBy::Value => { hir::CaptureBy::Value => {
if ty::TyS::is_box(place.base_ty) { if ty::TyS::is_box(place.base_ty) {
Place { projections: Vec::new(), ..rv } place.projections.truncate(0);
} else { } else {
// Either the box is the last access or there is a deref applied on the box // 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. // In either case we want to stop at the box.
let pos = place.projections.iter().position(|proj| ty::TyS::is_box(proj.ty)); let pos = place.projections.iter().position(|proj| ty::TyS::is_box(proj.ty));
match pos { match pos {
None => rv, None => {}
Some(idx) => { Some(idx) => {
Place { projections: rv.projections.drain(0..=idx).collect(), ..rv } place.projections.truncate(idx + 1);
} }
} }
} }
} }
} }
place
} }
/// Truncate projections so that following rules are obeyed by the captured `place`: /// 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 /// - No projections are applied to raw pointers, since these require unsafe blocks. We capture
/// them completely. /// them completely.
/// - No Index projections are captured, since arrays are captured completely. /// - No Index projections are captured, since arrays are captured completely.
fn restrict_capture_precision<'tcx>(mut place: Place<'tcx>) -> Place<'tcx> { /// - Deref of a box isn't captured in move clousres.
fn restrict_capture_precision<'tcx>(
capture_clause: hir::CaptureBy,
mut place: Place<'tcx>,
) -> Place<'tcx> {
if place.projections.is_empty() { if place.projections.is_empty() {
// Nothing to do here // Nothing to do here
return place; return place;
@ -1728,7 +1733,8 @@ fn restrict_capture_precision<'tcx>(mut place: Place<'tcx>) -> Place<'tcx> {
place.projections.truncate(length); place.projections.truncate(length);
place // Dont't capture projections on top of a box in move closures.
restrict_precision_for_box(capture_clause, place)
} }
/// Truncates a place so that the resultant capture doesn't move data out of a reference /// Truncates a place so that the resultant capture doesn't move data out of a reference

View file

@ -141,7 +141,7 @@ fn truncate_box_derefs() {
//~^ ERROR: First Pass analysis includes: //~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes: //~| ERROR: Min Capture analysis includes:
println!("{}", b.0); println!("{}", b.0);
//~^ NOTE: Capturing b[] -> ByValue //~^ NOTE: Capturing b[Deref,(0, 0)] -> ByValue
//~| NOTE: Min Capture b[] -> ByValue //~| NOTE: Min Capture b[] -> ByValue
}; };
@ -158,7 +158,7 @@ fn truncate_box_derefs() {
//~^ ERROR: First Pass analysis includes: //~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes: //~| ERROR: Min Capture analysis includes:
println!("{}", t.1.0); println!("{}", t.1.0);
//~^ NOTE: Capturing t[(1, 0)] -> ByValue //~^ NOTE: Capturing t[(1, 0),Deref,(0, 0)] -> ByValue
//~| NOTE: Min Capture t[(1, 0)] -> ByValue //~| NOTE: Min Capture t[(1, 0)] -> ByValue
}; };
} }

View file

@ -317,7 +317,7 @@ LL | |
LL | | }; LL | | };
| |_____^ | |_____^
| |
note: Capturing b[] -> ByValue note: Capturing b[Deref,(0, 0)] -> ByValue
--> $DIR/move_closure.rs:143:24 --> $DIR/move_closure.rs:143:24
| |
LL | println!("{}", b.0); LL | println!("{}", b.0);
@ -353,7 +353,7 @@ LL | |
LL | | }; LL | | };
| |_____^ | |_____^
| |
note: Capturing t[(1, 0)] -> ByValue note: Capturing t[(1, 0),Deref,(0, 0)] -> ByValue
--> $DIR/move_closure.rs:160:24 --> $DIR/move_closure.rs:160:24
| |
LL | println!("{}", t.1.0); LL | println!("{}", t.1.0);