}
};
+ // 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 {
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!(
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.
--- /dev/null
+// 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() {}
--- /dev/null
+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`.
--- /dev/null
+// 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() {}