diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 4d6d1da194fc6..e0f0215a58c61 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -148,9 +148,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fcx: self, closure_def_id, closure_span: span, - capture_clause, - current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM, - current_origin: None, capture_information: Default::default(), fake_reads: Default::default(), }; @@ -167,9 +164,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "For closure={:?}, capture_information={:#?}", closure_def_id, delegate.capture_information ); + self.log_capture_analysis_first_pass(closure_def_id, &delegate.capture_information, span); - self.compute_min_captures(closure_def_id, capture_clause, delegate.capture_information); + let (capture_information, closure_kind, origin) = self + .process_collected_capture_information(capture_clause, delegate.capture_information); + + self.compute_min_captures(closure_def_id, capture_information); let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id); @@ -204,7 +205,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // This will update the min captures based on this new fake information. - self.compute_min_captures(closure_def_id, capture_clause, capture_information); + self.compute_min_captures(closure_def_id, capture_information); } let before_feature_tys = self.final_upvar_tys(closure_def_id); @@ -212,14 +213,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(closure_substs) = infer_kind { // Unify the (as yet unbound) type variable in the closure // substs with the kind we inferred. - let inferred_kind = delegate.current_closure_kind; let closure_kind_ty = closure_substs.as_closure().kind_ty(); - self.demand_eqtype(span, inferred_kind.to_ty(self.tcx), closure_kind_ty); + self.demand_eqtype(span, closure_kind.to_ty(self.tcx), closure_kind_ty); // If we have an origin, store it. - if let Some(origin) = delegate.current_origin.clone() { + if let Some(origin) = origin { let origin = if enable_precise_capture(self.tcx, span) { - (origin.0, restrict_capture_precision(capture_clause, origin.1)) + (origin.0, origin.1) } else { (origin.0, Place { projections: vec![], ..origin.1 }) }; @@ -306,6 +306,85 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect() } + /// Adjusts the closure capture information to ensure that the operations aren't unsafe, + /// and that the path can be captured with required capture kind (depending on use in closure, + /// move closure etc.) + /// + /// Returns the set of of adjusted information along with the inferred closure kind and span + /// associated with the closure kind inference. + /// + /// Note that we *always* infer a minimal kind, even if + /// we don't always *use* that in the final result (i.e., sometimes + /// we've taken the closure kind from the expectations instead, and + /// for generators we don't even implement the closure traits + /// really). + /// + /// If we inferred that the closure needs to be FnMut/FnOnce, last element of the returned tuple + /// contains a `Some()` with the `Place` that caused us to do so. + fn process_collected_capture_information( + &self, + capture_clause: hir::CaptureBy, + capture_information: InferredCaptureInformation<'tcx>, + ) -> (InferredCaptureInformation<'tcx>, ty::ClosureKind, Option<(Span, Place<'tcx>)>) { + let mut processed: InferredCaptureInformation<'tcx> = Default::default(); + + let mut closure_kind = ty::ClosureKind::LATTICE_BOTTOM; + let mut origin: Option<(Span, Place<'tcx>)> = None; + + for (place, mut capture_info) in capture_information { + // Apply rules for safety before inferring closure kind + let place = restrict_capture_precision(place); + + let place = truncate_capture_for_optimization(&place); + + let usage_span = if let Some(usage_expr) = capture_info.path_expr_id { + self.tcx.hir().span(usage_expr) + } else { + unreachable!() + }; + + let updated = match capture_info.capture_kind { + ty::UpvarCapture::ByValue(..) => match closure_kind { + ty::ClosureKind::Fn | ty::ClosureKind::FnMut => { + (ty::ClosureKind::FnOnce, Some((usage_span, place.clone()))) + } + // If closure is already FnOnce, don't update + ty::ClosureKind::FnOnce => (closure_kind, origin), + }, + + ty::UpvarCapture::ByRef(ty::UpvarBorrow { + kind: ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow, + .. + }) => { + match closure_kind { + ty::ClosureKind::Fn => { + (ty::ClosureKind::FnMut, Some((usage_span, place.clone()))) + } + // Don't update the origin + ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce => (closure_kind, origin), + } + } + + _ => (closure_kind, origin), + }; + + closure_kind = updated.0; + origin = updated.1; + + let (place, capture_kind) = match capture_clause { + hir::CaptureBy::Value => adjust_for_move_closure(place, capture_info.capture_kind), + hir::CaptureBy::Ref => { + adjust_for_non_move_closure(place, capture_info.capture_kind) + } + }; + + capture_info.capture_kind = capture_kind; + processed.insert(place, capture_info); + } + + (processed, closure_kind, origin) + } + /// Analyzes the information collected by `InferBorrowKind` to compute the min number of /// Places (and corresponding capture kind) that we need to keep track of to support all /// the required captured paths. @@ -378,7 +457,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn compute_min_captures( &self, closure_def_id: DefId, - capture_clause: hir::CaptureBy, capture_information: InferredCaptureInformation<'tcx>, ) { if capture_information.is_empty() { @@ -396,8 +474,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { base => bug!("Expected upvar, found={:?}", base), }; - let place = restrict_capture_precision(capture_clause, place); - let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) { None => { let mutability = self.determine_capture_mutability(&typeck_results, &place); @@ -1328,20 +1404,6 @@ struct InferBorrowKind<'a, 'tcx> { closure_span: Span, - capture_clause: hir::CaptureBy, - - // The kind that we have inferred that the current closure - // requires. Note that we *always* infer a minimal kind, even if - // we don't always *use* that in the final result (i.e., sometimes - // we've taken the closure kind from the expectations instead, and - // for generators we don't even implement the closure traits - // really). - current_closure_kind: ty::ClosureKind, - - // If we modified `current_closure_kind`, this field contains a `Some()` with the - // variable access that caused us to do so. - current_origin: Option<(Span, Place<'tcx>)>, - /// For each Place that is captured by the closure, we track the minimal kind of /// access we need (ref, ref mut, move, etc) and the expression that resulted in such access. /// @@ -1384,27 +1446,13 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { place_with_id, diag_expr_id, mode ); - match (self.capture_clause, mode) { - // In non-move closures, we only care about moves - (hir::CaptureBy::Ref, euv::Copy) => return, - - // We want to capture Copy types that read through a ref via a reborrow - (hir::CaptureBy::Value, euv::Copy) - if place_with_id.place.deref_tys().any(ty::TyS::is_ref) => - { - return; - } - - (hir::CaptureBy::Ref, euv::Move) | (hir::CaptureBy::Value, euv::Move | euv::Copy) => {} + // Copy type being used as ByValue are equivalent to ImmBorrow and don't require any + // escalation. + match mode { + euv::ConsumeMode::Copy => return, + euv::ConsumeMode::Move => {} }; - let place = truncate_capture_for_move(place_with_id.place.clone()); - let place_with_id = PlaceWithHirId { place: place.clone(), hir_id: place_with_id.hir_id }; - - if !self.capture_information.contains_key(&place) { - self.init_capture_info_for_place(&place_with_id, diag_expr_id); - } - let tcx = self.fcx.tcx; let upvar_id = if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { upvar_id @@ -1416,16 +1464,6 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { let usage_span = tcx.hir().span(diag_expr_id); - if matches!(mode, euv::Move) { - // To move out of an upvar, this must be a FnOnce closure - self.adjust_closure_kind( - upvar_id.closure_expr_id, - ty::ClosureKind::FnOnce, - usage_span, - place.clone(), - ); - } - let capture_info = ty::CaptureInfo { capture_kind_expr_id: Some(diag_expr_id), path_expr_id: Some(diag_expr_id), @@ -1503,22 +1541,11 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { ty::ImmBorrow => false, }); - let tcx = self.fcx.tcx; - // if this is an implicit deref of an // upvar, then we need to modify the // borrow_kind of the upvar to make sure it // is inferred to mutable if necessary self.adjust_upvar_borrow_kind(place_with_id, diag_expr_id, borrow_kind); - - if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { - self.adjust_closure_kind( - upvar_id.closure_expr_id, - ty::ClosureKind::FnMut, - tcx.hir().span(diag_expr_id), - place_with_id.place.clone(), - ); - } } /// We infer the borrow_kind with which to borrow upvars in a stack closure. @@ -1557,48 +1584,6 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { }; } - fn adjust_closure_kind( - &mut self, - closure_id: LocalDefId, - new_kind: ty::ClosureKind, - upvar_span: Span, - place: Place<'tcx>, - ) { - debug!( - "adjust_closure_kind(closure_id={:?}, new_kind={:?}, upvar_span={:?}, place={:?})", - closure_id, new_kind, upvar_span, place - ); - - // Is this the closure whose kind is currently being inferred? - if closure_id.to_def_id() != self.closure_def_id { - debug!("adjust_closure_kind: not current closure"); - return; - } - - // closures start out as `Fn`. - let existing_kind = self.current_closure_kind; - - debug!( - "adjust_closure_kind: closure_id={:?}, existing_kind={:?}, new_kind={:?}", - closure_id, existing_kind, new_kind - ); - - match (existing_kind, new_kind) { - (ty::ClosureKind::Fn, ty::ClosureKind::Fn) - | (ty::ClosureKind::FnMut, ty::ClosureKind::Fn | ty::ClosureKind::FnMut) - | (ty::ClosureKind::FnOnce, _) => { - // no change needed - } - - (ty::ClosureKind::Fn, ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce) - | (ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => { - // new kind is stronger than the old kind - self.current_closure_kind = new_kind; - self.current_origin = Some((upvar_span, place)); - } - } - } - fn init_capture_info_for_place( &mut self, place_with_id: &PlaceWithHirId<'tcx>, @@ -1607,12 +1592,12 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base { assert_eq!(self.closure_def_id.expect_local(), upvar_id.closure_expr_id); - let capture_kind = self.fcx.init_capture_kind_for_place( - &place_with_id.place, - self.capture_clause, - upvar_id, - self.closure_span, - ); + // Initialize to ImmBorrow + // We will escalate the CaptureKind based on any uses we see or in `process_collected_capture_information`. + let origin = UpvarRegion(upvar_id, self.closure_span); + let upvar_region = self.fcx.next_region_var(origin); + let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, region: upvar_region }; + let capture_kind = ty::UpvarCapture::ByRef(upvar_borrow); let expr_id = Some(diag_expr_id); let capture_info = ty::CaptureInfo { @@ -1635,7 +1620,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { if let PlaceBase::Upvar(_) = place.base { // We need to restrict Fake Read precision to avoid fake reading unsafe code, // such as deref of a raw pointer. - let place = restrict_capture_precision(self.capture_clause, place); + let place = restrict_capture_precision(place); let place = restrict_repr_packed_field_ref_capture(self.fcx.tcx, self.fcx.param_env, &place); self.fake_reads.push((place, cause, diag_expr_id)); @@ -1653,11 +1638,6 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { place_with_id, diag_expr_id, mode ); - let place_with_id = PlaceWithHirId { - place: truncate_capture_for_optimization(&place_with_id.place), - ..*place_with_id - }; - if !self.capture_information.contains_key(&place_with_id.place) { self.init_capture_info_for_place(&place_with_id, diag_expr_id); } @@ -1677,7 +1657,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { ); // 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 + // struct, and not when the data is being moved. Therefore we call this method here instead // of in `restrict_capture_precision`. let place = restrict_repr_packed_field_ref_capture( self.fcx.tcx, @@ -1685,8 +1665,6 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { &place_with_id.place, ); - let place = truncate_capture_for_optimization(&place); - let place_with_id = PlaceWithHirId { place, ..*place_with_id }; if !self.capture_information.contains_key(&place_with_id.place) { @@ -1711,46 +1689,11 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { } } -/// Deref of a box isn't captured in move clousres. This is motivated by: -/// 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 -/// moved (if size of pointer < size of data). We want to make sure that this optimization that -/// the user made is respected. -fn restrict_precision_for_box<'tcx>( - capture_clause: hir::CaptureBy, - mut place: Place<'tcx>, -) -> Place<'tcx> { - match capture_clause { - hir::CaptureBy::Ref => {} - hir::CaptureBy::Value => { - if ty::TyS::is_box(place.base_ty) { - place.projections.truncate(0); - } 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 => {} - Some(idx) => { - place.projections.truncate(idx + 1); - } - } - } - } - } - - 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 /// them completely. /// - No Index projections are captured, since arrays are captured completely. -/// - 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> { +fn restrict_capture_precision<'tcx>(mut place: Place<'tcx>) -> Place<'tcx> { if place.projections.is_empty() { // Nothing to do here return place; @@ -1785,19 +1728,68 @@ fn restrict_capture_precision<'tcx>( place.projections.truncate(length); - // Dont't capture projections on top of a box in move closures. - restrict_precision_for_box(capture_clause, place) + place } -/// Truncates a place so that the resultant capture doesn't move data out of a reference -fn truncate_capture_for_move(mut place: Place<'tcx>) -> Place<'tcx> { - if let Some(i) = place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref) { - // We only drop Derefs in case of move closures - // There might be an index projection or raw ptr ahead, so we don't stop here. - place.projections.truncate(i); +/// Take ownership if data being accessed is owned by the variable used to access it +/// (or if closure attempts to move data that it doesn’t own). +/// Note: When taking ownership, only capture data found on the stack. +fn adjust_for_move_closure<'tcx>( + mut place: Place<'tcx>, + kind: ty::UpvarCapture<'tcx>, +) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { + let contains_deref_of_ref = place.deref_tys().any(|ty| ty.is_ref()); + let first_deref = place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref); + + match kind { + ty::UpvarCapture::ByRef(..) if contains_deref_of_ref => (place, kind), + + // If there's any Deref and the data needs to be moved into the closure body, + // or it's a Deref of a Box, truncate the path to the first deref + _ if first_deref.is_some() => { + let place = match first_deref { + Some(idx) => { + place.projections.truncate(idx); + place + } + None => place, + }; + + // AMAN: I think we don't need the span inside the ByValue anymore + // we have more detailed span in CaptureInfo + (place, ty::UpvarCapture::ByValue(None)) + } + + _ => (place, ty::UpvarCapture::ByValue(None)), } +} - place +/// Adjust closure capture just that if taking ownership of data, only move data +/// from enclosing stack frame. +fn adjust_for_non_move_closure<'tcx>( + mut place: Place<'tcx>, + kind: ty::UpvarCapture<'tcx>, +) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { + let contains_deref = + place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref); + + match kind { + ty::UpvarCapture::ByValue(..) if contains_deref.is_some() => { + let place = match contains_deref { + Some(idx) => { + place.projections.truncate(idx); + place + } + // Because of the if guard on the match on `kind`, we should never get here. + None => unreachable!(), + }; + + (place, kind) + } + + ty::UpvarCapture::ByValue(..) => (place, kind), + ty::UpvarCapture::ByRef(..) => (place, kind), + } } fn construct_place_string(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String { diff --git a/src/test/ui/closures/2229_closure_analysis/by_value.rs b/src/test/ui/closures/2229_closure_analysis/by_value.rs index 02a243e050646..d8d3bbee200db 100644 --- a/src/test/ui/closures/2229_closure_analysis/by_value.rs +++ b/src/test/ui/closures/2229_closure_analysis/by_value.rs @@ -22,8 +22,7 @@ fn big_box() { //~^ First Pass analysis includes: //~| Min Capture analysis includes: let p = t.0.0; - //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow - //~| NOTE: Capturing t[(0, 0)] -> ByValue + //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue //~| NOTE: Min Capture t[(0, 0)] -> ByValue println!("{} {:?}", t.1, p); //~^ NOTE: Capturing t[(1, 0)] -> ImmBorrow diff --git a/src/test/ui/closures/2229_closure_analysis/by_value.stderr b/src/test/ui/closures/2229_closure_analysis/by_value.stderr index 7014ae6a5e6af..097462253aae7 100644 --- a/src/test/ui/closures/2229_closure_analysis/by_value.stderr +++ b/src/test/ui/closures/2229_closure_analysis/by_value.stderr @@ -19,18 +19,13 @@ LL | | LL | | }; | |_____^ | -note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow - --> $DIR/by_value.rs:24:17 - | -LL | let p = t.0.0; - | ^^^^^ -note: Capturing t[(0, 0)] -> ByValue +note: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue --> $DIR/by_value.rs:24:17 | LL | let p = t.0.0; | ^^^^^ note: Capturing t[(1, 0)] -> ImmBorrow - --> $DIR/by_value.rs:28:29 + --> $DIR/by_value.rs:27:29 | LL | println!("{} {:?}", t.1, p); | ^^^ @@ -53,7 +48,7 @@ note: Min Capture t[(0, 0)] -> ByValue LL | let p = t.0.0; | ^^^^^ note: Min Capture t[(1, 0)] -> ImmBorrow - --> $DIR/by_value.rs:28:29 + --> $DIR/by_value.rs:27:29 | LL | println!("{} {:?}", t.1, p); | ^^^ diff --git a/src/test/ui/closures/2229_closure_analysis/move_closure.rs b/src/test/ui/closures/2229_closure_analysis/move_closure.rs index 76874e03dc02a..3b284eadbd0e3 100644 --- a/src/test/ui/closures/2229_closure_analysis/move_closure.rs +++ b/src/test/ui/closures/2229_closure_analysis/move_closure.rs @@ -16,7 +16,7 @@ fn simple_move_closure() { //~^ ERROR: First Pass analysis includes: //~| ERROR: Min Capture analysis includes: t.0.0 = "new S".into(); - //~^ NOTE: Capturing t[(0, 0),(0, 0)] -> ByValue + //~^ NOTE: Capturing t[(0, 0),(0, 0)] -> MutBorrow //~| NOTE: Min Capture t[(0, 0),(0, 0)] -> ByValue }; c(); @@ -78,7 +78,7 @@ fn struct_contains_ref_to_another_struct_2() { //~^ ERROR: First Pass analysis includes: //~| ERROR: Min Capture analysis includes: let _t = t.0.0; - //~^ NOTE: Capturing t[(0, 0),Deref] -> ImmBorrow + //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow //~| NOTE: Min Capture t[(0, 0),Deref] -> ImmBorrow }; @@ -100,8 +100,7 @@ fn struct_contains_ref_to_another_struct_3() { //~^ ERROR: First Pass analysis includes: //~| ERROR: Min Capture analysis includes: let _t = t.0.0; - //~^ NOTE: Capturing t[(0, 0),Deref] -> ImmBorrow - //~| NOTE: Capturing t[(0, 0)] -> ByValue + //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue //~| NOTE: Min Capture t[(0, 0)] -> ByValue }; @@ -122,8 +121,7 @@ fn truncate_box_derefs() { //~^ ERROR: First Pass analysis includes: //~| ERROR: Min Capture analysis includes: let _t = b.0; - //~^ NOTE: Capturing b[Deref,(0, 0)] -> ByValue - //~| NOTE: Capturing b[] -> ByValue + //~^ NOTE: Capturing b[Deref,(0, 0)] -> ImmBorrow //~| NOTE: Min Capture b[] -> ByValue }; @@ -139,7 +137,7 @@ fn truncate_box_derefs() { //~^ ERROR: First Pass analysis includes: //~| ERROR: Min Capture analysis includes: println!("{}", b.0); - //~^ NOTE: Capturing b[Deref,(0, 0)] -> ByValue + //~^ NOTE: Capturing b[Deref,(0, 0)] -> ImmBorrow //~| NOTE: Min Capture b[] -> ByValue }; @@ -156,11 +154,47 @@ fn truncate_box_derefs() { //~^ ERROR: First Pass analysis includes: //~| ERROR: Min Capture analysis includes: println!("{}", t.1.0); - //~^ NOTE: Capturing t[(1, 0),Deref,(0, 0)] -> ByValue + //~^ NOTE: Capturing t[(1, 0),Deref,(0, 0)] -> ImmBorrow //~| NOTE: Min Capture t[(1, 0)] -> ByValue }; } +struct Foo { x: i32 } + +// Ensure that even in move closures, if the data is not owned by the root variable +// then we don't truncate the derefs or a ByValue capture, rather do a reborrow +fn box_mut_1() { + let mut foo = Foo { x: 0 } ; + + let p_foo = &mut foo; + let box_p_foo = Box::new(p_foo); + + let c = #[rustc_capture_analysis] move || box_p_foo.x += 10; + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + //~| First Pass analysis includes: + //~| NOTE: Capturing box_p_foo[Deref,Deref,(0, 0)] -> UniqueImmBorrow + //~| Min Capture analysis includes: + //~| NOTE: Min Capture box_p_foo[Deref,Deref,(0, 0)] -> UniqueImmBorrow +} + +// Ensure that even in move closures, if the data is not owned by the root variable +// then we don't truncate the derefs or a ByValue capture, rather do a reborrow +fn box_mut_2() { + let foo = Foo { x: 0 } ; + + let mut box_foo = Box::new(foo); + let p_foo = &mut box_foo; + + let c = #[rustc_capture_analysis] move || p_foo.x += 10; + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + //~| First Pass analysis includes: + //~| NOTE: Capturing p_foo[Deref,Deref,(0, 0)] -> UniqueImmBorrow + //~| Min Capture analysis includes: + //~| NOTE: Min Capture p_foo[Deref,Deref,(0, 0)] -> UniqueImmBorrow +} + fn main() { simple_move_closure(); simple_ref(); @@ -168,4 +202,6 @@ fn main() { struct_contains_ref_to_another_struct_2(); struct_contains_ref_to_another_struct_3(); truncate_box_derefs(); + box_mut_2(); + box_mut_1(); } diff --git a/src/test/ui/closures/2229_closure_analysis/move_closure.stderr b/src/test/ui/closures/2229_closure_analysis/move_closure.stderr index b35aadfcbd419..c8e2708feee31 100644 --- a/src/test/ui/closures/2229_closure_analysis/move_closure.stderr +++ b/src/test/ui/closures/2229_closure_analysis/move_closure.stderr @@ -44,7 +44,7 @@ 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:118:13 + --> $DIR/move_closure.rs:117:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,7 +53,7 @@ LL | let 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:135:13 + --> $DIR/move_closure.rs:133:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,7 +62,7 @@ LL | let 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:152:13 + --> $DIR/move_closure.rs:150:13 | LL | let c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -70,6 +70,24 @@ LL | let c = #[rustc_capture_analysis] = note: see issue #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:172:13 + | +LL | let c = #[rustc_capture_analysis] move || box_p_foo.x += 10; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #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:189:13 + | +LL | let c = #[rustc_capture_analysis] move || p_foo.x += 10; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #15701 for more information + = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable + error: First Pass analysis includes: --> $DIR/move_closure.rs:15:5 | @@ -82,7 +100,7 @@ LL | | LL | | }; | |_____^ | -note: Capturing t[(0, 0),(0, 0)] -> ByValue +note: Capturing t[(0, 0),(0, 0)] -> MutBorrow --> $DIR/move_closure.rs:18:9 | LL | t.0.0 = "new S".into(); @@ -190,7 +208,7 @@ LL | | LL | | }; | |_____^ | -note: Capturing t[(0, 0),Deref] -> ImmBorrow +note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow --> $DIR/move_closure.rs:80:18 | LL | let _t = t.0.0; @@ -221,17 +239,12 @@ LL | / move || { LL | | LL | | LL | | let _t = t.0.0; -... | +LL | | LL | | LL | | }; | |_____^ | -note: Capturing t[(0, 0),Deref] -> ImmBorrow - --> $DIR/move_closure.rs:102:18 - | -LL | let _t = t.0.0; - | ^^^^^ -note: Capturing t[(0, 0)] -> ByValue +note: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue --> $DIR/move_closure.rs:102:18 | LL | let _t = t.0.0; @@ -244,7 +257,7 @@ LL | / move || { LL | | LL | | LL | | let _t = t.0.0; -... | +LL | | LL | | LL | | }; | |_____^ @@ -256,48 +269,43 @@ LL | let _t = t.0.0; | ^^^^^ error: First Pass analysis includes: - --> $DIR/move_closure.rs:121:5 + --> $DIR/move_closure.rs:120:5 | LL | / move || { LL | | LL | | LL | | let _t = b.0; -... | +LL | | LL | | LL | | }; | |_____^ | -note: Capturing b[Deref,(0, 0)] -> ByValue - --> $DIR/move_closure.rs:124:18 - | -LL | let _t = b.0; - | ^^^ -note: Capturing b[] -> ByValue - --> $DIR/move_closure.rs:124:18 +note: Capturing b[Deref,(0, 0)] -> ImmBorrow + --> $DIR/move_closure.rs:123:18 | LL | let _t = b.0; | ^^^ error: Min Capture analysis includes: - --> $DIR/move_closure.rs:121:5 + --> $DIR/move_closure.rs:120:5 | LL | / move || { LL | | LL | | LL | | let _t = b.0; -... | +LL | | LL | | LL | | }; | |_____^ | note: Min Capture b[] -> ByValue - --> $DIR/move_closure.rs:124:18 + --> $DIR/move_closure.rs:123:18 | LL | let _t = b.0; | ^^^ error: First Pass analysis includes: - --> $DIR/move_closure.rs:138:5 + --> $DIR/move_closure.rs:136:5 | LL | / move || { LL | | @@ -308,14 +316,14 @@ LL | | LL | | }; | |_____^ | -note: Capturing b[Deref,(0, 0)] -> ByValue - --> $DIR/move_closure.rs:141:24 +note: Capturing b[Deref,(0, 0)] -> ImmBorrow + --> $DIR/move_closure.rs:139:24 | LL | println!("{}", b.0); | ^^^ error: Min Capture analysis includes: - --> $DIR/move_closure.rs:138:5 + --> $DIR/move_closure.rs:136:5 | LL | / move || { LL | | @@ -327,13 +335,13 @@ LL | | }; | |_____^ | note: Min Capture b[] -> ByValue - --> $DIR/move_closure.rs:141:24 + --> $DIR/move_closure.rs:139:24 | LL | println!("{}", b.0); | ^^^ error: First Pass analysis includes: - --> $DIR/move_closure.rs:155:5 + --> $DIR/move_closure.rs:153:5 | LL | / move || { LL | | @@ -344,14 +352,14 @@ LL | | LL | | }; | |_____^ | -note: Capturing t[(1, 0),Deref,(0, 0)] -> ByValue - --> $DIR/move_closure.rs:158:24 +note: Capturing t[(1, 0),Deref,(0, 0)] -> ImmBorrow + --> $DIR/move_closure.rs:156:24 | LL | println!("{}", t.1.0); | ^^^^^ error: Min Capture analysis includes: - --> $DIR/move_closure.rs:155:5 + --> $DIR/move_closure.rs:153:5 | LL | / move || { LL | | @@ -363,11 +371,59 @@ LL | | }; | |_____^ | note: Min Capture t[(1, 0)] -> ByValue - --> $DIR/move_closure.rs:158:24 + --> $DIR/move_closure.rs:156:24 | LL | println!("{}", t.1.0); | ^^^^^ -error: aborting due to 24 previous errors +error: First Pass analysis includes: + --> $DIR/move_closure.rs:172:39 + | +LL | let c = #[rustc_capture_analysis] move || box_p_foo.x += 10; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: Capturing box_p_foo[Deref,Deref,(0, 0)] -> UniqueImmBorrow + --> $DIR/move_closure.rs:172:47 + | +LL | let c = #[rustc_capture_analysis] move || box_p_foo.x += 10; + | ^^^^^^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/move_closure.rs:172:39 + | +LL | let c = #[rustc_capture_analysis] move || box_p_foo.x += 10; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: Min Capture box_p_foo[Deref,Deref,(0, 0)] -> UniqueImmBorrow + --> $DIR/move_closure.rs:172:47 + | +LL | let c = #[rustc_capture_analysis] move || box_p_foo.x += 10; + | ^^^^^^^^^^^ + +error: First Pass analysis includes: + --> $DIR/move_closure.rs:189:39 + | +LL | let c = #[rustc_capture_analysis] move || p_foo.x += 10; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: Capturing p_foo[Deref,Deref,(0, 0)] -> UniqueImmBorrow + --> $DIR/move_closure.rs:189:47 + | +LL | let c = #[rustc_capture_analysis] move || p_foo.x += 10; + | ^^^^^^^ + +error: Min Capture analysis includes: + --> $DIR/move_closure.rs:189:39 + | +LL | let c = #[rustc_capture_analysis] move || p_foo.x += 10; + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: Min Capture p_foo[Deref,Deref,(0, 0)] -> UniqueImmBorrow + --> $DIR/move_closure.rs:189:47 + | +LL | let c = #[rustc_capture_analysis] move || p_foo.x += 10; + | ^^^^^^^ + +error: aborting due to 30 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.rs b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.rs index 37a2a97d44279..e7edc0bbce39d 100644 --- a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.rs +++ b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.rs @@ -22,7 +22,7 @@ fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static { //~| NOTE: see issue #15701 //~| ERROR: First Pass analysis includes: //~| ERROR: Min Capture analysis includes: - //~| NOTE: Capturing m[Deref,(0, 0),Deref] -> ImmBorrow + //~| NOTE: Capturing m[Deref,(0, 0),Deref,(0, 0)] -> ImmBorrow //~| NOTE: Min Capture m[Deref,(0, 0),Deref] -> ImmBorrow c } diff --git a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.stderr b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.stderr index b727c06d9528f..87d5d5bee07d8 100644 --- a/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.stderr +++ b/src/test/ui/closures/2229_closure_analysis/optimization/edge_case.stderr @@ -13,7 +13,7 @@ error: First Pass analysis includes: LL | let c = #[rustc_capture_analysis] || drop(&m.a.0); | ^^^^^^^^^^^^^^^ | -note: Capturing m[Deref,(0, 0),Deref] -> ImmBorrow +note: Capturing m[Deref,(0, 0),Deref,(0, 0)] -> ImmBorrow --> $DIR/edge_case.rs:20:48 | LL | let c = #[rustc_capture_analysis] || drop(&m.a.0);