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>(