about summary refs log tree commit diff
diff options
context:
space:
mode:
authorCamille GILLOT <gillot.camille@gmail.com>2023-08-26 16:58:42 +0000
committerCamille GILLOT <gillot.camille@gmail.com>2023-09-05 17:20:07 +0000
commit26c48e6f95c4075951a21d54cf1e61af68cbb5e0 (patch)
treee982eadb93ab34c1d0fe5d95da57003a604b4121
parent429a9258f10936c3b36b8b265175efbcbdfba1d5 (diff)
downloadrust-26c48e6f95c4075951a21d54cf1e61af68cbb5e0.tar.gz
rust-26c48e6f95c4075951a21d54cf1e61af68cbb5e0.zip
Refactor how MIR represents composite debuginfo.
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/debuginfo.rs155
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs58
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs70
-rw-r--r--compiler/rustc_middle/src/mir/pretty.rs5
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs19
-rw-r--r--compiler/rustc_mir_build/src/build/custom/parse.rs1
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs2
-rw-r--r--compiler/rustc_mir_transform/src/remove_zsts.rs5
-rw-r--r--compiler/rustc_mir_transform/src/sroa.rs93
-rw-r--r--tests/mir-opt/const_debuginfo.main.ConstDebugInfo.diff12
-rw-r--r--tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir12
-rw-r--r--tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir12
-rw-r--r--tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff3
-rw-r--r--tests/mir-opt/sroa/structs.constant.ScalarReplacementOfAggregates.diff3
-rw-r--r--tests/mir-opt/sroa/structs.copies.ScalarReplacementOfAggregates.diff10
-rw-r--r--tests/mir-opt/sroa/structs.ref_copies.ScalarReplacementOfAggregates.diff5
17 files changed, 223 insertions, 244 deletions
diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
index 526c16a59de..7cd9efe3ee2 100644
--- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
@@ -484,54 +484,89 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 None
             };
 
