From 1b86ad8485a1937ecd16b2dd5d9c5feb4eac93ec Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Thu, 11 Feb 2021 19:36:05 -0500 Subject: [PATCH] Treat read of COpy types via refs as not move in move-closure --- compiler/rustc_typeck/src/check/upvar.rs | 9 +- .../2229_closure_analysis/move_closure.rs | 93 +++++++-- .../2229_closure_analysis/move_closure.stderr | 189 ++++++++++++++++-- 3 files changed, 256 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 12eacdf25e2f1..fbbc1fba8771f 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -1116,14 +1116,21 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { place_with_id, diag_expr_id, mode ); - let place = truncate_capture_for_move(place_with_id.place.clone()); 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) => {} }; + 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) { 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 d57c2280438ba..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; @@ -24,8 +42,8 @@ fn simple_ref() { 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); @@ -46,27 +64,78 @@ fn struct_contains_ref_to_another_struct() { 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 554dc11f6badb..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,6 +67,42 @@ error: First Pass analysis includes: LL | / move || { LL | | LL | | +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: 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 | | @@ -47,13 +110,13 @@ LL | | }; | |_____^ | note: Capturing ref_s[Deref] -> UniqueImmBorrow - --> $DIR/move_closure.rs:20:9 + --> $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 | | @@ -65,13 +128,13 @@ LL | | }; | |_____^ | note: Min Capture ref_s[Deref] -> UniqueImmBorrow - --> $DIR/move_closure.rs:20:9 + --> $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 | | @@ -83,13 +146,13 @@ LL | | }; | |_____^ | note: Capturing t[(0, 0),Deref,(0, 0)] -> UniqueImmBorrow - --> $DIR/move_closure.rs:41:9 + --> $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 | | @@ -101,47 +164,129 @@ LL | | }; | |_____^ | note: Min Capture t[(0, 0),Deref,(0, 0)] -> UniqueImmBorrow - --> $DIR/move_closure.rs:41:9 + --> $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`.