From 7c1c2541a46ede62cd248a2f4a977c6068c40b63 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Fri, 23 Oct 2026 12:42:46 -0400 Subject: [PATCH 1/3] Don't allow dereferencing dangling refs inside `PlaceMention` --- compiler/rustc_borrowck/src/lib.rs | 25 +++++- tests/ui/borrowck/placemention-deref.rs | 33 ++++++++ tests/ui/borrowck/placemention-deref.stderr | 86 +++++++++++++++++++++ 3 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 tests/ui/borrowck/placemention-deref.rs create mode 100644 tests/ui/borrowck/placemention-deref.stderr diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 7e1ba1fad72c4..0cac67af0b3ad 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -851,8 +851,29 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, }, // Only relevant for mir typeck StatementKind::AscribeUserType(..) => {} - // Only relevant for liveness and unsafeck - StatementKind::PlaceMention(..) => {} + // Only relevant for liveness and unsafeck, + // except for dereferences which must be materialized + StatementKind::PlaceMention(place) => { + if let Some((idx, _)) = place + .projection + .iter() + .enumerate() + .rev() + .find(|(_, proj)| matches!(proj.kind(), ProjectionKind::Deref)) + { + let truncated_projections = place.projection[..idx].to_vec(); + let truncated_place = Place { + local: place.local, + projection: self.infcx.tcx.mk_place_elems(&truncated_projections), + }; + self.check_if_full_path_is_moved( + location, + InitializationRequiringAction::Use, + (truncated_place.as_ref(), span), + state, + ); + } + } // Doesn't have any language semantics StatementKind::Coverage(..) => {} // These do not actually affect borrowck diff --git a/tests/ui/borrowck/placemention-deref.rs b/tests/ui/borrowck/placemention-deref.rs new file mode 100644 index 0000000000000..72bd6b6f7132d --- /dev/null +++ b/tests/ui/borrowck/placemention-deref.rs @@ -0,0 +1,33 @@ +enum Thing { + Wut, +} + +struct A(Box); + +fn main() { + let x: &'static String; + let _ = *x; + //~^ ERROR used binding `x` isn't initialized + + let x: &Thing; + let Thing::Wut = *x; + //~^ ERROR used binding `x` isn't initialized + + let x = Box::new(1); + drop(x); + _ = *x; + //~^ ERROR use of moved value: `x` + + let x: A; + _ = x.0.0.0.0.0; + //~^ ERROR used binding `x` isn't initialized + + let x: A; + let _ = x.0.0; + //~^ ERROR used binding `x` isn't initialized + + let x = Box::new(Box::new(Box::new(Box::new(Box::new(Box::new(1)))))); + drop(x); + _ = ******x; + //~^ ERROR use of moved value: `x` +} diff --git a/tests/ui/borrowck/placemention-deref.stderr b/tests/ui/borrowck/placemention-deref.stderr new file mode 100644 index 0000000000000..2ba8d45aec482 --- /dev/null +++ b/tests/ui/borrowck/placemention-deref.stderr @@ -0,0 +1,86 @@ +error[E0381]: used binding `x` isn't initialized + --> $DIR/placemention-deref.rs:9:13 + | +LL | let x: &'static String; + | - binding declared here but left uninitialized +LL | let _ = *x; + | ^^ `x` used here but it isn't initialized + | +help: consider assigning a value + | +LL | let x: &'static String = &Default::default(); + | +++++++++++++++++++++ + +error[E0381]: used binding `x` isn't initialized + --> $DIR/placemention-deref.rs:13:22 + | +LL | let x: &Thing; + | - binding declared here but left uninitialized +LL | let Thing::Wut = *x; + | ^^ `x` used here but it isn't initialized + | +help: consider assigning a value + | +LL | let x: &Thing = &/* value */; + | ++++++++++++++ + +error[E0382]: use of moved value: `x` + --> $DIR/placemention-deref.rs:18:9 + | +LL | let x = Box::new(1); + | - move occurs because `x` has type `Box`, which does not implement the `Copy` trait +LL | drop(x); + | - value moved here +LL | _ = *x; + | ^^ value used here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL | drop(x.clone()); + | ++++++++ + +error[E0381]: used binding `x` isn't initialized + --> $DIR/placemention-deref.rs:22:9 + | +LL | let x: A; + | - binding declared here but left uninitialized +LL | _ = x.0.0.0.0.0; + | ^^^^^^^^^^^ `x.0.0.0.0` used here but it isn't initialized + | +help: consider assigning a value + | +LL | let x: A = /* value */; + | +++++++++++++ + +error[E0381]: used binding `x` isn't initialized + --> $DIR/placemention-deref.rs:26:13 + | +LL | let x: A; + | - binding declared here but left uninitialized +LL | let _ = x.0.0; + | ^^^^^ `x.0` used here but it isn't initialized + | +help: consider assigning a value + | +LL | let x: A = /* value */; + | +++++++++++++ + +error[E0382]: use of moved value: `x` + --> $DIR/placemention-deref.rs:31:9 + | +LL | let x = Box::new(Box::new(Box::new(Box::new(Box::new(Box::new(1)))))); + | - move occurs because `x` has type `Box>>>>>`, which does not implement the `Copy` trait +LL | drop(x); + | - value moved here +LL | _ = ******x; + | ^^^^^^^ value used here after move + | +help: consider cloning the value if the performance cost is acceptable + | +LL | drop(x.clone()); + | ++++++++ + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0381, E0382. +For more information about an error, try `rustc --explain E0381`. From 58d3745589d8cd2afc48f37be14d591cb2701187 Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Mon, 26 Oct 2026 11:37:39 -0400 Subject: [PATCH 2/3] Avoid extra allocations Co-authored-by: dianne --- compiler/rustc_borrowck/src/lib.rs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 0cac67af0b3ad..87de12342e0fc 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -854,22 +854,14 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, // Only relevant for liveness and unsafeck, // except for dereferences which must be materialized StatementKind::PlaceMention(place) => { - if let Some((idx, _)) = place - .projection - .iter() - .enumerate() - .rev() - .find(|(_, proj)| matches!(proj.kind(), ProjectionKind::Deref)) + if let Some((dereferenced_place, _)) = place + .iter_projections() + .rfind(|(_, proj)| matches!(proj, ProjectionElem::Deref)) { - let truncated_projections = place.projection[..idx].to_vec(); - let truncated_place = Place { - local: place.local, - projection: self.infcx.tcx.mk_place_elems(&truncated_projections), - }; self.check_if_full_path_is_moved( location, InitializationRequiringAction::Use, - (truncated_place.as_ref(), span), + (dereferenced_place, span), state, ); } From 2766312c830c80af31449f94bfc96a11555a02af Mon Sep 17 00:00:00 2001 From: Jules Bertholet Date: Mon, 26 Oct 2026 17:47:45 -0400 Subject: [PATCH 3/3] Add tests for partial move out of `Box` --- src/tools/miri/tests/pass/placemention-box-deref.rs | 5 +++++ tests/ui/borrowck/placemention-deref.rs | 9 +++++++++ tests/ui/borrowck/placemention-deref.stderr | 12 +++++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 src/tools/miri/tests/pass/placemention-box-deref.rs diff --git a/src/tools/miri/tests/pass/placemention-box-deref.rs b/src/tools/miri/tests/pass/placemention-box-deref.rs new file mode 100644 index 0000000000000..56ab4252f7cf0 --- /dev/null +++ b/src/tools/miri/tests/pass/placemention-box-deref.rs @@ -0,0 +1,5 @@ +fn main() { + let x = Box::new(Box::new(Box::new(0))); + drop(**x); + _ = **x; +} diff --git a/tests/ui/borrowck/placemention-deref.rs b/tests/ui/borrowck/placemention-deref.rs index 72bd6b6f7132d..3ff80fec028df 100644 --- a/tests/ui/borrowck/placemention-deref.rs +++ b/tests/ui/borrowck/placemention-deref.rs @@ -30,4 +30,13 @@ fn main() { drop(x); _ = ******x; //~^ ERROR use of moved value: `x` + + let x = Box::new(Box::new(Box::new(0))); + drop(**x); + _ = ***x; + //~^ ERROR use of moved value: `**x` + + let x = Box::new(Box::new(Box::new(0))); + drop(**x); + _ = **x; // OK } diff --git a/tests/ui/borrowck/placemention-deref.stderr b/tests/ui/borrowck/placemention-deref.stderr index 2ba8d45aec482..6d03094881998 100644 --- a/tests/ui/borrowck/placemention-deref.stderr +++ b/tests/ui/borrowck/placemention-deref.stderr @@ -80,7 +80,17 @@ help: consider cloning the value if the performance cost is acceptable LL | drop(x.clone()); | ++++++++ -error: aborting due to 6 previous errors +error[E0382]: use of moved value: `**x` + --> $DIR/placemention-deref.rs:36:9 + | +LL | drop(**x); + | --- value moved here +LL | _ = ***x; + | ^^^^ value used here after move + | + = note: move occurs because `**x` has type `Box`, which does not implement the `Copy` trait + +error: aborting due to 7 previous errors Some errors have detailed explanations: E0381, E0382. For more information about an error, try `rustc --explain E0381`.