-            let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| {
-                let (var_ty, var_kind) = match var.value {
+            let var_ty = if let Some(ref fragment) = var.composite {
+                self.monomorphize(fragment.ty)
+            } else {
+                match var.value {
                     mir::VarDebugInfoContents::Place(place) => {
-                        let var_ty = self.monomorphized_place_ty(place.as_ref());
-                        let var_kind = if let Some(arg_index) = var.argument_index
-                            && place.projection.is_empty()
-                        {
-                            let arg_index = arg_index as usize;
-                            if target_is_msvc {
-                                // ScalarPair parameters are spilled to the stack so they need to
-                                // be marked as a `LocalVariable` for MSVC debuggers to visualize
-                                // their data correctly. (See #81894 & #88625)
-                                let var_ty_layout = self.cx.layout_of(var_ty);
-                                if let Abi::ScalarPair(_, _) = var_ty_layout.abi {
-                                    VariableKind::LocalVariable
-                                } else {
-                                    VariableKind::ArgumentVariable(arg_index)
-                                }
-                            } else {
-                                // FIXME(eddyb) shouldn't `ArgumentVariable` indices be
-                                // offset in closures to account for the hidden environment?
-                                VariableKind::ArgumentVariable(arg_index)
-                            }
-                        } else {
-                            VariableKind::LocalVariable
-                        };
-                        (var_ty, var_kind)
+                        self.monomorphized_place_ty(place.as_ref())
                     }
-                    mir::VarDebugInfoContents::Const(c) => {
-                        let ty = self.monomorphize(c.ty());
-                        (ty, VariableKind::LocalVariable)
-                    }
-                    mir::VarDebugInfoContents::Composite { ty, fragments: _ } => {
-                        let ty = self.monomorphize(ty);
-                        (ty, VariableKind::LocalVariable)
+                    mir::VarDebugInfoContents::Const(c) => self.monomorphize(c.ty()),
+                }
+            };
+
+            let dbg_var = dbg_scope_and_span.map(|(dbg_scope, _, span)| {
+                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()
+                {
+                    let arg_index = arg_index as usize;
+                    if target_is_msvc {
+                        // ScalarPair parameters are spilled to the stack so they need to
+                        // be marked as a `LocalVariable` for MSVC debuggers to visualize
+                        // their data correctly. (See #81894 & #88625)
+                        let var_ty_layout = self.cx.layout_of(var_ty);
+                        if let Abi::ScalarPair(_, _) = var_ty_layout.abi {
+                            VariableKind::LocalVariable
+                        } else {
+                            VariableKind::ArgumentVariable(arg_index)
+                        }
+                    } else {
+                        // FIXME(eddyb) shouldn't `ArgumentVariable` indices be
+                        // offset in closures to account for the hidden environment?
+                        VariableKind::ArgumentVariable(arg_index)
                     }
+                } else {
+                    VariableKind::LocalVariable
                 };
 
                 self.cx.create_dbg_var(var.name, var_ty, dbg_scope, var_kind, span)
             });
 
+            let fragment = if let Some(ref fragment) = var.composite {
+                let var_layout = self.cx.layout_of(var_ty);
+
+                let mut fragment_start = Size::ZERO;
+                let mut fragment_layout = var_layout;
+
+                for elem in &fragment.projection {
+                    match *elem {
+                        mir::ProjectionElem::Field(field, _) => {
+                            let i = field.index();
+                            fragment_start += fragment_layout.fields.offset(i);
+                            fragment_layout = fragment_layout.field(self.cx, i);
+                        }
+                        _ => span_bug!(
+                            var.source_info.span,
+                            "unsupported fragment projection `{:?}`",
+                            elem,
+                        ),
+                    }
+                }
+
+                if fragment_layout.size == Size::ZERO {
+                    // Fragment is a ZST, so does not represent anything. Avoid generating anything
+                    // as this may conflict with a fragment that covers the entire variable.
+                    continue;
+                } else if fragment_layout.size == var_layout.size {
+                    // Fragment covers entire variable, so as far as
+                    // DWARF is concerned, it's not really a fragment.
+                    None
+                } else {
+                    Some(fragment_start..fragment_start + fragment_layout.size)
+                }
+            } else {
+                None
+            };
+
             match var.value {
                 mir::VarDebugInfoContents::Place(place) => {
                     per_local[place.local].push(PerLocalVarDebugInfo {
                         name: var.name,
                         source_info: var.source_info,
                         dbg_var,
-                        fragment: None,
+                        fragment,
                         projection: place.projection,
                     });
                 }
@@ -547,51 +582,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                                 bx,
                             );
 
-                            bx.dbg_var_addr(dbg_var, dbg_loc, base.llval, Size::ZERO, &[], None);
-                        }
-                    }
-                }
-                mir::VarDebugInfoContents::Composite { ty, ref fragments } => {
-                    let var_ty = self.monomorphize(ty);
-                    let var_layout = self.cx.layout_of(var_ty);
-                    for fragment in fragments {
-                        let mut fragment_start = Size::ZERO;
-                        let mut fragment_layout = var_layout;
-
-                        for elem in &fragment.projection {
-                            match *elem {
-                                mir::ProjectionElem::Field(field, _) => {
-                                    let i = field.index();
-                                    fragment_start += fragment_layout.fields.offset(i);
-                                    fragment_layout = fragment_layout.field(self.cx, i);
-                                }
-                                _ => span_bug!(
-                                    var.source_info.span,
-                                    "unsupported fragment projection `{:?}`",
-                                    elem,
-                                ),
-                            }
+                            bx.dbg_var_addr(
+                                dbg_var,
+                                dbg_loc,
+                                base.llval,
+                                Size::ZERO,
+                                &[],
+                                fragment,
+                            );
                         }
-
-                        let place = fragment.contents;
-                        let fragment = if fragment_layout.size == Size::ZERO {
-                            // Fragment is a ZST, so does not represent anything.
-                            continue;
-                        } else if fragment_layout.size == var_layout.size {
-                            // Fragment covers entire variable, so as far as
-                            // DWARF is concerned, it's not really a fragment.
-                            None
-                        } else {
-                            Some(fragment_start..fragment_start + fragment_layout.size)
-                        };
-
-                        per_local[place.local].push(PerLocalVarDebugInfo {
-                            name: var.name,
-                            source_info: var.source_info,
-                            dbg_var,
-                            fragment,
-                            projection: place.projection,
-                        });
                     }
                 }
             }
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 770c3f7f02c..2f5f2ad6534 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -6,13 +6,7 @@ use rustc_index::IndexVec;
 use rustc_infer::traits::Reveal;
 use rustc_middle::mir::interpret::Scalar;
 use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
-use rustc_middle::mir::{
-    traversal, BasicBlock, BinOp, Body, BorrowKind, CastKind, CopyNonOverlapping, Local, Location,
-    MirPass, MirPhase, NonDivergingIntrinsic, NullOp, Operand, Place, PlaceElem, PlaceRef,
-    ProjectionElem, RetagKind, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind,
-    Terminator, TerminatorKind, UnOp, UnwindAction, UnwindTerminateReason, VarDebugInfo,
-    VarDebugInfoContents, START_BLOCK,
-};
+use rustc_middle::mir::*;
 use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitableExt};
 use rustc_mir_dataflow::impls::MaybeStorageLive;
 use rustc_mir_dataflow::storage::always_storage_live_locals;
