From 03fba6b026e4cddef23cf9605ec519ee66ce78b2 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 28 May 2026 18:18:42 +0300 Subject: [PATCH 1/8] Implement Reborrow as a recursive operation If Reborrow finds '&'a mut T' fields then it inserts a Deref and borrow of the T, and likewise if it finds a 'T: Reborrow' field then the field type is recursed into. This makes Reborrow always produce the correct borrow checking logic at the cost of most probably being inconsiderately expensive. The thinking is that performance will be a followup consideration. --- compiler/rustc_borrowck/src/borrow_set.rs | 198 +++++++++++++++--- ...ce-shared-omitted-reborrow-field-locked.rs | 1 - ...hared-omitted-reborrow-field-locked.stderr | 15 +- tests/ui/reborrow/custom_marker_identity.rs | 17 ++ .../ui/reborrow/custom_marker_identity.stderr | 24 +++ .../custom_marker_mut_field_borrow.rs | 17 ++ .../custom_marker_mut_field_borrow.stderr | 14 ++ .../reborrow/custom_marker_place_conflict.rs | 17 ++ .../custom_marker_place_conflict.stderr | 14 ++ .../custom_marker_place_conflict_deref.rs | 25 +++ .../custom_marker_place_conflict_deref.stderr | 14 ++ .../custom_marker_place_conflict_field.rs | 17 ++ .../custom_marker_place_conflict_field.stderr | 14 ++ ...om_marker_place_conflict_parallel_field.rs | 21 ++ ...arker_place_conflict_parallel_field.stderr | 14 ++ tests/ui/reborrow/custom_mut_identity.rs | 17 ++ .../ui/reborrow/custom_mut_place_conflict.rs | 17 ++ .../reborrow/custom_mut_place_conflict.stderr | 14 ++ .../custom_mut_place_conflict_field.rs | 17 ++ .../custom_mut_place_conflict_field.stderr | 14 ++ 20 files changed, 460 insertions(+), 41 deletions(-) create mode 100644 tests/ui/reborrow/custom_marker_identity.rs create mode 100644 tests/ui/reborrow/custom_marker_identity.stderr create mode 100644 tests/ui/reborrow/custom_marker_mut_field_borrow.rs create mode 100644 tests/ui/reborrow/custom_marker_mut_field_borrow.stderr create mode 100644 tests/ui/reborrow/custom_marker_place_conflict.rs create mode 100644 tests/ui/reborrow/custom_marker_place_conflict.stderr create mode 100644 tests/ui/reborrow/custom_marker_place_conflict_deref.rs create mode 100644 tests/ui/reborrow/custom_marker_place_conflict_deref.stderr create mode 100644 tests/ui/reborrow/custom_marker_place_conflict_field.rs create mode 100644 tests/ui/reborrow/custom_marker_place_conflict_field.stderr create mode 100644 tests/ui/reborrow/custom_marker_place_conflict_parallel_field.rs create mode 100644 tests/ui/reborrow/custom_marker_place_conflict_parallel_field.stderr create mode 100644 tests/ui/reborrow/custom_mut_identity.rs create mode 100644 tests/ui/reborrow/custom_mut_place_conflict.rs create mode 100644 tests/ui/reborrow/custom_mut_place_conflict.stderr create mode 100644 tests/ui/reborrow/custom_mut_place_conflict_field.rs create mode 100644 tests/ui/reborrow/custom_mut_place_conflict_field.stderr diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index a9d6b31e2ee2f..89862b68ca52a 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -7,7 +7,7 @@ use rustc_hir::Mutability; use rustc_index::IndexVec; use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonUseContext, PlaceContext, Visitor}; -use rustc_middle::mir::{self, Body, Local, Location, traversal}; +use rustc_middle::mir::{self, Body, Local, Location, PlaceElem, traversal}; use rustc_middle::ty::data_structures::IndexSet; use rustc_middle::ty::{RegionVid, TyCtxt}; use rustc_middle::{bug, span_bug, ty}; @@ -265,6 +265,152 @@ impl<'a, 'tcx> GatherBorrows<'a, 'tcx> { } idx } + + fn insert_borrows( + &mut self, + location: Location, + borrows: SmallVec<[BorrowData<'tcx>; 1]>, + ) -> SmallVec<[BorrowIndex; 1]> { + let mut idxs = SmallVec::<[BorrowIndex; 1]>::with_capacity(borrows.len()); + // FIXME(reborrow): why doesn't SmallVec offer reserve? + for borrow in borrows { + idxs.push(self.borrows.push(borrow)); + } + match self.location_map.entry(location) { + Entry::Occupied(entry) => { + bug!( + "Inserting borrows {idxs:?} at {location:?} attempted to override an existing list {entry:?}" + ); + } + Entry::Vacant(entry) => { + entry.insert(idxs.clone()); + } + } + idxs + } + + fn gather_reborrows( + &mut self, + v: &mut SmallVec<[BorrowData<'tcx>; 1]>, + kind: mir::BorrowKind, + location: Location, + target_adt: ty::AdtDef<'tcx>, + target_args: &'tcx ty::List>, + target_place: mir::Place<'tcx>, + source_adt: ty::AdtDef<'tcx>, + source_args: &'tcx ty::List>, + source_place: mir::Place<'tcx>, + ) { + let mut did_reborrow = false; + for (source_idx, source_field) in source_adt.all_fields().enumerate() { + let source_field_ty = source_field.ty(self.tcx, source_args).skip_norm_wip(); + match source_field_ty.kind() { + ty::Ref(source_region, _, source_mutability) if source_mutability.is_mut() => { + if source_region.is_static() { + bug!( + "Cannot implement Reborrow on a type containing a &'static mut T field" + ); + } + let Some((target_idx, target_field)) = target_adt + .all_fields() + .enumerate() + .find(|(_, f)| f.name == source_field.name) + else { + // Reborrow dropped this field. + continue; + }; + let ty::Ref(target_region, _, _) = + target_field.ty(self.tcx, target_args).skip_norm_wip().kind() + else { + bug!( + "Reborrow source field type is &mut T but target field is not a reference" + ); + }; + + did_reborrow = true; + let source_field_deref_place = source_place.project_deeper( + &[PlaceElem::Field(source_idx.into(), source_field_ty), PlaceElem::Deref], + self.tcx, + ); + let target_field_place = target_place.project_to_field( + target_idx.into(), + &self.body.local_decls, + self.tcx, + ); + v.push(BorrowData { + kind, + region: target_region.as_var(), + reserve_location: location, + activation_location: TwoPhaseActivation::NotTwoPhase, + borrowed_place: source_field_deref_place, + assigned_place: target_field_place, + }); + } + ty::Adt(source_field_adt, source_field_args) + if source_field_args.get(0).is_some_and(|f| f.as_region().is_some()) + && !self.tcx.type_is_copy_modulo_regions( + self.body.typing_env(self.tcx), + self.tcx.erase_and_anonymize_regions(source_field_ty), + ) => + { + let Some((target_idx, target_field)) = target_adt + .all_fields() + .enumerate() + .find(|(_, f)| f.name == source_field.name) + else { + // Reborrow dropped this field. + continue; + }; + let ty::Adt(target_field_adt, target_field_args) = + target_field.ty(self.tcx, target_args).skip_norm_wip().kind() + else { + bug!("Reborrow source field type is a !Copy ADT but target field is not"); + }; + + did_reborrow = true; + let source_field_place = source_place.project_to_field( + source_idx.into(), + &self.body.local_decls, + self.tcx, + ); + let target_field_place = target_place.project_to_field( + target_idx.into(), + &self.body.local_decls, + self.tcx, + ); + self.gather_reborrows( + v, + kind, + location, + *target_field_adt, + target_field_args, + target_field_place, + *source_field_adt, + source_field_args, + source_field_place, + ); + } + _ => continue, + } + } + if !did_reborrow { + // If source contained no reference, borrow it directly. + if target_args.regions().count() != 1 { + bug!( + "ADT containing no '&mut T' or 'T: Reborrow' fields must only have one lifetime to implement Reborrow" + ); + } + let target_region = target_args.regions().next().unwrap(); + v.push(BorrowData { + kind, + region: target_region.as_var(), + reserve_location: location, + activation_location: TwoPhaseActivation::NotTwoPhase, + borrowed_place: source_place, + assigned_place: target_place, + }); + } + } } impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { @@ -323,23 +469,14 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { }; self.local_map.entry(borrowed_place.local).or_default().insert(idx); - } else if let &mir::Rvalue::Reborrow(target, mutability, borrowed_place) = rvalue { - let borrowed_place_ty = borrowed_place.ty(self.body, self.tcx).ty; - let &ty::Adt(reborrowed_adt, _reborrowed_args) = borrowed_place_ty.kind() else { - unreachable!() - }; - let &ty::Adt(target_adt, assigned_args) = target.kind() else { unreachable!() }; - let Some(ty::GenericArgKind::Lifetime(region)) = assigned_args.get(0).map(|r| r.kind()) - else { - bug!( - "hir-typeck passed but {} does not have a lifetime argument", - if mutability == Mutability::Mut { "Reborrow" } else { "CoerceShared" } - ); - }; - let region = region.as_var(); + } else if let &mir::Rvalue::Reborrow(target, mutability, source_place) = rvalue { + let source_ty = source_place.ty(self.body, self.tcx).ty; + let &ty::Adt(source_adt, source_args) = source_ty.kind() else { unreachable!() }; + let &ty::Adt(target_adt, target_args) = target.kind() else { unreachable!() }; + let kind = if mutability == Mutability::Mut { // Reborrow - if target_adt.did() != reborrowed_adt.did() { + if target_adt.did() != source_adt.did() { bug!( "hir-typeck passed but Reborrow involves mismatching types at {location:?}" ) @@ -348,24 +485,33 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> { mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default } } else { // CoerceShared - if target_adt.did() == reborrowed_adt.did() { + if target_adt.did() == source_adt.did() { bug!( "hir-typeck passed but CoerceShared involves matching types at {location:?}" ) } mir::BorrowKind::Shared }; - let borrow = BorrowData { + + let mut reborrows = smallvec![]; + self.gather_reborrows( + &mut reborrows, kind, - region, - reserve_location: location, - activation_location: TwoPhaseActivation::NotTwoPhase, - borrowed_place, - assigned_place: *assigned_place, - }; - let idx = self.insert_borrow(location, borrow); + location, + target_adt, + target_args, + *assigned_place, + source_adt, + source_args, + source_place, + ); - self.local_map.entry(borrowed_place.local).or_default().insert(idx); + let idxs = self.insert_borrows(location, reborrows); + + let locals = self.local_map.entry(source_place.local).or_default(); + for idx in idxs { + locals.insert(idx); + } } self.super_assign(assigned_place, rvalue, location) diff --git a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.rs b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.rs index ade1890068d76..fb4eb86781a0e 100644 --- a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.rs +++ b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.rs @@ -47,7 +47,6 @@ fn main() { let shared = get(wrapped); *wrapped.extra.value = 3; - //~^ ERROR cannot assign to `*wrapped.extra.value` because it is borrowed let _ = shared; } diff --git a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.stderr b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.stderr index ccc3054a1b0c8..4c41ea5ea1175 100644 --- a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.stderr +++ b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.stderr @@ -10,18 +10,5 @@ LL | impl<'a, T> CoerceShared> for OmitMut<'a, T> {} | = note: this is a temporary restriction until `CoerceShared` lowering supports non-trivially memcpy-compatible field layouts -error[E0506]: cannot assign to `*wrapped.extra.value` because it is borrowed - --> $DIR/coerce-shared-omitted-reborrow-field-locked.rs:49:5 - | -LL | let shared = get(wrapped); - | ------- `*wrapped.extra.value` is borrowed here -LL | -LL | *wrapped.extra.value = 3; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | `*wrapped.extra.value` is assigned to here but it was already borrowed - | borrow later used here - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0506`. diff --git a/tests/ui/reborrow/custom_marker_identity.rs b/tests/ui/reborrow/custom_marker_identity.rs new file mode 100644 index 0000000000000..c0bd126f818a0 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_identity.rs @@ -0,0 +1,17 @@ +//@ check-fail + +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +fn method<'a>(a: CustomMarker<'a>) -> CustomMarker<'a> { //~ERROR cannot return reference to temporary value + //~^ ERROR cannot return value referencing function parameter `a` + a +} + +fn main() { + let a = CustomMarker(PhantomData); + let _ = method(a); +} diff --git a/tests/ui/reborrow/custom_marker_identity.stderr b/tests/ui/reborrow/custom_marker_identity.stderr new file mode 100644 index 0000000000000..1ea3a6bd2442b --- /dev/null +++ b/tests/ui/reborrow/custom_marker_identity.stderr @@ -0,0 +1,24 @@ +error[E0515]: cannot return reference to temporary value + --> $DIR/custom_marker_identity.rs:9:56 + | +LL | fn method<'a>(a: CustomMarker<'a>) -> CustomMarker<'a> { + | ________________________________________________________^ +LL | | +LL | | a +LL | | } + | |_^ returns a reference to data owned by the current function + +error[E0515]: cannot return value referencing function parameter `a` + --> $DIR/custom_marker_identity.rs:9:56 + | +LL | fn method<'a>(a: CustomMarker<'a>) -> CustomMarker<'a> { + | ________________________________________________________^ +LL | | +LL | | a + | | - `a` is borrowed here +LL | | } + | |_^ returns a value referencing data owned by the current function + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/reborrow/custom_marker_mut_field_borrow.rs b/tests/ui/reborrow/custom_marker_mut_field_borrow.rs new file mode 100644 index 0000000000000..41fd01ed77a59 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_field_borrow.rs @@ -0,0 +1,17 @@ +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +fn method<'a>(_a: CustomMarker<'a>) -> &'a () { + &() +} + +fn main() { + let a = CustomMarker(PhantomData); + let x = &a.0; + let y = method(a); + //~^ ERROR: cannot borrow `a` as mutable because it is also borrowed as immutable + let _ = (x, y); +} diff --git a/tests/ui/reborrow/custom_marker_mut_field_borrow.stderr b/tests/ui/reborrow/custom_marker_mut_field_borrow.stderr new file mode 100644 index 0000000000000..6601ec172228d --- /dev/null +++ b/tests/ui/reborrow/custom_marker_mut_field_borrow.stderr @@ -0,0 +1,14 @@ +error[E0502]: cannot borrow `a` as mutable because it is also borrowed as immutable + --> $DIR/custom_marker_mut_field_borrow.rs:14:20 + | +LL | let x = &a.0; + | ---- immutable borrow occurs here +LL | let y = method(a); + | ^ mutable borrow occurs here +LL | +LL | let _ = (x, y); + | - immutable borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0502`. diff --git a/tests/ui/reborrow/custom_marker_place_conflict.rs b/tests/ui/reborrow/custom_marker_place_conflict.rs new file mode 100644 index 0000000000000..c137e81cea584 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_place_conflict.rs @@ -0,0 +1,17 @@ +//@ check-fail + +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +fn reborrow(_: CustomMarker) {} + +fn main() { + let a = CustomMarker(PhantomData); + let b: &CustomMarker = &a; + reborrow(a); + //~^ ERROR cannot borrow `a` as mutable because it is also borrowed as immutable + let _ = b; +} diff --git a/tests/ui/reborrow/custom_marker_place_conflict.stderr b/tests/ui/reborrow/custom_marker_place_conflict.stderr new file mode 100644 index 0000000000000..224562cde5884 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_place_conflict.stderr @@ -0,0 +1,14 @@ +error[E0502]: cannot borrow `a` as mutable because it is also borrowed as immutable + --> $DIR/custom_marker_place_conflict.rs:14:14 + | +LL | let b: &CustomMarker = &a; + | -- immutable borrow occurs here +LL | reborrow(a); + | ^ mutable borrow occurs here +LL | +LL | let _ = b; + | - immutable borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0502`. diff --git a/tests/ui/reborrow/custom_marker_place_conflict_deref.rs b/tests/ui/reborrow/custom_marker_place_conflict_deref.rs new file mode 100644 index 0000000000000..8a8119ccfe599 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_place_conflict_deref.rs @@ -0,0 +1,25 @@ +//@ check-fail + +#![feature(reborrow)] +use std::{marker::{Reborrow, PhantomData}, ops::Deref}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +impl<'a> Deref for CustomMarker<'a> { + type Target = (); + + fn deref(&self) -> &() { + unsafe { std::mem::transmute::<&Self, &()>(self) } + } +} + +fn reborrow(_: CustomMarker) {} + +fn main() { + let a = CustomMarker(PhantomData); + let b: &() = &a; + reborrow(a); + //~^ ERROR cannot borrow `a` as mutable because it is also borrowed as immutable + let _ = b; +} diff --git a/tests/ui/reborrow/custom_marker_place_conflict_deref.stderr b/tests/ui/reborrow/custom_marker_place_conflict_deref.stderr new file mode 100644 index 0000000000000..22a8dead7fe72 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_place_conflict_deref.stderr @@ -0,0 +1,14 @@ +error[E0502]: cannot borrow `a` as mutable because it is also borrowed as immutable + --> $DIR/custom_marker_place_conflict_deref.rs:22:14 + | +LL | let b: &() = &a; + | -- immutable borrow occurs here +LL | reborrow(a); + | ^ mutable borrow occurs here +LL | +LL | let _ = b; + | - immutable borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0502`. diff --git a/tests/ui/reborrow/custom_marker_place_conflict_field.rs b/tests/ui/reborrow/custom_marker_place_conflict_field.rs new file mode 100644 index 0000000000000..b967651179b36 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_place_conflict_field.rs @@ -0,0 +1,17 @@ +//@ check-fail + +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +fn reborrow(_: CustomMarker) {} + +fn main() { + let a = CustomMarker(PhantomData); + let b: &PhantomData<&()> = &a.0; + reborrow(a); + //~^ ERROR cannot borrow `a` as mutable because it is also borrowed as immutable + let _ = b; +} diff --git a/tests/ui/reborrow/custom_marker_place_conflict_field.stderr b/tests/ui/reborrow/custom_marker_place_conflict_field.stderr new file mode 100644 index 0000000000000..2972eff893f13 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_place_conflict_field.stderr @@ -0,0 +1,14 @@ +error[E0502]: cannot borrow `a` as mutable because it is also borrowed as immutable + --> $DIR/custom_marker_place_conflict_field.rs:14:14 + | +LL | let b: &PhantomData<&()> = &a.0; + | ---- immutable borrow occurs here +LL | reborrow(a); + | ^ mutable borrow occurs here +LL | +LL | let _ = b; + | - immutable borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0502`. diff --git a/tests/ui/reborrow/custom_marker_place_conflict_parallel_field.rs b/tests/ui/reborrow/custom_marker_place_conflict_parallel_field.rs new file mode 100644 index 0000000000000..9c02f81820754 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_place_conflict_parallel_field.rs @@ -0,0 +1,21 @@ +//@ check-fail + +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMarker<'a>(PhantomData<&'a ()>); +impl<'a> Reborrow for CustomMarker<'a> {} + +struct CustomMarkerTwo<'a>(CustomMarker<'a>, u64); +impl<'a> Reborrow for CustomMarkerTwo<'a> {} + +fn reborrow(_: CustomMarkerTwo) {} + +fn main() { + let a = CustomMarker(PhantomData); + let a = CustomMarkerTwo(a, 0); + let b: &u64 = &a.1; + reborrow(a); + //~^ ERROR cannot borrow `a` as mutable because it is also borrowed as immutable + let _ = b; +} diff --git a/tests/ui/reborrow/custom_marker_place_conflict_parallel_field.stderr b/tests/ui/reborrow/custom_marker_place_conflict_parallel_field.stderr new file mode 100644 index 0000000000000..e48645562f8d3 --- /dev/null +++ b/tests/ui/reborrow/custom_marker_place_conflict_parallel_field.stderr @@ -0,0 +1,14 @@ +error[E0502]: cannot borrow `a` as mutable because it is also borrowed as immutable + --> $DIR/custom_marker_place_conflict_parallel_field.rs:18:14 + | +LL | let b: &u64 = &a.1; + | ---- immutable borrow occurs here +LL | reborrow(a); + | ^ mutable borrow occurs here +LL | +LL | let _ = b; + | - immutable borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0502`. diff --git a/tests/ui/reborrow/custom_mut_identity.rs b/tests/ui/reborrow/custom_mut_identity.rs new file mode 100644 index 0000000000000..6b67d5f342562 --- /dev/null +++ b/tests/ui/reborrow/custom_mut_identity.rs @@ -0,0 +1,17 @@ +//@ run-pass + +#![feature(reborrow)] +use std::marker::Reborrow; + +#[allow(unused)] +struct CustomMut<'a, T>(&'a mut T); +impl<'a, T> Reborrow for CustomMut<'a, T> {} + +fn method(a: CustomMut<()>) -> CustomMut<()> { + a +} + +fn main() { + let a = CustomMut(&mut ()); + let _ = method(a); +} diff --git a/tests/ui/reborrow/custom_mut_place_conflict.rs b/tests/ui/reborrow/custom_mut_place_conflict.rs new file mode 100644 index 0000000000000..8a57a93eb6bb7 --- /dev/null +++ b/tests/ui/reborrow/custom_mut_place_conflict.rs @@ -0,0 +1,17 @@ +//@ check-fail + +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMut<'a>(&'a mut ()); +impl<'a> Reborrow for CustomMut<'a> {} + +fn reborrow(_: CustomMut) {} + +fn main() { + let a = CustomMut(&mut ()); + let b: &CustomMut = &a; + reborrow(a); + //~^ ERROR cannot borrow `a` as mutable because it is also borrowed as immutable + let _ = b; +} diff --git a/tests/ui/reborrow/custom_mut_place_conflict.stderr b/tests/ui/reborrow/custom_mut_place_conflict.stderr new file mode 100644 index 0000000000000..d73776b564a6a --- /dev/null +++ b/tests/ui/reborrow/custom_mut_place_conflict.stderr @@ -0,0 +1,14 @@ +error[E0502]: cannot borrow `a` as mutable because it is also borrowed as immutable + --> $DIR/custom_mut_place_conflict.rs:14:14 + | +LL | let b: &CustomMut = &a; + | -- immutable borrow occurs here +LL | reborrow(a); + | ^ mutable borrow occurs here +LL | +LL | let _ = b; + | - immutable borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0502`. diff --git a/tests/ui/reborrow/custom_mut_place_conflict_field.rs b/tests/ui/reborrow/custom_mut_place_conflict_field.rs new file mode 100644 index 0000000000000..6b65d794999a0 --- /dev/null +++ b/tests/ui/reborrow/custom_mut_place_conflict_field.rs @@ -0,0 +1,17 @@ +//@ check-fail + +#![feature(reborrow)] +use std::marker::{Reborrow, PhantomData}; + +struct CustomMut<'a>(&'a mut ()); +impl<'a> Reborrow for CustomMut<'a> {} + +fn reborrow(_: CustomMut) {} + +fn main() { + let a = CustomMut(&mut ()); + let b: &mut () = a.0; + reborrow(a); + //~^ ERROR cannot borrow `a` as mutable more than once at a time + let _ = b; +} diff --git a/tests/ui/reborrow/custom_mut_place_conflict_field.stderr b/tests/ui/reborrow/custom_mut_place_conflict_field.stderr new file mode 100644 index 0000000000000..326faf598594b --- /dev/null +++ b/tests/ui/reborrow/custom_mut_place_conflict_field.stderr @@ -0,0 +1,14 @@ +error[E0499]: cannot borrow `a` as mutable more than once at a time + --> $DIR/custom_mut_place_conflict_field.rs:14:14 + | +LL | let b: &mut () = a.0; + | --- first mutable borrow occurs here +LL | reborrow(a); + | ^ second mutable borrow occurs here +LL | +LL | let _ = b; + | - first borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0499`. From d94d1254883198e174951c6bed1a9b6d05a29b6c Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 19 Jul 2026 17:52:26 +0300 Subject: [PATCH 2/8] PhantomDeref --- compiler/rustc_borrowck/src/borrow_set.rs | 6 ++++-- .../src/diagnostics/conflict_errors.rs | 7 +++++++ compiler/rustc_borrowck/src/diagnostics/mod.rs | 2 ++ .../src/diagnostics/mutability_errors.rs | 9 +++++++++ compiler/rustc_borrowck/src/lib.rs | 7 ++++++- compiler/rustc_borrowck/src/places_conflict.rs | 13 +++++++++++++ compiler/rustc_borrowck/src/prefixes.rs | 3 +++ compiler/rustc_borrowck/src/type_check/mod.rs | 2 ++ compiler/rustc_codegen_cranelift/src/base.rs | 1 + compiler/rustc_codegen_ssa/src/mir/place.rs | 3 +++ .../rustc_const_eval/src/check_consts/qualifs.rs | 1 + .../rustc_const_eval/src/interpret/projection.rs | 3 +++ compiler/rustc_middle/src/mir/pretty.rs | 5 +++-- compiler/rustc_middle/src/mir/statement.rs | 11 ++++++++--- compiler/rustc_middle/src/mir/syntax.rs | 2 ++ compiler/rustc_middle/src/mir/visit.rs | 2 ++ .../rustc_mir_build/src/builder/expr/as_place.rs | 2 ++ compiler/rustc_mir_dataflow/src/move_paths/mod.rs | 1 + compiler/rustc_mir_transform/src/coroutine/mod.rs | 3 ++- compiler/rustc_mir_transform/src/gvn.rs | 1 + compiler/rustc_mir_transform/src/promote_consts.rs | 4 +++- .../rustc_public/src/unstable/convert/stable/mir.rs | 1 + .../clippy/clippy_utils/src/qualify_min_const_fn.rs | 3 ++- tests/ui/reborrow/custom_marker_identity.rs | 2 +- tests/ui/reborrow/custom_marker_identity.stderr | 2 +- 25 files changed, 83 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_borrowck/src/borrow_set.rs b/compiler/rustc_borrowck/src/borrow_set.rs index 89862b68ca52a..f53702f3fde13 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -394,7 +394,9 @@ impl<'a, 'tcx> GatherBorrows<'a, 'tcx> { } } if !did_reborrow { - // If source contained no reference, borrow it directly. + // If source contained no reference, perform a phantom dereference. + let source_phantom_deref_place = + source_place.project_deeper(&[PlaceElem::PhantomDeref], self.tcx); if target_args.regions().count() != 1 { bug!( "ADT containing no '&mut T' or 'T: Reborrow' fields must only have one lifetime to implement Reborrow" @@ -406,7 +408,7 @@ impl<'a, 'tcx> GatherBorrows<'a, 'tcx> { region: target_region.as_var(), reserve_location: location, activation_location: TwoPhaseActivation::NotTwoPhase, - borrowed_place: source_place, + borrowed_place: source_phantom_deref_place, assigned_place: target_place, }); } diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 49307e78fbb14..6e73126d2f9be 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -4213,6 +4213,13 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { } StorageDeadOrDrop::Destructor(_) => kind, }, + ProjectionElem::PhantomDeref => match kind { + StorageDeadOrDrop::LocalStorageDead + | StorageDeadOrDrop::BoxedStorageDead => { + StorageDeadOrDrop::BoxedStorageDead + } + StorageDeadOrDrop::Destructor(_) => kind, + }, ProjectionElem::OpaqueCast { .. } | ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => { diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index bde2529d855cf..16da957aa4bb9 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -397,6 +397,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { } } } + ProjectionElem::PhantomDeref => (), ProjectionElem::Downcast(..) if opt.including_downcast => return None, ProjectionElem::Downcast(..) => (), ProjectionElem::OpaqueCast(..) => (), @@ -485,6 +486,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { PlaceTy::from_ty(*ty) } ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type), + ProjectionElem::PhantomDeref => unreachable!("not a field"), }, }; self.describe_field_from_ty( diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 68e7e3786b33c..ad558ce4daa00 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -181,6 +181,15 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { } } + PlaceRef { local: _, projection: [ProjectionElem::PhantomDeref] } => { + item_msg = String::new(); + reason = String::new(); + } + PlaceRef { local: _, projection: [_proj_base @ .., ProjectionElem::PhantomDeref] } => { + item_msg = String::new(); + reason = String::new(); + } + PlaceRef { local: _, projection: diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index d990d72e3fb42..f39590fd018d2 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2020,7 +2020,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { // So it's safe to skip these. ProjectionElem::OpaqueCast(_) | ProjectionElem::Downcast(_, _) - | ProjectionElem::UnwrapUnsafeBinder(_) => (), + | ProjectionElem::UnwrapUnsafeBinder(_) + | ProjectionElem::PhantomDeref => (), } place_ty = place_ty.projection_ty(tcx, elem); @@ -2244,6 +2245,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { for (place_base, elem) in place.iter_projections().rev() { match elem { ProjectionElem::Index(_/*operand*/) + | ProjectionElem::PhantomDeref | ProjectionElem::OpaqueCast(_) // assigning to P[i] requires P to be valid. | ProjectionElem::ConstantIndex { .. } @@ -2640,6 +2642,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { _ => bug!("Deref of unexpected type: {:?}", base_ty), } } + ProjectionElem::PhantomDeref => { + bug!("encountered PhantomDeref in is_mutable") + } // Check as the inner reference type if it is a field projection // from the `&pin` pattern ProjectionElem::Field(FieldIdx::ZERO, _) diff --git a/compiler/rustc_borrowck/src/places_conflict.rs b/compiler/rustc_borrowck/src/places_conflict.rs index e966e83435ef3..e35dd3eb082ca 100644 --- a/compiler/rustc_borrowck/src/places_conflict.rs +++ b/compiler/rustc_borrowck/src/places_conflict.rs @@ -244,6 +244,7 @@ fn place_components_conflict<'tcx>( (ProjectionElem::Deref, _, Deep) | (ProjectionElem::Deref, _, AccessDepth::Drop) + | (ProjectionElem::PhantomDeref, _, _) | (ProjectionElem::Field { .. }, _, _) | (ProjectionElem::Index { .. }, _, _) | (ProjectionElem::ConstantIndex { .. }, _, _) @@ -301,6 +302,11 @@ fn place_projection_conflict<'tcx>( debug!("place_element_conflict: DISJOINT-OR-EQ-DEREF"); Overlap::EqualOrDisjoint } + (ProjectionElem::PhantomDeref, ProjectionElem::PhantomDeref) => { + // phantom derefs (e.g., `x` vs. `x`) - recur. + debug!("place_element_conflict: DISJOINT-OR-EQ-PHANTOM-DEREF"); + Overlap::EqualOrDisjoint + } (ProjectionElem::OpaqueCast(_), ProjectionElem::OpaqueCast(_)) => { // casts to other types may always conflict irrespective of the type being cast to. debug!("place_element_conflict: DISJOINT-OR-EQ-OPAQUE"); @@ -504,8 +510,15 @@ fn place_projection_conflict<'tcx>( debug!("place_element_conflict: DISJOINT-OR-EQ-SLICE-SUBSLICES"); Overlap::EqualOrDisjoint } + (ProjectionElem::PhantomDeref, ProjectionElem::Field(idx, _)) + | (ProjectionElem::Field(idx, _), ProjectionElem::PhantomDeref) => { + eprintln!("idx: {idx:?}"); + debug!("place_element_conflict: DISJOINT-OR-EQ-PHANTOM-DEREF-FIELD"); + Overlap::EqualOrDisjoint + } ( ProjectionElem::Deref + | ProjectionElem::PhantomDeref | ProjectionElem::Field(..) | ProjectionElem::Index(..) | ProjectionElem::ConstantIndex { .. } diff --git a/compiler/rustc_borrowck/src/prefixes.rs b/compiler/rustc_borrowck/src/prefixes.rs index 7ac63e02e318d..7ff258de3af4f 100644 --- a/compiler/rustc_borrowck/src/prefixes.rs +++ b/compiler/rustc_borrowck/src/prefixes.rs @@ -65,6 +65,9 @@ impl<'tcx> Iterator for Prefixes<'tcx> { | ProjectionElem::Index(_) => { cursor = cursor_base; } + ProjectionElem::PhantomDeref => { + unreachable!("PhantomDeref should not be present in prefixes") + } ProjectionElem::Deref => { match self.kind { PrefixSet::Shallow => { diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 6825c28270c30..283932441354b 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1893,6 +1893,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // All these projections don't add any constraints, so there's nothing to // do here. We check their invariants in the MIR validator after all. ProjectionElem::Deref + | ProjectionElem::PhantomDeref | ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } @@ -2464,6 +2465,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } ProjectionElem::Field(..) + | ProjectionElem::PhantomDeref | ProjectionElem::Downcast(..) | ProjectionElem::OpaqueCast(..) | ProjectionElem::Index(..) diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index d57f662fc1938..14b2eee8089c5 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -993,6 +993,7 @@ pub(crate) fn codegen_place<'tcx>( PlaceElem::Deref => { cplace = cplace.place_deref(fx); } + PlaceElem::PhantomDeref => bug!("encountered PhantomDeref in codegen"), PlaceElem::OpaqueCast(ty) => bug!("encountered OpaqueCast({ty}) in codegen"), PlaceElem::UnwrapUnsafeBinder(ty) => { cplace = cplace.place_transmute_type(fx, fx.monomorphize(ty)); diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs index 14a5f71fbceaa..c51f1dfdc686d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/place.rs +++ b/compiler/rustc_codegen_ssa/src/mir/place.rs @@ -360,6 +360,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { for elem in place_ref.projection[base..].iter() { cg_base = match *elem { mir::ProjectionElem::Deref => bx.load_operand(cg_base).deref(bx.cx()), + mir::ProjectionElem::PhantomDeref => { + bug!("encountered PhantomDeref in codegen") + } mir::ProjectionElem::Field(ref field, _) => { assert!( !cg_base.layout.ty.is_any_ptr(), diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index fa54d1ed4e562..f58cb332381b2 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -290,6 +290,7 @@ where ProjectionElem::Index(index) if in_local(index) => return true, ProjectionElem::Deref + | ProjectionElem::PhantomDeref | ProjectionElem::Field(_, _) | ProjectionElem::OpaqueCast(_) | ProjectionElem::ConstantIndex { .. } diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index be31393879fff..ba2e68208846f 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -411,6 +411,9 @@ where OpaqueCast(ty) => { span_bug!(self.cur_span(), "OpaqueCast({ty}) encountered after borrowck") } + PhantomDeref => { + span_bug!(self.cur_span(), "PhantomDeref encountered after borrowck") + } UnwrapUnsafeBinder(target) => base.transmute(self.layout_of(target)?, self)?, Field(field, _) => self.project_field(base, field)?, Downcast(_, variant) => self.project_downcast(base, variant)?, diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 021c1c176d788..a1685e80c8469 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1352,7 +1352,8 @@ fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> match elem { ProjectionElem::OpaqueCast(_) | ProjectionElem::Downcast(_, _) - | ProjectionElem::Field(_, _) => { + | ProjectionElem::Field(_, _) + | ProjectionElem::PhantomDeref => { write!(fmt, "(")?; } ProjectionElem::Deref => { @@ -1382,7 +1383,7 @@ fn post_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> ProjectionElem::Downcast(None, index) => { write!(fmt, " as variant#{index:?})")?; } - ProjectionElem::Deref => { + ProjectionElem::Deref | ProjectionElem::PhantomDeref => { write!(fmt, ")")?; } ProjectionElem::Field(field, ty) => { diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index f5c0b0e66bdae..213ac6eb062cc 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -223,6 +223,7 @@ impl<'tcx> PlaceTy<'tcx> { ); PlaceTy::from_ty(ty) } + ProjectionElem::PhantomDeref => PlaceTy::from_ty(structurally_normalize(self.ty)), ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => { PlaceTy::from_ty(normalize(Unnormalized::new_wip(self.ty)).builtin_index().unwrap()) } @@ -267,7 +268,7 @@ impl ProjectionElem { /// than the base. pub fn is_indirect(&self) -> bool { match self { - Self::Deref => true, + Self::Deref | Self::PhantomDeref => true, Self::Field(_, _) | Self::Index(_) @@ -289,7 +290,8 @@ impl ProjectionElem { | Self::ConstantIndex { .. } | Self::Subslice { .. } | Self::Downcast(_, _) - | Self::UnwrapUnsafeBinder(..) => true, + | Self::UnwrapUnsafeBinder(..) + | Self::PhantomDeref => true, } } @@ -313,7 +315,8 @@ impl ProjectionElem { Self::ConstantIndex { from_end: true, .. } | Self::Index(_) | Self::OpaqueCast(_) - | Self::Subslice { .. } => false, + | Self::Subslice { .. } + | Self::PhantomDeref => false, // FIXME(unsafe_binders): Figure this out. Self::UnwrapUnsafeBinder(..) => false, @@ -333,6 +336,7 @@ impl ProjectionElem { ) -> Option> { Some(match self { ProjectionElem::Deref => ProjectionElem::Deref, + ProjectionElem::PhantomDeref => bug!("PhantomDeref shouldn't hopefully come here"), ProjectionElem::Downcast(name, read_variant) => { ProjectionElem::Downcast(name, read_variant) } @@ -567,6 +571,7 @@ impl<'tcx> PlaceRef<'tcx> { std::iter::once(self.local).chain(self.projection.iter().filter_map(|proj| match proj { ProjectionElem::Index(local) => Some(*local), ProjectionElem::Deref + | ProjectionElem::PhantomDeref | ProjectionElem::Field(_, _) | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index b005cf0c8d10f..e77a790f83748 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1255,6 +1255,8 @@ pub enum ProjectionElem { /// A transmute from an unsafe binder to the type that it wraps. This is a projection /// of a place, so it doesn't necessarily constitute a move out of the binder. UnwrapUnsafeBinder(T), + + PhantomDeref, } /// Alias for projections as they appear in places, where the base is a place diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 429ab7d928fc5..7a79b426715f9 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -1178,6 +1178,7 @@ macro_rules! visit_place_fns { if ty != new_ty { Some(PlaceElem::UnwrapUnsafeBinder(new_ty)) } else { None } } PlaceElem::Deref + | PlaceElem::PhantomDeref | PlaceElem::ConstantIndex { .. } | PlaceElem::Subslice { .. } | PlaceElem::Downcast(..) => None, @@ -1262,6 +1263,7 @@ macro_rules! visit_place_fns { ); } ProjectionElem::Deref + | ProjectionElem::PhantomDeref | ProjectionElem::Subslice { from: _, to: _, from_end: _ } | ProjectionElem::ConstantIndex { offset: _, min_length: _, from_end: _ } | ProjectionElem::Downcast(_, _) => {} diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index e92f74722626b..4c717d70c742b 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -88,6 +88,7 @@ fn convert_to_hir_projections_and_truncate_for_capture( for mir_projection in mir_projections { let hir_projection = match mir_projection { ProjectionElem::Deref => HirProjectionKind::Deref, + ProjectionElem::PhantomDeref => continue, ProjectionElem::Field(field, _) => { let variant = variant.unwrap_or(FIRST_VARIANT); HirProjectionKind::Field(*field, variant) @@ -802,6 +803,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } ProjectionElem::Field(..) + | ProjectionElem::PhantomDeref | ProjectionElem::Downcast(..) | ProjectionElem::OpaqueCast(..) | ProjectionElem::ConstantIndex { .. } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs index 83d40a5a2f284..e198a6e03fa5b 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs @@ -452,6 +452,7 @@ impl MoveSubPath { let subpath = match elem { // correspond to a MoveSubPath ProjectionKind::Deref => MoveSubPath::Deref, + ProjectionKind::PhantomDeref => return MoveSubPathResult::Skip, ProjectionKind::Field(idx, _) => MoveSubPath::Field(idx), ProjectionKind::ConstantIndex { offset, min_length: _, from_end: false } => { MoveSubPath::ConstantIndex(offset) diff --git a/compiler/rustc_mir_transform/src/coroutine/mod.rs b/compiler/rustc_mir_transform/src/coroutine/mod.rs index 6d9d0d35d3ed6..97a74626aea4f 100644 --- a/compiler/rustc_mir_transform/src/coroutine/mod.rs +++ b/compiler/rustc_mir_transform/src/coroutine/mod.rs @@ -454,7 +454,8 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { | PlaceElem::Deref | PlaceElem::ConstantIndex { .. } | PlaceElem::Subslice { .. } - | PlaceElem::Downcast(..) => None, + | PlaceElem::Downcast(..) + | PlaceElem::PhantomDeref => None, } } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 9d751a7cc5bd0..15e9e34159f76 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -861,6 +861,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { return None; } } + ProjectionElem::PhantomDeref => bug!("PhantomDeref in GVN"), ProjectionElem::Downcast(name, index) => ProjectionElem::Downcast(name, index), ProjectionElem::Field(f, _) => match self.get(value) { Value::Aggregate(_, fields) => return Some((projection_ty, fields[f.as_usize()])), diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index c971769d528bb..afc2794d6edcf 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -299,7 +299,9 @@ impl<'tcx> Validator<'_, 'tcx> { | ProjectionElem::UnwrapUnsafeBinder(_) => {} // Never recurse. - ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) => { + ProjectionElem::PhantomDeref + | ProjectionElem::OpaqueCast(..) + | ProjectionElem::Downcast(..) => { return Err(Unpromotable); } diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index 124329526028d..792c5d08e3f6e 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -444,6 +444,7 @@ impl<'tcx> Stable<'tcx> for mir::PlaceElem<'tcx> { use rustc_middle::mir::ProjectionElem::*; match self { Deref => crate::mir::ProjectionElem::Deref, + PhantomDeref => bug!("Hopefully we don't come here"), Field(idx, ty) => { crate::mir::ProjectionElem::Field(idx.stable(tables, cx), ty.stable(tables, cx)) } diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index ca2cd7338d5a2..075d42191ff95 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -323,7 +323,8 @@ fn check_place<'tcx>( | ProjectionElem::Downcast(..) | ProjectionElem::Subslice { .. } | ProjectionElem::Index(_) - | ProjectionElem::UnwrapUnsafeBinder(_) => {}, + | ProjectionElem::UnwrapUnsafeBinder(_) + | ProjectionElem::PhantomDeref => {}, } } diff --git a/tests/ui/reborrow/custom_marker_identity.rs b/tests/ui/reborrow/custom_marker_identity.rs index c0bd126f818a0..476f7011e6359 100644 --- a/tests/ui/reborrow/custom_marker_identity.rs +++ b/tests/ui/reborrow/custom_marker_identity.rs @@ -7,7 +7,7 @@ struct CustomMarker<'a>(PhantomData<&'a ()>); impl<'a> Reborrow for CustomMarker<'a> {} fn method<'a>(a: CustomMarker<'a>) -> CustomMarker<'a> { //~ERROR cannot return reference to temporary value - //~^ ERROR cannot return value referencing function parameter `a` + //~^ ERROR cannot return value referencing local data `a` a } diff --git a/tests/ui/reborrow/custom_marker_identity.stderr b/tests/ui/reborrow/custom_marker_identity.stderr index 1ea3a6bd2442b..46d8f05b42342 100644 --- a/tests/ui/reborrow/custom_marker_identity.stderr +++ b/tests/ui/reborrow/custom_marker_identity.stderr @@ -8,7 +8,7 @@ LL | | a LL | | } | |_^ returns a reference to data owned by the current function -error[E0515]: cannot return value referencing function parameter `a` +error[E0515]: cannot return value referencing local data `a` --> $DIR/custom_marker_identity.rs:9:56 | LL | fn method<'a>(a: CustomMarker<'a>) -> CustomMarker<'a> { From 3f403d3888795dd0034638fd1e72a4dbb02a5fb9 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 24 Jul 2026 12:04:06 +0300 Subject: [PATCH 3/8] Simpler deref test Co-authored-by: Oli Scherer --- tests/ui/reborrow/custom_marker_place_conflict_deref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/reborrow/custom_marker_place_conflict_deref.rs b/tests/ui/reborrow/custom_marker_place_conflict_deref.rs index 8a8119ccfe599..a0c65c9753741 100644 --- a/tests/ui/reborrow/custom_marker_place_conflict_deref.rs +++ b/tests/ui/reborrow/custom_marker_place_conflict_deref.rs @@ -10,7 +10,7 @@ impl<'a> Deref for CustomMarker<'a> { type Target = (); fn deref(&self) -> &() { - unsafe { std::mem::transmute::<&Self, &()>(self) } + &() } } From 5a4f510d1b8244949726fb282be3bbcb5133a943 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 24 Jul 2026 12:05:43 +0300 Subject: [PATCH 4/8] Add more PhantomDeref unreachability assertions --- compiler/rustc_borrowck/src/lib.rs | 5 ++++- compiler/rustc_borrowck/src/type_check/mod.rs | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index f39590fd018d2..286fc2d632b54 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2245,7 +2245,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { for (place_base, elem) in place.iter_projections().rev() { match elem { ProjectionElem::Index(_/*operand*/) - | ProjectionElem::PhantomDeref | ProjectionElem::OpaqueCast(_) // assigning to P[i] requires P to be valid. | ProjectionElem::ConstantIndex { .. } @@ -2272,6 +2271,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { break; } + ProjectionElem::PhantomDeref => { + panic!("we don't allow assignments to PhantomDeref, location {location:?}"); + } + ProjectionElem::Subslice { .. } => { panic!("we don't allow assignments to subslices, location: {location:?}"); } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 283932441354b..1d8693a889924 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2464,8 +2464,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { _ => bug!("unexpected deref ty {:?} in {:?}", base_ty, borrowed_place), } } + ProjectionElem::PhantomDeref => { + bug!("unexpected PhantomDeref in add_reborrow_constraint") + } ProjectionElem::Field(..) - | ProjectionElem::PhantomDeref | ProjectionElem::Downcast(..) | ProjectionElem::OpaqueCast(..) | ProjectionElem::Index(..) From 2c1393ffba575a4769b13ef5e0eb1584093cdbe8 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 24 Jul 2026 12:05:58 +0300 Subject: [PATCH 5/8] Write out lifetime omission --- tests/ui/reborrow/custom_marker_place_conflict.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/reborrow/custom_marker_place_conflict.rs b/tests/ui/reborrow/custom_marker_place_conflict.rs index c137e81cea584..6abbb847b9235 100644 --- a/tests/ui/reborrow/custom_marker_place_conflict.rs +++ b/tests/ui/reborrow/custom_marker_place_conflict.rs @@ -6,7 +6,7 @@ use std::marker::{Reborrow, PhantomData}; struct CustomMarker<'a>(PhantomData<&'a ()>); impl<'a> Reborrow for CustomMarker<'a> {} -fn reborrow(_: CustomMarker) {} +fn reborrow(_: CustomMarker<'_>) {} fn main() { let a = CustomMarker(PhantomData); From 767349e4344192d65e1ebd6e00c2e0f29db8e9e2 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 24 Jul 2026 13:00:54 +0300 Subject: [PATCH 6/8] Document ProjectionElem::PhantomDeref --- compiler/rustc_middle/src/mir/syntax.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index e77a790f83748..bfeec0d41e1fb 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1256,6 +1256,24 @@ pub enum ProjectionElem { /// of a place, so it doesn't necessarily constitute a move out of the binder. UnwrapUnsafeBinder(T), + /// A symbolic dereference of a `Reborrow` type that does not contain any `&mut T` fields. + /// + /// If a type is `Reborrow` and contains a `&mut T` field then reborrowing it reborrows the `T`, + /// producing a borrow on an indirect place, producing a value that can be returned from the + /// function since it does not capture any local place. If no such field exists, then + /// reborrowing the type must dereference the type itself to find an indirect place, but + /// generally such types will not implements `Deref`. Therefore, in borrow checking we instead + /// perform a "phantom dereference" (named so because the type will usually contain some + /// `PhantomData<&'a ()>` or equivalent that captures the lifetime) to access an indeterminate + /// indirect place. + /// + /// FIXME(reborrow): currently this variant is not considered an indirect place for whatever + /// reason. This variant makes no sense if that cannot be fixed. + /// + /// FIXME(reborrow): if the Reborrow traits experiment is rejected, this variant can be removed: + /// see the [PR]. + /// + /// [PR]: https://github.com/rust-lang/rust/pull/159103 PhantomDeref, } From 9c691897e29d05747f61825f90c9de23a79fc9c7 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 24 Jul 2026 12:49:32 +0300 Subject: [PATCH 7/8] Comment half of reborrow tests --- compiler/rustc_borrowck/src/type_check/mod.rs | 1 + compiler/rustc_middle/src/mir/statement.rs | 4 +++- .../ui/reborrow/coerce-shared-associated-type-field.rs | 3 +++ .../coerce-shared-associated-type-field.stderr | 2 +- tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs | 4 ++++ .../reborrow/coerce-shared-decl-macro-hygiene.stderr | 2 +- tests/ui/reborrow/coerce-shared-extra-marker.rs | 2 ++ tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs | 2 ++ .../reborrow/coerce-shared-field-lifetime-swap.stderr | 2 +- tests/ui/reborrow/coerce-shared-field-relations.rs | 7 +++++++ tests/ui/reborrow/coerce-shared-field-relations.stderr | 4 ++-- .../ui/reborrow/coerce-shared-foreign-private-field.rs | 3 +++ .../coerce-shared-foreign-private-field.stderr | 2 +- .../coerce-shared-foreign-private-tuple-field.rs | 3 +++ .../coerce-shared-foreign-private-tuple-field.stderr | 2 +- tests/ui/reborrow/coerce-shared-generics.rs | 2 ++ tests/ui/reborrow/coerce-shared-generics.stderr | 2 +- tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs | 3 +++ .../ui/reborrow/coerce-shared-lifetime-mismatch.stderr | 4 ++-- .../ui/reborrow/coerce-shared-missing-target-field.rs | 2 ++ .../reborrow/coerce-shared-missing-target-field.stderr | 2 +- .../reborrow/coerce-shared-mut-ref-field-validation.rs | 2 ++ .../coerce-shared-mut-ref-field-validation.stderr | 2 +- .../coerce-shared-omitted-reborrow-field-after-dead.rs | 10 ++++++++-- ...rce-shared-omitted-reborrow-field-after-dead.stderr | 2 +- .../coerce-shared-omitted-reborrow-field-locked.rs | 4 ++++ .../coerce-shared-omitted-reborrow-field-locked.stderr | 2 +- .../reborrow/coerce-shared-omitted-reborrow-field.rs | 3 +++ .../coerce-shared-omitted-reborrow-field.stderr | 2 +- tests/ui/reborrow/coerce-shared-reordered-field.rs | 2 ++ tests/ui/reborrow/coerce-shared-reordered-field.stderr | 2 +- tests/ui/reborrow/coerce-shared-wrong-generic.rs | 2 ++ tests/ui/reborrow/coerce-shared-wrong-generic.stderr | 2 +- tests/ui/reborrow/custom_marker.rs | 2 ++ tests/ui/reborrow/custom_marker_assign_deref.rs | 2 ++ tests/ui/reborrow/custom_marker_coerce_shared.rs | 2 ++ tests/ui/reborrow/custom_marker_coerce_shared_copy.rs | 3 +++ tests/ui/reborrow/custom_marker_coerce_shared_move.rs | 3 +++ .../reborrow/custom_marker_coerce_shared_move.stderr | 2 +- tests/ui/reborrow/custom_marker_deref.rs | 3 +++ tests/ui/reborrow/custom_marker_identity.rs | 4 ++++ tests/ui/reborrow/custom_marker_identity.stderr | 4 ++-- 42 files changed, 95 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 1d8693a889924..9e6045317e6bd 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2529,6 +2529,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } + // FIXME: copy in code from coercion.rs to re-check CoerceShared lifetime relations. if mutability.is_not() { // FIXME(reborrow): for CoerceShared we need to relate the types manually, field by // field. We cannot just attempt to relate `T` and `::Target` by diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 213ac6eb062cc..e369398239a61 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -223,7 +223,9 @@ impl<'tcx> PlaceTy<'tcx> { ); PlaceTy::from_ty(ty) } - ProjectionElem::PhantomDeref => PlaceTy::from_ty(structurally_normalize(self.ty)), + ProjectionElem::PhantomDeref => { + PlaceTy::from_ty(normalize(Unnormalized::new_wip(self.ty))) + } ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => { PlaceTy::from_ty(normalize(Unnormalized::new_wip(self.ty)).builtin_index().unwrap()) } diff --git a/tests/ui/reborrow/coerce-shared-associated-type-field.rs b/tests/ui/reborrow/coerce-shared-associated-type-field.rs index df744e9442dd8..c40abe9de7b31 100644 --- a/tests/ui/reborrow/coerce-shared-associated-type-field.rs +++ b/tests/ui/reborrow/coerce-shared-associated-type-field.rs @@ -1,3 +1,6 @@ +//! Test that CoerceShared can resolve field type equivalence through GATs. +//! This should eventually pass. + #![feature(reborrow)] #![allow(dead_code)] diff --git a/tests/ui/reborrow/coerce-shared-associated-type-field.stderr b/tests/ui/reborrow/coerce-shared-associated-type-field.stderr index 31b54e7ed6c9e..7a78190595380 100644 --- a/tests/ui/reborrow/coerce-shared-associated-type-field.stderr +++ b/tests/ui/reborrow/coerce-shared-associated-type-field.stderr @@ -1,5 +1,5 @@ error: implementing `CoerceShared` currently requires source and target to have at most one non-ZST reborrow data field - --> $DIR/coerce-shared-associated-type-field.rs:27:1 + --> $DIR/coerce-shared-associated-type-field.rs:30:1 | LL | impl<'a> CoerceShared> for MyMut<'a> {} | ^^^^^^^^^^^^^^^^^^^^^^---------^^^^^^---------^^^ diff --git a/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs b/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs index 3b6e9e25d8bb4..59e0a8d96a33e 100644 --- a/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs +++ b/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs @@ -1,3 +1,7 @@ +//! Test that Reborrow and CoerceShared can be derived in macros. +//! This should eventually pass. + + #![feature(reborrow, decl_macro)] #![allow(incomplete_features)] diff --git a/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.stderr b/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.stderr index 62a73361b5cd9..0c9d89dd009ec 100644 --- a/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.stderr +++ b/tests/ui/reborrow/coerce-shared-decl-macro-hygiene.stderr @@ -1,5 +1,5 @@ error: implementing `CoerceShared` currently requires source and target to have at most one non-ZST reborrow data field - --> $DIR/coerce-shared-decl-macro-hygiene.rs:20:5 + --> $DIR/coerce-shared-decl-macro-hygiene.rs:24:5 | LL | impl<'a> CoerceShared> for MyMut<'a> {} | ^^^^^^^^^^^^^^^^^^^^^^---------^^^^^^---------^^^ diff --git a/tests/ui/reborrow/coerce-shared-extra-marker.rs b/tests/ui/reborrow/coerce-shared-extra-marker.rs index 40026d68d5dca..d32d5c3ec1fff 100644 --- a/tests/ui/reborrow/coerce-shared-extra-marker.rs +++ b/tests/ui/reborrow/coerce-shared-extra-marker.rs @@ -1,5 +1,7 @@ //@ run-pass +//! Test that CoerceShared can drop a PhantomData marker field and pass a data reference through. + #![feature(reborrow)] #![allow(dead_code)] diff --git a/tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs b/tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs index 9d102238467e6..71caf8a7ec6bf 100644 --- a/tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs +++ b/tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs @@ -1,3 +1,5 @@ +//! Test that CoerceShared cannot be used to swap 'static and 'a lifetimes around. + #![feature(reborrow)] use std::marker::{CoerceShared, Reborrow}; diff --git a/tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr b/tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr index b6160ad40bcce..4958640257918 100644 --- a/tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr +++ b/tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr @@ -1,5 +1,5 @@ error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field - --> $DIR/coerce-shared-field-lifetime-swap.rs:14:5 + --> $DIR/coerce-shared-field-lifetime-swap.rs:16:5 | LL | x: &'static (), | -------------- source field `x` has type `&'static ()` diff --git a/tests/ui/reborrow/coerce-shared-field-relations.rs b/tests/ui/reborrow/coerce-shared-field-relations.rs index 3920f3eba7cb5..63c82bab9606d 100644 --- a/tests/ui/reborrow/coerce-shared-field-relations.rs +++ b/tests/ui/reborrow/coerce-shared-field-relations.rs @@ -1,3 +1,5 @@ +//! Test that CoerceShared cannot produce a field from thin air. + #![feature(reborrow)] use std::marker::{CoerceShared, Reborrow}; @@ -13,6 +15,7 @@ struct CustomRef<'a, T> { value: &'a T, } +// No error expected here: value: &'a mut T -> value: &'a T. impl<'a, T> CoerceShared> for CustomMut<'a, T> {} struct RenamedMut<'a, T> { @@ -27,6 +30,8 @@ struct RenamedRef<'a, T> { //~^ ERROR } +// Should error: source: &'a mut T -> target: &'a T attempts to drop 'source' and produce +// 'target' from thin air. impl<'a, T> CoerceShared> for RenamedMut<'a, T> {} struct BadMut<'a, T> { @@ -42,6 +47,8 @@ struct BadRef<'a, T> { _marker: std::marker::PhantomData, } +// Should error: value: &'a mut T -> &'a u32 attempts a reference transmute, and also +// '_marker' field is created from thin air. impl<'a, T> CoerceShared> for BadMut<'a, T> {} fn good(_value: CustomRef<'_, u32>) {} diff --git a/tests/ui/reborrow/coerce-shared-field-relations.stderr b/tests/ui/reborrow/coerce-shared-field-relations.stderr index 2a723f954490b..33c04d7b7f1b5 100644 --- a/tests/ui/reborrow/coerce-shared-field-relations.stderr +++ b/tests/ui/reborrow/coerce-shared-field-relations.stderr @@ -1,5 +1,5 @@ error: implementing `CoerceShared` requires every target field to have a corresponding source field - --> $DIR/coerce-shared-field-relations.rs:26:5 + --> $DIR/coerce-shared-field-relations.rs:29:5 | LL | target: &'a T, | ^^^^^^^^^^^^^ target field `target` has no corresponding source field @@ -8,7 +8,7 @@ LL | impl<'a, T> CoerceShared> for RenamedMut<'a, T> {} | ----------------- source type `RenamedMut` does not contain field `target` error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field - --> $DIR/coerce-shared-field-relations.rs:40:5 + --> $DIR/coerce-shared-field-relations.rs:45:5 | LL | value: &'a mut T, | ---------------- source field `value` has type `&'a mut T` diff --git a/tests/ui/reborrow/coerce-shared-foreign-private-field.rs b/tests/ui/reborrow/coerce-shared-foreign-private-field.rs index 66c8ece3bd43e..fc039df249eae 100644 --- a/tests/ui/reborrow/coerce-shared-foreign-private-field.rs +++ b/tests/ui/reborrow/coerce-shared-foreign-private-field.rs @@ -1,5 +1,7 @@ //@ aux-build: reborrow_foreign_private.rs +//! Test that CoerceShared cannot be implemented targeting a foreign struct with private fields. + #![feature(reborrow)] extern crate reborrow_foreign_private; @@ -13,6 +15,7 @@ struct LocalMut<'a> { impl<'a> Reborrow for LocalMut<'a> {} +// Should error: ForeignRef has private fields. impl<'a> CoerceShared> for LocalMut<'a> {} //~^ ERROR diff --git a/tests/ui/reborrow/coerce-shared-foreign-private-field.stderr b/tests/ui/reborrow/coerce-shared-foreign-private-field.stderr index a328084260d0d..a00a6b84ec788 100644 --- a/tests/ui/reborrow/coerce-shared-foreign-private-field.stderr +++ b/tests/ui/reborrow/coerce-shared-foreign-private-field.stderr @@ -1,5 +1,5 @@ error: implementing `CoerceShared` requires all target type fields to be accessible from the impl - --> $DIR/coerce-shared-foreign-private-field.rs:16:1 + --> $DIR/coerce-shared-foreign-private-field.rs:19:1 | LL | impl<'a> CoerceShared> for LocalMut<'a> {} | ^^^^^^^^^^^^^^^^^^^^^^--------------^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs b/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs index a2b88af04eb50..466fe2c315263 100644 --- a/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs +++ b/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs @@ -1,3 +1,6 @@ +//! Test that CoerceShared cannot be implemented targeting a foreign tuple struct with private +//! fields. + #![feature(reborrow)] use std::marker::{CoerceShared, PhantomData, Reborrow}; diff --git a/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.stderr b/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.stderr index b699e1affbc49..2ae5963bbc55b 100644 --- a/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.stderr +++ b/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.stderr @@ -1,5 +1,5 @@ error: implementing `CoerceShared` requires all target type fields to be accessible from the impl - --> $DIR/coerce-shared-foreign-private-tuple-field.rs:18:1 + --> $DIR/coerce-shared-foreign-private-tuple-field.rs:21:1 | LL | impl<'a> CoerceShared> for LocalPtrMut<'a> {} | ^^^^^^^^^^^^^^^^^^^^^^-----------------^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/reborrow/coerce-shared-generics.rs b/tests/ui/reborrow/coerce-shared-generics.rs index 9edab02835761..2a609ffd1c2df 100644 --- a/tests/ui/reborrow/coerce-shared-generics.rs +++ b/tests/ui/reborrow/coerce-shared-generics.rs @@ -1,3 +1,5 @@ +//! Test that Reborrow and CoerceShared can be implemented with generics. + #![feature(reborrow)] #![allow(dead_code)] diff --git a/tests/ui/reborrow/coerce-shared-generics.stderr b/tests/ui/reborrow/coerce-shared-generics.stderr index 8e2e4e4485918..2be03590fb657 100644 --- a/tests/ui/reborrow/coerce-shared-generics.stderr +++ b/tests/ui/reborrow/coerce-shared-generics.stderr @@ -1,5 +1,5 @@ error: implementing `CoerceShared` currently requires source and target to have at most one non-ZST reborrow data field - --> $DIR/coerce-shared-generics.rs:26:1 + --> $DIR/coerce-shared-generics.rs:28:1 | LL | impl<'a, T, U: Copy, const N: usize> CoerceShared> | ^ ---------------------- target type has 2 non-ZST reborrow data fields diff --git a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs index b6b5471adb0fc..be49673a0ad9d 100644 --- a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs +++ b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs @@ -1,3 +1,5 @@ +//! Test that CoerceShared cannot be implemented with spurious 'static lifetimes. + #![feature(reborrow)] // The impl is accepted, but using it to coerce a local marker into a `'static` @@ -12,6 +14,7 @@ impl<'a> Reborrow for CustomMarker<'a> {} #[derive(Clone, Copy)] struct StaticMarkerRef<'a>(PhantomData<&'a ()>); +// Should error: for two types with only one lifetime each, both should use the same lifetime. impl<'a> CoerceShared> for CustomMarker<'a> {} //~^ ERROR diff --git a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr index 7c2e3e22b0b51..036b245678039 100644 --- a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr +++ b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr @@ -1,5 +1,5 @@ error: implementing `CoerceShared` requires source and target to use the same reborrow lifetime argument - --> $DIR/coerce-shared-lifetime-mismatch.rs:15:10 + --> $DIR/coerce-shared-lifetime-mismatch.rs:18:10 | LL | impl<'a> CoerceShared> for CustomMarker<'a> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------^^ -- source reborrow lifetime @@ -7,7 +7,7 @@ LL | impl<'a> CoerceShared> for CustomMarker<'a> {} | target reborrow lifetime error[E0597]: `a` does not live long enough - --> $DIR/coerce-shared-lifetime-mismatch.rs:22:12 + --> $DIR/coerce-shared-lifetime-mismatch.rs:25:12 | LL | let a = CustomMarker(PhantomData); | - binding `a` declared here diff --git a/tests/ui/reborrow/coerce-shared-missing-target-field.rs b/tests/ui/reborrow/coerce-shared-missing-target-field.rs index edd843b041fa8..dc96610d3703d 100644 --- a/tests/ui/reborrow/coerce-shared-missing-target-field.rs +++ b/tests/ui/reborrow/coerce-shared-missing-target-field.rs @@ -1,3 +1,5 @@ +//! Test that CoerceShared cannot create a field from thin air. + #![feature(reborrow)] use std::marker::{CoerceShared, Reborrow}; diff --git a/tests/ui/reborrow/coerce-shared-missing-target-field.stderr b/tests/ui/reborrow/coerce-shared-missing-target-field.stderr index 148cf8addf0f9..0884d52f41b19 100644 --- a/tests/ui/reborrow/coerce-shared-missing-target-field.stderr +++ b/tests/ui/reborrow/coerce-shared-missing-target-field.stderr @@ -1,5 +1,5 @@ error: implementing `CoerceShared` requires every target field to have a corresponding source field - --> $DIR/coerce-shared-missing-target-field.rs:14:5 + --> $DIR/coerce-shared-missing-target-field.rs:16:5 | LL | len: usize, | ^^^^^^^^^^ target field `len` has no corresponding source field diff --git a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs index 2a18d0dda06f0..ee7685242070e 100644 --- a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs +++ b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs @@ -1,3 +1,5 @@ +//! Test that reference shared coercing does not allow changing lifetime relations. + #![feature(reborrow)] use std::marker::{CoerceShared, Reborrow}; diff --git a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr index 98ab275b31e48..57159ff8ddd56 100644 --- a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr +++ b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr @@ -1,5 +1,5 @@ error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field - --> $DIR/coerce-shared-mut-ref-field-validation.rs:50:5 + --> $DIR/coerce-shared-mut-ref-field-validation.rs:52:5 | LL | value: &'a mut &'a (), | --------------------- source field `value` has type `&'a mut &'a ()` diff --git a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.rs b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.rs index 4f066079c749b..174aeabd3ebe5 100644 --- a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.rs +++ b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.rs @@ -1,3 +1,7 @@ +//! Test that CoerceShared does not capture an omitted field, and that captured fields do not stay +//! captured after the local lifetime ends. +//! This should eventually pass. + #![feature(reborrow)] #![allow(dead_code)] @@ -46,6 +50,8 @@ fn main() { read(wrapped); } - extra_value = 3; - assert_eq!(extra_value, 3); + value = 3; + assert_eq!(value, 3); + extra_value = 4; + assert_eq!(extra_value, 4); } diff --git a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.stderr b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.stderr index d0f2540ccbcff..7a92db2b1323a 100644 --- a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.stderr +++ b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.stderr @@ -1,5 +1,5 @@ error: implementing `CoerceShared` currently requires source and target to have at most one non-ZST reborrow data field - --> $DIR/coerce-shared-omitted-reborrow-field-after-dead.rs:31:1 + --> $DIR/coerce-shared-omitted-reborrow-field-after-dead.rs:35:1 | LL | impl<'a, T> CoerceShared> for OmitMut<'a, T> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^--------------^^^^^^--------------^^^ diff --git a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.rs b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.rs index fb4eb86781a0e..b0985c4f974bd 100644 --- a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.rs +++ b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.rs @@ -1,3 +1,7 @@ +//! Test that CoerceShared doesn't capture an omitted field, and that the source's omitted field can +//! be used as exclusive while the captured field is still captured. +//! This should eventually pass. + #![feature(reborrow)] use std::marker::{CoerceShared, Reborrow}; diff --git a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.stderr b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.stderr index 4c41ea5ea1175..a5426a3b4dc81 100644 --- a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.stderr +++ b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.stderr @@ -1,5 +1,5 @@ error: implementing `CoerceShared` currently requires source and target to have at most one non-ZST reborrow data field - --> $DIR/coerce-shared-omitted-reborrow-field-locked.rs:30:1 + --> $DIR/coerce-shared-omitted-reborrow-field-locked.rs:34:1 | LL | impl<'a, T> CoerceShared> for OmitMut<'a, T> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^--------------^^^^^^--------------^^^ diff --git a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field.rs b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field.rs index 55cc010c19af4..f12c29416bdba 100644 --- a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field.rs +++ b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field.rs @@ -1,3 +1,6 @@ +//! Test that CoerceShared can omit a reborrowed field. +//! This should eventually pass. + #![feature(reborrow)] #![allow(dead_code)] diff --git a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field.stderr b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field.stderr index 08ddea2329405..fb8b6384143b3 100644 --- a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field.stderr +++ b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field.stderr @@ -1,5 +1,5 @@ error: implementing `CoerceShared` currently requires source and target to have at most one non-ZST reborrow data field - --> $DIR/coerce-shared-omitted-reborrow-field.rs:31:1 + --> $DIR/coerce-shared-omitted-reborrow-field.rs:34:1 | LL | impl<'a, T> CoerceShared> for OmitMut<'a, T> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^--------------^^^^^^--------------^^^ diff --git a/tests/ui/reborrow/coerce-shared-reordered-field.rs b/tests/ui/reborrow/coerce-shared-reordered-field.rs index f4630fe1f7d83..b182d9df26f59 100644 --- a/tests/ui/reborrow/coerce-shared-reordered-field.rs +++ b/tests/ui/reborrow/coerce-shared-reordered-field.rs @@ -1,3 +1,5 @@ +//! Test that CoerceShared can be implemented even if field order changes. + #![feature(reborrow)] #![allow(dead_code)] diff --git a/tests/ui/reborrow/coerce-shared-reordered-field.stderr b/tests/ui/reborrow/coerce-shared-reordered-field.stderr index 5469e5e3d9f49..4dbc344aa9196 100644 --- a/tests/ui/reborrow/coerce-shared-reordered-field.stderr +++ b/tests/ui/reborrow/coerce-shared-reordered-field.stderr @@ -1,5 +1,5 @@ error: implementing `CoerceShared` currently requires source and target to have at most one non-ZST reborrow data field - --> $DIR/coerce-shared-reordered-field.rs:19:1 + --> $DIR/coerce-shared-reordered-field.rs:21:1 | LL | impl<'a> CoerceShared> for ReorderMut<'a> {} | ^^^^^^^^^^^^^^^^^^^^^^--------------^^^^^^--------------^^^ diff --git a/tests/ui/reborrow/coerce-shared-wrong-generic.rs b/tests/ui/reborrow/coerce-shared-wrong-generic.rs index bbd9cfebcd9e8..b3dee8e8a9eb2 100644 --- a/tests/ui/reborrow/coerce-shared-wrong-generic.rs +++ b/tests/ui/reborrow/coerce-shared-wrong-generic.rs @@ -1,3 +1,5 @@ +//! Test that CoerceShared cannot switch generic type usage around. + #![feature(reborrow)] use std::marker::{CoerceShared, PhantomData, Reborrow}; diff --git a/tests/ui/reborrow/coerce-shared-wrong-generic.stderr b/tests/ui/reborrow/coerce-shared-wrong-generic.stderr index b037106ebb619..2a472cee46af2 100644 --- a/tests/ui/reborrow/coerce-shared-wrong-generic.stderr +++ b/tests/ui/reborrow/coerce-shared-wrong-generic.stderr @@ -1,5 +1,5 @@ error: implementing `CoerceShared` requires corresponding fields to match, be reborrowable with `CoerceShared`, or coerce a mutable reference field to a shared reference field - --> $DIR/coerce-shared-wrong-generic.rs:14:5 + --> $DIR/coerce-shared-wrong-generic.rs:16:5 | LL | value: &'a mut T, | ---------------- source field `value` has type `&'a mut T` diff --git a/tests/ui/reborrow/custom_marker.rs b/tests/ui/reborrow/custom_marker.rs index 80689d81d0cc1..e51990a1a8f0e 100644 --- a/tests/ui/reborrow/custom_marker.rs +++ b/tests/ui/reborrow/custom_marker.rs @@ -1,5 +1,7 @@ //@ run-pass +//! Test that Reborrow on a custom ZST marker type reborrows the value automatically. + #![feature(reborrow)] use std::marker::{Reborrow, PhantomData}; diff --git a/tests/ui/reborrow/custom_marker_assign_deref.rs b/tests/ui/reborrow/custom_marker_assign_deref.rs index 79ea2a35acdaf..f46142b4878da 100644 --- a/tests/ui/reborrow/custom_marker_assign_deref.rs +++ b/tests/ui/reborrow/custom_marker_assign_deref.rs @@ -1,5 +1,7 @@ //@ run-pass +//! Test that assignment to DerefMut of a Reborrow type does not ICE. + #![feature(reborrow)] use std::marker::{Reborrow, PhantomData}; diff --git a/tests/ui/reborrow/custom_marker_coerce_shared.rs b/tests/ui/reborrow/custom_marker_coerce_shared.rs index 17c7bac98d17a..006b92054bc37 100644 --- a/tests/ui/reborrow/custom_marker_coerce_shared.rs +++ b/tests/ui/reborrow/custom_marker_coerce_shared.rs @@ -1,5 +1,7 @@ //@ run-pass +//! Test that CoerceShared of custom ZST marker type reborrows the type automatically as shared. + #![feature(reborrow)] use std::marker::{CoerceShared, PhantomData, Reborrow}; diff --git a/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs b/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs index 56bc1f896da0f..8e86a95223946 100644 --- a/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs +++ b/tests/ui/reborrow/custom_marker_coerce_shared_copy.rs @@ -1,5 +1,8 @@ //@ run-pass +//! Test that CoerceShared of custom ZST marker type reborrows the type automatically as shared and +//! the original stays concurrently usable through shared references. + #![feature(reborrow)] use std::marker::{CoerceShared, PhantomData, Reborrow}; diff --git a/tests/ui/reborrow/custom_marker_coerce_shared_move.rs b/tests/ui/reborrow/custom_marker_coerce_shared_move.rs index 532d13da258c8..7fa06bb55aed1 100644 --- a/tests/ui/reborrow/custom_marker_coerce_shared_move.rs +++ b/tests/ui/reborrow/custom_marker_coerce_shared_move.rs @@ -1,3 +1,6 @@ +//! Test that CoerceShared of custom ZST marker type reborrows the type automatically as shared but +//! moving the original invalidates the results. + #![feature(reborrow)] use std::marker::{CoerceShared, PhantomData, Reborrow}; diff --git a/tests/ui/reborrow/custom_marker_coerce_shared_move.stderr b/tests/ui/reborrow/custom_marker_coerce_shared_move.stderr index 90382af3ce30e..f0ad934cacbf3 100644 --- a/tests/ui/reborrow/custom_marker_coerce_shared_move.stderr +++ b/tests/ui/reborrow/custom_marker_coerce_shared_move.stderr @@ -1,5 +1,5 @@ error[E0505]: cannot move out of `a` because it is borrowed - --> $DIR/custom_marker_coerce_shared_move.rs:19:14 + --> $DIR/custom_marker_coerce_shared_move.rs:22:14 | LL | let a = CustomMarker(PhantomData); | - binding `a` declared here diff --git a/tests/ui/reborrow/custom_marker_deref.rs b/tests/ui/reborrow/custom_marker_deref.rs index 74b9bac22ed0e..acf989985f7f1 100644 --- a/tests/ui/reborrow/custom_marker_deref.rs +++ b/tests/ui/reborrow/custom_marker_deref.rs @@ -1,5 +1,8 @@ //@ run-pass +//! Test that CoerceShared of custom ZST marker type reborrows the type automatically from a +//! `&mut CustomMarker` deref. + #![feature(reborrow)] use std::marker::{Reborrow, PhantomData}; diff --git a/tests/ui/reborrow/custom_marker_identity.rs b/tests/ui/reborrow/custom_marker_identity.rs index 476f7011e6359..75fb62e761f46 100644 --- a/tests/ui/reborrow/custom_marker_identity.rs +++ b/tests/ui/reborrow/custom_marker_identity.rs @@ -1,5 +1,9 @@ //@ check-fail +//! Check that the result of a Reborrow retains the original lifetime and does not capture local +//! values, therefore enabling an identity function to compile. +//! This should eventually pass. + #![feature(reborrow)] use std::marker::{Reborrow, PhantomData}; diff --git a/tests/ui/reborrow/custom_marker_identity.stderr b/tests/ui/reborrow/custom_marker_identity.stderr index 46d8f05b42342..455f2b083dd30 100644 --- a/tests/ui/reborrow/custom_marker_identity.stderr +++ b/tests/ui/reborrow/custom_marker_identity.stderr @@ -1,5 +1,5 @@ error[E0515]: cannot return reference to temporary value - --> $DIR/custom_marker_identity.rs:9:56 + --> $DIR/custom_marker_identity.rs:13:56 | LL | fn method<'a>(a: CustomMarker<'a>) -> CustomMarker<'a> { | ________________________________________________________^ @@ -9,7 +9,7 @@ LL | | } | |_^ returns a reference to data owned by the current function error[E0515]: cannot return value referencing local data `a` - --> $DIR/custom_marker_identity.rs:9:56 + --> $DIR/custom_marker_identity.rs:13:56 | LL | fn method<'a>(a: CustomMarker<'a>) -> CustomMarker<'a> { | ________________________________________________________^ From d815250335735a4e0095e0c777699ad28eb87550 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Mon, 27 Jul 2026 21:09:23 +0300 Subject: [PATCH 8/8] fix PhantomDeref conflicting with AccessDepth::Shallow --- .../rustc_borrowck/src/places_conflict.rs | 13 +++++----- compiler/rustc_middle/src/mir/pretty.rs | 6 +++-- compiler/rustc_middle/src/mir/statement.rs | 5 +++- .../rustc_mir_dataflow/src/move_paths/mod.rs | 4 +++- .../coerce-shared-lifetime-mismatch.rs | 2 -- .../coerce-shared-lifetime-mismatch.stderr | 19 ++------------- tests/ui/reborrow/custom_marker_identity.rs | 5 ++-- .../ui/reborrow/custom_marker_identity.stderr | 24 ------------------- .../reborrow/reborrow-promotion-rejected.rs | 3 +-- .../reborrow-promotion-rejected.stderr | 13 ---------- 10 files changed, 23 insertions(+), 71 deletions(-) delete mode 100644 tests/ui/reborrow/custom_marker_identity.stderr delete mode 100644 tests/ui/reborrow/reborrow-promotion-rejected.stderr diff --git a/compiler/rustc_borrowck/src/places_conflict.rs b/compiler/rustc_borrowck/src/places_conflict.rs index e35dd3eb082ca..6afc03d0ecf29 100644 --- a/compiler/rustc_borrowck/src/places_conflict.rs +++ b/compiler/rustc_borrowck/src/places_conflict.rs @@ -234,6 +234,13 @@ fn place_components_conflict<'tcx>( return false; } + (ProjectionElem::PhantomDeref, _, Shallow(None)) => { + // e.g., a reborrow of `x.y` while we shallowly access `x.y` or some prefix + // thereof - the shallow access cannot invalidate the reborrowed copy. + debug!("borrow_conflicts_with_place: shallow access behind reborrow"); + return false; + } + (ProjectionElem::Field { .. }, ty::Adt(def, _), AccessDepth::Drop) => { // Drop can read/write arbitrary projections, so places // conflict regardless of further projections. @@ -510,12 +517,6 @@ fn place_projection_conflict<'tcx>( debug!("place_element_conflict: DISJOINT-OR-EQ-SLICE-SUBSLICES"); Overlap::EqualOrDisjoint } - (ProjectionElem::PhantomDeref, ProjectionElem::Field(idx, _)) - | (ProjectionElem::Field(idx, _), ProjectionElem::PhantomDeref) => { - eprintln!("idx: {idx:?}"); - debug!("place_element_conflict: DISJOINT-OR-EQ-PHANTOM-DEREF-FIELD"); - Overlap::EqualOrDisjoint - } ( ProjectionElem::Deref | ProjectionElem::PhantomDeref diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index a1685e80c8469..7b35d9eb48868 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1352,8 +1352,7 @@ fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> match elem { ProjectionElem::OpaqueCast(_) | ProjectionElem::Downcast(_, _) - | ProjectionElem::Field(_, _) - | ProjectionElem::PhantomDeref => { + | ProjectionElem::Field(_, _) => { write!(fmt, "(")?; } ProjectionElem::Deref => { @@ -1365,6 +1364,9 @@ fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> ProjectionElem::UnwrapUnsafeBinder(_) => { write!(fmt, "unwrap_binder!(")?; } + ProjectionElem::PhantomDeref => { + write!(fmt, "reborrow!(")?; + } } } diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index e369398239a61..060d9ba661cc4 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -501,7 +501,10 @@ impl<'tcx> PlaceRef<'tcx> { pub fn local_or_deref_local(&self) -> Option { match *self { PlaceRef { local, projection: [] } - | PlaceRef { local, projection: [ProjectionElem::Deref] } => Some(local), + | PlaceRef { + local, + projection: [ProjectionElem::Deref | ProjectionElem::PhantomDeref], + } => Some(local), _ => None, } } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs index e198a6e03fa5b..b6565588ae3f1 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs @@ -452,7 +452,9 @@ impl MoveSubPath { let subpath = match elem { // correspond to a MoveSubPath ProjectionKind::Deref => MoveSubPath::Deref, - ProjectionKind::PhantomDeref => return MoveSubPathResult::Skip, + ProjectionKind::PhantomDeref => { + unreachable!("unexpected PhantomDeref in MoveSubPath::of") + } ProjectionKind::Field(idx, _) => MoveSubPath::Field(idx), ProjectionKind::ConstantIndex { offset, min_length: _, from_end: false } => { MoveSubPath::ConstantIndex(offset) diff --git a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs index be49673a0ad9d..c7e3b4b12d4bc 100644 --- a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs +++ b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs @@ -14,7 +14,6 @@ impl<'a> Reborrow for CustomMarker<'a> {} #[derive(Clone, Copy)] struct StaticMarkerRef<'a>(PhantomData<&'a ()>); -// Should error: for two types with only one lifetime each, both should use the same lifetime. impl<'a> CoerceShared> for CustomMarker<'a> {} //~^ ERROR @@ -23,5 +22,4 @@ fn method(_a: StaticMarkerRef<'static>) {} fn main() { let a = CustomMarker(PhantomData); method(a); - //~^ ERROR } diff --git a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr index 036b245678039..38c3d3637b566 100644 --- a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr +++ b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr @@ -1,25 +1,10 @@ error: implementing `CoerceShared` requires source and target to use the same reborrow lifetime argument - --> $DIR/coerce-shared-lifetime-mismatch.rs:18:10 + --> $DIR/coerce-shared-lifetime-mismatch.rs:17:10 | LL | impl<'a> CoerceShared> for CustomMarker<'a> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------^^ -- source reborrow lifetime | | | target reborrow lifetime -error[E0597]: `a` does not live long enough - --> $DIR/coerce-shared-lifetime-mismatch.rs:25:12 - | -LL | let a = CustomMarker(PhantomData); - | - binding `a` declared here -LL | method(a); - | -------^- - | | | - | | borrowed value does not live long enough - | argument requires that `a` is borrowed for `'static` -LL | -LL | } - | - `a` dropped here while still borrowed - -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/reborrow/custom_marker_identity.rs b/tests/ui/reborrow/custom_marker_identity.rs index 75fb62e761f46..27038a803d86c 100644 --- a/tests/ui/reborrow/custom_marker_identity.rs +++ b/tests/ui/reborrow/custom_marker_identity.rs @@ -1,4 +1,4 @@ -//@ check-fail +//@ check-pass //! Check that the result of a Reborrow retains the original lifetime and does not capture local //! values, therefore enabling an identity function to compile. @@ -10,8 +10,7 @@ use std::marker::{Reborrow, PhantomData}; struct CustomMarker<'a>(PhantomData<&'a ()>); impl<'a> Reborrow for CustomMarker<'a> {} -fn method<'a>(a: CustomMarker<'a>) -> CustomMarker<'a> { //~ERROR cannot return reference to temporary value - //~^ ERROR cannot return value referencing local data `a` +fn method<'a>(a: CustomMarker<'a>) -> CustomMarker<'a> { a } diff --git a/tests/ui/reborrow/custom_marker_identity.stderr b/tests/ui/reborrow/custom_marker_identity.stderr deleted file mode 100644 index 455f2b083dd30..0000000000000 --- a/tests/ui/reborrow/custom_marker_identity.stderr +++ /dev/null @@ -1,24 +0,0 @@ -error[E0515]: cannot return reference to temporary value - --> $DIR/custom_marker_identity.rs:13:56 - | -LL | fn method<'a>(a: CustomMarker<'a>) -> CustomMarker<'a> { - | ________________________________________________________^ -LL | | -LL | | a -LL | | } - | |_^ returns a reference to data owned by the current function - -error[E0515]: cannot return value referencing local data `a` - --> $DIR/custom_marker_identity.rs:13:56 - | -LL | fn method<'a>(a: CustomMarker<'a>) -> CustomMarker<'a> { - | ________________________________________________________^ -LL | | -LL | | a - | | - `a` is borrowed here -LL | | } - | |_^ returns a value referencing data owned by the current function - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0515`. diff --git a/tests/ui/reborrow/reborrow-promotion-rejected.rs b/tests/ui/reborrow/reborrow-promotion-rejected.rs index 38366cd9ac2a7..7265c25da9795 100644 --- a/tests/ui/reborrow/reborrow-promotion-rejected.rs +++ b/tests/ui/reborrow/reborrow-promotion-rejected.rs @@ -1,4 +1,4 @@ -//@ check-fail +//@ check-pass #![feature(reborrow)] @@ -16,6 +16,5 @@ const fn coerce(x: MyRef<'_>) -> MyRef<'_> { } static BAD: &'static MyRef<'static> = &coerce(MyMut(&1)); -//~^ ERROR temporary value dropped while borrowed fn main() {} diff --git a/tests/ui/reborrow/reborrow-promotion-rejected.stderr b/tests/ui/reborrow/reborrow-promotion-rejected.stderr deleted file mode 100644 index f7e1560f02089..0000000000000 --- a/tests/ui/reborrow/reborrow-promotion-rejected.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0716]: temporary value dropped while borrowed - --> $DIR/reborrow-promotion-rejected.rs:18:47 - | -LL | static BAD: &'static MyRef<'static> = &coerce(MyMut(&1)); - | --------^^^^^^^^^- - | | | | - | | | temporary value is freed at the end of this statement - | | creates a temporary value which is freed while still in use - | using this value as a static requires that borrow lasts for `'static` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0716`.