diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 7e20a5133e07f..8b3ebbc077a93 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -558,7 +558,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
err: &mut Diag<'infcx>,
) {
let var_info = self.body.var_debug_info.iter().find(|info| match info.value {
- VarDebugInfoContents::Place(ref p) => p == place,
+ VarDebugInfoContents::Place(ref p) => {
+ // This will become a simple == when Derefer is moved before borrowck
+ place.local == p.local
+ && p.iter_projection_elems()
+ .enumerate()
+ .all(|(i, elem)| place.projection.get(i) == Some(&elem))
+ }
_ => false,
});
let arg_name = if let Some(var_info) = var_info {
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 19cbcd139aa56..a47b3bbd42e2a 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -19,7 +19,7 @@ use rustc_infer::infer::{
BoundRegionConversionTime, InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin,
};
use rustc_infer::traits::PredicateObligations;
-use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
+use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, ProjectionBase, Visitor};
use rustc_middle::mir::*;
use rustc_middle::traits::query::NoSolution;
use rustc_middle::ty::adjustment::PointerCoercion;
@@ -1807,13 +1807,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
}
}
- fn visit_projection_elem(
+ fn visit_projection_elem
(
&mut self,
- place: PlaceRef<'tcx>,
+ place: P,
elem: PlaceElem<'tcx>,
context: PlaceContext,
location: Location,
- ) {
+ ) where
+ P: ProjectionBase<'tcx>,
+ {
let tcx = self.tcx();
let base_ty = place.ty(self.body(), tcx);
match elem {
diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
index b8f635ab78161..d64157a5693c4 100644
--- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
@@ -44,8 +44,11 @@ pub struct PerLocalVarDebugInfo<'tcx, D> {
/// if this is a fragment of a composite `VarDebugInfo`.
pub fragment: Option>,
- /// `.place.projection` from `mir::VarDebugInfo`.
- pub projection: &'tcx ty::List>,
+ /// `.place.direct_projection` from `mir::VarDebugInfo`.
+ pub direct_projection: &'tcx ty::List>,
+
+ /// `.place.projection_chain` from `mir::VarDebugInfo`.
+ pub projection_chain: &'tcx ty::List>,
}
/// Information needed to emit a constant.
@@ -168,46 +171,68 @@ fn calculate_debuginfo_offset<
L: DebugInfoOffsetLocation<'tcx, Bx>,
>(
bx: &mut Bx,
- projection: &[mir::PlaceElem<'tcx>],
+ direct_projection: &[mir::PlaceElem<'tcx>],
+ projection_chain: &[mir::ProjectionFragment<'tcx>],
base: L,
) -> DebugInfoOffset {
- let mut direct_offset = Size::ZERO;
- // FIXME(eddyb) use smallvec here.
- let mut indirect_offsets = vec![];
- let mut place = base;
-
- for elem in projection {
- match *elem {
- mir::ProjectionElem::Deref => {
- indirect_offsets.push(Size::ZERO);
- place = place.deref(bx);
- }
+ let project_direct = |bx: &mut Bx, place: L, offset, elem| {
+ let (new_place, new_offset) = match elem {
mir::ProjectionElem::Field(field, _) => {
- let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
- *offset += place.layout().fields.offset(field.index());
- place = place.project_field(bx, field);
- }
- mir::ProjectionElem::Downcast(_, variant) => {
- place = place.downcast(bx, variant);
+ let offset = place.layout().fields.offset(field.index());
+ let place = place.project_field(bx, field);
+
+ (place, offset)
}
+ mir::ProjectionElem::Downcast(_, variant) => (place.downcast(bx, variant), Size::ZERO),
mir::ProjectionElem::ConstantIndex {
offset: index,
min_length: _,
from_end: false,
} => {
- let offset = indirect_offsets.last_mut().unwrap_or(&mut direct_offset);
let FieldsShape::Array { stride, count: _ } = place.layout().fields else {
bug!("ConstantIndex on non-array type {:?}", place.layout())
};
- *offset += stride * index;
- place = place.project_constant_index(bx, index);
+ let offset = stride * index;
+ let place = place.project_constant_index(bx, index);
+
+ (place, offset)
+ }
+
+ mir::ProjectionElem::Deref => {
+ // derefs handled separately
+ bug!(
+ "unexpected deref in var debuginfo projection `{direct_projection:?} {projection_chain:?}`"
+ )
}
_ => {
// Sanity check for `can_use_in_debuginfo`.
assert!(!elem.can_use_in_debuginfo());
- bug!("unsupported var debuginfo projection `{:?}`", projection)
+ bug!(
+ "unsupported var debuginfo projection `{direct_projection:?} {projection_chain:?}`"
+ )
}
- }
+ };
+ (new_place, offset + new_offset)
+ };
+
+ let (mut place, direct_offset) = direct_projection
+ .iter()
+ .fold((base, Size::ZERO), |(place, offset), &elem| project_direct(bx, place, offset, elem));
+
+ // FIXME(eddyb) use smallvec here.
+ let mut indirect_offsets = vec![];
+
+ for projection in projection_chain {
+ debug_assert_eq!(projection[0], mir::ProjectionElem::Deref);
+ let pointee = place.deref(bx);
+
+ let (projected, offset) =
+ projection[1..].iter().fold((pointee, Size::ZERO), |(place, offset), &elem| {
+ project_direct(bx, place, offset, elem)
+ });
+
+ place = projected;
+ indirect_offsets.push(offset);
}
DebugInfoOffset { direct_offset, indirect_offsets, result: place }
@@ -262,8 +287,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
Some(per_local) => &per_local[local],
None => return,
};
- let whole_local_var = vars.iter().find(|var| var.projection.is_empty()).cloned();
- let has_proj = || vars.iter().any(|var| !var.projection.is_empty());
+ let whole_local_var = vars
+ .iter()
+ .find(|var| var.direct_projection.is_empty() && var.projection_chain.is_empty())
+ .cloned();
+ let has_proj = || {
+ vars.iter()
+ .any(|var| !var.direct_projection.is_empty() || !var.projection_chain.is_empty())
+ };
let fallback_var = if self.mir.local_kind(local) == mir::LocalKind::Arg {
let arg_index = local.index() - 1;
@@ -305,7 +336,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
source_info: decl.source_info,
dbg_var,
fragment: None,
- projection: ty::List::empty(),
+ direct_projection: ty::List::empty(),
+ projection_chain: ty::List::empty(),
})
}
} else {
@@ -388,7 +420,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let Some(dbg_loc) = self.dbg_loc(var.source_info) else { return };
let DebugInfoOffset { direct_offset, indirect_offsets, result: _ } =
- calculate_debuginfo_offset(bx, var.projection, base.layout);
+ calculate_debuginfo_offset(
+ bx,
+ var.direct_projection,
+ var.projection_chain,
+ base.layout,
+ );
// When targeting MSVC, create extra allocas for arguments instead of pointing multiple
// dbg_var_addr() calls into the same alloca with offsets. MSVC uses CodeView records
@@ -406,7 +443,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
if should_create_individual_allocas {
let DebugInfoOffset { direct_offset: _, indirect_offsets: _, result: place } =
- calculate_debuginfo_offset(bx, var.projection, base);
+ calculate_debuginfo_offset(bx, var.direct_projection, var.projection_chain, base);
// Create a variable which will be a pointer to the actual value
let ptr_ty = Ty::new_mut_ptr(bx.tcx(), place.layout.ty);
@@ -491,7 +528,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
} else {
match var.value {
mir::VarDebugInfoContents::Place(place) => {
- self.monomorphized_place_ty(place.as_ref())
+ let tcx = bx.tcx();
+ let place_ty = place.ty(self.mir, tcx);
+ self.monomorphize(place_ty.ty)
}
mir::VarDebugInfoContents::Const(c) => self.monomorphize(c.ty()),
}
@@ -501,7 +540,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let var_kind = if let Some(arg_index) = var.argument_index
&& var.composite.is_none()
&& let mir::VarDebugInfoContents::Place(place) = var.value
- && place.projection.is_empty()
+ && place.as_local().is_some()
{
let arg_index = arg_index as usize;
if target_is_msvc {
@@ -541,7 +580,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let var_layout = self.cx.layout_of(var_ty);
let DebugInfoOffset { direct_offset, indirect_offsets, result: fragment_layout } =
- calculate_debuginfo_offset(bx, &fragment.projection, var_layout);
+ calculate_debuginfo_offset(bx, &fragment.projection, &[], var_layout);
assert!(indirect_offsets.is_empty());
if fragment_layout.size == Size::ZERO {
@@ -560,13 +599,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
};
match var.value {
- mir::VarDebugInfoContents::Place(place) => {
- per_local[place.local].push(PerLocalVarDebugInfo {
+ mir::VarDebugInfoContents::Place(mir::CompoundPlace {
+ local,
+ direct_projection,
+ projection_chain,
+ }) => {
+ per_local[local].push(PerLocalVarDebugInfo {
name: var.name,
source_info: var.source_info,
dbg_var,
fragment,
- projection: place.projection,
+ direct_projection,
+ projection_chain,
});
}
mir::VarDebugInfoContents::Const(c) => {
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 28142382b130b..cf62a16a02a8b 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -1187,8 +1187,8 @@ impl<'tcx> LocalDecl<'tcx> {
#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
pub enum VarDebugInfoContents<'tcx> {
- /// This `Place` only contains projection which satisfy `can_use_in_debuginfo`.
- Place(Place<'tcx>),
+ /// This `CompoundPlace` only contains projection which satisfy `can_use_in_debuginfo`.
+ Place(CompoundPlace<'tcx>),
Const(ConstOperand<'tcx>),
}
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 96148fd5b9269..720e678dfac6a 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -1270,6 +1270,34 @@ impl Debug for PlaceRef<'_> {
}
}
+impl Debug for CompoundPlace<'_> {
+ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+ self.as_ref().fmt(fmt)
+ }
+}
+
+impl Debug for CompoundPlaceRef<'_> {
+ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+ let (stem, suffix) = self.projection_chain.unwrap_or_default();
+
+ pre_fmt_projection(suffix, fmt)?;
+ for projection in stem.iter().rev() {
+ pre_fmt_projection(projection, fmt)?;
+ }
+ pre_fmt_projection(self.direct_projection, fmt)?;
+
+ write!(fmt, "{:?}", self.local)?;
+
+ post_fmt_projection(self.direct_projection, fmt)?;
+ for projection in stem {
+ post_fmt_projection(projection, fmt)?;
+ }
+ post_fmt_projection(suffix, fmt)?;
+
+ Ok(())
+ }
+}
+
fn pre_fmt_projection(projection: &[PlaceElem<'_>], fmt: &mut Formatter<'_>) -> fmt::Result {
for &elem in projection.iter().rev() {
match elem {
diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs
index 28294b47e90f2..2fb9d26d06710 100644
--- a/compiler/rustc_middle/src/mir/statement.rs
+++ b/compiler/rustc_middle/src/mir/statement.rs
@@ -1,5 +1,8 @@
//! Functionality for statements, operands, places, and things that appear in them.
+use std::iter::once;
+
+use smallvec::SmallVec;
use tracing::{debug, instrument};
use super::interpret::GlobalAlloc;
@@ -157,6 +160,17 @@ impl<'tcx> PlaceTy<'tcx> {
elems.iter().fold(self, |place_ty, &elem| place_ty.projection_ty(tcx, elem))
}
+ pub fn projection_chain_ty(
+ self,
+ tcx: TyCtxt<'tcx>,
+ chain: &[ProjectionFragment<'tcx>],
+ ) -> PlaceTy<'tcx> {
+ chain
+ .iter()
+ .flat_map(|&elems| elems)
+ .fold(self, |place_ty, elem| place_ty.projection_ty(tcx, elem))
+ }
+
/// Convenience wrapper around `projection_ty_core` for `PlaceElem`,
/// where we can just use the `Ty` that is already stored inline on
/// field projection elems.
@@ -544,6 +558,243 @@ impl From for PlaceRef<'_> {
}
}
+/// A deref and subsequent direct projection of the pointee
+///
+/// In the future this will also have the pointee type alongside the projection.
+/// This will be needed to represent derefs of non-pointer types like `Box`.
+/// (`Ty::builtin_deref` currently works on `Box` but that will be changed too.)
+pub type ProjectionFragment<'tcx> = &'tcx List>;
+pub type ProjectionFragmentRef<'tcx> = &'tcx [PlaceElem<'tcx>];
+
+/// A place with multiple direct projections separated by derefs.
+///
+/// In `AnalysisPhase::PostCleanup` and later, [`Place`] and [`PlaceRef`] cannot represent these
+/// kinds of places, requiring this struct to be used instead.
+#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, HashStable, TypeFoldable, TypeVisitable)]
+pub struct CompoundPlace<'tcx> {
+ pub local: Local,
+ /// The projection from `local` until the first deref.
+ ///
+ /// Invariants:
+ /// - This does not contain derefs.
+ pub direct_projection: &'tcx List>,
+ /// A chain of projection fragments -- derefs followed by direct projections of the pointee place.
+ ///
+ /// Invariants:
+ /// - Each fragment begins with a deref and has no other derefs.
+ pub projection_chain: &'tcx List>,
+}
+
+/// Borrowed form of [`CompoundPlace`].
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+pub struct CompoundPlaceRef<'tcx> {
+ pub local: Local,
+ pub direct_projection: &'tcx [PlaceElem<'tcx>],
+ /// `None` is equivalent to an empty projection chain,
+ /// `Some((stem, suffix))` is equivalent to `stem` with `suffix` appended to it.
+ pub projection_chain: Option<(&'tcx [ProjectionFragment<'tcx>], ProjectionFragmentRef<'tcx>)>,
+}
+
+impl<'tcx> CompoundPlace<'tcx> {
+ pub fn from_place(place: Place<'tcx>, tcx: TyCtxt<'tcx>) -> CompoundPlace<'tcx> {
+ let Some(first_deref) = place.projection.iter().position(|elem| elem == PlaceElem::Deref)
+ else {
+ // simple case, no derefs
+ return CompoundPlace {
+ local: place.local,
+ direct_projection: place.projection,
+ projection_chain: List::empty(),
+ };
+ };
+
+ let mut current_fragment_start = first_deref;
+ let mut new_projection_chain = SmallVec::<[_; 1]>::new();
+
+ for i in first_deref + 1..place.projection.len() {
+ if place.projection[i] == PlaceElem::Deref {
+ new_projection_chain
+ .push(tcx.mk_place_elems(&place.projection[current_fragment_start..i]));
+ current_fragment_start = i;
+ }
+ }
+
+ if current_fragment_start == 0 {
+ // don't try to re-intern the projection for no reason
+ new_projection_chain.push(place.projection);
+ } else {
+ new_projection_chain
+ .push(tcx.mk_place_elems(&place.projection[current_fragment_start..]));
+ }
+
+ CompoundPlace {
+ local: place.local,
+ direct_projection: tcx.mk_place_elems(&place.projection[..first_deref]),
+ projection_chain: tcx.mk_place_elem_chain(&new_projection_chain),
+ }
+ }
+
+ pub fn as_ref(&self) -> CompoundPlaceRef<'tcx> {
+ CompoundPlaceRef {
+ local: self.local,
+ direct_projection: self.direct_projection.as_slice(),
+ projection_chain: CompoundPlaceRef::balance_chain(self.projection_chain),
+ }
+ }
+
+ pub fn as_local(&self) -> Option {
+ self.as_ref().as_local()
+ }
+
+ pub fn local_or_deref_local(&self) -> Option {
+ self.as_ref().local_or_deref_local()
+ }
+
+ pub fn is_indirect(&self) -> bool {
+ !self.projection_chain.is_empty()
+ }
+
+ /// Returns a [`Place`] with only `direct_projection`
+ pub fn base_place(&self) -> Place<'tcx> {
+ Place { local: self.local, projection: self.direct_projection }
+ }
+
+ /// Replaces the local with `new_base`.
+ ///
+ /// `new_base` must be a post-derefer compatible local (no derefs after the start of the projection)
+ pub fn replace_local_with_place(&mut self, new_base: Place<'tcx>, tcx: TyCtxt<'tcx>) {
+ self.local = new_base.local;
+
+ if new_base.projection.is_empty() {
+ // already done
+ } else if new_base.is_indirect_first_projection() {
+ let new_prefix = new_base.project_deeper(self.direct_projection, tcx);
+
+ self.direct_projection = List::empty();
+ self.projection_chain = tcx.mk_place_elem_chain_from_iter(
+ once(new_prefix.projection).chain(self.projection_chain),
+ )
+ } else if self.direct_projection.is_empty() {
+ self.direct_projection = new_base.projection
+ } else {
+ self.direct_projection = tcx
+ .mk_place_elems_from_iter(new_base.projection.iter().chain(self.direct_projection))
+ }
+ }
+
+ pub fn iter_projections(
+ self,
+ ) -> impl Iterator- , PlaceElem<'tcx>)> + DoubleEndedIterator {
+ let base_iter = self.direct_projection.iter().enumerate().map(move |(i, elem)| {
+ let base = CompoundPlaceRef {
+ local: self.local,
+ direct_projection: &self.direct_projection[..i],
+ projection_chain: None,
+ };
+
+ (base, elem)
+ });
+
+ let chain_iter = self.projection_chain.iter().enumerate().flat_map(move |(i, projs)| {
+ projs.iter().enumerate().map(move |(j, elem)| {
+ let base = CompoundPlaceRef {
+ local: self.local,
+ direct_projection: self.direct_projection.as_slice(),
+ projection_chain: CompoundPlaceRef::balance_stem_and_suffix(
+ &self.projection_chain[..i],
+ &projs[..j],
+ ),
+ };
+
+ (base, elem)
+ })
+ });
+
+ base_iter.chain(chain_iter)
+ }
+
+ pub fn iter_projection_elems(
+ &self,
+ ) -> impl Iterator
- > + DoubleEndedIterator {
+ self.direct_projection.iter().chain(self.projection_chain.iter().flatten())
+ }
+
+ pub fn ty(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx>
+ where
+ D: HasLocalDecls<'tcx>,
+ {
+ PlaceTy::from_ty(local_decls.local_decls()[self.local].ty)
+ .multi_projection_ty(tcx, self.direct_projection)
+ .projection_chain_ty(tcx, self.projection_chain)
+ }
+}
+
+impl From for CompoundPlace<'_> {
+ #[inline]
+ fn from(local: Local) -> Self {
+ CompoundPlace { local, direct_projection: List::empty(), projection_chain: List::empty() }
+ }
+}
+
+impl<'tcx> CompoundPlaceRef<'tcx> {
+ pub fn ty(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx>
+ where
+ D: HasLocalDecls<'tcx>,
+ {
+ let base_ty = PlaceTy::from_ty(local_decls.local_decls()[self.local].ty)
+ .multi_projection_ty(tcx, self.direct_projection);
+
+ match self.projection_chain {
+ Some((stem, suffix)) => {
+ base_ty.projection_chain_ty(tcx, stem).multi_projection_ty(tcx, suffix)
+ }
+ None => base_ty,
+ }
+ }
+
+ pub fn as_local(&self) -> Option {
+ match *self {
+ CompoundPlaceRef { local, direct_projection: [], projection_chain: None } => {
+ Some(local)
+ }
+ _ => None,
+ }
+ }
+
+ pub fn local_or_deref_local(&self) -> Option {
+ match *self {
+ CompoundPlaceRef {
+ local,
+ direct_projection: [],
+ projection_chain: None | Some(([], [PlaceElem::Deref])),
+ } => Some(local),
+ _ => None,
+ }
+ }
+
+ /// Balances `stem` and `suffix` into the layout expected by `CompoundPlaceRef`.
+ /// If `suffix` is empty and `stem` is not, `stem`'s last element is split off to replace `suffix`.
+ /// If both are empty, `None` is returned.
+ fn balance_stem_and_suffix(
+ stem: &'tcx [ProjectionFragment<'tcx>],
+ suffix: ProjectionFragmentRef<'tcx>,
+ ) -> Option<(&'tcx [ProjectionFragment<'tcx>], ProjectionFragmentRef<'tcx>)> {
+ match (stem, suffix) {
+ ([], []) => None,
+ ([stem @ .., suffix], []) => Some((stem, suffix.as_slice())),
+ _ => Some((stem, suffix)),
+ }
+ }
+
+ fn balance_chain(
+ projection_chain: &'tcx [ProjectionFragment<'tcx>],
+ ) -> Option<(&'tcx [ProjectionFragment<'tcx>], ProjectionFragmentRef<'tcx>)> {
+ match projection_chain {
+ [] => None,
+ [stem @ .., suffix] => Some((stem, suffix.as_slice())),
+ }
+ }
+}
+
///////////////////////////////////////////////////////////////////////////
// Operands
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 81df239dee42d..173cdc300bcea 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -180,6 +180,15 @@ macro_rules! make_mir_visitor {
self.super_place(place, context, location);
}
+ fn visit_compound_place(
+ &mut self,
+ place: & $($mutability)? CompoundPlace<'tcx>,
+ context: PlaceContext,
+ location: Location,
+ ) {
+ self.super_compound_place(place, context, location);
+ }
+
visit_place_fns!($($mutability)?);
/// This is called for every constant in the MIR body and every `required_consts`
@@ -914,7 +923,7 @@ macro_rules! make_mir_visitor {
match value {
VarDebugInfoContents::Const(c) => self.visit_const_operand(c, location),
VarDebugInfoContents::Place(place) =>
- self.visit_place(
+ self.visit_compound_place(
place,
PlaceContext::NonUse(NonUseContext::VarDebugInfo),
location
@@ -1182,6 +1191,51 @@ macro_rules! visit_place_fns {
| PlaceElem::Downcast(..) => None,
}
}
+
+ fn super_compound_place(
+ &mut self,
+ place: &mut CompoundPlace<'tcx>,
+ context: PlaceContext,
+ location: Location,
+ ) {
+ self.visit_local(&mut place.local, context, location);
+
+ if let Some(new_direct_projection) =
+ self.process_projection(&place.direct_projection, location)
+ {
+ place.direct_projection = self.tcx().mk_place_elems(&new_direct_projection);
+ }
+
+ if let Some(new_projection_chain) =
+ self.process_projection_chain(&place.projection_chain, location)
+ {
+ place.projection_chain = self.tcx().mk_place_elem_chain(&new_projection_chain);
+ }
+ }
+
+ fn process_projection_chain<'a>(
+ &mut self,
+ projection_chain: &'a [&'tcx List>],
+ location: Location,
+ ) -> Option>>> {
+ let mut projection_chain = Cow::Borrowed(projection_chain);
+
+ for i in 0..projection_chain.len() {
+ if let Some(segment) = projection_chain.get(i) {
+ if let Some(segment) = self.process_projection(segment, location) {
+ // This converts the borrowed projection chain into `Cow::Owned(_)` and returns a
+ // clone of the projection chain so we can mutate and reintern later.
+ let vec = projection_chain.to_mut();
+ vec[i] = self.tcx().mk_place_elems(&segment);
+ }
+ }
+ }
+
+ match projection_chain {
+ Cow::Borrowed(_) => None,
+ Cow::Owned(vec) => Some(vec),
+ }
+ }
};
() => {
@@ -1194,13 +1248,15 @@ macro_rules! visit_place_fns {
self.super_projection(place_ref, context, location);
}
- fn visit_projection_elem(
+ fn visit_projection_elem
(
&mut self,
- place_ref: PlaceRef<'tcx>,
+ place_ref: P,
elem: PlaceElem<'tcx>,
context: PlaceContext,
location: Location,
- ) {
+ ) where
+ P: ProjectionBase<'tcx>,
+ {
self.super_projection_elem(place_ref, elem, context, location);
}
@@ -1235,13 +1291,15 @@ macro_rules! visit_place_fns {
}
}
- fn super_projection_elem(
+ fn super_projection_elem
(
&mut self,
- _place_ref: PlaceRef<'tcx>,
+ _place_ref: P,
elem: PlaceElem<'tcx>,
context: PlaceContext,
location: Location,
- ) {
+ ) where
+ P: ProjectionBase<'tcx>,
+ {
match elem {
ProjectionElem::OpaqueCast(ty)
| ProjectionElem::Subtype(ty)
@@ -1267,6 +1325,28 @@ macro_rules! visit_place_fns {
| ProjectionElem::Downcast(_, _) => {}
}
}
+
+ fn super_compound_place(
+ &mut self,
+ place: &CompoundPlace<'tcx>,
+ mut context: PlaceContext,
+ location: Location,
+ ) {
+ if !place.projection_chain.is_empty() && context.is_use() {
+ // ^ Only change the context if it is a real use, not a "use" in debuginfo.
+ context = if context.is_mutating_use() {
+ PlaceContext::MutatingUse(MutatingUseContext::Projection)
+ } else {
+ PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
+ };
+ }
+
+ self.visit_local(place.local, context, location);
+
+ for (base, elem) in place.iter_projections().rev() {
+ self.visit_projection_elem(base, elem, context, location);
+ }
+ }
};
}
@@ -1492,3 +1572,28 @@ where
self.visit_projection(place.as_ref(), ctxt, location);
}
}
+
+/// Base of a projection in [`Visitor::visit_projection_elem`].
+pub trait ProjectionBase<'tcx>: Debug + Copy {
+ fn ty(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx>
+ where
+ D: HasLocalDecls<'tcx>;
+}
+
+impl<'tcx> ProjectionBase<'tcx> for PlaceRef<'tcx> {
+ fn ty(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx>
+ where
+ D: HasLocalDecls<'tcx>,
+ {
+ self.ty(local_decls, tcx)
+ }
+}
+
+impl<'tcx> ProjectionBase<'tcx> for CompoundPlaceRef<'tcx> {
+ fn ty(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx>
+ where
+ D: HasLocalDecls<'tcx>,
+ {
+ self.ty(local_decls, tcx)
+ }
+}
diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs
index 3f37595d0eef0..096bef3190163 100644
--- a/compiler/rustc_middle/src/ty/codec.rs
+++ b/compiler/rustc_middle/src/ty/codec.rs
@@ -298,6 +298,25 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable for mir::Place<'tcx> {
}
}
+impl<'tcx, D: TyDecoder<'tcx>> Decodable for mir::CompoundPlace<'tcx> {
+ fn decode(decoder: &mut D) -> Self {
+ let base_place: mir::Place<'tcx> = Decodable::decode(decoder);
+ let chain_len = decoder.read_usize();
+ let projection_chain =
+ decoder.interner().mk_place_elem_chain_from_iter((0..chain_len).map(|_| {
+ let projs_len = decoder.read_usize();
+ decoder.interner().mk_place_elems_from_iter(
+ (0..projs_len).map::, _>(|_| Decodable::decode(decoder)),
+ )
+ }));
+ mir::CompoundPlace {
+ local: base_place.local,
+ direct_projection: base_place.projection,
+ projection_chain,
+ }
+ }
+}
+
impl<'tcx, D: TyDecoder<'tcx>> Decodable for ty::Region<'tcx> {
fn decode(decoder: &mut D) -> Self {
ty::Region::new_from_kind(decoder.interner(), Decodable::decode(decoder))
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 7d3e2c9965dad..79fa1f903ce06 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -68,7 +68,7 @@ use crate::metadata::ModChild;
use crate::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature};
use crate::middle::resolve_bound_vars;
use crate::mir::interpret::{self, Allocation, ConstAllocation};
-use crate::mir::{Body, Local, Place, PlaceElem, ProjectionKind, Promoted};
+use crate::mir::{Body, Local, Place, PlaceElem, ProjectionFragment, ProjectionKind, Promoted};
use crate::query::plumbing::QuerySystem;
use crate::query::{IntoQueryParam, LocalCrate, Providers, TyCtxtAt};
use crate::thir::Thir;
@@ -934,6 +934,7 @@ pub struct CtxtInterners<'tcx> {
clauses: InternedSet<'tcx, ListWithCachedTypeInfo>>,
projs: InternedSet<'tcx, List>,
place_elems: InternedSet<'tcx, List>>,
+ place_elem_chains: InternedSet<'tcx, List<&'tcx List>>>,
const_: InternedSet<'tcx, WithCachedTypeInfo>>,
pat: InternedSet<'tcx, PatternKind<'tcx>>,
const_allocation: InternedSet<'tcx, Allocation>,
@@ -972,6 +973,7 @@ impl<'tcx> CtxtInterners<'tcx> {
clauses: InternedSet::with_capacity(N),
projs: InternedSet::with_capacity(N * 4),
place_elems: InternedSet::with_capacity(N * 2),
+ place_elem_chains: InternedSet::with_capacity(N * 2), // FIXME non-empirical factor - just copying place_elems
const_: InternedSet::with_capacity(N * 2),
pat: InternedSet::with_capacity(N),
const_allocation: InternedSet::with_capacity(N),
@@ -2779,6 +2781,7 @@ slice_interners!(
poly_existential_predicates: intern_poly_existential_predicates(PolyExistentialPredicate<'tcx>),
projs: pub mk_projs(ProjectionKind),
place_elems: pub mk_place_elems(PlaceElem<'tcx>),
+ place_elem_chains: pub mk_place_elem_chain(ProjectionFragment<'tcx>),
bound_variable_kinds: pub mk_bound_variable_kinds(ty::BoundVariableKind),
fields: pub mk_fields(FieldIdx),
local_def_ids: intern_local_def_ids(LocalDefId),
@@ -3169,6 +3172,14 @@ impl<'tcx> TyCtxt<'tcx> {
T::collect_and_apply(iter, |xs| self.mk_place_elems(xs))
}
+ pub fn mk_place_elem_chain_from_iter(self, iter: I) -> T::Output
+ where
+ I: Iterator- ,
+ T: CollectAndApply<&'tcx List>, &'tcx List>>,
+ {
+ T::collect_and_apply(iter, |xs| self.mk_place_elem_chain(xs))
+ }
+
pub fn mk_fields_from_iter(self, iter: I) -> T::Output
where
I: Iterator
- ,
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index 11d109b463d90..fabf67e1bfa8a 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -13,7 +13,7 @@ use rustc_span::source_map::Spanned;
use rustc_type_ir::{ConstKind, TypeFolder, VisitorResult, try_visit};
use super::{GenericArg, GenericArgKind, Pattern, Region};
-use crate::mir::PlaceElem;
+use crate::mir::{PlaceElem, ProjectionFragment};
use crate::ty::print::{FmtPrinter, Printer, with_no_trimmed_paths};
use crate::ty::{
self, FallibleTypeFolder, Lift, Term, TermKind, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,
@@ -797,6 +797,7 @@ macro_rules! list_fold {
list_fold! {
&'tcx ty::List> : mk_poly_existential_predicates,
&'tcx ty::List> : mk_place_elems,
+ &'tcx ty::List> : mk_place_elem_chain,
&'tcx ty::List> : mk_patterns,
&'tcx ty::List> : mk_outlives,
}
diff --git a/compiler/rustc_mir_build/src/builder/custom/parse.rs b/compiler/rustc_mir_build/src/builder/custom/parse.rs
index 10154461c3395..be27e0e935c51 100644
--- a/compiler/rustc_mir_build/src/builder/custom/parse.rs
+++ b/compiler/rustc_mir_build/src/builder/custom/parse.rs
@@ -260,7 +260,9 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
let operand = self.parse_operand(operand)?;
let value = match operand {
Operand::Constant(c) => VarDebugInfoContents::Const(*c),
- Operand::Copy(p) | Operand::Move(p) => VarDebugInfoContents::Place(p),
+ Operand::Copy(p) | Operand::Move(p) => {
+ VarDebugInfoContents::Place(CompoundPlace::from_place(p, self.tcx))
+ }
};
let dbginfo = VarDebugInfo {
name,
diff --git a/compiler/rustc_mir_build/src/builder/mod.rs b/compiler/rustc_mir_build/src/builder/mod.rs
index cdb2c5561ce6a..9a2adf6affd84 100644
--- a/compiler/rustc_mir_build/src/builder/mod.rs
+++ b/compiler/rustc_mir_build/src/builder/mod.rs
@@ -840,9 +840,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let Some(closure_arg) = self.local_decls.get(ty::CAPTURE_STRUCT_LOCAL) else { return };
let mut closure_ty = closure_arg.ty;
- let mut closure_env_projs = vec![];
+ let mut closure_env_projs: &[_] = &[];
if let ty::Ref(_, ty, _) = closure_ty.kind() {
- closure_env_projs.push(ProjectionElem::Deref);
+ closure_env_projs = &[ProjectionElem::Deref];
closure_ty = *ty;
}
@@ -881,7 +881,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let mutability = captured_place.mutability;
- let mut projs = closure_env_projs.clone();
+ let mut projs = closure_env_projs.to_vec();
projs.push(ProjectionElem::Field(FieldIdx::new(i), ty));
match capture {
ty::UpvarCapture::ByValue | ty::UpvarCapture::ByUse => {}
@@ -897,7 +897,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.var_debug_info.push(VarDebugInfo {
name,
source_info: SourceInfo::outermost(captured_place.var_ident.span),
- value: VarDebugInfoContents::Place(use_place),
+ value: VarDebugInfoContents::Place(CompoundPlace::from_place(use_place, tcx)),
composite: None,
argument_index: None,
});
diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index c1cd2788348a6..7a48fd48344d8 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -158,15 +158,29 @@ impl<'tcx> MutVisitor<'tcx> for SelfArgVisitor<'tcx> {
}
}
}
-}
-fn replace_base<'tcx>(place: &mut Place<'tcx>, new_base: Place<'tcx>, tcx: TyCtxt<'tcx>) {
- place.local = new_base.local;
+ fn visit_compound_place(
+ &mut self,
+ place: &mut CompoundPlace<'tcx>,
+ context: PlaceContext,
+ location: Location,
+ ) {
+ if place.local == SELF_ARG {
+ place.replace_local_with_place(self.new_base, self.tcx);
+ } else {
+ self.visit_local(&mut place.local, context, location);
- let mut new_projection = new_base.projection.to_vec();
- new_projection.append(&mut place.projection.to_vec());
+ for elem in place.iter_projection_elems() {
+ if let PlaceElem::Index(local) = elem {
+ assert_ne!(local, SELF_ARG);
+ }
+ }
+ }
+ }
+}
- place.projection = tcx.mk_place_elems(&new_projection);
+fn replace_base<'tcx>(place: &mut Place<'tcx>, new_base: Place<'tcx>, tcx: TyCtxt<'tcx>) {
+ *place = new_base.project_deeper(place.projection, tcx);
}
const SELF_ARG: Local = Local::from_u32(1);
@@ -406,6 +420,18 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> {
}
}
+ fn visit_compound_place(
+ &mut self,
+ place: &mut CompoundPlace<'tcx>,
+ _context: PlaceContext,
+ _location: Location,
+ ) {
+ // Replace an Local in the remap with a coroutine struct access
+ if let Some(&Some((ty, variant_index, idx))) = self.remap.get(place.local) {
+ place.replace_local_with_place(self.make_field(variant_index, idx, ty), self.tcx);
+ }
+ }
+
fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) {
// Remove StorageLive and StorageDead statements for remapped locals
for s in &mut data.statements {
@@ -1693,14 +1719,11 @@ impl EnsureCoroutineFieldAssignmentsNeverAlias<'_> {
}
impl<'tcx> Visitor<'tcx> for EnsureCoroutineFieldAssignmentsNeverAlias<'_> {
- fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
+ fn visit_place(&mut self, place: &Place<'tcx>, _context: PlaceContext, location: Location) {
let Some(lhs) = self.assigned_local else {
// This visitor only invokes `visit_place` for the right-hand side of an assignment
- // and only after setting `self.assigned_local`. However, the default impl of
- // `Visitor::super_body` may call `visit_place` with a `NonUseContext` for places
- // with debuginfo. Ignore them here.
- assert!(!context.is_use());
- return;
+ // and only after setting `self.assigned_local`.
+ bug!()
};
let Some(rhs) = self.saved_local_for_direct_place(*place) else { return };
diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
index 951ff69c19e3e..1330691d7bc5d 100644
--- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
+++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
@@ -326,6 +326,71 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> {
self.super_place(place, context, location);
}
+ fn visit_compound_place(
+ &mut self,
+ place: &mut mir::CompoundPlace<'tcx>,
+ context: mir::visit::PlaceContext,
+ location: mir::Location,
+ ) {
+ // See comments above in visit_place
+ if place.local == ty::CAPTURE_STRUCT_LOCAL
+ && let Some((&mir::ProjectionElem::Field(idx, _), direct_projection)) =
+ place.direct_projection.split_first()
+ && let Some(&(remapped_idx, remapped_ty, peel_deref, ref bridging_projections)) =
+ self.field_remapping.get(&idx)
+ {
+ // equivalent to `final_projections` above
+ let (next, tail) = if peel_deref {
+ assert!(direct_projection.is_empty());
+ let Some((next_projection, rest)) = place.projection_chain.split_first() else {
+ bug!(
+ "There should be at least a single deref for an upvar local initialization, found {direct_projection:#?}"
+ );
+ };
+
+ let Some((mir::ProjectionElem::Deref, projection)) = next_projection.split_first()
+ else {
+ bug!(
+ "There should be at least a single deref for an upvar local initialization, found {next_projection:#?}"
+ );
+ };
+
+ (projection, rest)
+ } else {
+ (direct_projection, place.projection_chain.as_slice())
+ };
+
+ // it might be good to use smallvec here
+ let mut new_projection_chain = vec![];
+ let mut last_projection = vec![mir::ProjectionElem::Field(remapped_idx, remapped_ty)];
+
+ for elem in bridging_projections {
+ match elem.kind {
+ ProjectionKind::Deref => {
+ new_projection_chain
+ .push(self.tcx.mk_place_elems(last_projection.drain(..).as_slice()));
+ last_projection.push(mir::ProjectionElem::Deref);
+ }
+ ProjectionKind::Field(idx, VariantIdx::ZERO) => {
+ last_projection.push(mir::ProjectionElem::Field(idx, elem.ty));
+ }
+ _ => unreachable!("precise captures only through fields and derefs"),
+ }
+ }
+
+ last_projection.extend_from_slice(next);
+ new_projection_chain.push(self.tcx.mk_place_elems(&last_projection));
+ new_projection_chain.extend_from_slice(tail);
+
+ *place = mir::CompoundPlace {
+ local: place.local,
+ direct_projection: new_projection_chain[0],
+ projection_chain: self.tcx.mk_place_elem_chain(&new_projection_chain[1..]),
+ };
+ }
+ self.super_compound_place(place, context, location);
+ }
+
fn visit_statement(&mut self, statement: &mut mir::Statement<'tcx>, location: mir::Location) {
// Remove fake borrows of closure captures if that capture has been
// replaced with a by-move version of that capture.
diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
index 5c984984d3cc3..1419f0d0a3568 100644
--- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
+++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
@@ -14,7 +14,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::DefKind;
use rustc_middle::bug;
use rustc_middle::mir::interpret::{InterpResult, Scalar};
-use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
+use rustc_middle::mir::visit::{MutVisitor, PlaceContext, ProjectionBase, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_mir_dataflow::fmt::DebugWithContext;
@@ -1069,13 +1069,15 @@ struct OperandCollector<'a, 'b, 'tcx> {
}
impl<'tcx> Visitor<'tcx> for OperandCollector<'_, '_, 'tcx> {
- fn visit_projection_elem(
+ fn visit_projection_elem
(
&mut self,
- _: PlaceRef<'tcx>,
+ _: P,
elem: PlaceElem<'tcx>,
_: PlaceContext,
location: Location,
- ) {
+ ) where
+ P: ProjectionBase<'tcx>,
+ {
if let PlaceElem::Index(local) = elem
&& let Some(value) =
self.visitor.try_make_constant(self.ecx, local.into(), self.state, self.map)
diff --git a/compiler/rustc_mir_transform/src/deref_separator.rs b/compiler/rustc_mir_transform/src/deref_separator.rs
index bc914ea656415..7ab92fa602f36 100644
--- a/compiler/rustc_mir_transform/src/deref_separator.rs
+++ b/compiler/rustc_mir_transform/src/deref_separator.rs
@@ -1,4 +1,3 @@
-use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
use rustc_middle::mir::visit::{MutVisitor, PlaceContext};
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
@@ -18,11 +17,8 @@ impl<'a, 'tcx> MutVisitor<'tcx> for DerefChecker<'a, 'tcx> {
self.tcx
}
- fn visit_place(&mut self, place: &mut Place<'tcx>, cntxt: PlaceContext, loc: Location) {
- if !place.projection.is_empty()
- && cntxt != PlaceContext::NonUse(VarDebugInfo)
- && place.projection[1..].contains(&ProjectionElem::Deref)
- {
+ fn visit_place(&mut self, place: &mut Place<'tcx>, _cntxt: PlaceContext, loc: Location) {
+ if !place.projection.is_empty() && place.projection[1..].contains(&ProjectionElem::Deref) {
let mut place_local = place.local;
let mut last_len = 0;
let mut last_deref_idx = 0;
diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
index 5c344a806880c..f162e03610844 100644
--- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
@@ -7,7 +7,7 @@ use rustc_hir::def_id::DefId;
use rustc_middle::mir::visit::MutVisitor;
use rustc_middle::mir::*;
use rustc_middle::span_bug;
-use rustc_middle::ty::{Ty, TyCtxt};
+use rustc_middle::ty::{List, Ty, TyCtxt};
use crate::patch::MirPatch;
@@ -116,37 +116,51 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs {
visitor.patch.apply(body);
+ let append_projection = |projs: &List<_>, boxed_ty| {
+ let mut new_projections = projs.to_vec();
+
+ let (unique_ty, nonnull_ty, ptr_ty) =
+ build_ptr_tys(tcx, boxed_ty, unique_did, nonnull_did);
+
+ new_projections.extend_from_slice(&build_projection(unique_ty, nonnull_ty));
+ // While we can't project into `NonNull<_>` in a basic block
+ // due to MCP#807, this is debug info where it's fine.
+ new_projections.push(PlaceElem::Field(FieldIdx::ZERO, ptr_ty));
+
+ tcx.mk_place_elems(&new_projections)
+ };
+
for debug_info in body.var_debug_info.iter_mut() {
if let VarDebugInfoContents::Place(place) = &mut debug_info.value {
- let mut new_projections: Option> = None;
+ let mut new_projection_chain = None;
+ let mut base_ty = place.base_place().ty(&body.local_decls, tcx);
+
+ // If this is None, there are no derefs.
+ let Some((suffix, stem)) = place.projection_chain.split_last() else { continue };
- for (base, elem) in place.iter_projections() {
- let base_ty = base.ty(&body.local_decls, tcx).ty;
+ if let Some(boxed_ty) = base_ty.ty.boxed_ty() {
+ place.direct_projection = append_projection(place.direct_projection, boxed_ty);
+ }
- if let PlaceElem::Deref = elem
- && let Some(boxed_ty) = base_ty.boxed_ty()
- {
+ for (i, projs) in stem.iter().copied().enumerate() {
+ base_ty = base_ty.multi_projection_ty(tcx, projs);
+
+ if let Some(boxed_ty) = base_ty.ty.boxed_ty() {
// Clone the projections before us, since now we need to mutate them.
- let new_projections =
- new_projections.get_or_insert_with(|| base.projection.to_vec());
-
- let (unique_ty, nonnull_ty, ptr_ty) =
- build_ptr_tys(tcx, boxed_ty, unique_did, nonnull_did);
-
- new_projections.extend_from_slice(&build_projection(unique_ty, nonnull_ty));
- // While we can't project into `NonNull<_>` in a basic block
- // due to MCP#807, this is debug info where it's fine.
- new_projections.push(PlaceElem::Field(FieldIdx::ZERO, ptr_ty));
- new_projections.push(PlaceElem::Deref);
- } else if let Some(new_projections) = new_projections.as_mut() {
+ let new_projection_chain = new_projection_chain
+ .get_or_insert_with(|| place.projection_chain[..i].to_vec());
+
+ new_projection_chain.push(append_projection(projs, boxed_ty));
+ } else if let Some(new_projection_chain) = new_projection_chain.as_mut() {
// Keep building up our projections list once we've started it.
- new_projections.push(elem);
+ new_projection_chain.push(projs);
}
}
// Store the mutated projections if we actually changed something.
- if let Some(new_projections) = new_projections {
- place.projection = tcx.mk_place_elems(&new_projections);
+ if let Some(mut new_projection_chain) = new_projection_chain {
+ new_projection_chain.push(suffix);
+ place.projection_chain = tcx.mk_place_elem_chain(&new_projection_chain);
}
}
}
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index ebec3d125003d..66c4dc4db5acb 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -1780,7 +1780,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> {
fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
self.simplify_place_projection(place, location);
- if context.is_mutating_use() && place.is_indirect() {
+ if context.is_mutating_use() && place.is_indirect_first_projection() {
// Non-local mutation maybe invalidate deref.
self.invalidate_derefs();
}
diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs
index aaacc5866a2ae..7d5c8469c83c9 100644
--- a/compiler/rustc_mir_transform/src/known_panics_lint.rs
+++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs
@@ -911,7 +911,7 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
use rustc_middle::mir::visit::PlaceContext::*;
// Dereferencing just read the address of `place.local`.
- if place.projection.first() == Some(&PlaceElem::Deref) {
+ if place.is_indirect_first_projection() {
context = NonMutatingUse(NonMutatingUseContext::Copy);
}
@@ -919,6 +919,10 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
self.visit_projection(place.as_ref(), context, loc);
}
+ fn visit_var_debug_info(&mut self, _: &VarDebugInfo<'tcx>) {
+ // explicitly skip debug info
+ }
+
fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) {
use rustc_middle::mir::visit::PlaceContext::*;
match context {
diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs
index b9d6e74ecae89..afd65b62ce656 100644
--- a/compiler/rustc_mir_transform/src/ref_prop.rs
+++ b/compiler/rustc_mir_transform/src/ref_prop.rs
@@ -386,6 +386,36 @@ struct Replacer<'tcx> {
any_replacement: bool,
}
+impl<'tcx> Replacer<'tcx> {
+ fn replace_base(&mut self, place: &mut Place<'tcx>, ctxt: PlaceContext, loc: Location) -> bool {
+ let mut replaced = false;
+
+ loop {
+ let Some((&PlaceElem::Deref, rest)) = place.projection.split_first() else { break };
+
+ let Value::Pointer(target, _) = self.targets[place.local] else { break };
+
+ let perform_opt = match ctxt {
+ PlaceContext::NonUse(NonUseContext::VarDebugInfo) => {
+ target.projection.iter().all(|p| p.can_use_in_debuginfo())
+ }
+ PlaceContext::NonUse(_) => true,
+ _ => self.allowed_replacements.contains(&(target.local, loc)),
+ };
+
+ if !perform_opt {
+ break;
+ }
+
+ *place = target.project_deeper(rest, self.tcx);
+ replaced = true;
+ self.any_replacement = true;
+ }
+
+ replaced
+ }
+}
+
impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
@@ -395,11 +425,11 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> {
// If the debuginfo is a pointer to another place
// and it's a reborrow: see through it
while let VarDebugInfoContents::Place(ref mut place) = debuginfo.value
- && place.projection.is_empty()
- && let Value::Pointer(target, _) = self.targets[place.local]
+ && let Some(local) = place.as_local()
+ && let Value::Pointer(target, _) = self.targets[local]
&& let &[PlaceElem::Deref] = &target.projection[..]
{
- *place = Place::from(target.local);
+ *place = CompoundPlace::from(target.local);
self.any_replacement = true;
}
@@ -408,25 +438,32 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> {
}
fn visit_place(&mut self, place: &mut Place<'tcx>, ctxt: PlaceContext, loc: Location) {
- loop {
- let Some((&PlaceElem::Deref, rest)) = place.projection.split_first() else { return };
-
- let Value::Pointer(target, _) = self.targets[place.local] else { return };
-
- let perform_opt = match ctxt {
- PlaceContext::NonUse(NonUseContext::VarDebugInfo) => {
- target.projection.iter().all(|p| p.can_use_in_debuginfo())
- }
- PlaceContext::NonUse(_) => true,
- _ => self.allowed_replacements.contains(&(target.local, loc)),
- };
+ self.replace_base(place, ctxt, loc);
+ }
- if !perform_opt {
- return;
+ fn visit_compound_place(
+ &mut self,
+ place: &mut CompoundPlace<'tcx>,
+ ctxt: PlaceContext,
+ loc: Location,
+ ) {
+ // We only replace if the place starts with a deref.
+ let Some(local) = place.base_place().as_local() else { return };
+ let Some(&first_indirect_projection) = place.projection_chain.first() else { return };
+
+ let mut base = Place { local, projection: first_indirect_projection };
+ let replaced = self.replace_base(&mut base, ctxt, loc);
+
+ if replaced {
+ place.local = base.local;
+ if base.is_indirect_first_projection() {
+ let mut new_projection_chain = place.projection_chain.to_vec();
+ new_projection_chain[0] = base.projection;
+ place.projection_chain = self.tcx.mk_place_elem_chain(&new_projection_chain);
+ } else {
+ place.direct_projection = base.projection;
+ place.projection_chain = self.tcx.mk_place_elem_chain(&place.projection_chain[1..]);
}
-
- *place = target.project_deeper(rest, self.tcx);
- self.any_replacement = true;
}
}
diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs
index 38769885f368b..352a25d53216e 100644
--- a/compiler/rustc_mir_transform/src/sroa.rs
+++ b/compiler/rustc_mir_transform/src/sroa.rs
@@ -269,12 +269,15 @@ impl<'tcx> ReplacementVisitor<'tcx, '_> {
VarDebugInfoContents::Place(ref mut place) => place,
};
- if let Some(repl) = self.replacements.replace_place(self.tcx, place.as_ref()) {
- *place = repl;
+ let base_place = place.base_place();
+
+ if let Some(repl) = self.replacements.replace_place(self.tcx, base_place.as_ref()) {
+ place.local = repl.local;
+ place.direct_projection = repl.projection;
return vec![var_debug_info];
}
- let Some(parts) = self.replacements.place_fragments(*place) else {
+ let Some(parts) = self.replacements.place_fragments(base_place) else {
return vec![var_debug_info];
};
diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs
index c8a9a88dc3fe3..3f3cd75ea0810 100644
--- a/compiler/rustc_mir_transform/src/validate.rs
+++ b/compiler/rustc_mir_transform/src/validate.rs
@@ -9,7 +9,7 @@ use rustc_index::bit_set::DenseBitSet;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{Obligation, ObligationCause};
use rustc_middle::mir::coverage::CoverageKind;
-use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
+use rustc_middle::mir::visit::{PlaceContext, ProjectionBase, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::print::with_no_trimmed_paths;
@@ -646,13 +646,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.super_operand(operand, location);
}
- fn visit_projection_elem(
+ fn visit_projection_elem(
&mut self,
- place_ref: PlaceRef<'tcx>,
+ place_ref: P,
elem: PlaceElem<'tcx>,
context: PlaceContext,
location: Location,
- ) {
+ ) where
+ P: ProjectionBase<'tcx>,
+ {
match elem {
ProjectionElem::OpaqueCast(ty)
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial) =>
@@ -881,7 +883,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
match debuginfo.value {
VarDebugInfoContents::Const(_) => {}
VarDebugInfoContents::Place(place) => {
- if place.projection.iter().any(|p| !p.can_use_in_debuginfo()) {
+ if place.iter_projection_elems().any(|p| !p.can_use_in_debuginfo()) {
self.fail(
START_BLOCK.start_location(),
format!("illegal place {:?} in debuginfo for {:?}", place, debuginfo.name),
@@ -898,7 +900,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
if self.body.phase >= MirPhase::Runtime(RuntimePhase::Initial)
&& place.projection.len() > 1
- && cntxt != PlaceContext::NonUse(NonUseContext::VarDebugInfo)
&& place.projection[1..].contains(&ProjectionElem::Deref)
{
self.fail(
@@ -925,6 +926,42 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.super_place(place, cntxt, location);
}
+ fn visit_compound_place(
+ &mut self,
+ place: &CompoundPlace<'tcx>,
+ cntxt: PlaceContext,
+ location: Location,
+ ) {
+ // Set off any `bug!`s in the type computation code
+ let _ = place.ty(&self.body.local_decls, self.tcx);
+
+ if place.direct_projection.contains(&PlaceElem::Deref) {
+ self.fail(location, format!("compound place {place:?} has deref in direct_projection"));
+ }
+
+ for (i, projection) in place.projection_chain.iter().enumerate() {
+ if projection.is_empty() {
+ self.fail(location, format!("compound place {place:?} has empty segment {i}"));
+ }
+
+ if projection.first() != Some(&ProjectionElem::Deref) {
+ self.fail(
+ location,
+ format!("compound place {place:?} missing deref in segment {i}"),
+ );
+ }
+
+ if projection[1..].contains(&ProjectionElem::Deref) {
+ self.fail(
+ location,
+ format!("compound place {place:?} has deref as a later projection in segment {i} (it is only permitted as the first projection)"),
+ );
+ }
+ }
+
+ self.super_compound_place(place, cntxt, location);
+ }
+
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
macro_rules! check_kinds {
($t:expr, $text:literal, $typat:pat) => {
diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs
index b10af6526ead5..a4aa0b9991e7b 100644
--- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs
+++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs
@@ -422,6 +422,21 @@ impl<'tcx> Stable<'tcx> for mir::Place<'tcx> {
}
}
+// lowering to just Place for now
+impl<'tcx> Stable<'tcx> for mir::CompoundPlace<'tcx> {
+ type T = crate::mir::Place;
+ fn stable<'cx>(
+ &self,
+ tables: &mut Tables<'cx, BridgeTys>,
+ cx: &CompilerCtxt<'cx, BridgeTys>,
+ ) -> Self::T {
+ crate::mir::Place {
+ local: self.local.as_usize(),
+ projection: self.iter_projection_elems().map(|e| e.stable(tables, cx)).collect(),
+ }
+ }
+}
+
impl<'tcx> Stable<'tcx> for mir::PlaceElem<'tcx> {
type T = crate::mir::ProjectionElem;
fn stable<'cx>(