@@ -757,37 +751,37 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
     }
 
     fn visit_var_debug_info(&mut self, debuginfo: &VarDebugInfo<'tcx>) {
-        let check_place = |this: &mut Self, place: Place<'_>| {
-            if place.projection.iter().any(|p| !p.can_use_in_debuginfo()) {
-                this.fail(
+        if let Some(box VarDebugInfoFragment { ty, ref projection }) = debuginfo.composite {
+            if ty.is_union() || ty.is_enum() {
+                self.fail(
                     START_BLOCK.start_location(),
-                    format!("illegal place {:?} in debuginfo for {:?}", place, debuginfo.name),
+                    format!("invalid type {ty:?} in debuginfo for {:?}", debuginfo.name),
                 );
             }
-        };
+            if projection.is_empty() {
+                self.fail(
+                    START_BLOCK.start_location(),
+                    format!("invalid empty projection in debuginfo for {:?}", debuginfo.name),
+                );
+            }
+            if projection.iter().any(|p| !matches!(p, PlaceElem::Field(..))) {
+                self.fail(
+                    START_BLOCK.start_location(),
+                    format!(
+                        "illegal projection {:?} in debuginfo for {:?}",
+                        projection, debuginfo.name
+                    ),
+                );
+            }
+        }
         match debuginfo.value {
             VarDebugInfoContents::Const(_) => {}
             VarDebugInfoContents::Place(place) => {
-                check_place(self, place);
-            }
-            VarDebugInfoContents::Composite { ty, ref fragments } => {
-                for f in fragments {
-                    check_place(self, f.contents);
-                    if ty.is_union() || ty.is_enum() {
-                        self.fail(
-                            START_BLOCK.start_location(),
-                            format!("invalid type {ty:?} for composite debuginfo"),
-                        );
-                    }
-                    if f.projection.iter().any(|p| !matches!(p, PlaceElem::Field(..))) {
-                        self.fail(
-                            START_BLOCK.start_location(),
-                            format!(
-                                "illegal projection {:?} in debuginfo for {:?}",
-                                f.projection, debuginfo.name
-                            ),
-                        );
-                    }
+                if place.projection.iter().any(|p| !p.can_use_in_debuginfo()) {
+                    self.fail(
+                        START_BLOCK.start_location(),
+                        format!("illegal place {:?} in debuginfo for {:?}", place, debuginfo.name),
+                    );
                 }
             }
         }
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 18ae6bbc7a1..239d20ae1b0 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -1028,19 +1028,6 @@ pub enum VarDebugInfoContents<'tcx> {
     /// This `Place` only contains projection which satisfy `can_use_in_debuginfo`.
     Place(Place<'tcx>),
     Const(Constant<'tcx>),
-    /// The user variable's data is split across several fragments,
-    /// each described by a `VarDebugInfoFragment`.
-    /// See DWARF 5's "2.6.1.2 Composite Location Descriptions"
-    /// and LLVM's `DW_OP_LLVM_fragment` for more details on
-    /// the underlying debuginfo feature this relies on.
-    Composite {
-        /// Type of the original user variable.
-        /// This cannot contain a union or an enum.
-        ty: Ty<'tcx>,
-        /// All the parts of the original user variable, which ended
-        /// up in disjoint places, due to optimizations.
-        fragments: Vec<VarDebugInfoFragment<'tcx>>,
-    },
 }
 
 impl<'tcx> Debug for VarDebugInfoContents<'tcx> {
@@ -1048,19 +1035,16 @@ impl<'tcx> Debug for VarDebugInfoContents<'tcx> {
         match self {
             VarDebugInfoContents::Const(c) => write!(fmt, "{c}"),
             VarDebugInfoContents::Place(p) => write!(fmt, "{p:?}"),
-            VarDebugInfoContents::Composite { ty, fragments } => {
-                write!(fmt, "{ty:?}{{ ")?;
-                for f in fragments.iter() {
-                    write!(fmt, "{f:?}, ")?;
-                }
-                write!(fmt, "}}")
-            }
         }
     }
 }
 
-#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
+#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
 pub struct VarDebugInfoFragment<'tcx> {
+    /// Type of the original user variable.
+    /// This cannot contain a union or an enum.
+    pub ty: Ty<'tcx>,
+
     /// Where in the composite user variable this fragment is,
     /// represented as a "projection" into the composite variable.
     /// At lower levels, this corresponds to a byte/bit range.
@@ -1071,29 +1055,10 @@ pub struct VarDebugInfoFragment<'tcx> {
     // to match on the discriminant, or by using custom type debuginfo
     // with non-overlapping variants for the composite variable.
     pub projection: Vec<PlaceElem<'tcx>>,
-
-    /// Where the data for this fragment can be found.
-    /// This `Place` only contains projection which satisfy `can_use_in_debuginfo`.
-    pub contents: Place<'tcx>,
-}
-
-impl Debug for VarDebugInfoFragment<'_> {
-    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
-        for elem in self.projection.iter() {
-            match elem {
-                ProjectionElem::Field(field, _) => {
-                    write!(fmt, ".{:?}", field.index())?;
-                }
-                _ => bug!("unsupported fragment projection `{:?}`", elem),
-            }
-        }
-
-        write!(fmt, " => {:?}", self.contents)
-    }
 }
 
 /// Debug information pertaining to a user variable.
-#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
+#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
 pub struct VarDebugInfo<'tcx> {
     pub name: Symbol,
 
@@ -1102,6 +1067,13 @@ pub struct VarDebugInfo<'tcx> {
     /// (see `LocalDecl`'s `source_info` field for more details).
     pub source_info: SourceInfo,
 
+    /// The user variable's data is split across several fragments,
+    /// each described by a `VarDebugInfoFragment`.
+    /// See DWARF 5's "2.6.1.2 Composite Location Descriptions"
+    /// and LLVM's `DW_OP_LLVM_fragment` for more details on
+    /// the underlying debuginfo feature this relies on.
+    pub composite: Option<Box<VarDebugInfoFragment<'tcx>>>,
+
     /// Where the data for this user variable is to be found.
     pub value: VarDebugInfoContents<'tcx>,
 
@@ -1111,6 +1083,20 @@ pub struct VarDebugInfo<'tcx> {
     pub argument_index: Option<u16>,
 }
 
