Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 174 additions & 26 deletions compiler/rustc_borrowck/src/borrow_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -261,6 +261,154 @@ 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<ty::GenericArg<'tcx>>,
target_place: mir::Place<'tcx>,
source_adt: ty::AdtDef<'tcx>,
source_args: &'tcx ty::List<ty::GenericArg<'tcx>>,
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, 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"
);
}
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_phantom_deref_place,
assigned_place: target_place,
});
}
}
}

impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> {
Expand Down Expand Up @@ -319,23 +467,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:?}"
)
Expand All @@ -344,24 +483,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)
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(..) => {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_borrowck/src/diagnostics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
}
}
}
ProjectionElem::PhantomDeref => (),
ProjectionElem::Downcast(..) if opt.including_downcast => return None,
ProjectionElem::Downcast(..) => (),
ProjectionElem::OpaqueCast(..) => (),
Expand Down Expand Up @@ -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(
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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 { .. }
Expand Down Expand Up @@ -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, _)
Expand Down
13 changes: 13 additions & 0 deletions compiler/rustc_borrowck/src/places_conflict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ fn place_components_conflict<'tcx>(

(ProjectionElem::Deref, _, Deep)
| (ProjectionElem::Deref, _, AccessDepth::Drop)
| (ProjectionElem::PhantomDeref, _, _)
| (ProjectionElem::Field { .. }, _, _)
| (ProjectionElem::Index { .. }, _, _)
| (ProjectionElem::ConstantIndex { .. }, _, _)
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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 { .. }
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_borrowck/src/prefixes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 { .. }
Expand Down Expand Up @@ -2467,6 +2468,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
}
}
ProjectionElem::Field(..)
| ProjectionElem::PhantomDeref
| ProjectionElem::Downcast(..)
| ProjectionElem::OpaqueCast(..)
| ProjectionElem::Index(..)
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_cranelift/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_ssa/src/mir/place.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_const_eval/src/check_consts/qualifs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ where
ProjectionElem::Index(index) if in_local(index) => return true,

ProjectionElem::Deref
| ProjectionElem::PhantomDeref
| ProjectionElem::Field(_, _)
| ProjectionElem::OpaqueCast(_)
| ProjectionElem::ConstantIndex { .. }
Expand Down
Loading
Loading