]> git.lizzy.rs Git - rust.git/commitdiff
2229: Don't move out of drop type
authorAman Arora <me@aman-arora.com>
Wed, 1 Sep 2021 07:00:50 +0000 (03:00 -0400)
committerAman Arora <me@aman-arora.com>
Fri, 3 Sep 2021 08:38:28 +0000 (04:38 -0400)
compiler/rustc_typeck/src/check/upvar.rs
src/test/ui/closures/2229_closure_analysis/issue-88476.rs [new file with mode: 0644]
src/test/ui/closures/2229_closure_analysis/issue-88476.stderr [new file with mode: 0644]
src/test/ui/closures/2229_closure_analysis/run_pass/issue-88476.rs [new file with mode: 0644]

index a25d0f8064404783d96d6912cbc90e2980136c59..2758b23a762f89e507aafd1fcb0861a0d80125cd 100644 (file)
@@ -399,7 +399,14 @@ fn process_collected_capture_information(
                 }
             };
 
+            // This restriction needs to be applied after we have handled adjustments for `move`
+            // closures. We want to make sure any adjustment that might make us move the place into
+            // the closure gets handled.
+            let (place, capture_kind) =
+                restrict_precision_for_drop_types(self, place, capture_kind, usage_span);
+
             capture_info.capture_kind = capture_kind;