+impl Debug for VarDebugInfo<'_> {
+    fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
+        if let Some(box VarDebugInfoFragment { ty, ref projection }) = self.composite {
+            pre_fmt_projection(&projection[..], fmt)?;
+            write!(fmt, "({}: {})", self.name, ty)?;
+            post_fmt_projection(&projection[..], fmt)?;
+        } else {
+            write!(fmt, "{}", self.name)?;
+        }
+
+        write!(fmt, " => {:?}", self.value)
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // BasicBlock
 
@@ -3070,6 +3056,6 @@ mod size_asserts {
     static_assert_size!(StatementKind<'_>, 16);
     static_assert_size!(Terminator<'_>, 104);
     static_assert_size!(TerminatorKind<'_>, 88);
-    static_assert_size!(VarDebugInfo<'_>, 80);
+    static_assert_size!(VarDebugInfo<'_>, 88);
     // tidy-alphabetical-end
 }
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 773056e8a17..488526edb43 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -554,10 +554,7 @@ fn write_scope_tree(
             continue;
         }
 
-        let indented_debug_info = format!(
-            "{0:1$}debug {2} => {3:?};",
-            INDENT, indent, var_debug_info.name, var_debug_info.value,
-        );
+        let indented_debug_info = format!("{0:1$}debug {2:?};", INDENT, indent, var_debug_info);
 
         if tcx.sess.opts.unstable_opts.mir_include_spans {
             writeln!(
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 87b04aabe6a..61244b94289 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -838,12 +838,20 @@ macro_rules! make_mir_visitor {
                 let VarDebugInfo {
                     name: _,
                     source_info,
+                    composite,
                     value,
                     argument_index: _,
                 } = var_debug_info;
 
                 self.visit_source_info(source_info);
                 let location = Location::START;
+                if let Some(box VarDebugInfoFragment { ref $($mutability)? ty, ref $($mutability)? projection }) = composite {
+                    self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
+                    for elem in projection {
+                        let ProjectionElem::Field(_, ty) = elem else { bug!() };
+                        self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
+                    }
+                }
                 match value {
                     VarDebugInfoContents::Const(c) => self.visit_constant(c, location),
                     VarDebugInfoContents::Place(place) =>
@@ -852,17 +860,6 @@ macro_rules! make_mir_visitor {
                             PlaceContext::NonUse(NonUseContext::VarDebugInfo),
                             location
                         ),
-                    VarDebugInfoContents::Composite { ty, fragments } => {
-                        // FIXME(eddyb) use a better `TyContext` here.
-                        self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
-                        for VarDebugInfoFragment { projection: _, contents } in fragments {
-                            self.visit_place(
-                                contents,
-                                PlaceContext::NonUse(NonUseContext::VarDebugInfo),
-                                location,
-                            );
-                        }
-                    }
                 }
             }
 
diff --git a/compiler/rustc_mir_build/src/build/custom/parse.rs b/compiler/rustc_mir_build/src/build/custom/parse.rs
index f83c62fd580..e2ab2cb90c7 100644
--- a/compiler/rustc_mir_build/src/build/custom/parse.rs
+++ b/compiler/rustc_mir_build/src/build/custom/parse.rs
@@ -241,6 +241,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
             let dbginfo = VarDebugInfo {
                 name,
                 source_info: SourceInfo { span, scope: self.source_scope },
+                composite: None,
                 argument_index: None,
                 value,
             };
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 5ec216cea61..7e81c8b50c2 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -2287,6 +2287,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             name,
             source_info: debug_source_info,
             value: VarDebugInfoContents::Place(for_arm_body.into()),
+            composite: None,
             argument_index: None,
         });
         let locals = if has_guard.0 {
@@ -2306,6 +2307,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 name,
                 source_info: debug_source_info,
                 value: VarDebugInfoContents::Place(ref_for_guard.into()),
+                composite: None,
                 argument_index: None,
             });
             LocalsForNode::ForGuard { ref_for_guard, for_arm_body }
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index e614046e83e..4e10916ad61 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -823,6 +823,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     name,
                     source_info: SourceInfo::outermost(captured_place.var_ident.span),
                     value: VarDebugInfoContents::Place(use_place),
+                    composite: None,
                     argument_index: None,
                 });
 
@@ -852,6 +853,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     name,
                     source_info,
                     value: VarDebugInfoContents::Place(arg_local.into()),
+                    composite: None,
                     argument_index: Some(argument_index as u16 + 1),
                 });
             }
diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs
index 9c6c55b0811..c13bafa9fbb 100644
--- a/compiler/rustc_mir_transform/src/remove_zsts.rs
+++ b/compiler/rustc_mir_transform/src/remove_zsts.rs
@@ -87,11 +87,6 @@ impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
                     var_debug_info.value = VarDebugInfoContents::Const(self.make_zst(place_ty))
                 }
             }
-            VarDebugInfoContents::Composite { ty, fragments: _ } => {
-                if self.known_to_be_zst(ty) {
-                    var_debug_info.value = VarDebugInfoContents::Const(self.make_zst(ty))
-                }
-            }
         }
     }
 
diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs
index e66ae8ff884..c21b1724cbb 100644
--- a/compiler/rustc_mir_transform/src/sroa.rs
+++ b/compiler/rustc_mir_transform/src/sroa.rs
@@ -1,4 +1,5 @@
 use crate::MirPass;
+use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
 use rustc_index::bit_set::{BitSet, GrowableBitSet};
 use rustc_index::IndexVec;
 use rustc_middle::mir::patch::MirPatch;
@@ -147,7 +148,7 @@ fn escaping_locals<'tcx>(
         }
 
         // We ignore anything that happens in debuginfo, since we expand it using
-        // `VarDebugInfoContents::Composite`.
+        // `VarDebugInfoFragment`.
         fn visit_var_debug_info(&mut self, _: &VarDebugInfo<'tcx>) {}
     }
 }
@@ -246,9 +247,7 @@ fn replace_flattened_locals<'tcx>(
     for (index, annotation) in body.user_type_annotations.iter_enumerated_mut() {
         visitor.visit_user_type_annotation(index, annotation);
     }
-    for var_debug_info in &mut body.var_debug_info {
-        visitor.visit_var_debug_info(var_debug_info);
-    }
+    visitor.expand_var_debug_info(&mut body.var_debug_info);
     let ReplacementVisitor { patch, all_dead_locals, .. } = visitor;
     patch.apply(body);
     all_dead_locals
