diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index d097f863ad2f9..69c09528662d3 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -180,7 +180,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("seed place {:?}", place); let upvar_id = ty::UpvarId::new(*var_hir_id, local_def_id); - let capture_kind = self.init_capture_kind(capture_clause, upvar_id, span); + let capture_kind = + self.init_capture_kind_for_place(&place, capture_clause, upvar_id, span); let fake_info = ty::CaptureInfo { capture_kind_expr_id: None, path_expr_id: None, @@ -205,11 +206,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we have an origin, store it. if let Some(origin) = delegate.current_origin.clone() { let origin = if self.tcx.features().capture_disjoint_fields { - origin + (origin.0, restrict_capture_precision(origin.1)) } else { - // FIXME(project-rfc-2229#31): Once the changes to support reborrowing are - // made, make sure we are selecting and restricting - // the origin correctly. (origin.0, Place { projections: vec![], ..origin.1 }) }; @@ -449,7 +447,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { base => bug!("Expected upvar, found={:?}", base), }; - let place = restrict_capture_precision(place, capture_info.capture_kind); + let place = restrict_capture_precision(place); let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) { None => { @@ -897,15 +895,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn init_capture_kind( + fn init_capture_kind_for_place( &self, + place: &Place<'tcx>, capture_clause: hir::CaptureBy, upvar_id: ty::UpvarId, closure_span: Span, ) -> ty::UpvarCapture<'tcx> { match capture_clause { - hir::CaptureBy::Value => ty::UpvarCapture::ByValue(None), - hir::CaptureBy::Ref => { + // In case of a move closure if the data is accessed through a reference we + // want to capture by ref to allow precise capture using reborrows. + // + // If the data will be moved out of this place, then the place will be truncated + // at the first Deref in `adjust_upvar_borrow_kind_for_consume` and then moved into + // the closure. + hir::CaptureBy::Value if !place.deref_tys().any(ty::TyS::is_ref) => { + ty::UpvarCapture::ByValue(None) + } + hir::CaptureBy::Value | hir::CaptureBy::Ref => { let origin = UpvarRegion(upvar_id, closure_span); let upvar_region = self.next_region_var(origin); let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, region: upvar_region }; @@ -1109,12 +1116,25 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { place_with_id, diag_expr_id, mode ); - // we only care about moves - match mode { - euv::Copy => { + 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; } - euv::Move => {} + + (hir::CaptureBy::Ref, euv::Move) | (hir::CaptureBy::Value, euv::Move | euv::Copy) => {} + }; + + 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; @@ -1128,13 +1148,15 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { let usage_span = tcx.hir().span(diag_expr_id); - // 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_with_id.place.clone(), - ); + 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), @@ -1317,8 +1339,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(self.capture_clause, upvar_id, self.closure_span); + let capture_kind = self.fcx.init_capture_kind_for_place( + &place_with_id.place, + self.capture_clause, + upvar_id, + self.closure_span, + ); let expr_id = Some(diag_expr_id); let capture_info = ty::CaptureInfo { @@ -1392,15 +1418,10 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { } /// Truncate projections so that following rules are obeyed by the captured `place`: -/// -/// - No Derefs in move closure, this will result in value behind a reference getting moved. /// - 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. -fn restrict_capture_precision<'tcx>( - mut place: Place<'tcx>, - capture_kind: ty::UpvarCapture<'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; @@ -1412,7 +1433,6 @@ fn restrict_capture_precision<'tcx>( } let mut truncated_length = usize::MAX; - let mut first_deref_projection = usize::MAX; for (i, proj) in place.projections.iter().enumerate() { if proj.ty.is_unsafe_ptr() { @@ -1426,31 +1446,30 @@ fn restrict_capture_precision<'tcx>( truncated_length = truncated_length.min(i); break; } - 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. - first_deref_projection = first_deref_projection.min(i); - } + ProjectionKind::Deref => {} ProjectionKind::Field(..) => {} // ignore ProjectionKind::Subslice => {} // We never capture this } } - let length = place - .projections - .len() - .min(truncated_length) - // In case of capture `ByValue` we want to not capture derefs - .min(match capture_kind { - ty::UpvarCapture::ByValue(..) => first_deref_projection, - ty::UpvarCapture::ByRef(..) => usize::MAX, - }); + let length = place.projections.len().min(truncated_length); place.projections.truncate(length); 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); + } + + place +} + fn construct_place_string(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String { let variable_name = match place.base { PlaceBase::Upvar(upvar_id) => var_name(tcx, upvar_id.var_path.hir_id).to_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 1007fb582e5ed..27c8fb1363f17 100644 --- a/src/test/ui/closures/2229_closure_analysis/by_value.rs +++ b/src/test/ui/closures/2229_closure_analysis/by_value.rs @@ -26,7 +26,8 @@ fn big_box() { //~^ First Pass analysis includes: //~| Min Capture analysis includes: let p = t.0.0; - //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue + //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow + //~| NOTE: Capturing t[(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 fe04dbef6d8b5..944e4c40a78ef 100644 --- a/src/test/ui/closures/2229_closure_analysis/by_value.stderr +++ b/src/test/ui/closures/2229_closure_analysis/by_value.stderr @@ -28,13 +28,18 @@ LL | | LL | | }; | |_____^ | -note: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue +note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow + --> $DIR/by_value.rs:28:17 + | +LL | let p = t.0.0; + | ^^^^^ +note: Capturing t[(0, 0)] -> ByValue --> $DIR/by_value.rs:28:17 | LL | let p = t.0.0; | ^^^^^ note: Capturing t[(1, 0)] -> ImmBorrow - --> $DIR/by_value.rs:31:29 + --> $DIR/by_value.rs:32:29 | LL | println!("{} {:?}", t.1, p); | ^^^ @@ -57,7 +62,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:31:29 + --> $DIR/by_value.rs:32:29 | LL | println!("{} {:?}", t.1, p); | ^^^ diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.rs b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.rs new file mode 100644 index 0000000000000..cd7c25620a7c4 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.rs @@ -0,0 +1,16 @@ +#![feature(capture_disjoint_fields)] +//~^ WARNING: the feature `capture_disjoint_fields` is incomplete +//~| `#[warn(incomplete_features)]` on by default +//~| see issue #53488 + +// Test that array access is not stored as part of closure kind origin + +fn expect_fn(_f: F) {} + +fn main() { + let s = [format!("s"), format!("s")]; + let c = || { //~ ERROR expected a closure that implements the `Fn` + let [_, _s] = s; + }; + expect_fn(c); +} diff --git a/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.stderr b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.stderr new file mode 100644 index 0000000000000..bd9428771f4c5 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/diagnostics/closure-origin-array-diagnostics.stderr @@ -0,0 +1,23 @@ +warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/closure-origin-array-diagnostics.rs:1:12 + | +LL | #![feature(capture_disjoint_fields)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #53488 for more information + +error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce` + --> $DIR/closure-origin-array-diagnostics.rs:12:13 + | +LL | let c = || { + | ^^ this closure implements `FnOnce`, not `Fn` +LL | let [_, _s] = s; + | - closure is `FnOnce` because it moves the variable `s` out of its environment +LL | }; +LL | expect_fn(c); + | --------- the requirement to implement `Fn` derives from here + +error: aborting due to previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0525`. 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 8bdc999ca3c3f..1c574da5f48bc 100644 --- a/src/test/ui/closures/2229_closure_analysis/move_closure.rs +++ b/src/test/ui/closures/2229_closure_analysis/move_closure.rs @@ -6,7 +6,25 @@ //~| NOTE: see issue #53488 #![feature(rustc_attrs)] -// Test we truncate derefs properly +fn simple_move_closure() { + struct S(String); + struct T(S); + + let t = T(S("s".into())); + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + move || { + //~^ 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: Min Capture t[(0, 0),(0, 0)] -> ByValue + }; + c(); +} + +// Test move closure use reborrows when using references fn simple_ref() { let mut s = 10; let ref_s = &mut s; @@ -18,14 +36,14 @@ fn simple_ref() { //~^ ERROR: First Pass analysis includes: //~| ERROR: Min Capture analysis includes: *ref_s += 10; - //~^ NOTE: Capturing ref_s[Deref] -> ByValue - //~| NOTE: Min Capture ref_s[] -> ByValue + //~^ NOTE: Capturing ref_s[Deref] -> UniqueImmBorrow + //~| NOTE: Min Capture ref_s[Deref] -> UniqueImmBorrow }; c(); } -// Test we truncate derefs properly -fn struct_contains_ref_to_another_struct() { +// Test move closure use reborrows when using references +fn struct_contains_ref_to_another_struct_1() { struct S(String); struct T<'a>(&'a mut S); @@ -39,34 +57,85 @@ fn struct_contains_ref_to_another_struct() { //~^ ERROR: First Pass analysis includes: //~| ERROR: Min Capture analysis includes: t.0.0 = "new s".into(); - //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue - //~| NOTE: Min Capture t[(0, 0)] -> ByValue + //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> UniqueImmBorrow + //~| NOTE: Min Capture t[(0, 0),Deref,(0, 0)] -> UniqueImmBorrow }; c(); } -// Test that we don't reduce precision when there is nothing deref. -fn no_ref() { +// Test that we can use reborrows to read data of Copy types +// i.e. without truncating derefs +fn struct_contains_ref_to_another_struct_2() { + struct S(i32); + struct T<'a>(&'a S); + + let s = S(0); + let t = T(&s); + + let mut c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + move || { + //~^ ERROR: First Pass analysis includes: + //~| ERROR: Min Capture analysis includes: + let _t = t.0.0; + //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow + //~| NOTE: Min Capture t[(0, 0),Deref,(0, 0)] -> ImmBorrow + }; + + c(); +} + +// Test that we can use truncate to move out of !Copy types +fn struct_contains_ref_to_another_struct_3() { struct S(String); - struct T(S); + struct T<'a>(&'a S); + + let s = S("s".into()); + let t = T(&s); - let t = T(S("s".into())); let mut c = #[rustc_capture_analysis] //~^ ERROR: attributes on expressions are experimental //~| NOTE: see issue #15701 move || { //~^ 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: Min Capture t[(0, 0),(0, 0)] -> ByValue + let _t = t.0.0; + //~^ NOTE: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow + //~| NOTE: Capturing t[(0, 0)] -> ByValue + //~| NOTE: Min Capture t[(0, 0)] -> ByValue }; + + c(); +} + +// Test that derefs of box are truncated in move closures +fn truncate_box_derefs() { + struct S(i32); + + let b = Box::new(S(10)); + + let c = #[rustc_capture_analysis] + //~^ ERROR: attributes on expressions are experimental + //~| NOTE: see issue #15701 + move || { + //~^ 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: Min Capture b[] -> ByValue + }; + c(); } fn main() { + simple_move_closure(); simple_ref(); - struct_contains_ref_to_another_struct(); - no_ref(); + struct_contains_ref_to_another_struct_1(); + struct_contains_ref_to_another_struct_2(); + struct_contains_ref_to_another_struct_3(); + truncate_box_derefs(); } 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 a745f14598ee2..b91ef4dd85c48 100644 --- a/src/test/ui/closures/2229_closure_analysis/move_closure.stderr +++ b/src/test/ui/closures/2229_closure_analysis/move_closure.stderr @@ -8,7 +8,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:35:17 + --> $DIR/move_closure.rs:32:17 | LL | let mut c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,7 +17,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:55:17 + --> $DIR/move_closure.rs:53:17 | LL | let mut c = #[rustc_capture_analysis] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,6 +25,33 @@ LL | let mut 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:76:17 + | +LL | let mut 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:98:17 + | +LL | let mut 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:119:13 + | +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 + warning: the feature `capture_disjoint_fields` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/move_closure.rs:3:12 | @@ -40,20 +67,56 @@ error: First Pass analysis includes: LL | / move || { LL | | LL | | -LL | | *ref_s += 10; +LL | | t.0.0 = "new S".into(); +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0),(0, 0)] -> ByValue + --> $DIR/move_closure.rs:20:9 + | +LL | t.0.0 = "new S".into(); + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/move_closure.rs:17:5 + | +LL | / move || { +LL | | +LL | | +LL | | t.0.0 = "new S".into(); LL | | LL | | LL | | }; | |_____^ | -note: Capturing ref_s[Deref] -> ByValue +note: Min Capture t[(0, 0),(0, 0)] -> ByValue --> $DIR/move_closure.rs:20:9 | +LL | t.0.0 = "new S".into(); + | ^^^^^ + +error: First Pass analysis includes: + --> $DIR/move_closure.rs:35:5 + | +LL | / move || { +LL | | +LL | | +LL | | *ref_s += 10; +LL | | +LL | | +LL | | }; + | |_____^ + | +note: Capturing ref_s[Deref] -> UniqueImmBorrow + --> $DIR/move_closure.rs:38:9 + | LL | *ref_s += 10; | ^^^^^^ error: Min Capture analysis includes: - --> $DIR/move_closure.rs:17:5 + --> $DIR/move_closure.rs:35:5 | LL | / move || { LL | | @@ -64,14 +127,14 @@ LL | | LL | | }; | |_____^ | -note: Min Capture ref_s[] -> ByValue - --> $DIR/move_closure.rs:20:9 +note: Min Capture ref_s[Deref] -> UniqueImmBorrow + --> $DIR/move_closure.rs:38:9 | LL | *ref_s += 10; | ^^^^^^ error: First Pass analysis includes: - --> $DIR/move_closure.rs:38:5 + --> $DIR/move_closure.rs:56:5 | LL | / move || { LL | | @@ -82,14 +145,14 @@ LL | | LL | | }; | |_____^ | -note: Capturing t[(0, 0),Deref,(0, 0)] -> ByValue - --> $DIR/move_closure.rs:41:9 +note: Capturing t[(0, 0),Deref,(0, 0)] -> UniqueImmBorrow + --> $DIR/move_closure.rs:59:9 | LL | t.0.0 = "new s".into(); | ^^^^^ error: Min Capture analysis includes: - --> $DIR/move_closure.rs:38:5 + --> $DIR/move_closure.rs:56:5 | LL | / move || { LL | | @@ -100,48 +163,130 @@ LL | | LL | | }; | |_____^ | -note: Min Capture t[(0, 0)] -> ByValue - --> $DIR/move_closure.rs:41:9 +note: Min Capture t[(0, 0),Deref,(0, 0)] -> UniqueImmBorrow + --> $DIR/move_closure.rs:59:9 | LL | t.0.0 = "new s".into(); | ^^^^^ error: First Pass analysis includes: - --> $DIR/move_closure.rs:58:5 + --> $DIR/move_closure.rs:79:5 | LL | / move || { LL | | LL | | -LL | | t.0.0 = "new S".into(); +LL | | let _t = t.0.0; LL | | LL | | LL | | }; | |_____^ | -note: Capturing t[(0, 0),(0, 0)] -> ByValue - --> $DIR/move_closure.rs:61:9 +note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow + --> $DIR/move_closure.rs:82:18 | -LL | t.0.0 = "new S".into(); - | ^^^^^ +LL | let _t = t.0.0; + | ^^^^^ error: Min Capture analysis includes: - --> $DIR/move_closure.rs:58:5 + --> $DIR/move_closure.rs:79:5 | LL | / move || { LL | | LL | | -LL | | t.0.0 = "new S".into(); +LL | | let _t = t.0.0; LL | | LL | | LL | | }; | |_____^ | -note: Min Capture t[(0, 0),(0, 0)] -> ByValue - --> $DIR/move_closure.rs:61:9 +note: Min Capture t[(0, 0),Deref,(0, 0)] -> ImmBorrow + --> $DIR/move_closure.rs:82:18 | -LL | t.0.0 = "new S".into(); - | ^^^^^ +LL | let _t = t.0.0; + | ^^^^^ + +error: First Pass analysis includes: + --> $DIR/move_closure.rs:101:5 + | +LL | / move || { +LL | | +LL | | +LL | | let _t = t.0.0; +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing t[(0, 0),Deref,(0, 0)] -> ImmBorrow + --> $DIR/move_closure.rs:104:18 + | +LL | let _t = t.0.0; + | ^^^^^ +note: Capturing t[(0, 0)] -> ByValue + --> $DIR/move_closure.rs:104:18 + | +LL | let _t = t.0.0; + | ^^^^^ + +error: Min Capture analysis includes: + --> $DIR/move_closure.rs:101:5 + | +LL | / move || { +LL | | +LL | | +LL | | let _t = t.0.0; +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture t[(0, 0)] -> ByValue + --> $DIR/move_closure.rs:104:18 + | +LL | let _t = t.0.0; + | ^^^^^ + +error: First Pass analysis includes: + --> $DIR/move_closure.rs:122:5 + | +LL | / move || { +LL | | +LL | | +LL | | let _t = b.0; +... | +LL | | +LL | | }; + | |_____^ + | +note: Capturing b[Deref,(0, 0)] -> ByValue + --> $DIR/move_closure.rs:125:18 + | +LL | let _t = b.0; + | ^^^ +note: Capturing b[] -> ByValue + --> $DIR/move_closure.rs:125:18 + | +LL | let _t = b.0; + | ^^^ + +error: Min Capture analysis includes: + --> $DIR/move_closure.rs:122:5 + | +LL | / move || { +LL | | +LL | | +LL | | let _t = b.0; +... | +LL | | +LL | | }; + | |_____^ + | +note: Min Capture b[] -> ByValue + --> $DIR/move_closure.rs:125:18 + | +LL | let _t = b.0; + | ^^^ -error: aborting due to 9 previous errors; 1 warning emitted +error: aborting due to 18 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs index 4007a5a48aaec..afaafbda01877 100644 --- a/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs +++ b/src/test/ui/closures/2229_closure_analysis/run_pass/move_closure.rs @@ -56,9 +56,50 @@ fn no_ref_nested() { c(); } +struct A<'a>(&'a mut String, &'a mut String); +// Test that reborrowing works as expected for move closures +// by attempting a disjoint capture through a reference. +fn disjoint_via_ref() { + let mut x = String::new(); + let mut y = String::new(); + + let mut a = A(&mut x, &mut y); + let a = &mut a; + + let mut c1 = move || { + a.0.truncate(0); + }; + + let mut c2 = move || { + a.1.truncate(0); + }; + + c1(); + c2(); +} + +// Test that even if a path is moved into the closure, the closure is not FnOnce +// if the path is not moved by the closure call. +fn data_moved_but_not_fn_once() { + let x = Box::new(10i32); + + let c = move || { + // *x has type i32 which is Copy. So even though the box `x` will be moved + // into the closure, `x` is never moved when the closure is called, i.e. the + // ownership stays with the closure and therefore we can call the function multiple times. + let _x = *x; + }; + + c(); + c(); +} + fn main() { simple_ref(); struct_contains_ref_to_another_struct(); no_ref(); no_ref_nested(); + + disjoint_via_ref(); + data_moved_but_not_fn_once(); }