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
123 changes: 107 additions & 16 deletions compiler/rustc_borrowck/src/borrow_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_hir::Mutability;
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::{RegionVid, TyCtxt};
use rustc_middle::{bug, span_bug, ty};
use rustc_mir_dataflow::move_paths::MoveData;
Expand Down Expand Up @@ -240,6 +240,88 @@ struct GatherBorrows<'a, 'tcx> {
locals_state_at_exit: LocalsStateAtExit,
}

impl<'a, 'tcx> GatherBorrows<'a, 'tcx> {
fn do_reborrow(
&mut self,
kind: mir::BorrowKind,
source_region: RegionVid,
target_region: RegionVid,
location: Location,
assigned_place: &mir::Place<'tcx>,
reborrowed_adt: ty::AdtDef<'tcx>,
reborrowed_args: &'tcx ty::List<ty::GenericArg<'tcx>>,
source_place: mir::Place<'tcx>,
) {
let mut did_reborrow = false;
for (idx, ele) in reborrowed_adt.all_fields().enumerate() {
// FIXME(reborrow): recurse into Reborrow fields
let ele_ty = ele.ty(self.tcx, reborrowed_args).skip_norm_wip();
match ele_ty.kind() {
ty::Ref(ref_region, _, mutability)
if ref_region.as_var() == source_region && mutability.is_mut() =>
{
did_reborrow = true;
let borrowed_place = source_place.project_deeper(
&[PlaceElem::Field(idx.into(), ele_ty), PlaceElem::Deref],
self.tcx,
);
let borrow = BorrowData {
kind,
region: target_region,
reserve_location: location,
activation_location: TwoPhaseActivation::NotTwoPhase,
borrowed_place,
assigned_place: *assigned_place,
};
let (idx, _) = self.location_map.insert_full(location, borrow);
let idx = BorrowIndex::from(idx);

self.local_map.entry(source_place.local).or_default().insert(idx);
}
ty::Adt(field_def, field_args)
if field_args.get(0).and_then(|f| f.as_region()).map(|r| r.as_var())
== Some(source_region)
&& !self.tcx.type_is_copy_modulo_regions(
self.body.typing_env(self.tcx),
self.tcx.erase_and_anonymize_regions(ele_ty),
) =>
{
did_reborrow = true;
let field_place = source_place
.project_deeper(&[PlaceElem::Field(idx.into(), ele_ty)], self.tcx);
self.do_reborrow(
kind,
source_region,
target_region,
location,
assigned_place,
*field_def,
field_args,
field_place,
);
}
_ => continue,
}
}
if !did_reborrow {
// If source contained no reference, perform a phantom dereference.
let borrowed_place = source_place.project_deeper(&[PlaceElem::PhantomDeref], self.tcx);
let borrow = BorrowData {
kind,
region: target_region,
reserve_location: location,
activation_location: TwoPhaseActivation::NotTwoPhase,
borrowed_place,
assigned_place: *assigned_place,
};
let (idx, _) = self.location_map.insert_full(location, borrow);
let idx = BorrowIndex::from(idx);

self.local_map.entry(source_place.local).or_default().insert(idx);
}
}
}

impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> {
fn visit_assign(
&mut self,
Expand Down Expand Up @@ -302,11 +384,21 @@ 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 {
} else if let &mir::Rvalue::Reborrow(target, mutability, source_place) = rvalue {
let borrowed_place_ty = source_place.ty(self.body, self.tcx).ty;
let &ty::Adt(reborrowed_adt, reborrowed_args) = borrowed_place_ty.kind() else {
unreachable!()
};
let Some(ty::GenericArgKind::Lifetime(source_region)) =
reborrowed_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 source_region = source_region.as_var();

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 {
Expand All @@ -315,7 +407,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> {
if mutability == Mutability::Mut { "Reborrow" } else { "CoerceShared" }
);
};
let region = region.as_var();
let target_region = region.as_var();
let kind = if mutability == Mutability::Mut {
// Reborrow
if target_adt.did() != reborrowed_adt.did() {
Expand All @@ -334,18 +426,17 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> {
}
mir::BorrowKind::Shared
};
let borrow = BorrowData {
kind,
region,
reserve_location: location,
activation_location: TwoPhaseActivation::NotTwoPhase,
borrowed_place,
assigned_place: *assigned_place,
};
let (idx, _) = self.location_map.insert_full(location, borrow);
let idx = BorrowIndex::from(idx);

self.local_map.entry(borrowed_place.local).or_default().insert(idx);
self.do_reborrow(
kind,
source_region,
target_region,
location,
assigned_place,
reborrowed_adt,
reborrowed_args,
source_place,
);
}

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 @@ -4206,6 +4206,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, '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 @@ -399,6 +399,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
}
}
ProjectionElem::PhantomDeref => (),
ProjectionElem::Downcast(..) if opt.including_downcast => return None,
ProjectionElem::Downcast(..) => (),
ProjectionElem::OpaqueCast(..) => (),
Expand Down Expand Up @@ -487,6 +488,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, '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<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, '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 @@ -2019,7 +2019,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 @@ -2243,6 +2244,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 @@ -2634,6 +2636,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
7 changes: 7 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 @@ -506,6 +512,7 @@ fn place_projection_conflict<'tcx>(
}
(
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 @@ -1884,6 +1884,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 @@ -2452,6 +2453,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
3 changes: 3 additions & 0 deletions compiler/rustc_const_eval/src/interpret/projection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?,
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_middle/src/mir/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down Expand Up @@ -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) => {
Expand Down
11 changes: 8 additions & 3 deletions compiler/rustc_middle/src/mir/statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}
Expand Down Expand Up @@ -265,7 +266,7 @@ impl<V, T> ProjectionElem<V, T> {
/// than the base.
pub fn is_indirect(&self) -> bool {
match self {
Self::Deref => true,
Self::Deref | Self::PhantomDeref => true,

Self::Field(_, _)
| Self::Index(_)
Expand All @@ -287,7 +288,8 @@ impl<V, T> ProjectionElem<V, T> {
| Self::ConstantIndex { .. }
| Self::Subslice { .. }
| Self::Downcast(_, _)
| Self::UnwrapUnsafeBinder(..) => true,
| Self::UnwrapUnsafeBinder(..)
| Self::PhantomDeref => true,
}
}

Expand All @@ -311,7 +313,8 @@ impl<V, T> ProjectionElem<V, T> {
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,
Expand All @@ -331,6 +334,7 @@ impl<V, T> ProjectionElem<V, T> {
) -> Option<ProjectionElem<V2, T2>> {
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)
}
Expand Down Expand Up @@ -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 { .. }
Expand Down
Loading
Loading