@@ -256,7 +255,7 @@ fn replace_flattened_locals<'tcx>(
 
 struct ReplacementVisitor<'tcx, 'll> {
     tcx: TyCtxt<'tcx>,
-    /// This is only used to compute the type for `VarDebugInfoContents::Composite`.
+    /// This is only used to compute the type for `VarDebugInfoFragment`.
     local_decls: &'ll LocalDecls<'tcx>,
     /// Work to do.
     replacements: &'ll ReplacementMap<'tcx>,
@@ -266,16 +265,38 @@ struct ReplacementVisitor<'tcx, 'll> {
 }
 
 impl<'tcx> ReplacementVisitor<'tcx, '_> {
-    fn gather_debug_info_fragments(&self, local: Local) -> Option<Vec<VarDebugInfoFragment<'tcx>>> {
-        let mut fragments = Vec::new();
-        let parts = self.replacements.place_fragments(local.into())?;
-        for (field, ty, replacement_local) in parts {
-            fragments.push(VarDebugInfoFragment {
-                projection: vec![PlaceElem::Field(field, ty)],
-                contents: Place::from(replacement_local),
-            });
-        }
-        Some(fragments)
+    #[instrument(level = "trace", skip(self))]
+    fn expand_var_debug_info(&mut self, var_debug_info: &mut Vec<VarDebugInfo<'tcx>>) {
+        var_debug_info.flat_map_in_place(|mut var_debug_info| {
+            let place = match var_debug_info.value {
+                VarDebugInfoContents::Const(_) => return vec![var_debug_info],
+                VarDebugInfoContents::Place(ref mut place) => place,
+            };
+
+            if let Some(repl) = self.replacements.replace_place(self.tcx, place.as_ref()) {
+                *place = repl;
+                return vec![var_debug_info];
+            }
+
+            let Some(parts) = self.replacements.place_fragments(*place) else {
+                return vec![var_debug_info];
+            };
+
+            let ty = place.ty(self.local_decls, self.tcx).ty;
+
+            parts
+                .map(|(field, field_ty, replacement_local)| {
+                    let mut var_debug_info = var_debug_info.clone();
+                    let composite = var_debug_info.composite.get_or_insert_with(|| {
+                        Box::new(VarDebugInfoFragment { ty, projection: Vec::new() })
+                    });
+                    composite.projection.push(PlaceElem::Field(field, field_ty));
+
+                    var_debug_info.value = VarDebugInfoContents::Place(replacement_local.into());
+                    var_debug_info
+                })
+                .collect()
+        });
     }
 }
 
@@ -422,48 +443,6 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
         self.super_statement(statement, location)
     }
 
-    #[instrument(level = "trace", skip(self))]
-    fn visit_var_debug_info(&mut self, var_debug_info: &mut VarDebugInfo<'tcx>) {
-        match &mut var_debug_info.value {
-            VarDebugInfoContents::Place(ref mut place) => {
-                if let Some(repl) = self.replacements.replace_place(self.tcx, place.as_ref()) {
-                    *place = repl;
-                } else if let Some(local) = place.as_local()
-                    && let Some(fragments) = self.gather_debug_info_fragments(local)
-                {
-                    let ty = place.ty(self.local_decls, self.tcx).ty;
-                    var_debug_info.value = VarDebugInfoContents::Composite { ty, fragments };
-                }
-            }
-            VarDebugInfoContents::Composite { ty: _, ref mut fragments } => {
-                let mut new_fragments = Vec::new();
-                debug!(?fragments);
-                fragments.retain_mut(|fragment| {
-                    if let Some(repl) =
-                            self.replacements.replace_place(self.tcx, fragment.contents.as_ref())
-                        {
-                            fragment.contents = repl;
-                            true
-                        } else if let Some(local) = fragment.contents.as_local()
-                            && let Some(frg) = self.gather_debug_info_fragments(local)
-                        {
-                            new_fragments.extend(frg.into_iter().map(|mut f| {
-                                f.projection.splice(0..0, fragment.projection.iter().copied());
-                                f
-                            }));
-                            false
-                        } else {
-                            true
-                        }
-                });
-                debug!(?fragments);
-                debug!(?new_fragments);
-                fragments.extend(new_fragments);
-            }
-            VarDebugInfoContents::Const(_) => {}
-        }
-    }
-
     fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) {
         assert!(!self.all_dead_locals.contains(*local));
     }
