From dadd374aaefe40ad01533aa6da3394e57eaa67ff Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Mon, 25 Aug 2025 22:10:19 -0700 Subject: [PATCH 1/7] add CompoundPlace and CompoundPlaceRef --- compiler/rustc_middle/src/mir/pretty.rs | 24 +++++ compiler/rustc_middle/src/mir/statement.rs | 93 +++++++++++++++++++ compiler/rustc_middle/src/ty/context.rs | 11 +++ .../rustc_middle/src/ty/structural_impls.rs | 1 + 4 files changed, 129 insertions(+) diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 96148fd5b9269..9f235028d98e2 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1270,6 +1270,30 @@ 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 { + pre_fmt_projection(self.last_projection, fmt)?; + for projection in self.projection_chain_base.iter().rev() { + pre_fmt_projection(projection, fmt)?; + } + + write!(fmt, "{:?}", self.local)?; + + for projection in self.projection_chain_base { + post_fmt_projection(projection, fmt)?; + } + post_fmt_projection(self.last_projection, 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..b183ec3e69b85 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -157,6 +157,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: &[&List>], + ) -> 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 +555,88 @@ impl From for PlaceRef<'_> { } } +/// A place possibly containing derefs in the middle of its projection by chaining projection lists. +/// +/// In `AnalysisPhase::PostCleanup` and later, [`Place`] and [`PlaceRef`] cannot represent these +/// kinds of places. +#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, HashStable, TypeFoldable, TypeVisitable)] +pub struct CompoundPlace<'tcx> { + pub local: Local, + /// Invariants: + /// - All segments in the chain other than the first must start with a deref. + /// - Derefs may only appear at the start of a segment. + /// - No segment may be empty. + pub projection_chain: &'tcx List<&'tcx List>>, +} + +/// Borrowed form of [`CompoundPlace`]. +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct CompoundPlaceRef<'tcx> { + pub local: Local, + pub projection_chain_base: &'tcx [&'tcx List>], + pub last_projection: &'tcx [PlaceElem<'tcx>], +} + +// these impls are bare-bones for now +impl<'tcx> CompoundPlace<'tcx> { + pub fn as_ref(&self) -> CompoundPlaceRef<'tcx> { + let (last, base) = self + .projection_chain + .split_last() + .map(|(&last, base)| (last, base)) + .unwrap_or_default(); + + CompoundPlaceRef { local: self.local, projection_chain_base: base, last_projection: last } + } + + 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) + .projection_chain_ty(tcx, self.projection_chain) + } + + pub fn iter_projections( + self, + ) -> impl Iterator, PlaceElem<'tcx>)> + DoubleEndedIterator { + self.projection_chain.iter().enumerate().flat_map(move |(i, projs)| { + projs.iter().enumerate().map(move |(j, elem)| { + let base = if j == 0 && i > 0 { + // last_projection should only be empty if projection_chain_base is too + // otherwise it has to have a deref at least + + debug_assert_eq!(elem, PlaceElem::Deref); + CompoundPlaceRef { + local: self.local, + projection_chain_base: &self.projection_chain[..i - 1], + last_projection: &projs[..=j], + } + } else { + CompoundPlaceRef { + local: self.local, + projection_chain_base: &self.projection_chain[..i], + last_projection: &projs[..j], + } + }; + + (base, elem) + }) + }) + } +} + +impl<'tcx> CompoundPlaceRef<'tcx> { + 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) + .projection_chain_ty(tcx, self.projection_chain_base) + .multi_projection_ty(tcx, self.last_projection) + } +} + /////////////////////////////////////////////////////////////////////////// // Operands diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 7d3e2c9965dad..ef91b640ff624 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -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(&'tcx List>), 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<&'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..81782d3b3e7bf 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -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<&'tcx ty::List>> : mk_place_elem_chain, &'tcx ty::List> : mk_patterns, &'tcx ty::List> : mk_outlives, } From 5e31f5880368d2efea4508645b0185012f92d6d7 Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Mon, 25 Aug 2025 23:04:47 -0700 Subject: [PATCH 2/7] visit_compound_place --- compiler/rustc_borrowck/src/type_check/mod.rs | 10 +- compiler/rustc_middle/src/mir/statement.rs | 16 +-- compiler/rustc_middle/src/mir/visit.rs | 111 +++++++++++++++++- .../src/dataflow_const_prop.rs | 10 +- compiler/rustc_mir_transform/src/validate.rs | 44 ++++++- 5 files changed, 165 insertions(+), 26 deletions(-) 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_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index b183ec3e69b85..a4f93c8f66f30 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -589,14 +589,6 @@ impl<'tcx> CompoundPlace<'tcx> { CompoundPlaceRef { local: self.local, projection_chain_base: base, last_projection: last } } - 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) - .projection_chain_ty(tcx, self.projection_chain) - } - pub fn iter_projections( self, ) -> impl Iterator, PlaceElem<'tcx>)> + DoubleEndedIterator { @@ -624,6 +616,14 @@ impl<'tcx> CompoundPlace<'tcx> { }) }) } + + 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) + .projection_chain_ty(tcx, self.projection_chain) + } } impl<'tcx> CompoundPlaceRef<'tcx> { diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 81df239dee42d..d4078b24dfba3 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` @@ -1182,6 +1191,45 @@ 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_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 +1242,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 +1285,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 +1319,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 +1566,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_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/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index c8a9a88dc3fe3..ce47343658dcb 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::{NonUseContext, 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) => @@ -925,6 +927,40 @@ 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); + + 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 i > 0 && projection.first() != Some(&ProjectionElem::Deref) { + self.fail( + location, + format!( + "compound place {place:?} has later segment without deref (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) => { From 6344d921f36caf9006db53248d19cc583f39da75 Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Sun, 7 Sep 2025 22:20:05 -0700 Subject: [PATCH 3/7] use compoundplace in debuginfo --- .../src/diagnostics/conflict_errors.rs | 10 +- .../rustc_codegen_ssa/src/mir/debuginfo.rs | 11 +- compiler/rustc_middle/src/mir/mod.rs | 4 +- compiler/rustc_middle/src/mir/statement.rs | 123 +++++++++++++++++- compiler/rustc_middle/src/mir/visit.rs | 2 +- compiler/rustc_middle/src/ty/codec.rs | 15 +++ .../src/builder/custom/parse.rs | 4 +- compiler/rustc_mir_build/src/builder/mod.rs | 8 +- compiler/rustc_mir_transform/src/coroutine.rs | 47 +++++-- .../src/coroutine/by_move_body.rs | 65 +++++++++ .../src/deref_separator.rs | 8 +- .../src/elaborate_box_derefs.rs | 38 ++++-- compiler/rustc_mir_transform/src/gvn.rs | 2 +- .../src/known_panics_lint.rs | 6 +- compiler/rustc_mir_transform/src/ref_prop.rs | 67 +++++++--- compiler/rustc_mir_transform/src/sroa.rs | 8 +- compiler/rustc_mir_transform/src/validate.rs | 5 +- .../src/unstable/convert/stable/mir.rs | 20 +++ 18 files changed, 366 insertions(+), 77 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 7e20a5133e07f..c9146f21dfba7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -558,7 +558,15 @@ 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.projection_chain + .iter() + .flatten() + .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_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index b8f635ab78161..5bff4552641ab 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -491,7 +491,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 +503,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 { @@ -566,7 +568,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { source_info: var.source_info, dbg_var, fragment, - projection: place.projection, + // FIXME change field to be projection chain + projection: bx + .tcx() + .mk_place_elems_from_iter(place.projection_chain.iter().flatten()), }); } 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/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index a4f93c8f66f30..d164c0cea920a 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -1,5 +1,6 @@ //! Functionality for statements, operands, places, and things that appear in them. +use smallvec::SmallVec; use tracing::{debug, instrument}; use super::interpret::GlobalAlloc; @@ -558,7 +559,7 @@ impl From for PlaceRef<'_> { /// A place possibly containing derefs in the middle of its projection by chaining projection lists. /// /// In `AnalysisPhase::PostCleanup` and later, [`Place`] and [`PlaceRef`] cannot represent these -/// kinds of places. +/// 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, @@ -579,6 +580,35 @@ pub struct CompoundPlaceRef<'tcx> { // these impls are bare-bones for now impl<'tcx> CompoundPlace<'tcx> { + pub fn from_place(place: Place<'tcx>, tcx: TyCtxt<'tcx>) -> CompoundPlace<'tcx> { + let mut segment_start = 0; + // size picked from sole user + let mut new_projection_chain = SmallVec::<[_; 2]>::new(); + + for (i, elem) in place.projection.iter().enumerate() { + if elem == PlaceElem::Deref && i > 0 { + new_projection_chain.push(tcx.mk_place_elems(&place.projection[segment_start..i])); + segment_start = i; + } + } + + if segment_start == 0 { + new_projection_chain.push(place.projection); + } else { + new_projection_chain.push(tcx.mk_place_elems(&place.projection[segment_start..])); + } + + if cfg!(debug_assertions) { + let new_projections: Vec<_> = new_projection_chain.iter().copied().flatten().collect(); + assert_eq!(new_projections.as_slice(), place.projection.as_slice()); + } + + CompoundPlace { + local: place.local, + projection_chain: tcx.mk_place_elem_chain(&new_projection_chain), + } + } + pub fn as_ref(&self) -> CompoundPlaceRef<'tcx> { let (last, base) = self .projection_chain @@ -589,6 +619,59 @@ impl<'tcx> CompoundPlace<'tcx> { CompoundPlaceRef { local: self.local, projection_chain_base: base, last_projection: last } } + pub fn as_local(&self) -> Option { + if self.projection_chain.is_empty() { Some(self.local) } else { None } + } + + pub fn local_or_deref_local(&self) -> Option { + self.as_ref().local_or_deref_local() + } + + /// Returns a [`Place`] with only the first segment in the projection chain. + pub fn base_place(&self) -> Place<'tcx> { + Place { + local: self.local, + projection: self.projection_chain.first().copied().unwrap_or_default(), + } + } + + /// Replaces the local and first segment of the projection with `new_base`. + pub fn replace_base_place(&mut self, new_base: Place<'tcx>, tcx: TyCtxt<'tcx>) { + self.local = new_base.local; + self.projection_chain = + match (new_base.projection.is_empty(), self.projection_chain.is_empty()) { + (false, false) => { + let mut new_projection_chain = self.projection_chain.to_vec(); + new_projection_chain[0] = new_base.projection; + tcx.mk_place_elem_chain(&new_projection_chain) + } + (false, true) => tcx.mk_place_elem_chain(&[new_base.projection]), + + (true, false) => tcx.mk_place_elem_chain(&self.projection_chain[1..]), + (true, true) => List::empty(), + } + // FIXME: this logic is a mess + // maybe separate out projection before first deref? + } + + /// Replaces the local with `new_base`. + pub fn replace_local_with_place(&mut self, new_base: Place<'tcx>, tcx: TyCtxt<'tcx>) { + let base_place = self.base_place(); + + if new_base.projection.is_empty() { + self.local = new_base.local; + } else if base_place.is_indirect_first_projection() { + let mut new_projection_chain = Vec::with_capacity(self.projection_chain.len() + 1); + new_projection_chain.push(new_base.projection); + new_projection_chain.extend_from_slice(self.projection_chain); + + self.local = new_base.local; + self.projection_chain = tcx.mk_place_elem_chain(&new_projection_chain); + } else { + self.replace_base_place(new_base.project_deeper(base_place.projection, tcx), tcx); + } + } + pub fn iter_projections( self, ) -> impl Iterator, PlaceElem<'tcx>)> + DoubleEndedIterator { @@ -596,14 +679,15 @@ impl<'tcx> CompoundPlace<'tcx> { projs.iter().enumerate().map(move |(j, elem)| { let base = if j == 0 && i > 0 { // last_projection should only be empty if projection_chain_base is too - // otherwise it has to have a deref at least - debug_assert_eq!(elem, PlaceElem::Deref); CompoundPlaceRef { local: self.local, projection_chain_base: &self.projection_chain[..i - 1], - last_projection: &projs[..=j], + last_projection: &self.projection_chain[i - 1], } + + // FIXME: the fact that i messed up this logic the first time is good evidence that + // the invariants are confusing and difficult to uphold } else { CompoundPlaceRef { local: self.local, @@ -612,6 +696,19 @@ impl<'tcx> CompoundPlace<'tcx> { } }; + if cfg!(debug_assertions) { + let self_projections: Vec<_> = self.projection_chain.iter().flatten().collect(); + let base_projections: Vec<_> = base + .projection_chain_base + .iter() + .copied() + .flatten() + .chain(base.last_projection.iter().copied()) + .collect(); + + assert_eq!(self_projections[..base_projections.len()], base_projections); + } + (base, elem) }) }) @@ -626,6 +723,13 @@ impl<'tcx> CompoundPlace<'tcx> { } } +impl From for CompoundPlace<'_> { + #[inline] + fn from(local: Local) -> Self { + CompoundPlace { local, projection_chain: List::empty() } + } +} + impl<'tcx> CompoundPlaceRef<'tcx> { pub fn ty(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> PlaceTy<'tcx> where @@ -635,6 +739,17 @@ impl<'tcx> CompoundPlaceRef<'tcx> { .projection_chain_ty(tcx, self.projection_chain_base) .multi_projection_ty(tcx, self.last_projection) } + + pub fn local_or_deref_local(&self) -> Option { + match *self { + CompoundPlaceRef { + local, + projection_chain_base: [], + last_projection: [] | [ProjectionElem::Deref], + } => Some(local), + _ => None, + } + } } /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index d4078b24dfba3..74b2b4fb69dfa 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -923,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 diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 3f37595d0eef0..3a889848dd4a9 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -298,6 +298,21 @@ 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 local: mir::Local = 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, 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_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..6409e0c7eda90 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.projection_chain.iter().flatten() { + 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..e41a22cb806d7 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, + ) { + if place.local == ty::CAPTURE_STRUCT_LOCAL + && let Some((first_projection, rest)) = place.projection_chain.split_first() + && let Some((&mir::ProjectionElem::Field(idx, _), first_projection)) = + first_projection.split_first() + && let Some(&(remapped_idx, remapped_ty, peel_deref, ref bridging_projections)) = + self.field_remapping.get(&idx) + { + // 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"), + } + } + + let (next, tail) = if peel_deref { + assert!(first_projection.is_empty()); + let Some((next_projection, rest)) = rest.split_first() else { + bug!( + "There should be at least a single deref for an upvar local initialization, found {rest:#?}" + ); + }; + + 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 { + (first_projection, rest) + }; + + 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, + projection_chain: self.tcx.mk_place_elem_chain_from_iter( + new_projection_chain.into_iter().map(|p| self.tcx.mk_place_elems(&p)), + ), + }; + } + 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/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..c332cafe49df1 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; @@ -118,17 +118,25 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs { 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; - for (base, elem) in place.iter_projections() { - let base_ty = base.ty(&body.local_decls, tcx).ty; + let mut base_ty = PlaceTy::from_ty(body.local_decls.local_decls()[place.local].ty); - if let PlaceElem::Deref = elem - && let Some(boxed_ty) = base_ty.boxed_ty() - { + // Maybe the first segment should just be empty if place starts with a deref? + let new_base = + place.base_place().is_indirect_first_projection().then_some((0, List::empty())); + + let Some((last, rest)) = place.projection_chain.split_last() else { continue }; + + for (i, projs) in new_base.into_iter().chain(rest.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 new_projection_chain = new_projection_chain + .get_or_insert_with(|| place.projection_chain[..i].to_vec()); + + let mut new_projections = projs.to_vec(); let (unique_ty, nonnull_ty, ptr_ty) = build_ptr_tys(tcx, boxed_ty, unique_did, nonnull_did); @@ -137,16 +145,18 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs { // 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() { + + new_projection_chain.push(tcx.mk_place_elems(&new_projections)) + } 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(last); + 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..7125ce1ce7430 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,20 @@ 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)), - }; - - if !perform_opt { - return; - } + self.replace_base(place, ctxt, loc); + } - *place = target.project_deeper(rest, self.tcx); - self.any_replacement = true; + fn visit_compound_place( + &mut self, + place: &mut CompoundPlace<'tcx>, + ctxt: PlaceContext, + loc: Location, + ) { + let mut base = place.base_place(); + let replaced = self.replace_base(&mut base, ctxt, loc); + + if replaced { + place.replace_base_place(base, self.tcx); } } diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs index 38769885f368b..22aff1c9e21bd 100644 --- a/compiler/rustc_mir_transform/src/sroa.rs +++ b/compiler/rustc_mir_transform/src/sroa.rs @@ -269,12 +269,14 @@ 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.replace_base_place(repl, self.tcx); 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 ce47343658dcb..850c781433a95 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, ProjectionBase, 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; @@ -883,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.projection_chain.iter().flatten().any(|p| !p.can_use_in_debuginfo()) { self.fail( START_BLOCK.start_location(), format!("illegal place {:?} in debuginfo for {:?}", place, debuginfo.name), @@ -900,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( diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index b10af6526ead5..dc1fae8426599 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -422,6 +422,26 @@ 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 + .projection_chain + .iter() + .flatten() + .map(|e| e.stable(tables, cx)) + .collect(), + } + } +} + impl<'tcx> Stable<'tcx> for mir::PlaceElem<'tcx> { type T = crate::mir::ProjectionElem; fn stable<'cx>( From 96f5644006b977a824217382e4f75b75497df21c Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Tue, 9 Sep 2025 13:02:43 -0700 Subject: [PATCH 4/7] use ProjectionFragment(Ref) type alias makes it easier to change in the future, which will be needed for borrowck and co --- compiler/rustc_middle/src/mir/statement.rs | 15 +++++++++++---- compiler/rustc_middle/src/ty/context.rs | 6 +++--- compiler/rustc_middle/src/ty/structural_impls.rs | 4 ++-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index d164c0cea920a..c4347bd719a38 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -161,7 +161,7 @@ impl<'tcx> PlaceTy<'tcx> { pub fn projection_chain_ty( self, tcx: TyCtxt<'tcx>, - chain: &[&List>], + chain: &[ProjectionFragment<'tcx>], ) -> PlaceTy<'tcx> { chain .iter() @@ -556,6 +556,13 @@ impl From for PlaceRef<'_> { } } +// In the future this will also have the type being projected alongside it. +// 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 possibly containing derefs in the middle of its projection by chaining projection lists. /// /// In `AnalysisPhase::PostCleanup` and later, [`Place`] and [`PlaceRef`] cannot represent these @@ -567,15 +574,15 @@ pub struct CompoundPlace<'tcx> { /// - All segments in the chain other than the first must start with a deref. /// - Derefs may only appear at the start of a segment. /// - No segment may be empty. - pub projection_chain: &'tcx List<&'tcx List>>, + pub projection_chain: &'tcx List>, } /// Borrowed form of [`CompoundPlace`]. #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct CompoundPlaceRef<'tcx> { pub local: Local, - pub projection_chain_base: &'tcx [&'tcx List>], - pub last_projection: &'tcx [PlaceElem<'tcx>], + pub projection_chain_base: &'tcx [ProjectionFragment<'tcx>], + pub last_projection: ProjectionFragmentRef<'tcx>, } // these impls are bare-bones for now diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index ef91b640ff624..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; @@ -2781,7 +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(&'tcx List>), + 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), @@ -3175,7 +3175,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn mk_place_elem_chain_from_iter(self, iter: I) -> T::Output where I: Iterator, - T: CollectAndApply<&'tcx List>, &'tcx List<&'tcx List>>>, + T: CollectAndApply<&'tcx List>, &'tcx List>>, { T::collect_and_apply(iter, |xs| self.mk_place_elem_chain(xs)) } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 81782d3b3e7bf..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,7 +797,7 @@ macro_rules! list_fold { list_fold! { &'tcx ty::List> : mk_poly_existential_predicates, &'tcx ty::List> : mk_place_elems, - &'tcx ty::List<&'tcx ty::List>> : mk_place_elem_chain, + &'tcx ty::List> : mk_place_elem_chain, &'tcx ty::List> : mk_patterns, &'tcx ty::List> : mk_outlives, } From c285f0f8f8236f6a230c851887ffc0cf5d8facb1 Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Mon, 8 Sep 2025 22:00:24 -0700 Subject: [PATCH 5/7] tighter representation of CompoundPlaceRef --- compiler/rustc_middle/src/mir/pretty.rs | 10 ++- compiler/rustc_middle/src/mir/statement.rs | 91 ++++++++++------------ 2 files changed, 49 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 9f235028d98e2..2667d55359e3c 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1278,17 +1278,19 @@ impl Debug for CompoundPlace<'_> { impl Debug for CompoundPlaceRef<'_> { fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { - pre_fmt_projection(self.last_projection, fmt)?; - for projection in self.projection_chain_base.iter().rev() { + 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)?; } write!(fmt, "{:?}", self.local)?; - for projection in self.projection_chain_base { + for projection in stem { post_fmt_projection(projection, fmt)?; } - post_fmt_projection(self.last_projection, fmt)?; + post_fmt_projection(suffix, fmt)?; Ok(()) } diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index c4347bd719a38..dd39942e2d64d 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -581,8 +581,9 @@ pub struct CompoundPlace<'tcx> { #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct CompoundPlaceRef<'tcx> { pub local: Local, - pub projection_chain_base: &'tcx [ProjectionFragment<'tcx>], - pub last_projection: ProjectionFragmentRef<'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>)>, } // these impls are bare-bones for now @@ -617,13 +618,7 @@ impl<'tcx> CompoundPlace<'tcx> { } pub fn as_ref(&self) -> CompoundPlaceRef<'tcx> { - let (last, base) = self - .projection_chain - .split_last() - .map(|(&last, base)| (last, base)) - .unwrap_or_default(); - - CompoundPlaceRef { local: self.local, projection_chain_base: base, last_projection: last } + CompoundPlaceRef::from_slice(self.local, self.projection_chain) } pub fn as_local(&self) -> Option { @@ -684,37 +679,11 @@ impl<'tcx> CompoundPlace<'tcx> { ) -> impl Iterator, PlaceElem<'tcx>)> + DoubleEndedIterator { self.projection_chain.iter().enumerate().flat_map(move |(i, projs)| { projs.iter().enumerate().map(move |(j, elem)| { - let base = if j == 0 && i > 0 { - // last_projection should only be empty if projection_chain_base is too - debug_assert_eq!(elem, PlaceElem::Deref); - CompoundPlaceRef { - local: self.local, - projection_chain_base: &self.projection_chain[..i - 1], - last_projection: &self.projection_chain[i - 1], - } - - // FIXME: the fact that i messed up this logic the first time is good evidence that - // the invariants are confusing and difficult to uphold - } else { - CompoundPlaceRef { - local: self.local, - projection_chain_base: &self.projection_chain[..i], - last_projection: &projs[..j], - } - }; - - if cfg!(debug_assertions) { - let self_projections: Vec<_> = self.projection_chain.iter().flatten().collect(); - let base_projections: Vec<_> = base - .projection_chain_base - .iter() - .copied() - .flatten() - .chain(base.last_projection.iter().copied()) - .collect(); - - assert_eq!(self_projections[..base_projections.len()], base_projections); - } + let base = CompoundPlaceRef::from_stem_with_suffix( + self.local, + &self.projection_chain[..i], + &projs[..j], + ); (base, elem) }) @@ -742,21 +711,47 @@ impl<'tcx> CompoundPlaceRef<'tcx> { where D: HasLocalDecls<'tcx>, { - PlaceTy::from_ty(local_decls.local_decls()[self.local].ty) - .projection_chain_ty(tcx, self.projection_chain_base) - .multi_projection_ty(tcx, self.last_projection) + let local_ty = PlaceTy::from_ty(local_decls.local_decls()[self.local].ty); + + match self.projection_chain { + Some((stem, suffix)) => { + local_ty.projection_chain_ty(tcx, stem).multi_projection_ty(tcx, suffix) + } + None => local_ty, + } } pub fn local_or_deref_local(&self) -> Option { match *self { - CompoundPlaceRef { - local, - projection_chain_base: [], - last_projection: [] | [ProjectionElem::Deref], - } => Some(local), + CompoundPlaceRef { local, projection_chain: None | Some(([], [PlaceElem::Deref])) } => { + Some(local) + } _ => None, } } + + fn from_slice(local: Local, chain: &'tcx [ProjectionFragment<'tcx>]) -> CompoundPlaceRef<'tcx> { + let projection_chain = match chain { + [stem @ .., suffix] => Some((stem, suffix.as_slice())), + [] => None, + }; + + CompoundPlaceRef { local, projection_chain } + } + + fn from_stem_with_suffix( + local: Local, + stem: &'tcx [ProjectionFragment<'tcx>], + suffix: ProjectionFragmentRef<'tcx>, + ) -> CompoundPlaceRef<'tcx> { + let projection_chain = match (stem, suffix) { + ([], []) => None, + ([stem @ .., suffix], []) => Some((stem, suffix.as_slice())), + _ => Some((stem, suffix)), + }; + + CompoundPlaceRef { local, projection_chain } + } } /////////////////////////////////////////////////////////////////////////// From 107d42e94d062f6095cbd37cf63eb283a8f5b7d7 Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Tue, 9 Sep 2025 11:39:50 -0700 Subject: [PATCH 6/7] tighter representation of CompoundPlace --- .../src/diagnostics/conflict_errors.rs | 4 +- .../rustc_codegen_ssa/src/mir/debuginfo.rs | 2 +- compiler/rustc_middle/src/mir/pretty.rs | 2 + compiler/rustc_middle/src/mir/statement.rs | 213 +++++++++++------- compiler/rustc_middle/src/mir/visit.rs | 6 + compiler/rustc_middle/src/ty/codec.rs | 8 +- compiler/rustc_mir_transform/src/coroutine.rs | 2 +- .../src/coroutine/by_move_body.rs | 52 ++--- .../src/elaborate_box_derefs.rs | 42 ++-- compiler/rustc_mir_transform/src/ref_prop.rs | 16 +- compiler/rustc_mir_transform/src/sroa.rs | 3 +- compiler/rustc_mir_transform/src/validate.rs | 12 +- .../src/unstable/convert/stable/mir.rs | 7 +- 13 files changed, 217 insertions(+), 152 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index c9146f21dfba7..8b3ebbc077a93 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -561,9 +561,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { VarDebugInfoContents::Place(ref p) => { // This will become a simple == when Derefer is moved before borrowck place.local == p.local - && p.projection_chain - .iter() - .flatten() + && p.iter_projection_elems() .enumerate() .all(|(i, elem)| place.projection.get(i) == Some(&elem)) } diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 5bff4552641ab..af540fe36f1ae 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -571,7 +571,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // FIXME change field to be projection chain projection: bx .tcx() - .mk_place_elems_from_iter(place.projection_chain.iter().flatten()), + .mk_place_elems_from_iter(place.iter_projection_elems()), }); } mir::VarDebugInfoContents::Const(c) => { diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 2667d55359e3c..720e678dfac6a 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -1284,9 +1284,11 @@ impl Debug for CompoundPlaceRef<'_> { 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)?; } diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index dd39942e2d64d..2fb9d26d06710 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -1,5 +1,7 @@ //! Functionality for statements, operands, places, and things that appear in them. +use std::iter::once; + use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -556,24 +558,30 @@ impl From for PlaceRef<'_> { } } -// In the future this will also have the type being projected alongside it. -// 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) +/// 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 possibly containing derefs in the middle of its projection by chaining projection lists. +/// 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: - /// - All segments in the chain other than the first must start with a deref. - /// - Derefs may only appear at the start of a segment. - /// - No segment may be empty. + /// - 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>, } @@ -581,113 +589,133 @@ pub struct CompoundPlace<'tcx> { #[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 + /// `Some((stem, suffix))` is equivalent to `stem` with `suffix` appended to it. pub projection_chain: Option<(&'tcx [ProjectionFragment<'tcx>], ProjectionFragmentRef<'tcx>)>, } -// these impls are bare-bones for now impl<'tcx> CompoundPlace<'tcx> { pub fn from_place(place: Place<'tcx>, tcx: TyCtxt<'tcx>) -> CompoundPlace<'tcx> { - let mut segment_start = 0; - // size picked from sole user - let mut new_projection_chain = SmallVec::<[_; 2]>::new(); - - for (i, elem) in place.projection.iter().enumerate() { - if elem == PlaceElem::Deref && i > 0 { - new_projection_chain.push(tcx.mk_place_elems(&place.projection[segment_start..i])); - segment_start = i; + 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 segment_start == 0 { + 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[segment_start..])); - } - - if cfg!(debug_assertions) { - let new_projections: Vec<_> = new_projection_chain.iter().copied().flatten().collect(); - assert_eq!(new_projections.as_slice(), place.projection.as_slice()); + 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::from_slice(self.local, self.projection_chain) + 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 { - if self.projection_chain.is_empty() { Some(self.local) } else { None } + self.as_ref().as_local() } pub fn local_or_deref_local(&self) -> Option { self.as_ref().local_or_deref_local() } - /// Returns a [`Place`] with only the first segment in the projection chain. - pub fn base_place(&self) -> Place<'tcx> { - Place { - local: self.local, - projection: self.projection_chain.first().copied().unwrap_or_default(), - } + pub fn is_indirect(&self) -> bool { + !self.projection_chain.is_empty() } - /// Replaces the local and first segment of the projection with `new_base`. - pub fn replace_base_place(&mut self, new_base: Place<'tcx>, tcx: TyCtxt<'tcx>) { - self.local = new_base.local; - self.projection_chain = - match (new_base.projection.is_empty(), self.projection_chain.is_empty()) { - (false, false) => { - let mut new_projection_chain = self.projection_chain.to_vec(); - new_projection_chain[0] = new_base.projection; - tcx.mk_place_elem_chain(&new_projection_chain) - } - (false, true) => tcx.mk_place_elem_chain(&[new_base.projection]), - - (true, false) => tcx.mk_place_elem_chain(&self.projection_chain[1..]), - (true, true) => List::empty(), - } - // FIXME: this logic is a mess - // maybe separate out projection before first deref? + /// 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>) { - let base_place = self.base_place(); + self.local = new_base.local; if new_base.projection.is_empty() { - self.local = new_base.local; - } else if base_place.is_indirect_first_projection() { - let mut new_projection_chain = Vec::with_capacity(self.projection_chain.len() + 1); - new_projection_chain.push(new_base.projection); - new_projection_chain.extend_from_slice(self.projection_chain); - - self.local = new_base.local; - self.projection_chain = tcx.mk_place_elem_chain(&new_projection_chain); + // 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.replace_base_place(new_base.project_deeper(base_place.projection, tcx), tcx); + 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 { - self.projection_chain.iter().enumerate().flat_map(move |(i, projs)| { + 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::from_stem_with_suffix( - self.local, - &self.projection_chain[..i], - &projs[..j], - ); + 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> @@ -695,6 +723,7 @@ impl<'tcx> CompoundPlace<'tcx> { 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) } } @@ -702,7 +731,7 @@ impl<'tcx> CompoundPlace<'tcx> { impl From for CompoundPlace<'_> { #[inline] fn from(local: Local) -> Self { - CompoundPlace { local, projection_chain: List::empty() } + CompoundPlace { local, direct_projection: List::empty(), projection_chain: List::empty() } } } @@ -711,46 +740,58 @@ impl<'tcx> CompoundPlaceRef<'tcx> { where D: HasLocalDecls<'tcx>, { - let local_ty = PlaceTy::from_ty(local_decls.local_decls()[self.local].ty); + 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)) => { - local_ty.projection_chain_ty(tcx, stem).multi_projection_ty(tcx, suffix) + base_ty.projection_chain_ty(tcx, stem).multi_projection_ty(tcx, suffix) } - None => local_ty, + None => base_ty, } } - pub fn local_or_deref_local(&self) -> Option { + pub fn as_local(&self) -> Option { match *self { - CompoundPlaceRef { local, projection_chain: None | Some(([], [PlaceElem::Deref])) } => { + CompoundPlaceRef { local, direct_projection: [], projection_chain: None } => { Some(local) } _ => None, } } - fn from_slice(local: Local, chain: &'tcx [ProjectionFragment<'tcx>]) -> CompoundPlaceRef<'tcx> { - let projection_chain = match chain { - [stem @ .., suffix] => Some((stem, suffix.as_slice())), - [] => None, - }; - - CompoundPlaceRef { local, projection_chain } + pub fn local_or_deref_local(&self) -> Option { + match *self { + CompoundPlaceRef { + local, + direct_projection: [], + projection_chain: None | Some(([], [PlaceElem::Deref])), + } => Some(local), + _ => None, + } } - fn from_stem_with_suffix( - local: Local, + /// 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>, - ) -> CompoundPlaceRef<'tcx> { - let projection_chain = match (stem, suffix) { + ) -> Option<(&'tcx [ProjectionFragment<'tcx>], ProjectionFragmentRef<'tcx>)> { + match (stem, suffix) { ([], []) => None, ([stem @ .., suffix], []) => Some((stem, suffix.as_slice())), _ => Some((stem, suffix)), - }; + } + } - CompoundPlaceRef { local, projection_chain } + 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())), + } } } diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 74b2b4fb69dfa..173cdc300bcea 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -1200,6 +1200,12 @@ macro_rules! visit_place_fns { ) { 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) { diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 3a889848dd4a9..096bef3190163 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -300,7 +300,7 @@ 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 local: mir::Local = Decodable::decode(decoder); + 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(|_| { @@ -309,7 +309,11 @@ impl<'tcx, D: TyDecoder<'tcx>> Decodable for mir::CompoundPlace<'tcx> { (0..projs_len).map::, _>(|_| Decodable::decode(decoder)), ) })); - mir::CompoundPlace { local, projection_chain } + mir::CompoundPlace { + local: base_place.local, + direct_projection: base_place.projection, + projection_chain, + } } } diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 6409e0c7eda90..7a48fd48344d8 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -170,7 +170,7 @@ impl<'tcx> MutVisitor<'tcx> for SelfArgVisitor<'tcx> { } else { self.visit_local(&mut place.local, context, location); - for elem in place.projection_chain.iter().flatten() { + for elem in place.iter_projection_elems() { if let PlaceElem::Index(local) = elem { assert_ne!(local, SELF_ARG); } 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 e41a22cb806d7..1330691d7bc5d 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -332,13 +332,34 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> { context: mir::visit::PlaceContext, location: mir::Location, ) { + // See comments above in visit_place if place.local == ty::CAPTURE_STRUCT_LOCAL - && let Some((first_projection, rest)) = place.projection_chain.split_first() - && let Some((&mir::ProjectionElem::Field(idx, _), first_projection)) = - first_projection.split_first() + && 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)]; @@ -357,35 +378,14 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> { } } - let (next, tail) = if peel_deref { - assert!(first_projection.is_empty()); - let Some((next_projection, rest)) = rest.split_first() else { - bug!( - "There should be at least a single deref for an upvar local initialization, found {rest:#?}" - ); - }; - - 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 { - (first_projection, rest) - }; - 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, - projection_chain: self.tcx.mk_place_elem_chain_from_iter( - new_projection_chain.into_iter().map(|p| self.tcx.mk_place_elems(&p)), - ), + 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); diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs index c332cafe49df1..f162e03610844 100644 --- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs +++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs @@ -116,19 +116,33 @@ 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_projection_chain = None; + let mut base_ty = place.base_place().ty(&body.local_decls, tcx); - let mut base_ty = PlaceTy::from_ty(body.local_decls.local_decls()[place.local].ty); + // If this is None, there are no derefs. + let Some((suffix, stem)) = place.projection_chain.split_last() else { continue }; - // Maybe the first segment should just be empty if place starts with a deref? - let new_base = - place.base_place().is_indirect_first_projection().then_some((0, List::empty())); - - let Some((last, rest)) = place.projection_chain.split_last() else { continue }; + if let Some(boxed_ty) = base_ty.ty.boxed_ty() { + place.direct_projection = append_projection(place.direct_projection, boxed_ty); + } - for (i, projs) in new_base.into_iter().chain(rest.iter().copied().enumerate()) { + 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() { @@ -136,17 +150,7 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs { let new_projection_chain = new_projection_chain .get_or_insert_with(|| place.projection_chain[..i].to_vec()); - 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)); - - new_projection_chain.push(tcx.mk_place_elems(&new_projections)) + 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_projection_chain.push(projs); @@ -155,7 +159,7 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs { // Store the mutated projections if we actually changed something. if let Some(mut new_projection_chain) = new_projection_chain { - new_projection_chain.push(last); + new_projection_chain.push(suffix); place.projection_chain = tcx.mk_place_elem_chain(&new_projection_chain); } } diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs index 7125ce1ce7430..afd65b62ce656 100644 --- a/compiler/rustc_mir_transform/src/ref_prop.rs +++ b/compiler/rustc_mir_transform/src/ref_prop.rs @@ -447,11 +447,23 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> { ctxt: PlaceContext, loc: Location, ) { - let mut base = place.base_place(); + // 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.replace_base_place(base, self.tcx); + 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..]); + } } } diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs index 22aff1c9e21bd..352a25d53216e 100644 --- a/compiler/rustc_mir_transform/src/sroa.rs +++ b/compiler/rustc_mir_transform/src/sroa.rs @@ -272,7 +272,8 @@ impl<'tcx> ReplacementVisitor<'tcx, '_> { let base_place = place.base_place(); if let Some(repl) = self.replacements.replace_place(self.tcx, base_place.as_ref()) { - place.replace_base_place(repl, self.tcx); + place.local = repl.local; + place.direct_projection = repl.projection; return vec![var_debug_info]; } diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 850c781433a95..3f3cd75ea0810 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -883,7 +883,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { match debuginfo.value { VarDebugInfoContents::Const(_) => {} VarDebugInfoContents::Place(place) => { - if place.projection_chain.iter().flatten().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), @@ -935,17 +935,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // 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 i > 0 && projection.first() != Some(&ProjectionElem::Deref) { + if projection.first() != Some(&ProjectionElem::Deref) { self.fail( location, - format!( - "compound place {place:?} has later segment without deref (segment {i})" - ), + format!("compound place {place:?} missing deref in segment {i}"), ); } diff --git a/compiler/rustc_public/src/unstable/convert/stable/mir.rs b/compiler/rustc_public/src/unstable/convert/stable/mir.rs index dc1fae8426599..a4aa0b9991e7b 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/mir.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/mir.rs @@ -432,12 +432,7 @@ impl<'tcx> Stable<'tcx> for mir::CompoundPlace<'tcx> { ) -> Self::T { crate::mir::Place { local: self.local.as_usize(), - projection: self - .projection_chain - .iter() - .flatten() - .map(|e| e.stable(tables, cx)) - .collect(), + projection: self.iter_projection_elems().map(|e| e.stable(tables, cx)).collect(), } } } From d32ca9bfa66b9ddf693d6060030753830954cedf Mon Sep 17 00:00:00 2001 From: beepster4096 <19316085+beepster4096@users.noreply.github.com> Date: Tue, 9 Sep 2025 14:19:59 -0700 Subject: [PATCH 7/7] properly use CompoundPlace in codegen debuginfo --- .../rustc_codegen_ssa/src/mir/debuginfo.rs | 113 ++++++++++++------ 1 file changed, 76 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index af540fe36f1ae..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); @@ -543,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 { @@ -562,16 +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, - // FIXME change field to be projection chain - projection: bx - .tcx() - .mk_place_elems_from_iter(place.iter_projection_elems()), + direct_projection, + projection_chain, }); } mir::VarDebugInfoContents::Const(c) => {