From 9d00dad303a0c4cd9169f349b6fec7109b7723ef Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 28 May 2026 18:18:42 +0300 Subject: [PATCH 1/7] 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 4f778ed461514..3b1c813fd1e4e 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::{RegionUtilitiesExt, RegionVid, TyCtxt}; use rustc_middle::{bug, span_bug, ty}; @@ -261,6 +261,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> { @@ -319,23 +465,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:?}" ) @@ -344,24 +481,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 d718103c80e7f..3610bcea23b64 100644 --- a/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.stderr +++ b/tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.stderr @@ -4,18 +4,5 @@ error: implementing `CoerceShared` does not allow multiple lifetimes or fields t LL | impl<'a, T> CoerceShared> for OmitMut<'a, T> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -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 549d21bfd37ec7f818ee8f948732c6a856ad5f33 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 19 Jul 2026 17:52:26 +0300 Subject: [PATCH 2/7] 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 3b1c813fd1e4e..b130854f7932d 100644 --- a/compiler/rustc_borrowck/src/borrow_set.rs +++ b/compiler/rustc_borrowck/src/borrow_set.rs @@ -390,7 +390,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" @@ -402,7 +404,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 e3e36f9bbc715..72b652f3e2f66 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -4212,6 +4212,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 e9c1c1d57b936..06333c7e1ba06 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 cce2bba7b3365..bfd9fde92ed34 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 31208d1bd5ad8..3ab8ac7270aa7 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2021,7 +2021,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); @@ -2245,6 +2246,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 { .. } @@ -2636,6 +2638,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 370d43d35d3ac..f0dca434e1470 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1896,6 +1896,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 { .. } @@ -2467,6 +2468,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 451983a9053b8..edbe7beb633b2 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -989,6 +989,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 b592e4a339346..0f4cdc165c3ec 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 d6578def58692..f1a9a98e933b3 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 27f91b2b89b2c..5d489976fecc8 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 5215dffed0717..91cbcb9da73b4 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -222,6 +222,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(structurally_normalize(self.ty).builtin_index().unwrap()) } @@ -265,7 +266,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(_) @@ -287,7 +288,8 @@ impl ProjectionElem { | Self::ConstantIndex { .. } | Self::Subslice { .. } | Self::Downcast(_, _) - | Self::UnwrapUnsafeBinder(..) => true, + | Self::UnwrapUnsafeBinder(..) + | Self::PhantomDeref => true, } } @@ -311,7 +313,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, @@ -331,6 +334,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) } @@ -565,6 +569,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 5771019ddca46..296dac59ade08 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 56f6ac4368d5f..6b9dcd310993e 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -1179,6 +1179,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, @@ -1263,6 +1264,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 7f8872b3e3493..c2ae87b8fe355 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs @@ -409,6 +409,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 a64d23b0b939f..df3d67f6a4d5d 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 f00dd3fde5aac..2c640b27ec6c7 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -864,6 +864,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 72f15d3c35b2a..3bf7acfcbb71b 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -296,7 +296,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 7233df686cc82..5fb13130b89ce 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -425,6 +425,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 08944d20f8ace..7fdd3ff747a1e 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 @@ -322,7 +322,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 9205375b15dce0d684e61e1226e719a14f61100e Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 24 Jul 2026 12:04:06 +0300 Subject: [PATCH 3/7] 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 a39a0315134bb723c5df49d683bd64fcb7454d75 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 24 Jul 2026 12:05:43 +0300 Subject: [PATCH 4/7] 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 3ab8ac7270aa7..9c31c1dba5346 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2246,7 +2246,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 { .. } @@ -2273,6 +2272,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 f0dca434e1470..2371530ffd0e1 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2467,8 +2467,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 ad2fea02092b18d704fbfc0d610b22df3ff8daf3 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 24 Jul 2026 12:05:58 +0300 Subject: [PATCH 5/7] 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 f2396b11136e12d15d04dae8167daf5001468328 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 24 Jul 2026 13:00:54 +0300 Subject: [PATCH 6/7] 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 296dac59ade08..68a689d3ca95b 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 44ad71ed5ecba9eb96388e4cfe8b71bcec85dbc8 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Fri, 24 Jul 2026 12:49:32 +0300 Subject: [PATCH 7/7] Comment half of reborrow tests --- .../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 | 2 +- .../ui/reborrow/coerce-shared-foreign-private-field.rs | 3 +++ .../coerce-shared-foreign-private-tuple-field.rs | 4 ++++ 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 | 2 +- .../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 | 5 +++++ .../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 ++-- 37 files changed, 90 insertions(+), 17 deletions(-) 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 d533fa2eb8748..2cd67dd98e58d 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` does not allow multiple lifetimes or fields to be coerced - --> $DIR/coerce-shared-associated-type-field.rs:27:10 + --> $DIR/coerce-shared-associated-type-field.rs:30:10 | 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 0b0b7bf048591..6e4b5120fc31b 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` does not allow multiple lifetimes or fields to be coerced - --> $DIR/coerce-shared-decl-macro-hygiene.rs:20:14 + --> $DIR/coerce-shared-decl-macro-hygiene.rs:24:14 | 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 d4558dd35d775..3fd54040fd391 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 bce87c68eb912..2fbbe8c72f2cc 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` does not allow multiple lifetimes or fields to be coerced - --> $DIR/coerce-shared-field-lifetime-swap.rs:18:10 + --> $DIR/coerce-shared-field-lifetime-swap.rs:20:10 | LL | impl<'a> CoerceShared> for MyMut<'a> {} | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/reborrow/coerce-shared-field-relations.rs b/tests/ui/reborrow/coerce-shared-field-relations.rs index b2104efb6028d..7d957427ffdb3 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> { @@ -26,6 +29,8 @@ struct RenamedRef<'a, T> { target: &'a T, } +// 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> { @@ -40,6 +45,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> {} //~^ ERROR diff --git a/tests/ui/reborrow/coerce-shared-field-relations.stderr b/tests/ui/reborrow/coerce-shared-field-relations.stderr index 033a29e1e554e..a906c189d4391 100644 --- a/tests/ui/reborrow/coerce-shared-field-relations.stderr +++ b/tests/ui/reborrow/coerce-shared-field-relations.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `&'a mut T: CoerceShared<&'a u32>` is not satisfied - --> $DIR/coerce-shared-field-relations.rs:43:1 + --> $DIR/coerce-shared-field-relations.rs:50:1 | LL | impl<'a, T> CoerceShared> for BadMut<'a, T> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `CoerceShared<&'a u32>` is not implemented for `&'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 3adda6733e16f..c193d6df0aefb 100644 --- a/tests/ui/reborrow/coerce-shared-foreign-private-field.rs +++ b/tests/ui/reborrow/coerce-shared-foreign-private-field.rs @@ -2,6 +2,8 @@ //@ 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; @@ -15,6 +17,7 @@ struct LocalMut<'a> { impl<'a> Reborrow for LocalMut<'a> {} +// Should error: ForeignRef has private fields. impl<'a> CoerceShared> for LocalMut<'a> {} fn main() {} 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 4a70fd49e38ff..eecb97d779d64 100644 --- a/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs +++ b/tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs @@ -1,5 +1,8 @@ //@ check-pass +//! Test that CoerceShared cannot be implemented targeting a foreign tuple struct with private +//! fields. + #![feature(reborrow)] use std::marker::{CoerceShared, PhantomData, Reborrow}; @@ -17,6 +20,7 @@ struct LocalPtrMut<'a>(*const i32, PhantomData<&'a ()>); impl<'a> Reborrow for LocalPtrMut<'a> {} +// Should error: ForeignPtrRef has private fields. impl<'a> CoerceShared> for LocalPtrMut<'a> {} fn main() {} 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 72efdd475fc10..6ca4c84d837ad 100644 --- a/tests/ui/reborrow/coerce-shared-generics.stderr +++ b/tests/ui/reborrow/coerce-shared-generics.stderr @@ -1,5 +1,5 @@ error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced - --> $DIR/coerce-shared-generics.rs:26:38 + --> $DIR/coerce-shared-generics.rs:28:38 | LL | impl<'a, T, U: Copy, const N: usize> CoerceShared> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs index 90bb4be4c1894..c99006814e3bb 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> {} fn method(_a: StaticMarkerRef<'static>) {} diff --git a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr index 337e4b6938944..f038aa4bf0d54 100644 --- a/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr +++ b/tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr @@ -1,5 +1,5 @@ error[E0597]: `a` does not live long enough - --> $DIR/coerce-shared-lifetime-mismatch.rs:21:12 + --> $DIR/coerce-shared-lifetime-mismatch.rs:24: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 c528fb85340ac..f4e9288480afe 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 15146341bc56c..e51b9451e02e2 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` does not allow multiple lifetimes or fields to be coerced - --> $DIR/coerce-shared-missing-target-field.rs:17:13 + --> $DIR/coerce-shared-missing-target-field.rs:19:13 | LL | impl<'a, T> CoerceShared> for MissingSourceMut<'a, T> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 a425ab5fd1ced..68a7489b3c3bc 100644 --- a/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs +++ b/tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs @@ -1,5 +1,8 @@ //@ check-pass +//! Test that CoerceShared can resolve field types through aliases and GATs. +//! Also test that reference shared coercing does not produce invalid lifetime relations. + #![feature(reborrow)] use std::marker::{CoerceShared, Reborrow}; @@ -39,6 +42,8 @@ struct InnerLifetimeRef<'a> { value: &'a &'a (), } +// No error explicitly necessary: &'a mut &'static T -> &'a &'a T is a valid coercion. We might +// still want to error on it because it's mostly meaningless, though. impl<'a> CoerceShared> for InnerLifetimeMut<'a> {} fn main() {} 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 70a0db88319a7..79a38f49f0695 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` does not allow multiple lifetimes or fields to be coerced - --> $DIR/coerce-shared-omitted-reborrow-field-after-dead.rs:31:13 + --> $DIR/coerce-shared-omitted-reborrow-field-after-dead.rs:35:13 | 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 3610bcea23b64..ec3ad78832ca8 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` does not allow multiple lifetimes or fields to be coerced - --> $DIR/coerce-shared-omitted-reborrow-field-locked.rs:30:13 + --> $DIR/coerce-shared-omitted-reborrow-field-locked.rs:34:13 | 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 c663576228f62..6a6c85f49c992 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` does not allow multiple lifetimes or fields to be coerced - --> $DIR/coerce-shared-omitted-reborrow-field.rs:31:13 + --> $DIR/coerce-shared-omitted-reborrow-field.rs:34:13 | 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 e3cd68547c428..37c4968617453 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` does not allow multiple lifetimes or fields to be coerced - --> $DIR/coerce-shared-reordered-field.rs:19:10 + --> $DIR/coerce-shared-reordered-field.rs:21:10 | 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 2dc818a7015b2..9c59f0e20c9bd 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 d0031a7c8a99e..3b07e0f64a212 100644 --- a/tests/ui/reborrow/coerce-shared-wrong-generic.stderr +++ b/tests/ui/reborrow/coerce-shared-wrong-generic.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `&'a mut T: CoerceShared<&'a U>` is not satisfied - --> $DIR/coerce-shared-wrong-generic.rs:18:1 + --> $DIR/coerce-shared-wrong-generic.rs:20:1 | LL | impl<'a, T, U> CoerceShared> for GenericMut<'a, T, U> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `CoerceShared<&'a U>` is not implemented for `&'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> { | ________________________________________________________^