diff --git a/tests/mir-opt/const_debuginfo.main.ConstDebugInfo.diff b/tests/mir-opt/const_debuginfo.main.ConstDebugInfo.diff
index 255ec94816c..ed47baa67da 100644
--- a/tests/mir-opt/const_debuginfo.main.ConstDebugInfo.diff
+++ b/tests/mir-opt/const_debuginfo.main.ConstDebugInfo.diff
@@ -33,14 +33,22 @@
                           let _15: bool;
                           let _16: u32;
                           scope 6 {
-                              debug f => (bool, bool, u32){ .0 => _14, .1 => _15, .2 => _16, };
+-                             debug ((f: (bool, bool, u32)).0: bool) => _14;
+-                             debug ((f: (bool, bool, u32)).1: bool) => _15;
+-                             debug ((f: (bool, bool, u32)).2: u32) => _16;
++                             debug ((f: (bool, bool, u32)).0: bool) => const true;
++                             debug ((f: (bool, bool, u32)).1: bool) => const false;
++                             debug ((f: (bool, bool, u32)).2: u32) => const 123_u32;
                               let _10: std::option::Option<u16>;
                               scope 7 {
                                   debug o => _10;
                                   let _17: u32;
                                   let _18: u32;
                                   scope 8 {
-                                      debug p => Point{ .0 => _17, .1 => _18, };
+-                                     debug ((p: Point).0: u32) => _17;
+-                                     debug ((p: Point).1: u32) => _18;
++                                     debug ((p: Point).0: u32) => const 32_u32;
++                                     debug ((p: Point).1: u32) => const 32_u32;
                                       let _11: u32;
                                       scope 9 {
 -                                         debug a => _11;
diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir
index 8dd2cd7900f..2fd669aeeb6 100644
--- a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-abort.mir
@@ -8,19 +8,22 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range<usize>) ->
     let mut _4: usize;
     scope 1 (inlined core::slice::<impl [u32]>::get_unchecked_mut::<std::ops::Range<usize>>) {
         debug self => _1;
-        debug index => std::ops::Range<usize>{ .0 => _3, .1 => _4, };
+        debug ((index: std::ops::Range<usize>).0: usize) => _3;
+        debug ((index: std::ops::Range<usize>).1: usize) => _4;
         let mut _5: *mut [u32];
         let mut _13: *mut [u32];
         scope 2 {
             scope 3 (inlined <std::ops::Range<usize> as SliceIndex<[u32]>>::get_unchecked_mut) {
-                debug self => std::ops::Range<usize>{ .0 => _3, .1 => _4, };
+                debug ((self: std::ops::Range<usize>).0: usize) => _3;
+                debug ((self: std::ops::Range<usize>).1: usize) => _4;
                 debug slice => _5;
                 let mut _7: *mut u32;
                 let mut _8: *mut u32;
                 let _15: usize;
                 let _16: usize;
                 scope 4 {
-                    debug this => std::ops::Range<usize>{ .0 => _15, .1 => _16, };
+                    debug ((this: std::ops::Range<usize>).0: usize) => _15;
+                    debug ((this: std::ops::Range<usize>).1: usize) => _16;
                     scope 5 {
                         let _6: usize;
                         scope 6 {
@@ -53,7 +56,8 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range<usize>) ->
                             }
                         }
                         scope 7 (inlined <std::ops::Range<usize> as SliceIndex<[T]>>::get_unchecked_mut::runtime::<u32>) {
-                            debug this => std::ops::Range<usize>{ .0 => _15, .1 => _16, };
+                            debug ((this: std::ops::Range<usize>).0: usize) => _15;
+                            debug ((this: std::ops::Range<usize>).1: usize) => _16;
                             debug slice => _5;
                             scope 8 (inlined ptr::mut_ptr::<impl *mut [u32]>::len) {
                                 debug self => _5;
diff --git a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir
index 8dd2cd7900f..2fd669aeeb6 100644
--- a/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/slice_index.slice_get_unchecked_mut_range.PreCodegen.after.panic-unwind.mir
@@ -8,19 +8,22 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range<usize>) ->
     let mut _4: usize;
     scope 1 (inlined core::slice::<impl [u32]>::get_unchecked_mut::<std::ops::Range<usize>>) {
         debug self => _1;
-        debug index => std::ops::Range<usize>{ .0 => _3, .1 => _4, };
+        debug ((index: std::ops::Range<usize>).0: usize) => _3;
+        debug ((index: std::ops::Range<usize>).1: usize) => _4;
         let mut _5: *mut [u32];
         let mut _13: *mut [u32];
         scope 2 {
             scope 3 (inlined <std::ops::Range<usize> as SliceIndex<[u32]>>::get_unchecked_mut) {
-                debug self => std::ops::Range<usize>{ .0 => _3, .1 => _4, };
+                debug ((self: std::ops::Range<usize>).0: usize) => _3;
+                debug ((self: std::ops::Range<usize>).1: usize) => _4;
                 debug slice => _5;
                 let mut _7: *mut u32;
                 let mut _8: *mut u32;
                 let _15: usize;
                 let _16: usize;
                 scope 4 {
-                    debug this => std::ops::Range<usize>{ .0 => _15, .1 => _16, };
+                    debug ((this: std::ops::Range<usize>).0: usize) => _15;
+                    debug ((this: std::ops::Range<usize>).1: usize) => _16;
                     scope 5 {
                         let _6: usize;
                         scope 6 {
@@ -53,7 +56,8 @@ fn slice_get_unchecked_mut_range(_1: &mut [u32], _2: std::ops::Range<usize>) ->
                             }
                         }
                         scope 7 (inlined <std::ops::Range<usize> as SliceIndex<[T]>>::get_unchecked_mut::runtime::<u32>) {
-                            debug this => std::ops::Range<usize>{ .0 => _15, .1 => _16, };
+                            debug ((this: std::ops::Range<usize>).0: usize) => _15;
+                            debug ((this: std::ops::Range<usize>).1: usize) => _16;
                             debug slice => _5;
                             scope 8 (inlined ptr::mut_ptr::<impl *mut [u32]>::len) {
                                 debug self => _5;
diff --git a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff
index bb14b909a95..b020d1baafa 100644
--- a/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff
+++ b/tests/mir-opt/sroa/lifetimes.foo.ScalarReplacementOfAggregates.diff
@@ -33,7 +33,8 @@
 +     let _32: u32;
       scope 1 {
 -         debug foo => _1;
-+         debug foo => Foo<T>{ .0 => _31, .1 => _32, };
++         debug ((foo: Foo<T>).0: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>) => _31;
++         debug ((foo: Foo<T>).1: u32) => _32;
           let _5: std::result::Result<std::boxed::Box<dyn std::fmt::Display>, <T as Err>::Err>;
           scope 2 {
               debug x => _5;
diff --git a/tests/mir-opt/sroa/structs.constant.ScalarReplacementOfAggregates.diff b/tests/mir-opt/sroa/structs.constant.ScalarReplacementOfAggregates.diff
index 7ee0431692c..1330f9b3ac8 100644
--- a/tests/mir-opt/sroa/structs.constant.ScalarReplacementOfAggregates.diff
+++ b/tests/mir-opt/sroa/structs.constant.ScalarReplacementOfAggregates.diff
@@ -8,7 +8,8 @@
 +     let _5: u8;
       scope 1 {
 -         debug y => _1;
-+         debug y => (usize, u8){ .0 => _4, .1 => _5, };
++         debug ((y: (usize, u8)).0: usize) => _4;
++         debug ((y: (usize, u8)).1: u8) => _5;
           let _2: usize;
           scope 2 {
               debug t => _2;
diff --git a/tests/mir-opt/sroa/structs.copies.ScalarReplacementOfAggregates.diff b/tests/mir-opt/sroa/structs.copies.ScalarReplacementOfAggregates.diff
index 0a1de891aee..3621338635e 100644
--- a/tests/mir-opt/sroa/structs.copies.ScalarReplacementOfAggregates.diff
+++ b/tests/mir-opt/sroa/structs.copies.ScalarReplacementOfAggregates.diff
@@ -11,7 +11,10 @@
 +     let _14: std::option::Option<isize>;
       scope 1 {
 -         debug y => _2;
-+         debug y => Foo{ .0 => _11, .1 => _12, .2 => _13, .3 => _14, };
++         debug ((y: Foo).0: u8) => _11;
++         debug ((y: Foo).1: ()) => _12;
++         debug ((y: Foo).2: &str) => _13;
++         debug ((y: Foo).3: std::option::Option<isize>) => _14;
           let _3: u8;
           scope 2 {
               debug t => _3;
@@ -25,7 +28,10 @@
 +                 let _10: std::option::Option<isize>;
                   scope 4 {
 -                     debug z => _5;
-+                     debug z => Foo{ .0 => _7, .1 => _8, .2 => _9, .3 => _10, };
++                     debug ((z: Foo).0: u8) => _7;
++                     debug ((z: Foo).1: ()) => _8;
++                     debug ((z: Foo).2: &str) => _9;
++                     debug ((z: Foo).3: std::option::Option<isize>) => _10;
                       let _6: ();
                       scope 5 {
                           debug a => _6;
diff --git a/tests/mir-opt/sroa/structs.ref_copies.ScalarReplacementOfAggregates.diff b/tests/mir-opt/sroa/structs.ref_copies.ScalarReplacementOfAggregates.diff
index d7c57c293c4..304bf2fb1a7 100644
--- a/tests/mir-opt/sroa/structs.ref_copies.ScalarReplacementOfAggregates.diff
+++ b/tests/mir-opt/sroa/structs.ref_copies.ScalarReplacementOfAggregates.diff
@@ -11,7 +11,10 @@
 +     let _8: std::option::Option<isize>;
       scope 1 {
 -         debug y => _2;
-+         debug y => Foo{ .0 => _5, .1 => _6, .2 => _7, .3 => _8, };
++         debug ((y: Foo).0: u8) => _5;
++         debug ((y: Foo).1: ()) => _6;
++         debug ((y: Foo).2: &str) => _7;
++         debug ((y: Foo).3: std::option::Option<isize>) => _8;
           let _3: u8;
           scope 2 {
               debug t => _3;