+
             let capture_info = if let Some(existing) = processed.get(&place) {
                 determine_capture_info(*existing, capture_info)
             } else {
@@ -626,7 +633,7 @@ fn perform_2229_migration_anaysis(
             self.tcx.struct_span_lint_hir(
                 lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES,
                 closure_hir_id,
-                closure_head_span,
+                 closure_head_span,
                 |lint| {
                     let mut diagnostics_builder = lint.build(
                         format!(
@@ -1835,6 +1842,31 @@ fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::H
         self.borrow(assignee_place, diag_expr_id, ty::BorrowKind::MutBorrow);
     }
 }
+
+/// Rust doesn't permit moving fields out of a type that implements drop
+fn restrict_precision_for_drop_types<'a, 'tcx>(
+    fcx: &'a FnCtxt<'a, 'tcx>,
+    mut place: Place<'tcx>,
+    mut curr_mode: ty::UpvarCapture<'tcx>,
+    span: Span,
+) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) {
+    let is_copy_type = fcx.infcx.type_is_copy_modulo_regions(fcx.param_env, place.ty(), span);
+
+    if let (false, UpvarCapture::ByValue(..)) = (is_copy_type, curr_mode) {
+        for i in 0..place.projections.len() {
+            match place.ty_before_projection(i).kind() {
+                ty::Adt(def, _) if def.destructor(fcx.tcx).is_some() => {
+                    truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_mode, i);
+                    break;
+                }
+                _ => {}
+            }
+        }
+    }
+
+    (place, curr_mode)
+}
+
 /// Truncate `place` so that an `unsafe` block isn't required to capture it.
 /// - No projections are applied to raw pointers, since these require unsafe blocks. We capture
 ///   them completely.
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-88476.rs b/src/test/ui/closures/2229_closure_analysis/issue-88476.rs
new file mode 100644 (file)
index 0000000..f5906d3
--- /dev/null
@@ -0,0 +1,62 @@
+// edition:2021
+
+#![feature(rustc_attrs)]
+
+// Test that we can't move out of struct that impls `Drop`.
+
+
+use std::rc::Rc;
+
+// Test that we restrict precision when moving not-`Copy` types, if any of the parent paths
+// implement `Drop`. This is to ensure that we don't move out of a type that implements Drop.
+pub fn test1() {
+    struct Foo(Rc<i32>);
+
+    impl Drop for Foo {
+        fn drop(self: &mut Foo) {}
+    }
+
+    let f = Foo(Rc::new(1));
+    let x = #[rustc_capture_analysis] move || {
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    //~| ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        println!("{:?}", f.0);
+        //~^ NOTE: Capturing f[(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture f[] -> ByValue
+    };
+
+    x();
+}
+
+// Test that we don't restrict precision when moving `Copy` types(i.e. when copying),
+// even if any of the parent paths implement `Drop`.
+fn test2() {
+    struct Character {
+        hp: u32,
+        name: String,
+    }
+
+    impl Drop for Character {
+        fn drop(&mut self) {}
+    }
+
+    let character = Character { hp: 100, name: format!("A") };
+
+    let c = #[rustc_capture_analysis] move || {
+    //~^ ERROR: attributes on expressions are experimental
+    //~| NOTE: see issue #15701 <https://github.com/rust-lang/rust/issues/15701>
+    //~| ERROR: First Pass analysis includes:
+    //~| ERROR: Min Capture analysis includes:
+        println!("{}", character.hp)
+        //~^ NOTE: Capturing character[(0, 0)] -> ImmBorrow
+        //~| NOTE: Min Capture character[(0, 0)] -> ByValue
+    };
+
+    c();
+
+    println!("{}", character.name);
+}
+
+fn main() {}
diff --git a/src/test/ui/closures/2229_closure_analysis/issue-88476.stderr b/src/test/ui/closures/2229_closure_analysis/issue-88476.stderr
new file mode 100644 (file)
index 0000000..c7c9ecb
--- /dev/null
@@ -0,0 +1,97 @@
+error[E0658]: attributes on expressions are experimental
+  --> $DIR/issue-88476.rs:20:13
+   |
+LL |     let x = #[rustc_capture_analysis] move || {
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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/issue-88476.rs:47:13
+   |
+LL |     let c = #[rustc_capture_analysis] move || {
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = 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: First Pass analysis includes:
+  --> $DIR/issue-88476.rs:20:39
+   |
+LL |       let x = #[rustc_capture_analysis] move || {
+   |  _______________________________________^
+LL | |
+LL | |
+LL | |
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing f[(0, 0)] -> ImmBorrow
+  --> $DIR/issue-88476.rs:25:26
+   |
+LL |         println!("{:?}", f.0);
+   |                          ^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/issue-88476.rs:20:39
+   |
+LL |       let x = #[rustc_capture_analysis] move || {
+   |  _______________________________________^
+LL | |
+LL | |
+LL | |
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture f[] -> ByValue
+  --> $DIR/issue-88476.rs:25:26
+   |
+LL |         println!("{:?}", f.0);
+   |                          ^^^
+
+error: First Pass analysis includes:
+  --> $DIR/issue-88476.rs:47:39
+   |
+LL |       let c = #[rustc_capture_analysis] move || {
+   |  _______________________________________^
+LL | |
+LL | |
+LL | |
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Capturing character[(0, 0)] -> ImmBorrow
+  --> $DIR/issue-88476.rs:52:24
+   |
+LL |         println!("{}", character.hp)
+   |                        ^^^^^^^^^^^^
+
+error: Min Capture analysis includes:
+  --> $DIR/issue-88476.rs:47:39
+   |
+LL |       let c = #[rustc_capture_analysis] move || {
+   |  _______________________________________^
+LL | |
+LL | |
+LL | |
+...  |
+LL | |
+LL | |     };
+   | |_____^
+   |
+note: Min Capture character[(0, 0)] -> ByValue
+  --> $DIR/issue-88476.rs:52:24
+   |
+LL |         println!("{}", character.hp)
+   |                        ^^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/issue-88476.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/issue-88476.rs
new file mode 100644 (file)
index 0000000..f44c2af
--- /dev/null
@@ -0,0 +1,47 @@
+// check-pass
+// edition:2021
+
+use std::rc::Rc;
+
+// Test that we restrict precision when moving not-`Copy` types, if any of the parent paths
+// implement `Drop`. This is to ensure that we don't move out of a type that implements Drop.
+pub fn test1() {
+    struct Foo(Rc<i32>);
+
+    impl Drop for Foo {
+        fn drop(self: &mut Foo) {}
+    }
+
+    let f = Foo(Rc::new(1));
+    let x = move || {
+        println!("{:?}", f.0);
+    };
+
+    x();
+}
+
+
+// Test that we don't restrict precision when moving `Copy` types(i.e. when copying),
+// even if any of the parent paths implement `Drop`.
+pub fn test2() {
+    struct Character {
+        hp: u32,
+        name: String,
+    }
+
+    impl Drop for Character {
+        fn drop(&mut self) {}
+    }
+
+    let character = Character { hp: 100, name: format!("A") };
+
+    let c = move || {
+        println!("{}", character.hp)
+    };
+
+    c();
+
+    println!("{}", character.name);
+}
+
+fn main() {}