about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_codegen_llvm/src/allocator.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/consts.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/context.rs10
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs2225
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs515
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs428
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs441
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs271
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/mod.rs58
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/utils.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/debuginfo/mod.rs32
-rw-r--r--compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs8
-rw-r--r--src/test/codegen/async-fn-debug-msvc.rs7
-rw-r--r--src/test/codegen/async-fn-debug.rs4
-rw-r--r--src/test/codegen/generator-debug-msvc.rs5
-rw-r--r--src/test/codegen/generator-debug.rs4
-rw-r--r--src/test/debuginfo/msvc-pretty-enums.rs14
-rw-r--r--src/test/debuginfo/type-names.rs25
18 files changed, 2293 insertions, 1764 deletions
diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs
index eb19e427217..b647cfa5f4a 100644
--- a/compiler/rustc_codegen_llvm/src/allocator.rs
+++ b/compiler/rustc_codegen_llvm/src/allocator.rs
@@ -140,8 +140,8 @@ pub(crate) unsafe fn codegen(
     llvm::LLVMDisposeBuilder(llbuilder);
 
     if tcx.sess.opts.debuginfo != DebugInfo::None {
-        let dbg_cx = debuginfo::CrateDebugContext::new(llmod);
-        debuginfo::metadata::compile_unit_metadata(tcx, module_name, &dbg_cx);
+        let dbg_cx = debuginfo::CodegenUnitDebugContext::new(llmod);
+        debuginfo::metadata::build_compile_unit_di_node(tcx, module_name, &dbg_cx);
         dbg_cx.finalize(tcx.sess);
     }
 }
diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs
index 7d3fe43eeab..413ef0ba764 100644
--- a/compiler/rustc_codegen_llvm/src/consts.rs
+++ b/compiler/rustc_codegen_llvm/src/consts.rs
@@ -428,7 +428,7 @@ impl<'ll> StaticMethods for CodegenCx<'ll, '_> {
                 llvm::LLVMSetGlobalConstant(g, llvm::True);
             }
 
-            debuginfo::create_global_var_metadata(self, def_id, g);
+            debuginfo::build_global_var_di_node(self, def_id, g);
 
             if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
                 llvm::set_thread_local_mode(g, self.tls_model);
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index 52e03e0ad3d..9fbc33d4b05 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -95,7 +95,7 @@ pub struct CodegenCx<'ll, 'tcx> {
     pub isize_ty: &'ll Type,
 
     pub coverage_cx: Option<coverageinfo::CrateCoverageContext<'ll, 'tcx>>,
-    pub dbg_cx: Option<debuginfo::CrateDebugContext<'ll, 'tcx>>,
+    pub dbg_cx: Option<debuginfo::CodegenUnitDebugContext<'ll, 'tcx>>,
 
     eh_personality: Cell<Option<&'ll Value>>,
     eh_catch_typeinfo: Cell<Option<&'ll Value>>,
@@ -396,8 +396,12 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
         };
 
         let dbg_cx = if tcx.sess.opts.debuginfo != DebugInfo::None {
-            let dctx = debuginfo::CrateDebugContext::new(llmod);
-            debuginfo::metadata::compile_unit_metadata(tcx, codegen_unit.name().as_str(), &dctx);
+            let dctx = debuginfo::CodegenUnitDebugContext::new(llmod);
+            debuginfo::metadata::build_compile_unit_di_node(
+                tcx,
+                codegen_unit.name().as_str(),
+                &dctx,
+            );
             Some(dctx)
         } else {
             None
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index f16a903ad2c..488dbe3456b 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -1,21 +1,22 @@
-use self::MemberDescriptionFactory::*;
-use self::RecursiveTypeDescription::*;
+use self::type_map::DINodeCreationResult;
+use self::type_map::Stub;
+use self::type_map::UniqueTypeId;
 
 use super::namespace::mangled_name_of_instance;
 use super::type_names::{compute_debuginfo_type_name, compute_debuginfo_vtable_name};
 use super::utils::{
     create_DIArray, debug_context, get_namespace_for_item, is_node_local_to_unit, DIB,
 };
-use super::CrateDebugContext;
+use super::CodegenUnitDebugContext;
 
 use crate::abi;
 use crate::common::CodegenCx;
+use crate::debuginfo::metadata::type_map::build_type_with_children;
 use crate::debuginfo::utils::fat_pointer_kind;
 use crate::debuginfo::utils::FatPtrKind;
 use crate::llvm;
 use crate::llvm::debuginfo::{
-    DIArray, DICompositeType, DIDescriptor, DIFile, DIFlags, DILexicalBlock, DIScope, DIType,
-    DebugEmissionKind,
+    DIDescriptor, DIFile, DIFlags, DILexicalBlock, DIScope, DIType, DebugEmissionKind,
 };
 use crate::value::Value;
 
@@ -23,30 +24,26 @@ use cstr::cstr;
 use rustc_codegen_ssa::debuginfo::type_names::cpp_like_debuginfo;
 use rustc_codegen_ssa::debuginfo::type_names::VTableNameKind;
 use rustc_codegen_ssa::traits::*;
-use rustc_data_structures::fx::FxHashMap;
 use rustc_fs_util::path_to_c_string;
 use rustc_hir::def::CtorKind;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_index::vec::{Idx, IndexVec};
 use rustc_middle::bug;
 use rustc_middle::mir::{self, GeneratorLayout};
-use rustc_middle::ty::layout::{self, IntegerExt, LayoutOf, PrimitiveExt, TyAndLayout};
+use rustc_middle::ty::layout::LayoutOf;
+use rustc_middle::ty::layout::TyAndLayout;
 use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::{
-    self, AdtKind, GeneratorSubsts, Instance, ParamEnv, Ty, TyCtxt, COMMON_VTABLE_ENTRIES,
-};
+use rustc_middle::ty::{self, AdtKind, Instance, ParamEnv, Ty, TyCtxt, COMMON_VTABLE_ENTRIES};
 use rustc_session::config::{self, DebugInfo};
 use rustc_span::symbol::Symbol;
 use rustc_span::FileNameDisplayPreference;
 use rustc_span::{self, SourceFile, SourceFileHash};
-use rustc_target::abi::{Abi, Align, HasDataLayout, Integer, TagEncoding};
-use rustc_target::abi::{Int, Pointer, F32, F64};
-use rustc_target::abi::{Primitive, Size, VariantIdx, Variants};
-use smallvec::SmallVec;
+use rustc_target::abi::{Align, Size};
+use smallvec::smallvec;
 use tracing::debug;
 
 use libc::{c_longlong, c_uint};
-use std::cell::RefCell;
+use std::borrow::Cow;
 use std::collections::hash_map::Entry;
 use std::fmt::{self, Write};
 use std::hash::{Hash, Hasher};
@@ -88,250 +85,51 @@ const DW_ATE_unsigned: c_uint = 0x07;
 #[allow(non_upper_case_globals)]
 const DW_ATE_UTF: c_uint = 0x10;
 
-pub const UNKNOWN_LINE_NUMBER: c_uint = 0;
-pub const UNKNOWN_COLUMN_NUMBER: c_uint = 0;
-
-pub const NO_SCOPE_METADATA: Option<&DIScope> = None;
-
-mod unique_type_id {
-    use rustc_data_structures::{
-        fingerprint::Fingerprint,
-        stable_hasher::{HashStable, NodeIdHashingMode, StableHasher},
-    };
-    use rustc_middle::ty::{ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt};
-    use rustc_target::abi::VariantIdx;
-
-    // This type cannot be constructed outside of this module because
-    // it has a private field. We make use of this in order to prevent
-    // `UniqueTypeId` from being constructed directly, without asserting
-    // the preconditions.
-    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, HashStable)]
-    pub struct HiddenZst {
-        _inaccessible: (),
-    }
-
-    /// A unique identifier for anything that we create a debuginfo node for.
-    /// The types it contains are expected to already be normalized (which
-    /// is debug_asserted in the constructors).
-    ///
-    /// Note that there are some things that only show up in debuginfo, like
-    /// the separate type descriptions for each enum variant. These get an ID
-    /// too because they have their own debuginfo node in LLVM IR.
-    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, HashStable)]
-    pub(super) enum UniqueTypeId<'tcx> {
-        /// The ID of a regular type as it shows up at the language level.
-        Ty(Ty<'tcx>, HiddenZst),
-        /// The ID for the artificial struct type describing a single enum variant.
-        Variant(Ty<'tcx>, VariantIdx, HiddenZst),
-        /// The ID for the single DW_TAG_variant_part nested inside the top-level
-        /// DW_TAG_structure_type that describes enums and generators.
-        VariantPart(Ty<'tcx>, HiddenZst),
-        /// The ID of the artificial type we create for VTables.
-        VTableTy(Ty<'tcx>, Option<PolyExistentialTraitRef<'tcx>>, HiddenZst),
-    }
-
-    impl<'tcx> UniqueTypeId<'tcx> {
-        pub fn for_ty(tcx: TyCtxt<'tcx>, t: Ty<'tcx>) -> Self {
-            debug_assert_eq!(t, tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t));
-            UniqueTypeId::Ty(t, HiddenZst { _inaccessible: () })
-        }
-
-        pub fn for_enum_variant(
-            tcx: TyCtxt<'tcx>,
-            enum_ty: Ty<'tcx>,
-            variant_idx: VariantIdx,
-        ) -> Self {
-            debug_assert_eq!(
-                enum_ty,
-                tcx.normalize_erasing_regions(ParamEnv::reveal_all(), enum_ty)
-            );
-            UniqueTypeId::Variant(enum_ty, variant_idx, HiddenZst { _inaccessible: () })
-        }
-
-        pub fn for_enum_variant_part(tcx: TyCtxt<'tcx>, enum_ty: Ty<'tcx>) -> Self {
-            debug_assert_eq!(
-                enum_ty,
-                tcx.normalize_erasing_regions(ParamEnv::reveal_all(), enum_ty)
-            );
-            UniqueTypeId::VariantPart(enum_ty, HiddenZst { _inaccessible: () })
-        }
-
-        pub fn for_vtable_ty(
-            tcx: TyCtxt<'tcx>,
-            self_type: Ty<'tcx>,
-            implemented_trait: Option<PolyExistentialTraitRef<'tcx>>,
-        ) -> Self {
-            debug_assert_eq!(
-                self_type,
-                tcx.normalize_erasing_regions(ParamEnv::reveal_all(), self_type)
-            );
-            debug_assert_eq!(
-                implemented_trait,
-                tcx.normalize_erasing_regions(ParamEnv::reveal_all(), implemented_trait)
-            );
-            UniqueTypeId::VTableTy(self_type, implemented_trait, HiddenZst { _inaccessible: () })
-        }
+pub(super) const UNKNOWN_LINE_NUMBER: c_uint = 0;
+pub(super) const UNKNOWN_COLUMN_NUMBER: c_uint = 0;
 
-        /// Generates a string version of this [UniqueTypeId], which can be used as the `UniqueId`
-        /// argument of the various `LLVMRustDIBuilderCreate*Type()` methods.
-        ///
-        /// Right now this takes the form of a hex-encoded opaque hash value.
-        pub fn generate_unique_id_string(&self, tcx: TyCtxt<'tcx>) -> String {
-            let mut hasher = StableHasher::new();
-            let mut hcx = tcx.create_stable_hashing_context();
-            hcx.while_hashing_spans(false, |hcx| {
-                hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
-                    self.hash_stable(hcx, &mut hasher);
-                });
-            });
-            hasher.finish::<Fingerprint>().to_hex()
-        }
-    }
-}
-use unique_type_id::*;
+const NO_SCOPE_METADATA: Option<&DIScope> = None;
+/// A function that returns an empty list of generic parameter debuginfo nodes.
+const NO_GENERICS: for<'ll> fn(&CodegenCx<'ll, '_>) -> SmallVec<&'ll DIType> = |_| SmallVec::new();
 
-/// The `TypeMap` is where the debug context holds the type metadata nodes
-/// created so far. The metadata nodes are indexed by `UniqueTypeId`.
-#[derive(Default)]
-pub struct TypeMap<'ll, 'tcx> {
-    unique_id_to_metadata: RefCell<FxHashMap<UniqueTypeId<'tcx>, &'ll DIType>>,
-}
+// SmallVec is used quite a bit in this module, so create a shorthand.
+// The actual number of elements is not so important.
+pub type SmallVec<T> = smallvec::SmallVec<[T; 16]>;
 
-impl<'ll, 'tcx> TypeMap<'ll, 'tcx> {
-    /// Adds a `UniqueTypeId` to metadata mapping to the `TypeMap`. The method will
-    /// fail if the mapping already exists.
-    fn register_unique_id_with_metadata(
-        &self,
-        unique_type_id: UniqueTypeId<'tcx>,
-        metadata: &'ll DIType,
-    ) {
-        if self.unique_id_to_metadata.borrow_mut().insert(unique_type_id, metadata).is_some() {
-            bug!("type metadata for unique ID '{:?}' is already in the `TypeMap`!", unique_type_id);
-        }
-    }
+mod enums;
+mod type_map;
 
-    fn find_metadata_for_unique_id(
-        &self,
-        unique_type_id: UniqueTypeId<'tcx>,
-    ) -> Option<&'ll DIType> {
-        self.unique_id_to_metadata.borrow().get(&unique_type_id).cloned()
-    }
-}
+pub(crate) use type_map::TypeMap;
 
-/// A description of some recursive type. It can either be already finished (as
-/// with `FinalMetadata`) or it is not yet finished, but contains all information
-/// needed to generate the missing parts of the description. See the
-/// documentation section on Recursive Types at the top of this file for more
-/// information.
-enum RecursiveTypeDescription<'ll, 'tcx> {
-    UnfinishedMetadata {
-        unfinished_type: Ty<'tcx>,
-        unique_type_id: UniqueTypeId<'tcx>,
-        metadata_stub: &'ll DICompositeType,
-        member_holding_stub: &'ll DICompositeType,
-        member_description_factory: MemberDescriptionFactory<'ll, 'tcx>,
-    },
-    FinalMetadata(&'ll DICompositeType),
-}
-
-fn create_and_register_recursive_type_forward_declaration<'ll, 'tcx>(
-    cx: &CodegenCx<'ll, 'tcx>,
-    unfinished_type: Ty<'tcx>,
-    unique_type_id: UniqueTypeId<'tcx>,
-    metadata_stub: &'ll DICompositeType,
-    member_holding_stub: &'ll DICompositeType,
-    member_description_factory: MemberDescriptionFactory<'ll, 'tcx>,
-) -> RecursiveTypeDescription<'ll, 'tcx> {
-    // Insert the stub into the `TypeMap` in order to allow for recursive references.
-    debug_context(cx).type_map.register_unique_id_with_metadata(unique_type_id, metadata_stub);
-
-    UnfinishedMetadata {
-        unfinished_type,
-        unique_type_id,
-        metadata_stub,
-        member_holding_stub,
-        member_description_factory,
-    }
-}
-
-impl<'ll, 'tcx> RecursiveTypeDescription<'ll, 'tcx> {
-    /// Finishes up the description of the type in question (mostly by providing
-    /// descriptions of the fields of the given type) and returns the final type
-    /// metadata.
-    fn finalize(&self, cx: &CodegenCx<'ll, 'tcx>) -> MetadataCreationResult<'ll> {
-        match *self {
-            FinalMetadata(metadata) => MetadataCreationResult::new(metadata, false),
-            UnfinishedMetadata {
-                unfinished_type,
-                unique_type_id,
-                metadata_stub,
-                member_holding_stub,
-                ref member_description_factory,
-            } => {
-                // Make sure that we have a forward declaration of the type in
-                // the TypeMap so that recursive references are possible. This
-                // will always be the case if the RecursiveTypeDescription has
-                // been properly created through the
-                // `create_and_register_recursive_type_forward_declaration()`
-                // function.
-                {
-                    if debug_context(cx)
-                        .type_map
-                        .find_metadata_for_unique_id(unique_type_id)
-                        .is_none()
-                    {
-                        bug!(
-                            "Forward declaration of potentially recursive type \
-                              '{:?}' was not found in TypeMap!",
-                            unfinished_type
-                        );
-                    }
-                }
-
-                // ... then create the member descriptions ...
-                let member_descriptions = member_description_factory.create_member_descriptions(cx);
-                let type_params = compute_type_parameters(cx, unfinished_type);
-
-                // ... and attach them to the stub to complete it.
-                set_members_of_composite_type(
-                    cx,
-                    member_holding_stub,
-                    member_descriptions,
-                    None,
-                    type_params,
-                );
-                MetadataCreationResult::new(metadata_stub, true)
-            }
-        }
-    }
-}
-
-/// Returns from the enclosing function if the type metadata with the given
+/// Returns from the enclosing function if the type debuginfo node with the given
 /// unique ID can be found in the type map.
-macro_rules! return_if_metadata_created_in_meantime {
+macro_rules! return_if_di_node_created_in_meantime {
     ($cx: expr, $unique_type_id: expr) => {
-        if let Some(metadata) =
-            debug_context($cx).type_map.find_metadata_for_unique_id($unique_type_id)
-        {
-            return MetadataCreationResult::new(metadata, true);
+        if let Some(di_node) = debug_context($cx).type_map.di_node_for_unique_id($unique_type_id) {
+            return DINodeCreationResult::new(di_node, true);
         }
     };
 }
 
+/// Extract size and alignment from a TyAndLayout.
+fn size_and_align_of<'tcx>(ty_and_layout: TyAndLayout<'tcx>) -> (Size, Align) {
+    (ty_and_layout.size, ty_and_layout.align.abi)
+}
+
 /// Creates debuginfo for a fixed size array (e.g. `[u64; 123]`).
-/// For slices (that is, "arrays" of unknown size) use [slice_type_metadata].
-fn fixed_size_array_metadata<'ll, 'tcx>(
+/// For slices (that is, "arrays" of unknown size) use [build_slice_type_di_node].
+fn build_fixed_size_array_di_node<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
     unique_type_id: UniqueTypeId<'tcx>,
     array_type: Ty<'tcx>,
-) -> MetadataCreationResult<'ll> {
+) -> DINodeCreationResult<'ll> {
     let ty::Array(element_type, len) = array_type.kind() else {
-        bug!("fixed_size_array_metadata() called with non-ty::Array type `{:?}`", array_type)
+        bug!("build_fixed_size_array_di_node() called with non-ty::Array type `{:?}`", array_type)
     };
 
-    let element_type_metadata = type_metadata(cx, *element_type);
+    let element_type_di_node = type_di_node(cx, *element_type);
 
-    return_if_metadata_created_in_meantime!(cx, unique_type_id);
+    return_if_di_node_created_in_meantime!(cx, unique_type_id);
 
     let (size, align) = cx.size_and_align_of(array_type);
 
@@ -341,17 +139,17 @@ fn fixed_size_array_metadata<'ll, 'tcx>(
         unsafe { Some(llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound)) };
 
     let subscripts = create_DIArray(DIB(cx), &[subrange]);
-    let metadata = unsafe {
+    let di_node = unsafe {
         llvm::LLVMRustDIBuilderCreateArrayType(
             DIB(cx),
             size.bits(),
             align.bits() as u32,
-            element_type_metadata,
+            element_type_di_node,
             subscripts,
         )
     };
 
-    MetadataCreationResult::new(metadata, false)
+    DINodeCreationResult::new(di_node, false)
 }
 
 /// Creates debuginfo for built-in pointer-like things:
@@ -362,21 +160,21 @@ fn fixed_size_array_metadata<'ll, 'tcx>(
 ///
 /// At some point we might want to remove the special handling of Box
 /// and treat it the same as other smart pointers (like Rc, Arc, ...).
-fn pointer_or_reference_metadata<'ll, 'tcx>(
+fn build_pointer_or_reference_di_node<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
     ptr_type: Ty<'tcx>,
     pointee_type: Ty<'tcx>,
     unique_type_id: UniqueTypeId<'tcx>,
-) -> MetadataCreationResult<'ll> {
-    let pointee_type_metadata = type_metadata(cx, pointee_type);
+) -> DINodeCreationResult<'ll> {
+    let pointee_type_di_node = type_di_node(cx, pointee_type);
 
-    return_if_metadata_created_in_meantime!(cx, unique_type_id);
+    return_if_di_node_created_in_meantime!(cx, unique_type_id);
 
     let (thin_pointer_size, thin_pointer_align) =
         cx.size_and_align_of(cx.tcx.mk_imm_ptr(cx.tcx.types.unit));
     let ptr_type_debuginfo_name = compute_debuginfo_type_name(cx.tcx, ptr_type, true);
 
-    let pointer_type_metadata = match fat_pointer_kind(cx, pointee_type) {
+    match fat_pointer_kind(cx, pointee_type) {
         None => {
             // This is a thin pointer. Create a regular pointer type and give it the correct name.
             debug_assert_eq!(
@@ -387,87 +185,90 @@ fn pointer_or_reference_metadata<'ll, 'tcx>(
                 pointee_type,
             );
 
-            unsafe {
+            let di_node = unsafe {
                 llvm::LLVMRustDIBuilderCreatePointerType(
                     DIB(cx),
-                    pointee_type_metadata,
+                    pointee_type_di_node,
                     thin_pointer_size.bits(),
                     thin_pointer_align.bits() as u32,
                     0, // Ignore DWARF address space.
                     ptr_type_debuginfo_name.as_ptr().cast(),
                     ptr_type_debuginfo_name.len(),
                 )
-            }
+            };
+
+            DINodeCreationResult { di_node, already_stored_in_typemap: false }
         }
         Some(fat_pointer_kind) => {
-            let layout = cx.layout_of(ptr_type);
-
-            let addr_field = layout.field(cx, abi::FAT_PTR_ADDR);
-            let extra_field = layout.field(cx, abi::FAT_PTR_EXTRA);
-
-            let (addr_field_name, extra_field_name) = match fat_pointer_kind {
-                FatPtrKind::Dyn => ("pointer", "vtable"),
-                FatPtrKind::Slice => ("data_ptr", "length"),
-            };
+            type_map::build_type_with_children(
+                cx,
+                type_map::stub(
+                    cx,
+                    Stub::Struct,
+                    unique_type_id,
+                    &ptr_type_debuginfo_name,
+                    cx.size_and_align_of(ptr_type),
+                    NO_SCOPE_METADATA,
+                    DIFlags::FlagZero,
+                ),
+                |cx, owner| {
+                    let layout = cx.layout_of(ptr_type);
+                    let addr_field = layout.field(cx, abi::FAT_PTR_ADDR);
+                    let extra_field = layout.field(cx, abi::FAT_PTR_EXTRA);
+
+                    let (addr_field_name, extra_field_name) = match fat_pointer_kind {
+                        FatPtrKind::Dyn => ("pointer", "vtable"),
+                        FatPtrKind::Slice => ("data_ptr", "length"),
+                    };
 
-            debug_assert_eq!(abi::FAT_PTR_ADDR, 0);
-            debug_assert_eq!(abi::FAT_PTR_EXTRA, 1);
+                    debug_assert_eq!(abi::FAT_PTR_ADDR, 0);
+                    debug_assert_eq!(abi::FAT_PTR_EXTRA, 1);
 
-            // The data pointer type is a regular, thin pointer, regardless of whether this is a slice
-            // or a trait object.
-            let data_ptr_type_metadata = unsafe {
-                llvm::LLVMRustDIBuilderCreatePointerType(
-                    DIB(cx),
-                    pointee_type_metadata,
-                    addr_field.size.bits(),
-                    addr_field.align.abi.bits() as u32,
-                    0, // Ignore DWARF address space.
-                    std::ptr::null(),
-                    0,
-                )
-            };
+                    // The data pointer type is a regular, thin pointer, regardless of whether this
+                    // is a slice or a trait object.
+                    let data_ptr_type_di_node = unsafe {
+                        llvm::LLVMRustDIBuilderCreatePointerType(
+                            DIB(cx),
+                            pointee_type_di_node,
+                            addr_field.size.bits(),
+                            addr_field.align.abi.bits() as u32,
+                            0, // Ignore DWARF address space.
+                            std::ptr::null(),
+                            0,
+                        )
+                    };
 
-            let member_descriptions = vec![
-                MemberDescription {
-                    name: addr_field_name.into(),
-                    type_metadata: data_ptr_type_metadata,
-                    offset: layout.fields.offset(abi::FAT_PTR_ADDR),
-                    size: addr_field.size,
-                    align: addr_field.align.abi,
-                    flags: DIFlags::FlagZero,
-                    discriminant: None,
-                    source_info: None,
-                },
-                MemberDescription {
-                    name: extra_field_name.into(),
-                    type_metadata: type_metadata(cx, extra_field.ty),
-                    offset: layout.fields.offset(abi::FAT_PTR_EXTRA),
-                    size: extra_field.size,
-                    align: extra_field.align.abi,
-                    flags: DIFlags::FlagZero,
-                    discriminant: None,
-                    source_info: None,
+                    smallvec![
+                        build_field_di_node(
+                            cx,
+                            owner,
+                            addr_field_name,
+                            (addr_field.size, addr_field.align.abi),
+                            layout.fields.offset(abi::FAT_PTR_ADDR),
+                            DIFlags::FlagZero,
+                            data_ptr_type_di_node,
+                        ),
+                        build_field_di_node(
+                            cx,
+                            owner,
+                            extra_field_name,
+                            (extra_field.size, extra_field.align.abi),
+                            layout.fields.offset(abi::FAT_PTR_EXTRA),
+                            DIFlags::FlagZero,
+                            type_di_node(cx, extra_field.ty),
+                        ),
+                    ]
                 },
-            ];
-
-            composite_type_metadata(
-                cx,
-                ptr_type,
-                &ptr_type_debuginfo_name,
-                unique_type_id,
-                member_descriptions,
-                NO_SCOPE_METADATA,
+                NO_GENERICS,
             )
         }
-    };
-
-    MetadataCreationResult { metadata: pointer_type_metadata, already_stored_in_typemap: false }
+    }
 }
 
-fn subroutine_type_metadata<'ll, 'tcx>(
+fn build_subroutine_type_di_node<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
     unique_type_id: UniqueTypeId<'tcx>,
-) -> MetadataCreationResult<'ll> {
+) -> DINodeCreationResult<'ll> {
     // It's possible to create a self-referential
     // type in Rust by using 'impl trait':
     //
@@ -483,49 +284,46 @@ fn subroutine_type_metadata<'ll, 'tcx>(
     // Once that is created, we replace the marker in the typemap with the actual type.
     debug_context(cx)
         .type_map
-        .unique_id_to_metadata
+        .unique_id_to_di_node
         .borrow_mut()
-        .insert(unique_type_id, recursion_marker_type(cx));
-
-    let UniqueTypeId::Ty(fn_ty, _) = unique_type_id else {
-        bug!("subroutine_type_metadata() called with unexpected input type: {:?}", unique_type_id)
-    };
+        .insert(unique_type_id, recursion_marker_type_di_node(cx));
 
+    let fn_ty = unique_type_id.expect_ty();
     let signature = cx
         .tcx
         .normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), fn_ty.fn_sig(cx.tcx));
 
-    let signature_metadata: SmallVec<[_; 32]> = iter::once(
+    let signature_di_nodes: SmallVec<_> = iter::once(
         // return type
         match signature.output().kind() {
             ty::Tuple(tys) if tys.is_empty() => {
                 // this is a "void" function
                 None
             }
-            _ => Some(type_metadata(cx, signature.output())),
+            _ => Some(type_di_node(cx, signature.output())),
         },
     )
     .chain(
         // regular arguments
-        signature.inputs().iter().map(|&argument_type| Some(type_metadata(cx, argument_type))),
+        signature.inputs().iter().map(|&argument_type| Some(type_di_node(cx, argument_type))),
     )
     .collect();
 
-    debug_context(cx).type_map.unique_id_to_metadata.borrow_mut().remove(&unique_type_id);
+    debug_context(cx).type_map.unique_id_to_di_node.borrow_mut().remove(&unique_type_id);
 
-    let fn_metadata = unsafe {
+    let fn_di_node = unsafe {
         llvm::LLVMRustDIBuilderCreateSubroutineType(
             DIB(cx),
-            create_DIArray(DIB(cx), &signature_metadata[..]),
+            create_DIArray(DIB(cx), &signature_di_nodes[..]),
         )
     };
 
     // This is actually a function pointer, so wrap it in pointer DI.
     let name = compute_debuginfo_type_name(cx.tcx, fn_ty, false);
-    let metadata = unsafe {
+    let di_node = unsafe {
         llvm::LLVMRustDIBuilderCreatePointerType(
             DIB(cx),
-            fn_metadata,
+            fn_di_node,
             cx.tcx.data_layout.pointer_size.bits(),
             cx.tcx.data_layout.pointer_align.abi.bits() as u32,
             0, // Ignore DWARF address space.
@@ -534,19 +332,32 @@ fn subroutine_type_metadata<'ll, 'tcx>(
         )
     };
 
-    MetadataCreationResult::new(metadata, false)
+    DINodeCreationResult::new(di_node, false)
 }
 
 /// Create debuginfo for `dyn SomeTrait` types. Currently these are empty structs
 /// we with the correct type name (e.g. "dyn SomeTrait<Foo, Item=u32> + Sync").
-fn dyn_type_metadata<'ll, 'tcx>(
+fn build_dyn_type_di_node<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
     dyn_type: Ty<'tcx>,
     unique_type_id: UniqueTypeId<'tcx>,
-) -> &'ll DIType {
+) -> DINodeCreationResult<'ll> {
     if let ty::Dynamic(..) = dyn_type.kind() {
         let type_name = compute_debuginfo_type_name(cx.tcx, dyn_type, true);
-        composite_type_metadata(cx, dyn_type, &type_name, unique_type_id, vec![], NO_SCOPE_METADATA)
+        type_map::build_type_with_children(
+            cx,
+            type_map::stub(
+                cx,
+                Stub::Struct,
+                unique_type_id,
+                &type_name,
+                cx.size_and_align_of(dyn_type),
+                NO_SCOPE_METADATA,
+                DIFlags::FlagZero,
+            ),
+            |_, _| smallvec![],
+            NO_GENERICS,
+        )
     } else {
         bug!("Only ty::Dynamic is valid for dyn_type_metadata(). Found {:?} instead.", dyn_type)
     }
@@ -569,11 +380,11 @@ fn dyn_type_metadata<'ll, 'tcx>(
 /// `struct Foo { unsized_field: u8 }` in debuginfo. If the length of the
 /// slice is zero, then accessing `unsized_field` in the debugger would
 /// result in an out-of-bounds access.
-fn slice_type_metadata<'ll, 'tcx>(
+fn build_slice_type_di_node<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
     slice_type: Ty<'tcx>,
     unique_type_id: UniqueTypeId<'tcx>,
-) -> MetadataCreationResult<'ll> {
+) -> DINodeCreationResult<'ll> {
     let element_type = match slice_type.kind() {
         ty::Slice(element_type) => *element_type,
         ty::Str => cx.tcx.types.u8,
@@ -585,82 +396,67 @@ fn slice_type_metadata<'ll, 'tcx>(
         }
     };
 
-    let element_type_metadata = type_metadata(cx, element_type);
-    return_if_metadata_created_in_meantime!(cx, unique_type_id);
-    MetadataCreationResult { metadata: element_type_metadata, already_stored_in_typemap: false }
+    let element_type_metadata = type_di_node(cx, element_type);
+    return_if_di_node_created_in_meantime!(cx, unique_type_id);
+    DINodeCreationResult { di_node: element_type_metadata, already_stored_in_typemap: false }
 }
 
-pub fn type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType {
+/// Get the debuginfo node for the given type.
+///
+/// This function will look up the debuginfo node in the TypeMap. If it can't find it, it
+/// will create the node by dispatching to the corresponding `build_*_di_node()` function.
+pub fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType {
     let unique_type_id = UniqueTypeId::for_ty(cx.tcx, t);
 
-    if let Some(metadata) = debug_context(cx).type_map.find_metadata_for_unique_id(unique_type_id) {
-        return metadata;
+    if let Some(existing_di_node) = debug_context(cx).type_map.di_node_for_unique_id(unique_type_id)
+    {
+        return existing_di_node;
     }
 
-    debug!("type_metadata: {:?}", t);
+    debug!("type_di_node: {:?}", t);
 
-    let MetadataCreationResult { metadata, already_stored_in_typemap } = match *t.kind() {
+    let DINodeCreationResult { di_node, already_stored_in_typemap } = match *t.kind() {
         ty::Never | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => {
-            MetadataCreationResult::new(basic_type_metadata(cx, t), false)
+            DINodeCreationResult::new(build_basic_type_di_node(cx, t), false)
         }
         ty::Tuple(elements) if elements.is_empty() => {
-            MetadataCreationResult::new(basic_type_metadata(cx, t), false)
-        }
-        ty::Array(..) => fixed_size_array_metadata(cx, unique_type_id, t),
-        ty::Slice(_) | ty::Str => slice_type_metadata(cx, t, unique_type_id),
-        ty::Dynamic(..) => {
-            MetadataCreationResult::new(dyn_type_metadata(cx, t, unique_type_id), false)
-        }
-        ty::Foreign(..) => {
-            MetadataCreationResult::new(foreign_type_metadata(cx, t, unique_type_id), false)
+            DINodeCreationResult::new(build_basic_type_di_node(cx, t), false)
         }
+        ty::Array(..) => build_fixed_size_array_di_node(cx, unique_type_id, t),
+        ty::Slice(_) | ty::Str => build_slice_type_di_node(cx, t, unique_type_id),
+        ty::Dynamic(..) => build_dyn_type_di_node(cx, t, unique_type_id),
+        ty::Foreign(..) => build_foreign_type_di_node(cx, t, unique_type_id),
         ty::RawPtr(ty::TypeAndMut { ty: pointee_type, .. }) | ty::Ref(_, pointee_type, _) => {
-            pointer_or_reference_metadata(cx, t, pointee_type, unique_type_id)
+            build_pointer_or_reference_di_node(cx, t, pointee_type, unique_type_id)
         }
         // Box<T, A> may have a non-ZST allocator A. In that case, we
         // cannot treat Box<T, A> as just an owned alias of `*mut T`.
         ty::Adt(def, substs) if def.is_box() && cx.layout_of(substs.type_at(1)).is_zst() => {
-            pointer_or_reference_metadata(cx, t, t.boxed_ty(), unique_type_id)
-        }
-        ty::FnDef(..) | ty::FnPtr(_) => subroutine_type_metadata(cx, unique_type_id),
-        ty::Closure(def_id, substs) => {
-            let upvar_tys: Vec<_> = substs.as_closure().upvar_tys().collect();
-            let containing_scope = get_namespace_for_item(cx, def_id);
-            prepare_tuple_metadata(cx, t, &upvar_tys, unique_type_id, Some(containing_scope))
-                .finalize(cx)
-        }
-        ty::Generator(def_id, substs, _) => {
-            let upvar_tys: Vec<_> = substs
-                .as_generator()
-                .prefix_tys()
-                .map(|t| cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t))
-                .collect();
-            prepare_enum_metadata(cx, t, def_id, unique_type_id, upvar_tys).finalize(cx)
+            build_pointer_or_reference_di_node(cx, t, t.boxed_ty(), unique_type_id)
         }
+        ty::FnDef(..) | ty::FnPtr(_) => build_subroutine_type_di_node(cx, unique_type_id),
+        ty::Closure(..) => build_closure_env_di_node(cx, unique_type_id),
+        ty::Generator(..) => enums::build_generator_di_node(cx, unique_type_id),
         ty::Adt(def, ..) => match def.adt_kind() {
-            AdtKind::Struct => prepare_struct_metadata(cx, t, unique_type_id).finalize(cx),
-            AdtKind::Union => prepare_union_metadata(cx, t, unique_type_id).finalize(cx),
-            AdtKind::Enum => {
-                prepare_enum_metadata(cx, t, def.did(), unique_type_id, vec![]).finalize(cx)
-            }
+            AdtKind::Struct => build_struct_type_di_node(cx, unique_type_id),
+            AdtKind::Union => build_union_type_di_node(cx, unique_type_id),
+            AdtKind::Enum => enums::build_enum_type_di_node(cx, unique_type_id),
         },
-        ty::Tuple(tys) => {
-            prepare_tuple_metadata(cx, t, tys, unique_type_id, NO_SCOPE_METADATA).finalize(cx)
-        }
+        ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id),
         // Type parameters from polymorphized functions.
-        ty::Param(_) => MetadataCreationResult::new(param_type_metadata(cx, t), false),
+        ty::Param(_) => build_param_type_di_node(cx, t),
         _ => bug!("debuginfo: unexpected type in type_metadata: {:?}", t),
     };
 
     {
         if already_stored_in_typemap {
             // Make sure that we really do have a `TypeMap` entry for the unique type ID.
-            let metadata_for_uid =
-                match debug_context(cx).type_map.find_metadata_for_unique_id(unique_type_id) {
-                    Some(metadata) => metadata,
+            let di_node_for_uid =
+                match debug_context(cx).type_map.di_node_for_unique_id(unique_type_id) {
+                    Some(di_node) => di_node,
                     None => {
                         bug!(
-                            "expected type metadata for unique \
+                            "expected type di_node for unique \
                                type ID '{:?}' to already be in \
                                the `debuginfo::TypeMap` but it \
                                was not.",
@@ -669,16 +465,17 @@ pub fn type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll
                     }
                 };
 
-            debug_assert_eq!(metadata_for_uid as *const _, metadata as *const _);
+            debug_assert_eq!(di_node_for_uid as *const _, di_node as *const _);
         } else {
-            debug_context(cx).type_map.register_unique_id_with_metadata(unique_type_id, metadata);
+            debug_context(cx).type_map.insert(unique_type_id, di_node);
         }
     }
 
-    metadata
+    di_node
 }
 
-fn recursion_marker_type<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> &'ll DIType {
+// FIXME(mw): Cache this via a regular UniqueTypeId instead of an extra field in the debug context.
+fn recursion_marker_type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> &'ll DIType {
     *debug_context(cx).recursion_marker_type.get_or_init(move || {
         unsafe {
             // The choice of type here is pretty arbitrary -
@@ -823,8 +620,8 @@ impl MsvcBasicName for ty::FloatTy {
     }
 }
 
-fn basic_type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType {
-    debug!("basic_type_metadata: {:?}", t);
+fn build_basic_type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType {
+    debug!("build_basic_type_di_node: {:?}", t);
 
     // When targeting MSVC, emit MSVC style type names for compatibility with
     // .natvis visualizers (and perhaps other existing native debuggers?)
@@ -841,10 +638,10 @@ fn basic_type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'l
         ty::Int(int_ty) => (int_ty.name_str(), DW_ATE_signed),
         ty::Uint(uint_ty) => (uint_ty.name_str(), DW_ATE_unsigned),
         ty::Float(float_ty) => (float_ty.name_str(), DW_ATE_float),
-        _ => bug!("debuginfo::basic_type_metadata - `t` is invalid type"),
+        _ => bug!("debuginfo::build_basic_type_di_node - `t` is invalid type"),
     };
 
-    let ty_metadata = unsafe {
+    let ty_di_node = unsafe {
         llvm::LLVMRustDIBuilderCreateBasicType(
             DIB(cx),
             name.as_ptr().cast(),
@@ -855,20 +652,20 @@ fn basic_type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'l
     };
 
     if !cpp_like_debuginfo {
-        return ty_metadata;
+        return ty_di_node;
     }
 
     let typedef_name = match t.kind() {
         ty::Int(int_ty) => int_ty.name_str(),
         ty::Uint(uint_ty) => uint_ty.name_str(),
         ty::Float(float_ty) => float_ty.name_str(),
-        _ => return ty_metadata,
+        _ => return ty_di_node,
     };
 
-    let typedef_metadata = unsafe {
+    let typedef_di_node = unsafe {
         llvm::LLVMRustDIBuilderCreateTypedef(
             DIB(cx),
-            ty_metadata,
+            ty_di_node,
             typedef_name.as_ptr().cast(),
             typedef_name.len(),
             unknown_file_metadata(cx),
@@ -877,48 +674,60 @@ fn basic_type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'l
         )
     };
 
-    typedef_metadata
+    typedef_di_node
 }
 
-fn foreign_type_metadata<'ll, 'tcx>(
+fn build_foreign_type_di_node<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
     t: Ty<'tcx>,
     unique_type_id: UniqueTypeId<'tcx>,
-) -> &'ll DIType {
-    debug!("foreign_type_metadata: {:?}", t);
+) -> DINodeCreationResult<'ll> {
+    debug!("build_foreign_type_di_node: {:?}", t);
+
+    let &ty::Foreign(def_id) = unique_type_id.expect_ty().kind() else {
+        bug!("build_foreign_type_di_node() called with unexpected type: {:?}", unique_type_id.expect_ty());
+    };
 
-    let name = compute_debuginfo_type_name(cx.tcx, t, false);
-    let (size, align) = cx.size_and_align_of(t);
-    create_struct_stub(
+    build_type_with_children(
         cx,
-        size,
-        align,
-        &name,
-        unique_type_id,
-        NO_SCOPE_METADATA,
-        DIFlags::FlagZero,
-        None,
+        type_map::stub(
+            cx,
+            Stub::Struct,
+            unique_type_id,
+            &compute_debuginfo_type_name(cx.tcx, t, false),
+            cx.size_and_align_of(t),
+            Some(get_namespace_for_item(cx, def_id)),
+            DIFlags::FlagZero,
+        ),
+        |_, _| smallvec![],
+        NO_GENERICS,
     )
 }
 
-fn param_type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType {
-    debug!("param_type_metadata: {:?}", t);
+fn build_param_type_di_node<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    t: Ty<'tcx>,
+) -> DINodeCreationResult<'ll> {
+    debug!("build_param_type_di_node: {:?}", t);
     let name = format!("{:?}", t);
-    unsafe {
-        llvm::LLVMRustDIBuilderCreateBasicType(
-            DIB(cx),
-            name.as_ptr().cast(),
-            name.len(),
-            Size::ZERO.bits(),
-            DW_ATE_unsigned,
-        )
+    DINodeCreationResult {
+        di_node: unsafe {
+            llvm::LLVMRustDIBuilderCreateBasicType(
+                DIB(cx),
+                name.as_ptr().cast(),
+                name.len(),
+                Size::ZERO.bits(),
+                DW_ATE_unsigned,
+            )
+        },
+        already_stored_in_typemap: false,
     }
 }
 
-pub fn compile_unit_metadata<'ll, 'tcx>(
+pub fn build_compile_unit_di_node<'ll, 'tcx>(
     tcx: TyCtxt<'tcx>,
     codegen_unit_name: &str,
-    debug_context: &CrateDebugContext<'ll, 'tcx>,
+    debug_context: &CodegenUnitDebugContext<'ll, 'tcx>,
 ) -> &'ll DIDescriptor {
     let mut name_in_debuginfo = match tcx.sess.local_crate_source_file {
         Some(ref path) => path.clone(),
@@ -1075,165 +884,86 @@ pub fn compile_unit_metadata<'ll, 'tcx>(
     }
 }
 
-struct MetadataCreationResult<'ll> {
-    metadata: &'ll DIType,
-    already_stored_in_typemap: bool,
-}
-
-impl<'ll> MetadataCreationResult<'ll> {
-    fn new(metadata: &'ll DIType, already_stored_in_typemap: bool) -> Self {
-        MetadataCreationResult { metadata, already_stored_in_typemap }
-    }
-}
-
-#[derive(Debug)]
-struct SourceInfo<'ll> {
-    file: &'ll DIFile,
-    line: u32,
-}
-
-/// Description of a type member, which can either be a regular field (as in
-/// structs or tuples) or an enum variant.
-#[derive(Debug)]
-struct MemberDescription<'ll> {
-    name: String,
-    type_metadata: &'ll DIType,
+/// Creates a `DW_TAG_member` entry inside the DIE represented by the given `type_di_node`.
+fn build_field_di_node<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    owner: &'ll DIScope,
+    name: &str,
+    size_and_align: (Size, Align),
     offset: Size,
-    size: Size,
-    align: Align,
     flags: DIFlags,
-    discriminant: Option<u64>,
-    source_info: Option<SourceInfo<'ll>>,
-}
-
-impl<'ll> MemberDescription<'ll> {
-    fn into_metadata(
-        self,
-        cx: &CodegenCx<'ll, '_>,
-        composite_type_metadata: &'ll DIScope,
-    ) -> &'ll DIType {
-        let (file, line) = self
-            .source_info
-            .map(|info| (info.file, info.line))
-            .unwrap_or_else(|| (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER));
-        unsafe {
-            llvm::LLVMRustDIBuilderCreateVariantMemberType(
-                DIB(cx),
-                composite_type_metadata,
-                self.name.as_ptr().cast(),
-                self.name.len(),
-                file,
-                line,
-                self.size.bits(),
-                self.align.bits() as u32,
-                self.offset.bits(),
-                self.discriminant.map(|v| cx.const_u64(v)),
-                self.flags,
-                self.type_metadata,
-            )
-        }
-    }
-}
-
-/// A factory for `MemberDescription`s. It produces a list of member descriptions
-/// for some record-like type. `MemberDescriptionFactory`s are used to defer the
-/// creation of type member descriptions in order to break cycles arising from
-/// recursive type definitions.
-enum MemberDescriptionFactory<'ll, 'tcx> {
-    StructMDF(StructMemberDescriptionFactory<'tcx>),
-    TupleMDF(TupleMemberDescriptionFactory<'tcx>),
-    EnumMDF(EnumMemberDescriptionFactory<'ll, 'tcx>),
-    UnionMDF(UnionMemberDescriptionFactory<'tcx>),
-    VariantMDF(VariantMemberDescriptionFactory<'tcx>),
-}
-
-impl<'ll, 'tcx> MemberDescriptionFactory<'ll, 'tcx> {
-    fn create_member_descriptions(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec<MemberDescription<'ll>> {
-        match *self {
-            StructMDF(ref this) => this.create_member_descriptions(cx),
-            TupleMDF(ref this) => this.create_member_descriptions(cx),
-            EnumMDF(ref this) => this.create_member_descriptions(cx),
-            UnionMDF(ref this) => this.create_member_descriptions(cx),
-            VariantMDF(ref this) => this.create_member_descriptions(cx),
-        }
-    }
-}
-
-//=-----------------------------------------------------------------------------
-// Structs
-//=-----------------------------------------------------------------------------
-
-/// Creates `MemberDescription`s for the fields of a struct.
-struct StructMemberDescriptionFactory<'tcx> {
-    ty: Ty<'tcx>,
-    variant: &'tcx ty::VariantDef,
-}
-
-impl<'tcx> StructMemberDescriptionFactory<'tcx> {
-    fn create_member_descriptions<'ll>(
-        &self,
-        cx: &CodegenCx<'ll, 'tcx>,
-    ) -> Vec<MemberDescription<'ll>> {
-        let layout = cx.layout_of(self.ty);
-        self.variant
-            .fields
-            .iter()
-            .enumerate()
-            .map(|(i, f)| {
-                let name = if self.variant.ctor_kind == CtorKind::Fn {
-                    format!("__{}", i)
-                } else {
-                    f.name.to_string()
-                };
-                let field = layout.field(cx, i);
-                MemberDescription {
-                    name,
-                    type_metadata: type_metadata(cx, field.ty),
-                    offset: layout.fields.offset(i),
-                    size: field.size,
-                    align: field.align.abi,
-                    flags: DIFlags::FlagZero,
-                    discriminant: None,
-                    source_info: None,
-                }
-            })
-            .collect()
+    type_di_node: &'ll DIType,
+) -> &'ll DIType {
+    unsafe {
+        llvm::LLVMRustDIBuilderCreateMemberType(
+            DIB(cx),
+            owner,
+            name.as_ptr().cast(),
+            name.len(),
+            unknown_file_metadata(cx),
+            UNKNOWN_LINE_NUMBER,
+            size_and_align.0.bits(),
+            size_and_align.1.bits() as u32,
+            offset.bits(),
+            flags,
+            type_di_node,
+        )
     }
 }
 
-fn prepare_struct_metadata<'ll, 'tcx>(
+/// Creates the debuginfo node for a Rust struct type. Maybe be a regular struct or a tuple-struct.
+fn build_struct_type_di_node<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
-    struct_type: Ty<'tcx>,
     unique_type_id: UniqueTypeId<'tcx>,
-) -> RecursiveTypeDescription<'ll, 'tcx> {
-    let struct_name = compute_debuginfo_type_name(cx.tcx, struct_type, false);
-
-    let (struct_def_id, variant) = match struct_type.kind() {
-        ty::Adt(def, _) => (def.did(), def.non_enum_variant()),
-        _ => bug!("prepare_struct_metadata on a non-ADT"),
+) -> DINodeCreationResult<'ll> {
+    let struct_type = unique_type_id.expect_ty();
+    let ty::Adt(adt_def, _) = struct_type.kind() else {
+        bug!("build_struct_type_di_node() called with non-struct-type: {:?}", struct_type);
     };
+    debug_assert!(adt_def.is_struct());
+    let containing_scope = get_namespace_for_item(cx, adt_def.did());
+    let struct_type_and_layout = cx.layout_of(struct_type);
+    let variant_def = adt_def.non_enum_variant();
 
-    let containing_scope = get_namespace_for_item(cx, struct_def_id);
-    let (size, align) = cx.size_and_align_of(struct_type);
-
-    let struct_metadata_stub = create_struct_stub(
+    type_map::build_type_with_children(
         cx,
-        size,
-        align,
-        &struct_name,
-        unique_type_id,
-        Some(containing_scope),
-        DIFlags::FlagZero,
-        None,
-    );
-
-    create_and_register_recursive_type_forward_declaration(
-        cx,
-        struct_type,
-        unique_type_id,
-        struct_metadata_stub,
-        struct_metadata_stub,
-        StructMDF(StructMemberDescriptionFactory { ty: struct_type, variant }),
+        type_map::stub(
+            cx,
+            Stub::Struct,
+            unique_type_id,
+            &compute_debuginfo_type_name(cx.tcx, struct_type, false),
+            size_and_align_of(struct_type_and_layout),
+            Some(containing_scope),
+            DIFlags::FlagZero,
+        ),
+        // Fields:
+        |cx, owner| {
+            variant_def
+                .fields
+                .iter()
+                .enumerate()
+                .map(|(i, f)| {
+                    let field_name = if variant_def.ctor_kind == CtorKind::Fn {
+                        // This is a tuple struct
+                        tuple_field_name(i)
+                    } else {
+                        // This is struct with named fields
+                        Cow::Borrowed(f.name.as_str())
+                    };
+                    let field_layout = struct_type_and_layout.field(cx, i);
+                    build_field_di_node(
+                        cx,
+                        owner,
+                        &field_name[..],
+                        (field_layout.size, field_layout.align.abi),
+                        struct_type_and_layout.fields.offset(i),
+                        DIFlags::FlagZero,
+                        type_di_node(cx, field_layout.ty),
+                    )
+                })
+                .collect()
+        },
+        |cx| build_generic_type_param_di_nodes(cx, struct_type),
     )
 }
 
@@ -1246,7 +976,9 @@ fn prepare_struct_metadata<'ll, 'tcx>(
 /// Here are some examples:
 ///  - `name__field1__field2` when the upvar is captured by value.
 ///  - `_ref__name__field` when the upvar is captured by reference.
-fn closure_saved_names_of_captured_variables(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<String> {
+///
+/// For generators this only contains upvars that are shared by all states.
+fn closure_saved_names_of_captured_variables(tcx: TyCtxt<'_>, def_id: DefId) -> SmallVec<String> {
     let body = tcx.optimized_mir(def_id);
 
     body.var_debug_info
@@ -1263,145 +995,184 @@ fn closure_saved_names_of_captured_variables(tcx: TyCtxt<'_>, def_id: DefId) ->
             let prefix = if is_ref { "_ref__" } else { "" };
             Some(prefix.to_owned() + var.name.as_str())
         })
-        .collect::<Vec<_>>()
+        .collect()
 }
 
-/// Creates `MemberDescription`s for the fields of a tuple.
-struct TupleMemberDescriptionFactory<'tcx> {
-    ty: Ty<'tcx>,
-    component_types: Vec<Ty<'tcx>>,
-}
+/// Builds the DW_TAG_member debuginfo nodes for the upvars of a closure or generator.
+/// For a generator, this will handle upvars shared by all states.
+fn build_upvar_field_di_nodes<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    closure_or_generator_ty: Ty<'tcx>,
+    closure_or_generator_metadata: &'ll DIType,
+) -> SmallVec<&'ll DIType> {
+    let (&def_id, up_var_tys) = match closure_or_generator_ty.kind() {
+        ty::Generator(def_id, substs, _) => {
+            let upvar_tys: SmallVec<_> = substs.as_generator().prefix_tys().collect();
+            (def_id, upvar_tys)
+        }
+        ty::Closure(def_id, substs) => {
+            let upvar_tys: SmallVec<_> = substs.as_closure().upvar_tys().collect();
+            (def_id, upvar_tys)
+        }
+        _ => {
+            bug!(
+                "new_upvar_member_descriptions() called with non-closure-or-generator-type: {:?}",
+                closure_or_generator_ty
+            )
+        }
+    };
 
-impl<'tcx> TupleMemberDescriptionFactory<'tcx> {
-    fn create_member_descriptions<'ll>(
-        &self,
-        cx: &CodegenCx<'ll, 'tcx>,
-    ) -> Vec<MemberDescription<'ll>> {
-        let mut capture_names = match *self.ty.kind() {
-            ty::Generator(def_id, ..) | ty::Closure(def_id, ..) => {
-                Some(closure_saved_names_of_captured_variables(cx.tcx, def_id).into_iter())
-            }
-            _ => None,
-        };
-        let layout = cx.layout_of(self.ty);
-        self.component_types
-            .iter()
-            .enumerate()
-            .map(|(i, &component_type)| {
-                let (size, align) = cx.size_and_align_of(component_type);
-                let name = if let Some(names) = capture_names.as_mut() {
-                    names.next().unwrap()
-                } else {
-                    format!("__{}", i)
-                };
-                MemberDescription {
-                    name,
-                    type_metadata: type_metadata(cx, component_type),
-                    offset: layout.fields.offset(i),
-                    size,
-                    align,
-                    flags: DIFlags::FlagZero,
-                    discriminant: None,
-                    source_info: None,
-                }
-            })
-            .collect()
-    }
+    debug_assert!(up_var_tys
+        .iter()
+        .all(|&t| t == cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t)));
+
+    let capture_names = closure_saved_names_of_captured_variables(cx.tcx, def_id);
+    let layout = cx.layout_of(closure_or_generator_ty);
+
+    up_var_tys
+        .into_iter()
+        .zip(capture_names.iter())
+        .enumerate()
+        .map(|(index, (up_var_ty, capture_name))| {
+            build_field_di_node(
+                cx,
+                closure_or_generator_metadata,
+                capture_name,
+                cx.size_and_align_of(up_var_ty),
+                layout.fields.offset(index),
+                DIFlags::FlagZero,
+                type_di_node(cx, up_var_ty),
+            )
+        })
+        .collect()
 }
 
-fn prepare_tuple_metadata<'ll, 'tcx>(
+/// Builds the DW_TAG_structure_type debuginfo node for a Rust tuple type.
+fn build_tuple_type_di_node<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
-    tuple_type: Ty<'tcx>,
-    component_types: &[Ty<'tcx>],
     unique_type_id: UniqueTypeId<'tcx>,
-    containing_scope: Option<&'ll DIScope>,
-) -> RecursiveTypeDescription<'ll, 'tcx> {
-    let (size, align) = cx.size_and_align_of(tuple_type);
-    let tuple_name = compute_debuginfo_type_name(cx.tcx, tuple_type, false);
+) -> DINodeCreationResult<'ll> {
+    let tuple_type = unique_type_id.expect_ty();
+    let &ty::Tuple(component_types) = tuple_type.kind() else {
+        bug!("build_tuple_type_di_node() called with non-tuple-type: {:?}", tuple_type)
+    };
 
-    let struct_stub = create_struct_stub(
-        cx,
-        size,
-        align,
-        &tuple_name[..],
-        unique_type_id,
-        containing_scope,
-        DIFlags::FlagZero,
-        None,
-    );
-
-    create_and_register_recursive_type_forward_declaration(
+    let tuple_type_and_layout = cx.layout_of(tuple_type);
+    let type_name = compute_debuginfo_type_name(cx.tcx, tuple_type, false);
+
+    type_map::build_type_with_children(
         cx,
-        tuple_type,
-        unique_type_id,
-        struct_stub,
-        struct_stub,
-        TupleMDF(TupleMemberDescriptionFactory {
-            ty: tuple_type,
-            component_types: component_types.to_vec(),
-        }),
+        type_map::stub(
+            cx,
+            Stub::Struct,
+            unique_type_id,
+            &type_name,
+            size_and_align_of(tuple_type_and_layout),
+            NO_SCOPE_METADATA,
+            DIFlags::FlagZero,
+        ),
+        // Fields:
+        |cx, tuple_metadata| {
+            component_types
+                .into_iter()
+                .enumerate()
+                .map(|(index, component_type)| {
+                    build_field_di_node(
+                        cx,
+                        tuple_metadata,
+                        &tuple_field_name(index),
+                        cx.size_and_align_of(component_type),
+                        tuple_type_and_layout.fields.offset(index),
+                        DIFlags::FlagZero,
+                        type_di_node(cx, component_type),
+                    )
+                })
+                .collect()
+        },
+        NO_GENERICS,
     )
 }
 
-//=-----------------------------------------------------------------------------
-// Unions
-//=-----------------------------------------------------------------------------
-
-struct UnionMemberDescriptionFactory<'tcx> {
-    layout: TyAndLayout<'tcx>,
-    variant: &'tcx ty::VariantDef,
-}
+/// Builds the debufinfo node for a closure environment.
+fn build_closure_env_di_node<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    unique_type_id: UniqueTypeId<'tcx>,
+) -> DINodeCreationResult<'ll> {
+    let closure_env_type = unique_type_id.expect_ty();
+    let &ty::Closure(def_id, _substs) = closure_env_type.kind() else {
+        bug!("new_closure_env_metadata() called with non-closure-type: {:?}", closure_env_type)
+    };
+    let containing_scope = get_namespace_for_item(cx, def_id);
+    let type_name = compute_debuginfo_type_name(cx.tcx, closure_env_type, false);
 
-impl<'tcx> UnionMemberDescriptionFactory<'tcx> {
-    fn create_member_descriptions<'ll>(
-        &self,
-        cx: &CodegenCx<'ll, 'tcx>,
-    ) -> Vec<MemberDescription<'ll>> {
-        self.variant
-            .fields
-            .iter()
-            .enumerate()
-            .map(|(i, f)| {
-                let field = self.layout.field(cx, i);
-                MemberDescription {
-                    name: f.name.to_string(),
-                    type_metadata: type_metadata(cx, field.ty),
-                    offset: Size::ZERO,
-                    size: field.size,
-                    align: field.align.abi,
-                    flags: DIFlags::FlagZero,
-                    discriminant: None,
-                    source_info: None,
-                }
-            })
-            .collect()
-    }
+    type_map::build_type_with_children(
+        cx,
+        type_map::stub(
+            cx,
+            Stub::Struct,
+            unique_type_id,
+            &type_name,
+            cx.size_and_align_of(closure_env_type),
+            Some(containing_scope),
+            DIFlags::FlagZero,
+        ),
+        // Fields:
+        |cx, owner| build_upvar_field_di_nodes(cx, closure_env_type, owner),
+        // Generics:
+        |_| {
+            // FIXME(mw): Should we specify generic parameters for closures?
+            smallvec![]
+        },
+    )
 }
 
-fn prepare_union_metadata<'ll, 'tcx>(
+/// Build the debuginfo node for a Rust `union` type.
+fn build_union_type_di_node<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
-    union_type: Ty<'tcx>,
     unique_type_id: UniqueTypeId<'tcx>,
-) -> RecursiveTypeDescription<'ll, 'tcx> {
-    let union_name = compute_debuginfo_type_name(cx.tcx, union_type, false);
-
-    let (union_def_id, variant) = match union_type.kind() {
+) -> DINodeCreationResult<'ll> {
+    let union_type = unique_type_id.expect_ty();
+    let (union_def_id, variant_def) = match union_type.kind() {
         ty::Adt(def, _) => (def.did(), def.non_enum_variant()),
-        _ => bug!("prepare_union_metadata on a non-ADT"),
+        _ => bug!("build_union_type_di_node on a non-ADT"),
     };
-
     let containing_scope = get_namespace_for_item(cx, union_def_id);
+    let union_ty_and_layout = cx.layout_of(union_type);
+    let type_name = compute_debuginfo_type_name(cx.tcx, union_type, false);
 
-    let union_metadata_stub =
-        create_union_stub(cx, union_type, &union_name, unique_type_id, containing_scope);
-
-    create_and_register_recursive_type_forward_declaration(
+    type_map::build_type_with_children(
         cx,
-        union_type,
-        unique_type_id,
-        union_metadata_stub,
-        union_metadata_stub,
-        UnionMDF(UnionMemberDescriptionFactory { layout: cx.layout_of(union_type), variant }),
+        type_map::stub(
+            cx,
+            Stub::Union,
+            unique_type_id,
+            &type_name,
+            size_and_align_of(union_ty_and_layout),
+            Some(containing_scope),
+            DIFlags::FlagZero,
+        ),
+        // Fields:
+        |cx, owner| {
+            variant_def
+                .fields
+                .iter()
+                .enumerate()
+                .map(|(i, f)| {
+                    let field_layout = union_ty_and_layout.field(cx, i);
+                    build_field_di_node(
+                        cx,
+                        owner,
+                        f.name.as_str(),
+                        size_and_align_of(field_layout),
+                        Size::ZERO,
+                        DIFlags::FlagZero,
+                        type_di_node(cx, field_layout.ty),
+                    )
+                })
+                .collect()
+        },
+        // Generics:
+        |cx| build_generic_type_param_di_nodes(cx, union_type),
     )
 }
 
@@ -1446,890 +1217,30 @@ fn generator_layout_and_saved_local_names<'tcx>(
     (generator_layout, generator_saved_local_names)
 }
 
-/// Describes the members of an enum value; an enum is described as a union of
-/// structs in DWARF. This `MemberDescriptionFactory` provides the description for
-/// the members of this union; so for every variant of the given enum, this
-/// factory will produce one `MemberDescription` (all with no name and a fixed
-/// offset of zero bytes).
-struct EnumMemberDescriptionFactory<'ll, 'tcx> {
-    enum_type: Ty<'tcx>,
-    layout: TyAndLayout<'tcx>,
-    tag_type_metadata: Option<&'ll DIType>,
-    common_members: Vec<Option<&'ll DIType>>,
-}
-
-impl<'ll, 'tcx> EnumMemberDescriptionFactory<'ll, 'tcx> {
-    fn create_member_descriptions(&self, cx: &CodegenCx<'ll, 'tcx>) -> Vec<MemberDescription<'ll>> {
-        let generator_variant_info_data = match *self.enum_type.kind() {
-            ty::Generator(def_id, ..) => {
-                Some(generator_layout_and_saved_local_names(cx.tcx, def_id))
-            }
-            _ => None,
-        };
-
-        let variant_info_for = |index: VariantIdx| match *self.enum_type.kind() {
-            ty::Adt(adt, _) => VariantInfo::Adt(&adt.variant(index), index),
-            ty::Generator(def_id, _, _) => {
-                let (generator_layout, generator_saved_local_names) =
-                    generator_variant_info_data.as_ref().unwrap();
-                VariantInfo::Generator {
-                    def_id,
-                    generator_layout: *generator_layout,
-                    generator_saved_local_names,
-                    variant_index: index,
-                }
-            }
-            _ => bug!(),
-        };
-
-        // While LLVM supports generating debuginfo for variant types (enums), it doesn't support
-        // lowering that debuginfo to CodeView records for msvc targets. So if we are targeting
-        // msvc, then we need to use a different, fallback encoding of the debuginfo.
-        let fallback = cpp_like_debuginfo(cx.tcx);
-        // This will always find the metadata in the type map.
-        let self_metadata = type_metadata(cx, self.enum_type);
-
-        match self.layout.variants {
-            Variants::Single { index } => {
-                if let ty::Adt(adt, _) = self.enum_type.kind() {
-                    if adt.variants().is_empty() {
-                        return vec![];
-                    }
-                }
-
-                let variant_info = variant_info_for(index);
-                let (variant_type_metadata, member_description_factory) =
-                    describe_enum_variant(cx, self.layout, variant_info, self_metadata);
-
-                let member_descriptions = member_description_factory.create_member_descriptions(cx);
-                let type_params = compute_type_parameters(cx, self.enum_type);
-
-                set_members_of_composite_type(
-                    cx,
-                    variant_type_metadata,
-                    member_descriptions,
-                    Some(&self.common_members),
-                    type_params,
-                );
-                vec![MemberDescription {
-                    name: variant_info.variant_name(),
-                    type_metadata: variant_type_metadata,
-                    offset: Size::ZERO,
-                    size: self.layout.size,
-                    align: self.layout.align.abi,
-                    flags: DIFlags::FlagZero,
-                    discriminant: None,
-                    source_info: variant_info.source_info(cx),
-                }]
-            }
-            Variants::Multiple {
-                tag_encoding: TagEncoding::Direct,
-                tag_field,
-                ref variants,
-                ..
-            } => {
-                let fallback_discr_variant = if fallback {
-                    // For MSVC, we generate a union of structs for each variant and an
-                    // explicit discriminant field roughly equivalent to the following C:
-                    // ```c
-                    // union enum$<{name}> {
-                    //   struct {variant 0 name} {
-                    //     <variant 0 fields>
-                    //   } variant0;
-                    //   <other variant structs>
-                    //   {name} discriminant;
-                    // }
-                    // ```
-                    // The natvis in `intrinsic.natvis` then matches on `this.discriminant` to
-                    // determine which variant is active and then displays it.
-                    let enum_layout = self.layout;
-                    let offset = enum_layout.fields.offset(tag_field);
-                    let discr_ty = enum_layout.field(cx, tag_field).ty;
-                    let (size, align) = cx.size_and_align_of(discr_ty);
-                    Some(MemberDescription {
-                        name: "discriminant".into(),
-                        type_metadata: self.tag_type_metadata.unwrap(),
-                        offset,
-                        size,
-                        align,
-                        flags: DIFlags::FlagZero,
-                        discriminant: None,
-                        source_info: None,
-                    })
-                } else {
-                    None
-                };
-
-                variants
-                    .iter_enumerated()
-                    .map(|(i, _)| {
-                        let variant = self.layout.for_variant(cx, i);
-                        let variant_info = variant_info_for(i);
-                        let (variant_type_metadata, member_desc_factory) =
-                            describe_enum_variant(cx, variant, variant_info, self_metadata);
-
-                        let member_descriptions =
-                            member_desc_factory.create_member_descriptions(cx);
-                        let type_params = compute_type_parameters(cx, self.enum_type);
-
-                        set_members_of_composite_type(
-                            cx,
-                            variant_type_metadata,
-                            member_descriptions,
-                            Some(&self.common_members),
-                            type_params,
-                        );
-
-                        MemberDescription {
-                            name: if fallback {
-                                format!("variant{}", i.as_u32())
-                            } else {
-                                variant_info.variant_name()
-                            },
-                            type_metadata: variant_type_metadata,
-                            offset: Size::ZERO,
-                            size: self.layout.size,
-                            align: self.layout.align.abi,
-                            flags: DIFlags::FlagZero,
-                            discriminant: Some(
-                                self.layout.ty.discriminant_for_variant(cx.tcx, i).unwrap().val
-                                    as u64,
-                            ),
-                            source_info: variant_info.source_info(cx),
-                        }
-                    })
-                    .chain(fallback_discr_variant.into_iter())
-                    .collect()
-            }
-            Variants::Multiple {
-                tag_encoding:
-                    TagEncoding::Niche { ref niche_variants, niche_start, dataful_variant },
-                tag,
-                ref variants,
-                tag_field,
-            } => {
-                let calculate_niche_value = |i: VariantIdx| {
-                    if i == dataful_variant {
-                        None
-                    } else {
-                        let value = (i.as_u32() as u128)
-                            .wrapping_sub(niche_variants.start().as_u32() as u128)
-                            .wrapping_add(niche_start);
-                        let value = tag.value.size(cx).truncate(value);
-                        // NOTE(eddyb) do *NOT* remove this assert, until
-                        // we pass the full 128-bit value to LLVM, otherwise
-                        // truncation will be silent and remain undetected.
-                        assert_eq!(value as u64 as u128, value);
-                        Some(value as u64)
-                    }
-                };
-
-                // For MSVC, we will generate a union of two fields, one for the dataful variant
-                // and one that just points to the discriminant. We also create an enum that
-                // contains tag values for the non-dataful variants and make the discriminant field
-                // that type. We then use natvis to render the enum type correctly in Windbg/VS.
-                // This will generate debuginfo roughly equivalent to the following C:
-                // ```c
-                // union enum$<{name}, {min niche}, {max niche}, {dataful variant name}> {
-                //   struct <dataful variant name> {
-                //     <fields in dataful variant>
-                //   } dataful_variant;
-                //   enum Discriminant$ {
-                //     <non-dataful variants>
-                //   } discriminant;
-                // }
-                // ```
-                // The natvis in `intrinsic.natvis` matches on the type name `enum$<*, *, *, *>`
-                // and evaluates `this.discriminant`. If the value is between the min niche and max
-                // niche, then the enum is in the dataful variant and `this.dataful_variant` is
-                // rendered. Otherwise, the enum is in one of the non-dataful variants. In that
-                // case, we just need to render the name of the `this.discriminant` enum.
-                if fallback {
-                    let dataful_variant_layout = self.layout.for_variant(cx, dataful_variant);
-
-                    let mut discr_enum_ty = tag.value.to_ty(cx.tcx);
-                    // If the niche is the NULL value of a reference, then `discr_enum_ty` will be a RawPtr.
-                    // CodeView doesn't know what to do with enums whose base type is a pointer so we fix this up
-                    // to just be `usize`.
-                    if let ty::RawPtr(_) = discr_enum_ty.kind() {
-                        discr_enum_ty = cx.tcx.types.usize;
-                    }
-
-                    let tags: Vec<_> = variants
-                        .iter_enumerated()
-                        .filter_map(|(variant_idx, _)| {
-                            calculate_niche_value(variant_idx).map(|tag| {
-                                let variant = variant_info_for(variant_idx);
-                                let name = variant.variant_name();
-
-                                Some(unsafe {
-                                    llvm::LLVMRustDIBuilderCreateEnumerator(
-                                        DIB(cx),
-                                        name.as_ptr().cast(),
-                                        name.len(),
-                                        tag as i64,
-                                        !discr_enum_ty.is_signed(),
-                                    )
-                                })
-                            })
-                        })
-                        .collect();
-
-                    let discr_enum = unsafe {
-                        llvm::LLVMRustDIBuilderCreateEnumerationType(
-                            DIB(cx),
-                            self_metadata,
-                            "Discriminant$".as_ptr().cast(),
-                            "Discriminant$".len(),
-                            unknown_file_metadata(cx),
-                            UNKNOWN_LINE_NUMBER,
-                            tag.value.size(cx).bits(),
-                            tag.value.align(cx).abi.bits() as u32,
-                            create_DIArray(DIB(cx), &tags),
-                            type_metadata(cx, discr_enum_ty),
-                            true,
-                        )
-                    };
-
-                    let variant_info = variant_info_for(dataful_variant);
-                    let (variant_type_metadata, member_desc_factory) = describe_enum_variant(
-                        cx,
-                        dataful_variant_layout,
-                        variant_info,
-                        self_metadata,
-                    );
-
-                    let member_descriptions = member_desc_factory.create_member_descriptions(cx);
-                    let type_params = compute_type_parameters(cx, self.enum_type);
-
-                    set_members_of_composite_type(
-                        cx,
-                        variant_type_metadata,
-                        member_descriptions,
-                        Some(&self.common_members),
-                        type_params,
-                    );
-
-                    let (size, align) =
-                        cx.size_and_align_of(dataful_variant_layout.field(cx, tag_field).ty);
-
-                    vec![
-                        MemberDescription {
-                            // Name the dataful variant so that we can identify it for natvis
-                            name: "dataful_variant".to_string(),
-                            type_metadata: variant_type_metadata,
-                            offset: Size::ZERO,
-                            size: self.layout.size,
-                            align: self.layout.align.abi,
-                            flags: DIFlags::FlagZero,
-                            discriminant: None,
-                            source_info: variant_info.source_info(cx),
-                        },
-                        MemberDescription {
-                            name: "discriminant".into(),
-                            type_metadata: discr_enum,
-                            offset: dataful_variant_layout.fields.offset(tag_field),
-                            size,
-                            align,
-                            flags: DIFlags::FlagZero,
-                            discriminant: None,
-                            source_info: None,
-                        },
-                    ]
-                } else {
-                    variants
-                        .iter_enumerated()
-                        .map(|(i, _)| {
-                            let variant = self.layout.for_variant(cx, i);
-                            let variant_info = variant_info_for(i);
-                            let (variant_type_metadata, member_desc_factory) =
-                                describe_enum_variant(cx, variant, variant_info, self_metadata);
-
-                            let member_descriptions =
-                                member_desc_factory.create_member_descriptions(cx);
-                            let type_params = compute_type_parameters(cx, self.enum_type);
-
-                            set_members_of_composite_type(
-                                cx,
-                                variant_type_metadata,
-                                member_descriptions,
-                                Some(&self.common_members),
-                                type_params,
-                            );
-
-                            let niche_value = calculate_niche_value(i);
-
-                            MemberDescription {
-                                name: variant_info.variant_name(),
-                                type_metadata: variant_type_metadata,
-                                offset: Size::ZERO,
-                                size: self.layout.size,
-                                align: self.layout.align.abi,
-                                flags: DIFlags::FlagZero,
-                                discriminant: niche_value,
-                                source_info: variant_info.source_info(cx),
-                            }
-                        })
-                        .collect()
-                }
-            }
-        }
-    }
-}
-
-// Creates `MemberDescription`s for the fields of a single enum variant.
-struct VariantMemberDescriptionFactory<'tcx> {
-    /// Cloned from the `layout::Struct` describing the variant.
-    offsets: Vec<Size>,
-    args: Vec<(String, Ty<'tcx>)>,
-}
-
-impl<'tcx> VariantMemberDescriptionFactory<'tcx> {
-    fn create_member_descriptions<'ll>(
-        &self,
-        cx: &CodegenCx<'ll, 'tcx>,
-    ) -> Vec<MemberDescription<'ll>> {
-        self.args
-            .iter()
-            .enumerate()
-            .map(|(i, &(ref name, ty))| {
-                let (size, align) = cx.size_and_align_of(ty);
-                MemberDescription {
-                    name: name.to_string(),
-                    type_metadata: type_metadata(cx, ty),
-                    offset: self.offsets[i],
-                    size,
-                    align,
-                    flags: DIFlags::FlagZero,
-                    discriminant: None,
-                    source_info: None,
-                }
-            })
-            .collect()
-    }
-}
-
-#[derive(Copy, Clone)]
-enum VariantInfo<'a, 'tcx> {
-    Adt(&'tcx ty::VariantDef, VariantIdx),
-    Generator {
-        def_id: DefId,
-        generator_layout: &'tcx GeneratorLayout<'tcx>,
-        generator_saved_local_names: &'a IndexVec<mir::GeneratorSavedLocal, Option<Symbol>>,
-        variant_index: VariantIdx,
-    },
-}
-
-impl<'tcx> VariantInfo<'_, 'tcx> {
-    fn variant_idx(&self) -> VariantIdx {
-        match self {
-            VariantInfo::Adt(_, variant_index) | VariantInfo::Generator { variant_index, .. } => {
-                *variant_index
-            }
-        }
-    }
-
-    fn map_struct_name<R>(&self, f: impl FnOnce(&str) -> R) -> R {
-        match self {
-            VariantInfo::Adt(variant, _) => f(variant.name.as_str()),
-            VariantInfo::Generator { variant_index, .. } => {
-                f(&GeneratorSubsts::variant_name(*variant_index))
-            }
-        }
-    }
-
-    fn variant_name(&self) -> String {
-        match self {
-            VariantInfo::Adt(variant, _) => variant.name.to_string(),
-            VariantInfo::Generator { variant_index, .. } => {
-                // Since GDB currently prints out the raw discriminant along
-                // with every variant, make each variant name be just the value
-                // of the discriminant. The struct name for the variant includes
-                // the actual variant description.
-                format!("{}", variant_index.as_usize())
-            }
-        }
-    }
-
-    fn field_name(&self, i: usize) -> String {
-        let field_name = match *self {
-            VariantInfo::Adt(variant, _) if variant.ctor_kind != CtorKind::Fn => {
-                Some(variant.fields[i].name)
-            }
-            VariantInfo::Generator {
-                generator_layout,
-                generator_saved_local_names,
-                variant_index,
-                ..
-            } => {
-                generator_saved_local_names
-                    [generator_layout.variant_fields[variant_index][i.into()]]
-            }
-            _ => None,
-        };
-        field_name.map(|name| name.to_string()).unwrap_or_else(|| format!("__{}", i))
-    }
-
-    fn source_info<'ll>(&self, cx: &CodegenCx<'ll, 'tcx>) -> Option<SourceInfo<'ll>> {
-        if let VariantInfo::Generator { def_id, variant_index, .. } = self {
-            let span =
-                cx.tcx.generator_layout(*def_id).unwrap().variant_source_info[*variant_index].span;
-            if !span.is_dummy() {
-                let loc = cx.lookup_debug_loc(span.lo());
-                return Some(SourceInfo { file: file_metadata(cx, &loc.file), line: loc.line });
-            }
-        }
-        None
-    }
-}
-
-/// Returns a tuple of (1) `type_metadata_stub` of the variant, (2) a
-/// `MemberDescriptionFactory` for producing the descriptions of the
-/// fields of the variant. This is a rudimentary version of a full
-/// `RecursiveTypeDescription`.
-fn describe_enum_variant<'ll, 'tcx>(
-    cx: &CodegenCx<'ll, 'tcx>,
-    layout: layout::TyAndLayout<'tcx>,
-    variant: VariantInfo<'_, 'tcx>,
-    containing_scope: &'ll DIScope,
-) -> (&'ll DICompositeType, MemberDescriptionFactory<'ll, 'tcx>) {
-    let metadata_stub = variant.map_struct_name(|variant_name| {
-        let unique_type_id =
-            UniqueTypeId::for_enum_variant(cx.tcx, layout.ty, variant.variant_idx());
-
-        let (size, align) = cx.size_and_align_of(layout.ty);
-
-        create_struct_stub(
-            cx,
-            size,
-            align,
-            variant_name,
-            unique_type_id,
-            Some(containing_scope),
-            DIFlags::FlagZero,
-            None,
-        )
-    });
-
-    let offsets = (0..layout.fields.count()).map(|i| layout.fields.offset(i)).collect();
-    let args = (0..layout.fields.count())
-        .map(|i| (variant.field_name(i), layout.field(cx, i).ty))
-        .collect();
-
-    let member_description_factory = VariantMDF(VariantMemberDescriptionFactory { offsets, args });
-
-    (metadata_stub, member_description_factory)
-}
-
-fn prepare_enum_metadata<'ll, 'tcx>(
-    cx: &CodegenCx<'ll, 'tcx>,
-    enum_type: Ty<'tcx>,
-    enum_def_id: DefId,
-    unique_type_id: UniqueTypeId<'tcx>,
-    outer_field_tys: Vec<Ty<'tcx>>,
-) -> RecursiveTypeDescription<'ll, 'tcx> {
-    let tcx = cx.tcx;
-    let enum_name = compute_debuginfo_type_name(tcx, enum_type, false);
-
-    let containing_scope = get_namespace_for_item(cx, enum_def_id);
-    // FIXME: This should emit actual file metadata for the enum, but we
-    // currently can't get the necessary information when it comes to types
-    // imported from other crates. Formerly we violated the ODR when performing
-    // LTO because we emitted debuginfo for the same type with varying file
-    // metadata, so as a workaround we pretend that the type comes from
-    // <unknown>
-    let file_metadata = unknown_file_metadata(cx);
-
-    let discriminant_type_metadata = |discr: Primitive| {
-        let enumerators_metadata: Vec<_> = match enum_type.kind() {
-            ty::Adt(def, _) => iter::zip(def.discriminants(tcx), def.variants())
-                .map(|((_, discr), v)| {
-                    let name = v.name.as_str();
-                    let is_unsigned = match discr.ty.kind() {
-                        ty::Int(_) => false,
-                        ty::Uint(_) => true,
-                        _ => bug!("non integer discriminant"),
-                    };
-                    unsafe {
-                        Some(llvm::LLVMRustDIBuilderCreateEnumerator(
-                            DIB(cx),
-                            name.as_ptr().cast(),
-                            name.len(),
-                            // FIXME: what if enumeration has i128 discriminant?
-                            discr.val as i64,
-                            is_unsigned,
-                        ))
-                    }
-                })
-                .collect(),
-            ty::Generator(_, substs, _) => substs
-                .as_generator()
-                .variant_range(enum_def_id, tcx)
-                .map(|variant_index| {
-                    debug_assert_eq!(tcx.types.u32, substs.as_generator().discr_ty(tcx));
-                    let name = GeneratorSubsts::variant_name(variant_index);
-                    unsafe {
-                        Some(llvm::LLVMRustDIBuilderCreateEnumerator(
-                            DIB(cx),
-                            name.as_ptr().cast(),
-                            name.len(),
-                            // Generators use u32 as discriminant type, verified above.
-                            variant_index.as_u32().into(),
-                            true, // IsUnsigned
-                        ))
-                    }
-                })
-                .collect(),
-            _ => bug!(),
-        };
-
-        let disr_type_key = (enum_def_id, discr);
-        let cached_discriminant_type_metadata =
-            debug_context(cx).created_enum_disr_types.borrow().get(&disr_type_key).cloned();
-        match cached_discriminant_type_metadata {
-            Some(discriminant_type_metadata) => discriminant_type_metadata,
-            None => {
-                let (discriminant_size, discriminant_align) = (discr.size(cx), discr.align(cx));
-                let discriminant_base_type_metadata = type_metadata(cx, discr.to_ty(tcx));
-
-                let item_name;
-                let discriminant_name = match enum_type.kind() {
-                    ty::Adt(..) => {
-                        item_name = tcx.item_name(enum_def_id);
-                        item_name.as_str()
-                    }
-                    ty::Generator(..) => enum_name.as_str(),
-                    _ => bug!(),
-                };
-
-                let discriminant_type_metadata = unsafe {
-                    llvm::LLVMRustDIBuilderCreateEnumerationType(
-                        DIB(cx),
-                        containing_scope,
-                        discriminant_name.as_ptr().cast(),
-                        discriminant_name.len(),
-                        file_metadata,
-                        UNKNOWN_LINE_NUMBER,
-                        discriminant_size.bits(),
-                        discriminant_align.abi.bits() as u32,
-                        create_DIArray(DIB(cx), &enumerators_metadata),
-                        discriminant_base_type_metadata,
-                        true,
-                    )
-                };
-
-                debug_context(cx)
-                    .created_enum_disr_types
-                    .borrow_mut()
-                    .insert(disr_type_key, discriminant_type_metadata);
-
-                discriminant_type_metadata
-            }
-        }
-    };
-
-    let layout = cx.layout_of(enum_type);
-
-    if let (Abi::Scalar(_), Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, .. }) =
-        (layout.abi, &layout.variants)
-    {
-        return FinalMetadata(discriminant_type_metadata(tag.value));
-    }
-
-    // While LLVM supports generating debuginfo for variant types (enums), it doesn't support
-    // lowering that debuginfo to CodeView records for msvc targets. So if we are targeting
-    // msvc, then we need to use a different encoding of the debuginfo.
-    if cpp_like_debuginfo(tcx) {
-        let discriminant_type_metadata = match layout.variants {
-            Variants::Single { .. } => None,
-            Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, tag, .. }
-            | Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, .. } => {
-                Some(discriminant_type_metadata(tag.value))
-            }
-        };
-
-        let enum_metadata = {
-            let unique_type_id_str = unique_type_id.generate_unique_id_string(tcx);
-
-            unsafe {
-                llvm::LLVMRustDIBuilderCreateUnionType(
-                    DIB(cx),
-                    None,
-                    enum_name.as_ptr().cast(),
-                    enum_name.len(),
-                    file_metadata,
-                    UNKNOWN_LINE_NUMBER,
-                    layout.size.bits(),
-                    layout.align.abi.bits() as u32,
-                    DIFlags::FlagZero,
-                    None,
-                    0, // RuntimeLang
-                    unique_type_id_str.as_ptr().cast(),
-                    unique_type_id_str.len(),
-                )
-            }
-        };
-
-        return create_and_register_recursive_type_forward_declaration(
-            cx,
-            enum_type,
-            unique_type_id,
-            enum_metadata,
-            enum_metadata,
-            EnumMDF(EnumMemberDescriptionFactory {
-                enum_type,
-                layout,
-                tag_type_metadata: discriminant_type_metadata,
-                common_members: vec![],
-            }),
-        );
-    }
-
-    let discriminator_name = match enum_type.kind() {
-        ty::Generator(..) => "__state",
-        _ => "",
-    };
-    let discriminator_metadata = match layout.variants {
-        // A single-variant enum has no discriminant.
-        Variants::Single { .. } => None,
-
-        Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, tag, tag_field, .. } => {
-            // Find the integer type of the correct size.
-            let size = tag.value.size(cx);
-            let align = tag.value.align(cx);
-
-            let tag_type = match tag.value {
-                Int(t, _) => t,
-                F32 => Integer::I32,
-                F64 => Integer::I64,
-                Pointer => cx.data_layout().ptr_sized_integer(),
-            }
-            .to_ty(cx.tcx, false);
-
-            let tag_metadata = basic_type_metadata(cx, tag_type);
-            unsafe {
-                Some(llvm::LLVMRustDIBuilderCreateMemberType(
-                    DIB(cx),
-                    containing_scope,
-                    discriminator_name.as_ptr().cast(),
-                    discriminator_name.len(),
-                    file_metadata,
-                    UNKNOWN_LINE_NUMBER,
-                    size.bits(),
-                    align.abi.bits() as u32,
-                    layout.fields.offset(tag_field).bits(),
-                    DIFlags::FlagArtificial,
-                    tag_metadata,
-                ))
-            }
-        }
-
-        Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, tag_field, .. } => {
-            let discr_type = tag.value.to_ty(cx.tcx);
-            let (size, align) = cx.size_and_align_of(discr_type);
-
-            let discr_metadata = basic_type_metadata(cx, discr_type);
-            unsafe {
-                Some(llvm::LLVMRustDIBuilderCreateMemberType(
-                    DIB(cx),
-                    containing_scope,
-                    discriminator_name.as_ptr().cast(),
-                    discriminator_name.len(),
-                    file_metadata,
-                    UNKNOWN_LINE_NUMBER,
-                    size.bits(),
-                    align.bits() as u32,
-                    layout.fields.offset(tag_field).bits(),
-                    DIFlags::FlagArtificial,
-                    discr_metadata,
-                ))
-            }
-        }
-    };
-
-    let outer_fields = match layout.variants {
-        Variants::Single { .. } => vec![],
-        Variants::Multiple { .. } => {
-            let tuple_mdf =
-                TupleMemberDescriptionFactory { ty: enum_type, component_types: outer_field_tys };
-            tuple_mdf
-                .create_member_descriptions(cx)
-                .into_iter()
-                .map(|desc| Some(desc.into_metadata(cx, containing_scope)))
-                .collect()
-        }
-    };
-
-    let variant_part_unique_type_id_str =
-        UniqueTypeId::for_enum_variant_part(tcx, enum_type).generate_unique_id_string(tcx);
-
-    let empty_array = create_DIArray(DIB(cx), &[]);
-    let name = "";
-    let variant_part = unsafe {
-        llvm::LLVMRustDIBuilderCreateVariantPart(
-            DIB(cx),
-            containing_scope,
-            name.as_ptr().cast(),
-            name.len(),
-            file_metadata,
-            UNKNOWN_LINE_NUMBER,
-            layout.size.bits(),
-            layout.align.abi.bits() as u32,
-            DIFlags::FlagZero,
-            discriminator_metadata,
-            empty_array,
-            variant_part_unique_type_id_str.as_ptr().cast(),
-            variant_part_unique_type_id_str.len(),
-        )
-    };
-
-    let struct_wrapper = {
-        // The variant part must be wrapped in a struct according to DWARF.
-        // All fields except the discriminant (including `outer_fields`)
-        // should be put into structures inside the variant part, which gives
-        // an equivalent layout but offers us much better integration with
-        // debuggers.
-        let type_array = create_DIArray(DIB(cx), &[Some(variant_part)]);
-        let unique_type_id_str = unique_type_id.generate_unique_id_string(tcx);
-
-        unsafe {
-            llvm::LLVMRustDIBuilderCreateStructType(
-                DIB(cx),
-                Some(containing_scope),
-                enum_name.as_ptr().cast(),
-                enum_name.len(),
-                file_metadata,
-                UNKNOWN_LINE_NUMBER,
-                layout.size.bits(),
-                layout.align.abi.bits() as u32,
-                DIFlags::FlagZero,
-                None,
-                type_array,
-                0,
-                None,
-                unique_type_id_str.as_ptr().cast(),
-                unique_type_id_str.len(),
-            )
-        }
-    };
-
-    create_and_register_recursive_type_forward_declaration(
-        cx,
-        enum_type,
-        unique_type_id,
-        struct_wrapper,
-        variant_part,
-        EnumMDF(EnumMemberDescriptionFactory {
-            enum_type,
-            layout,
-            tag_type_metadata: None,
-            common_members: outer_fields,
-        }),
-    )
-}
-
-/// Creates debug information for a composite type, that is, anything that
-/// results in a LLVM struct.
-///
-/// Examples of Rust types to use this are: structs, tuples, boxes, vecs, and enums.
-fn composite_type_metadata<'ll, 'tcx>(
-    cx: &CodegenCx<'ll, 'tcx>,
-    composite_type: Ty<'tcx>,
-    composite_type_name: &str,
-    composite_type_unique_id: UniqueTypeId<'tcx>,
-    member_descriptions: Vec<MemberDescription<'ll>>,
-    containing_scope: Option<&'ll DIScope>,
-) -> &'ll DICompositeType {
-    let (size, align) = cx.size_and_align_of(composite_type);
-
-    // Create the (empty) struct metadata node ...
-    let composite_type_metadata = create_struct_stub(
-        cx,
-        size,
-        align,
-        composite_type_name,
-        composite_type_unique_id,
-        containing_scope,
-        DIFlags::FlagZero,
-        None,
-    );
-
-    // ... and immediately create and add the member descriptions.
-    set_members_of_composite_type(
-        cx,
-        composite_type_metadata,
-        member_descriptions,
-        None,
-        compute_type_parameters(cx, composite_type),
-    );
-
-    composite_type_metadata
-}
-
-fn set_members_of_composite_type<'ll, 'tcx>(
-    cx: &CodegenCx<'ll, 'tcx>,
-    composite_type_metadata: &'ll DICompositeType,
-    member_descriptions: Vec<MemberDescription<'ll>>,
-    common_members: Option<&Vec<Option<&'ll DIType>>>,
-    type_params: &'ll DIArray,
-) {
-    // In some rare cases LLVM metadata uniquing would lead to an existing type
-    // description being used instead of a new one created in
-    // create_struct_stub. This would cause a hard to trace assertion in
-    // DICompositeType::SetTypeArray(). The following check makes sure that we
-    // get a better error message if this should happen again due to some
-    // regression.
-    {
-        let mut composite_types_completed =
-            debug_context(cx).composite_types_completed.borrow_mut();
-        if !composite_types_completed.insert(composite_type_metadata) {
-            bug!(
-                "debuginfo::set_members_of_composite_type() - \
-                  Already completed forward declaration re-encountered."
-            );
-        }
-    }
-
-    let mut member_metadata: Vec<_> = member_descriptions
-        .into_iter()
-        .map(|desc| Some(desc.into_metadata(cx, composite_type_metadata)))
-        .collect();
-    if let Some(other_members) = common_members {
-        member_metadata.extend(other_members.iter());
-    }
-
-    unsafe {
-        let field_array = create_DIArray(DIB(cx), &member_metadata);
-        llvm::LLVMRustDICompositeTypeReplaceArrays(
-            DIB(cx),
-            composite_type_metadata,
-            Some(field_array),
-            Some(type_params),
-        );
-    }
-}
-
 /// Computes the type parameters for a type, if any, for the given metadata.
-fn compute_type_parameters<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>) -> &'ll DIArray {
+fn build_generic_type_param_di_nodes<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    ty: Ty<'tcx>,
+) -> SmallVec<&'ll DIType> {
     if let ty::Adt(def, substs) = *ty.kind() {
         if substs.types().next().is_some() {
             let generics = cx.tcx.generics_of(def.did());
             let names = get_parameter_names(cx, generics);
-            let template_params: Vec<_> = iter::zip(substs, names)
+            let template_params: SmallVec<_> = iter::zip(substs, names)
                 .filter_map(|(kind, name)| {
                     if let GenericArgKind::Type(ty) = kind.unpack() {
                         let actual_type =
                             cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty);
-                        let actual_type_metadata = type_metadata(cx, actual_type);
+                        let actual_type_metadata = type_di_node(cx, actual_type);
                         let name = name.as_str();
                         Some(unsafe {
-                            Some(llvm::LLVMRustDIBuilderCreateTemplateTypeParameter(
+                            llvm::LLVMRustDIBuilderCreateTemplateTypeParameter(
                                 DIB(cx),
                                 None,
                                 name.as_ptr().cast(),
                                 name.len(),
                                 actual_type_metadata,
-                            ))
+                            )
                         })
                     } else {
                         None
@@ -2337,10 +1248,11 @@ fn compute_type_parameters<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>) -
                 })
                 .collect();
 
-            return create_DIArray(DIB(cx), &template_params);
+            return template_params;
         }
     }
-    return create_DIArray(DIB(cx), &[]);
+
+    return smallvec![];
 
     fn get_parameter_names(cx: &CodegenCx<'_, '_>, generics: &ty::Generics) -> Vec<Symbol> {
         let mut names = generics
@@ -2351,89 +1263,10 @@ fn compute_type_parameters<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, ty: Ty<'tcx>) -
     }
 }
 
-/// A convenience wrapper around `LLVMRustDIBuilderCreateStructType()`. Does not do
-/// any caching, does not add any fields to the struct. This can be done later
-/// with `set_members_of_composite_type()`.
-fn create_struct_stub<'ll, 'tcx>(
-    cx: &CodegenCx<'ll, 'tcx>,
-    size: Size,
-    align: Align,
-    type_name: &str,
-    unique_type_id: UniqueTypeId<'tcx>,
-    containing_scope: Option<&'ll DIScope>,
-    flags: DIFlags,
-    vtable_holder: Option<&'ll DIType>,
-) -> &'ll DICompositeType {
-    let unique_type_id = unique_type_id.generate_unique_id_string(cx.tcx);
-
-    let metadata_stub = unsafe {
-        // `LLVMRustDIBuilderCreateStructType()` wants an empty array. A null
-        // pointer will lead to hard to trace and debug LLVM assertions
-        // later on in `llvm/lib/IR/Value.cpp`.
-        let empty_array = create_DIArray(DIB(cx), &[]);
-
-        llvm::LLVMRustDIBuilderCreateStructType(
-            DIB(cx),
-            containing_scope,
-            type_name.as_ptr().cast(),
-            type_name.len(),
-            unknown_file_metadata(cx),
-            UNKNOWN_LINE_NUMBER,
-            size.bits(),
-            align.bits() as u32,
-            flags,
-            None,
-            empty_array,
-            0,
-            vtable_holder,
-            unique_type_id.as_ptr().cast(),
-            unique_type_id.len(),
-        )
-    };
-
-    metadata_stub
-}
-
-fn create_union_stub<'ll, 'tcx>(
-    cx: &CodegenCx<'ll, 'tcx>,
-    union_type: Ty<'tcx>,
-    union_type_name: &str,
-    unique_type_id: UniqueTypeId<'tcx>,
-    containing_scope: &'ll DIScope,
-) -> &'ll DICompositeType {
-    let (union_size, union_align) = cx.size_and_align_of(union_type);
-    let unique_type_id = unique_type_id.generate_unique_id_string(cx.tcx);
-
-    let metadata_stub = unsafe {
-        // `LLVMRustDIBuilderCreateUnionType()` wants an empty array. A null
-        // pointer will lead to hard to trace and debug LLVM assertions
-        // later on in `llvm/lib/IR/Value.cpp`.
-        let empty_array = create_DIArray(DIB(cx), &[]);
-
-        llvm::LLVMRustDIBuilderCreateUnionType(
-            DIB(cx),
-            Some(containing_scope),
-            union_type_name.as_ptr().cast(),
-            union_type_name.len(),
-            unknown_file_metadata(cx),
-            UNKNOWN_LINE_NUMBER,
-            union_size.bits(),
-            union_align.bits() as u32,
-            DIFlags::FlagZero,
-            Some(empty_array),
-            0, // RuntimeLang
-            unique_type_id.as_ptr().cast(),
-            unique_type_id.len(),
-        )
-    };
-
-    metadata_stub
-}
-
 /// Creates debug information for the given global variable.
 ///
-/// Adds the created metadata nodes directly to the crate's IR.
-pub fn create_global_var_metadata<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId, global: &'ll Value) {
+/// Adds the created debuginfo nodes directly to the crate's IR.
+pub fn build_global_var_di_node<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId, global: &'ll Value) {
     if cx.dbg_cx.is_none() {
         return;
     }
@@ -2459,7 +1292,7 @@ pub fn create_global_var_metadata<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId, g
 
     let is_local_to_unit = is_node_local_to_unit(cx, def_id);
     let variable_type = Instance::mono(cx.tcx, def_id).ty(cx.tcx, ty::ParamEnv::reveal_all());
-    let type_metadata = type_metadata(cx, variable_type);
+    let type_di_node = type_di_node(cx, variable_type);
     let var_name = tcx.item_name(def_id);
     let var_name = var_name.as_str();
     let linkage_name = mangled_name_of_instance(cx, Instance::mono(tcx, def_id)).name;
@@ -2479,7 +1312,7 @@ pub fn create_global_var_metadata<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId, g
             linkage_name.len(),
             file_metadata,
             line_number,
-            type_metadata,
+            type_di_node,
             is_local_to_unit,
             global,
             None,
@@ -2497,7 +1330,7 @@ pub fn create_global_var_metadata<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId, g
 /// the name of the method they implement. This can be implemented in the future once there
 /// is a proper disambiguation scheme for dealing with methods from different traits that have
 /// the same name.
-fn vtable_type_metadata<'ll, 'tcx>(
+fn build_vtable_type_di_node<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
     ty: Ty<'tcx>,
     poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
@@ -2516,8 +1349,8 @@ fn vtable_type_metadata<'ll, 'tcx>(
     // All function pointers are described as opaque pointers. This could be improved in the future
     // by describing them as actual function pointers.
     let void_pointer_ty = tcx.mk_imm_ptr(tcx.types.unit);
-    let void_pointer_type_debuginfo = type_metadata(cx, void_pointer_ty);
-    let usize_debuginfo = type_metadata(cx, tcx.types.usize);
+    let void_pointer_type_di_node = type_di_node(cx, void_pointer_ty);
+    let usize_di_node = type_di_node(cx, tcx.types.usize);
     let (pointer_size, pointer_align) = cx.size_and_align_of(void_pointer_ty);
     // If `usize` is not pointer-sized and -aligned then the size and alignment computations
     // for the vtable as a whole would be wrong. Let's make sure this holds even on weird
@@ -2531,67 +1364,66 @@ fn vtable_type_metadata<'ll, 'tcx>(
 
     // This gets mapped to a DW_AT_containing_type attribute which allows GDB to correlate
     // the vtable to the type it is for.
-    let vtable_holder = type_metadata(cx, ty);
+    let vtable_holder = type_di_node(cx, ty);
 
-    let vtable_type_metadata = create_struct_stub(
+    build_type_with_children(
         cx,
-        size,
-        pointer_align,
-        &vtable_type_name,
-        unique_type_id,
-        NO_SCOPE_METADATA,
-        DIFlags::FlagArtificial,
-        Some(vtable_holder),
-    );
-
-    // Create a field for each entry in the vtable.
-    let fields: Vec<_> = vtable_entries
-        .iter()
-        .enumerate()
-        .filter_map(|(index, vtable_entry)| {
-            let (field_name, field_type) = match vtable_entry {
-                ty::VtblEntry::MetadataDropInPlace => {
-                    ("drop_in_place".to_string(), void_pointer_type_debuginfo)
-                }
-                ty::VtblEntry::Method(_) => {
-                    // Note: This code does not try to give a proper name to each method
-                    //       because there might be multiple methods with the same name
-                    //       (coming from different traits).
-                    (format!("__method{}", index), void_pointer_type_debuginfo)
-                }
-                ty::VtblEntry::TraitVPtr(_) => {
-                    // Note: In the future we could try to set the type of this pointer
-                    //       to the type that we generate for the corresponding vtable.
-                    (format!("__super_trait_ptr{}", index), void_pointer_type_debuginfo)
-                }
-                ty::VtblEntry::MetadataAlign => ("align".to_string(), usize_debuginfo),
-                ty::VtblEntry::MetadataSize => ("size".to_string(), usize_debuginfo),
-                ty::VtblEntry::Vacant => return None,
-            };
+        type_map::stub(
+            cx,
+            Stub::VtableTy { vtable_holder },
+            unique_type_id,
+            &vtable_type_name,
+            (size, pointer_align),
+            NO_SCOPE_METADATA,
+            DIFlags::FlagArtificial,
+        ),
+        |cx, vtable_type_di_node| {
+            vtable_entries
+                .iter()
+                .enumerate()
+                .filter_map(|(index, vtable_entry)| {
+                    let (field_name, field_type_di_node) = match vtable_entry {
+                        ty::VtblEntry::MetadataDropInPlace => {
+                            ("drop_in_place".to_string(), void_pointer_type_di_node)
+                        }
+                        ty::VtblEntry::Method(_) => {
+                            // Note: This code does not try to give a proper name to each method
+                            //       because their might be multiple methods with the same name
+                            //       (coming from different traits).
+                            (format!("__method{}", index), void_pointer_type_di_node)
+                        }
+                        ty::VtblEntry::TraitVPtr(_) => {
+                            (format!("__super_trait_ptr{}", index), void_pointer_type_di_node)
+                        }
+                        ty::VtblEntry::MetadataAlign => ("align".to_string(), usize_di_node),
+                        ty::VtblEntry::MetadataSize => ("size".to_string(), usize_di_node),
+                        ty::VtblEntry::Vacant => return None,
+                    };
 
-            Some(MemberDescription {
-                name: field_name,
-                type_metadata: field_type,
-                offset: pointer_size * index as u64,
-                size: pointer_size,
-                align: pointer_align,
-                flags: DIFlags::FlagZero,
-                discriminant: None,
-                source_info: None,
-            })
-        })
-        .collect();
+                    let field_offset = pointer_size * index as u64;
 
-    let type_params = create_DIArray(DIB(cx), &[]);
-    set_members_of_composite_type(cx, vtable_type_metadata, fields, None, type_params);
-    vtable_type_metadata
+                    Some(build_field_di_node(
+                        cx,
+                        vtable_type_di_node,
+                        &field_name,
+                        (pointer_size, pointer_align),
+                        field_offset,
+                        DIFlags::FlagZero,
+                        field_type_di_node,
+                    ))
+                })
+                .collect()
+        },
+        NO_GENERICS,
+    )
+    .di_node
 }
 
 /// Creates debug information for the given vtable, which is for the
 /// given type.
 ///
 /// Adds the created metadata nodes directly to the crate's IR.
-pub fn create_vtable_metadata<'ll, 'tcx>(
+pub fn create_vtable_di_node<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
     ty: Ty<'tcx>,
     poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
@@ -2608,7 +1440,7 @@ pub fn create_vtable_metadata<'ll, 'tcx>(
 
     let vtable_name =
         compute_debuginfo_vtable_name(cx.tcx, ty, poly_trait_ref, VTableNameKind::GlobalVariable);
-    let vtable_type = vtable_type_metadata(cx, ty, poly_trait_ref);
+    let vtable_type_di_node = build_vtable_type_di_node(cx, ty, poly_trait_ref);
     let linkage_name = "";
 
     unsafe {
@@ -2621,7 +1453,7 @@ pub fn create_vtable_metadata<'ll, 'tcx>(
             linkage_name.len(),
             unknown_file_metadata(cx),
             UNKNOWN_LINE_NUMBER,
-            vtable_type,
+            vtable_type_di_node,
             true,
             vtable,
             None,
@@ -2639,3 +1471,14 @@ pub fn extend_scope_to_file<'ll>(
     let file_metadata = file_metadata(cx, file);
     unsafe { llvm::LLVMRustDIBuilderCreateLexicalBlockFile(DIB(cx), scope_metadata, file_metadata) }
 }
+
+pub fn tuple_field_name(field_index: usize) -> Cow<'static, str> {
+    const TUPLE_FIELD_NAMES: [&'static str; 16] = [
+        "__0", "__1", "__2", "__3", "__4", "__5", "__6", "__7", "__8", "__9", "__10", "__11",
+        "__12", "__13", "__14", "__15",
+    ];
+    TUPLE_FIELD_NAMES
+        .get(field_index)
+        .map(|s| Cow::from(*s))
+        .unwrap_or_else(|| Cow::from(format!("__{}", field_index)))
+}
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
new file mode 100644
index 00000000000..e9772cd78d7
--- /dev/null
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
@@ -0,0 +1,515 @@
+use std::borrow::Cow;
+
+use libc::c_uint;
+use rustc_codegen_ssa::debuginfo::{
+    type_names::compute_debuginfo_type_name, wants_c_like_enum_debuginfo,
+};
+use rustc_middle::{
+    bug,
+    ty::{
+        self,
+        layout::{LayoutOf, TyAndLayout},
+        util::Discr,
+        AdtDef, GeneratorSubsts,
+    },
+};
+use rustc_target::abi::{Size, TagEncoding, VariantIdx, Variants};
+use smallvec::smallvec;
+
+use crate::{
+    common::CodegenCx,
+    debuginfo::{
+        metadata::{
+            build_field_di_node, closure_saved_names_of_captured_variables,
+            enums::tag_base_type,
+            file_metadata, generator_layout_and_saved_local_names, size_and_align_of,
+            type_map::{self, UniqueTypeId},
+            unknown_file_metadata, DINodeCreationResult, SmallVec, NO_GENERICS, NO_SCOPE_METADATA,
+            UNKNOWN_LINE_NUMBER,
+        },
+        utils::DIB,
+    },
+    llvm::{
+        self,
+        debuginfo::{DIFile, DIFlags, DIType},
+    },
+};
+
+/// In CPP-like mode, we generate a union of structs for each variant and an
+/// explicit discriminant field roughly equivalent to the following C/C++ code:
+///
+/// ```c
+/// union enum$<{fully-qualified-name}> {
+///   struct {variant 0 name} {
+///     <variant 0 fields>
+///   } variant0;
+///   <other variant structs>
+///   {name} discriminant;
+/// }
+/// ```
+///
+/// As you can see, the type name is wrapped `enum$`. This way we can have a
+/// single NatVis rule for handling all enums.
+///
+/// At the LLVM IR level this looks like
+///
+/// ```txt
+///       DW_TAG_union_type              (top-level type for enum)
+///         DW_TAG_member                    (member for variant 1)
+///         DW_TAG_member                    (member for variant 2)
+///         DW_TAG_member                    (member for variant 3)
+///         DW_TAG_structure_type            (type of variant 1)
+///         DW_TAG_structure_type            (type of variant 2)
+///         DW_TAG_structure_type            (type of variant 3)
+///         DW_TAG_enumeration_type          (type of tag)
+/// ```
+///
+/// The above encoding applies for enums with a direct tag. For niche-tag we have to do things
+/// differently in order to allow a NatVis visualizer to extract all the information needed:
+/// We generate a union of two fields, one for the dataful variant
+/// and one that just points to the discriminant (which is some field within the dataful variant).
+/// We also create a DW_TAG_enumeration_type DIE that contains tag values for the non-dataful
+/// variants and make the discriminant field that type. We then use NatVis to render the enum type
+/// correctly in Windbg/VS. This will generate debuginfo roughly equivalent to the following C:
+///
+/// ```c
+/// union enum$<{name}, {min niche}, {max niche}, {dataful variant name}> {
+///   struct <dataful variant name> {
+///     <fields in dataful variant>
+///   } dataful_variant;
+///   enum Discriminant$ {
+///     <non-dataful variants>
+///   } discriminant;
+/// }
+/// ```
+///
+/// The NatVis in `intrinsic.natvis` matches on the type name `enum$<*, *, *, *>`
+/// and evaluates `this.discriminant`. If the value is between the min niche and max
+/// niche, then the enum is in the dataful variant and `this.dataful_variant` is
+/// rendered. Otherwise, the enum is in one of the non-dataful variants. In that
+/// case, we just need to render the name of the `this.discriminant` enum.
+pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    unique_type_id: UniqueTypeId<'tcx>,
+) -> DINodeCreationResult<'ll> {
+    let enum_type = unique_type_id.expect_ty();
+    let &ty::Adt(enum_adt_def, _) = enum_type.kind() else {
+        bug!("build_enum_type_di_node() called with non-enum type: `{:?}`", enum_type)
+        };
+
+    let enum_type_and_layout = cx.layout_of(enum_type);
+    let enum_type_name = compute_debuginfo_type_name(cx.tcx, enum_type, false);
+
+    debug_assert!(!wants_c_like_enum_debuginfo(enum_type_and_layout));
+
+    type_map::build_type_with_children(
+        cx,
+        type_map::stub(
+            cx,
+            type_map::Stub::Union,
+            unique_type_id,
+            &enum_type_name,
+            cx.size_and_align_of(enum_type),
+            NO_SCOPE_METADATA,
+            DIFlags::FlagZero,
+        ),
+        |cx, enum_type_di_node| {
+            match enum_type_and_layout.variants {
+                Variants::Single { index: variant_index } => {
+                    if enum_adt_def.variants().is_empty() {
+                        // Uninhabited enums have Variants::Single. We don't generate
+                        // any members for them.
+                        return smallvec![];
+                    }
+
+                    build_single_variant_union_fields(
+                        cx,
+                        enum_adt_def,
+                        enum_type_and_layout,
+                        enum_type_di_node,
+                        variant_index,
+                    )
+                }
+                Variants::Multiple {
+                    tag_encoding: TagEncoding::Direct,
+                    ref variants,
+                    tag_field,
+                    ..
+                } => build_union_fields_for_direct_tag_enum(
+                    cx,
+                    enum_adt_def,
+                    enum_type_and_layout,
+                    enum_type_di_node,
+                    &mut variants.indices(),
+                    tag_field,
+                ),
+                Variants::Multiple {
+                    tag_encoding: TagEncoding::Niche { dataful_variant, .. },
+                    ref variants,
+                    tag_field,
+                    ..
+                } => build_union_fields_for_niche_tag_enum(
+                    cx,
+                    enum_adt_def,
+                    enum_type_and_layout,
+                    enum_type_di_node,
+                    dataful_variant,
+                    &mut variants.indices(),
+                    tag_field,
+                ),
+            }
+        },
+        NO_GENERICS,
+    )
+}
+
+/// A generator debuginfo node looks the same as a that of an enum type.
+///
+/// See [build_enum_type_di_node] for more information.
+pub(super) fn build_generator_di_node<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    unique_type_id: UniqueTypeId<'tcx>,
+) -> DINodeCreationResult<'ll> {
+    let generator_type = unique_type_id.expect_ty();
+    let generator_type_and_layout = cx.layout_of(generator_type);
+    let generator_type_name = compute_debuginfo_type_name(cx.tcx, generator_type, false);
+
+    debug_assert!(!wants_c_like_enum_debuginfo(generator_type_and_layout));
+
+    type_map::build_type_with_children(
+        cx,
+        type_map::stub(
+            cx,
+            type_map::Stub::Union,
+            unique_type_id,
+            &generator_type_name,
+            size_and_align_of(generator_type_and_layout),
+            NO_SCOPE_METADATA,
+            DIFlags::FlagZero,
+        ),
+        |cx, generator_type_di_node| match generator_type_and_layout.variants {
+            Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => {
+                build_union_fields_for_direct_tag_generator(
+                    cx,
+                    generator_type_and_layout,
+                    generator_type_di_node,
+                )
+            }
+            Variants::Single { .. }
+            | Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, .. } => {
+                bug!(
+                    "Encountered generator with non-direct-tag layout: {:?}",
+                    generator_type_and_layout
+                )
+            }
+        },
+        NO_GENERICS,
+    )
+}
+
+fn build_single_variant_union_fields<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    enum_adt_def: AdtDef<'tcx>,
+    enum_type_and_layout: TyAndLayout<'tcx>,
+    enum_type_di_node: &'ll DIType,
+    variant_index: VariantIdx,
+) -> SmallVec<&'ll DIType> {
+    let variant_layout = enum_type_and_layout.for_variant(cx, variant_index);
+    let variant_struct_type_di_node = super::build_enum_variant_struct_type_di_node(
+        cx,
+        enum_type_and_layout.ty,
+        enum_type_di_node,
+        variant_index,
+        enum_adt_def.variant(variant_index),
+        variant_layout,
+    );
+
+    // NOTE: The field name of the union is the same as the variant name, not "variant0".
+    let variant_name = enum_adt_def.variant(variant_index).name.as_str();
+
+    smallvec![build_field_di_node(
+        cx,
+        enum_type_di_node,
+        variant_name,
+        // NOTE: We use the size and align of the entire type, not from variant_layout
+        //       since the later is sometimes smaller (if it has fewer fields).
+        size_and_align_of(enum_type_and_layout),
+        Size::ZERO,
+        DIFlags::FlagZero,
+        variant_struct_type_di_node,
+    )]
+}
+
+fn build_union_fields_for_direct_tag_enum<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    enum_adt_def: AdtDef<'tcx>,
+    enum_type_and_layout: TyAndLayout<'tcx>,
+    enum_type_di_node: &'ll DIType,
+    variant_indices: &mut dyn Iterator<Item = VariantIdx>,
+    tag_field: usize,
+) -> SmallVec<&'ll DIType> {
+    let variant_field_infos: SmallVec<VariantFieldInfo<'ll>> = variant_indices
+        .map(|variant_index| {
+            let variant_layout = enum_type_and_layout.for_variant(cx, variant_index);
+
+            VariantFieldInfo {
+                variant_index,
+                variant_struct_type_di_node: super::build_enum_variant_struct_type_di_node(
+                    cx,
+                    enum_type_and_layout.ty,
+                    enum_type_di_node,
+                    variant_index,
+                    enum_adt_def.variant(variant_index),
+                    variant_layout,
+                ),
+                source_info: None,
+            }
+        })
+        .collect();
+
+    let discr_type_name = cx.tcx.item_name(enum_adt_def.did());
+    let tag_base_type = super::tag_base_type(cx, enum_type_and_layout);
+    let discr_type_di_node = super::build_enumeration_type_di_node(
+        cx,
+        discr_type_name.as_str(),
+        tag_base_type,
+        &mut enum_adt_def.discriminants(cx.tcx).map(|(variant_index, discr)| {
+            (discr, Cow::from(enum_adt_def.variant(variant_index).name.as_str()))
+        }),
+        enum_type_di_node,
+    );
+
+    build_union_fields_for_direct_tag_enum_or_generator(
+        cx,
+        enum_type_and_layout,
+        enum_type_di_node,
+        &variant_field_infos,
+        discr_type_di_node,
+        tag_field,
+    )
+}
+
+fn build_union_fields_for_niche_tag_enum<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    enum_adt_def: AdtDef<'tcx>,
+    enum_type_and_layout: TyAndLayout<'tcx>,
+    enum_type_di_node: &'ll DIType,
+    dataful_variant_index: VariantIdx,
+    variant_indices: &mut dyn Iterator<Item = VariantIdx>,
+    tag_field: usize,
+) -> SmallVec<&'ll DIType> {
+    let dataful_variant_struct_type_di_node = super::build_enum_variant_struct_type_di_node(
+        cx,
+        enum_type_and_layout.ty,
+        enum_type_di_node,
+        dataful_variant_index,
+        &enum_adt_def.variant(dataful_variant_index),
+        enum_type_and_layout.for_variant(cx, dataful_variant_index),
+    );
+
+    let tag_base_type = super::tag_base_type(cx, enum_type_and_layout);
+    // Create an DW_TAG_enumerator for each variant except the dataful one.
+    let discr_type_di_node = super::build_enumeration_type_di_node(
+        cx,
+        "Discriminant$",
+        tag_base_type,
+        &mut variant_indices.filter_map(|variant_index| {
+            if let Some(discr_val) =
+                super::compute_discriminant_value(cx, enum_type_and_layout, variant_index)
+            {
+                let discr = Discr { val: discr_val as u128, ty: tag_base_type };
+                let variant_name = Cow::from(enum_adt_def.variant(variant_index).name.as_str());
+                Some((discr, variant_name))
+            } else {
+                debug_assert_eq!(variant_index, dataful_variant_index);
+                None
+            }
+        }),
+        enum_type_di_node,
+    );
+
+    smallvec![
+        build_field_di_node(
+            cx,
+            enum_type_di_node,
+            "dataful_variant",
+            size_and_align_of(enum_type_and_layout),
+            Size::ZERO,
+            DIFlags::FlagZero,
+            dataful_variant_struct_type_di_node,
+        ),
+        build_field_di_node(
+            cx,
+            enum_type_di_node,
+            "discriminant",
+            cx.size_and_align_of(tag_base_type),
+            enum_type_and_layout.fields.offset(tag_field),
+            DIFlags::FlagZero,
+            discr_type_di_node,
+        ),
+    ]
+}
+
+fn build_union_fields_for_direct_tag_generator<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    generator_type_and_layout: TyAndLayout<'tcx>,
+    generator_type_di_node: &'ll DIType,
+) -> SmallVec<&'ll DIType> {
+    let Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } = generator_type_and_layout.variants else {
+        bug!("This function only supports layouts with direcly encoded tags.")
+    };
+
+    let (generator_def_id, generator_substs) = match generator_type_and_layout.ty.kind() {
+        &ty::Generator(def_id, substs, _) => (def_id, substs.as_generator()),
+        _ => unreachable!(),
+    };
+
+    let (generator_layout, state_specific_upvar_names) =
+        generator_layout_and_saved_local_names(cx.tcx, generator_def_id);
+
+    let common_upvar_names = closure_saved_names_of_captured_variables(cx.tcx, generator_def_id);
+    let variant_range = generator_substs.variant_range(generator_def_id, cx.tcx);
+
+    // Build the type node for each field.
+    let variant_field_infos: SmallVec<VariantFieldInfo<'ll>> = variant_range
+        .clone()
+        .map(|variant_index| {
+            let variant_struct_type_di_node = super::build_generator_variant_struct_type_di_node(
+                cx,
+                variant_index,
+                generator_type_and_layout,
+                generator_type_di_node,
+                generator_layout,
+                &state_specific_upvar_names,
+                &common_upvar_names,
+            );
+
+            let span = generator_layout.variant_source_info[variant_index].span;
+            let source_info = if !span.is_dummy() {
+                let loc = cx.lookup_debug_loc(span.lo());
+                Some((file_metadata(cx, &loc.file), loc.line as c_uint))
+            } else {
+                None
+            };
+
+            VariantFieldInfo { variant_index, variant_struct_type_di_node, source_info }
+        })
+        .collect();
+
+    let tag_base_type = tag_base_type(cx, generator_type_and_layout);
+    let discr_type_name = "Discriminant$";
+    let discr_type_di_node = super::build_enumeration_type_di_node(
+        cx,
+        discr_type_name,
+        tag_base_type,
+        &mut generator_substs
+            .discriminants(generator_def_id, cx.tcx)
+            .map(|(variant_index, discr)| (discr, GeneratorSubsts::variant_name(variant_index))),
+        generator_type_di_node,
+    );
+
+    build_union_fields_for_direct_tag_enum_or_generator(
+        cx,
+        generator_type_and_layout,
+        generator_type_di_node,
+        &variant_field_infos[..],
+        discr_type_di_node,
+        tag_field,
+    )
+}
+
+/// This is a helper function shared between enums and generators that makes sure fields have the
+/// expect names.
+fn build_union_fields_for_direct_tag_enum_or_generator<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    enum_type_and_layout: TyAndLayout<'tcx>,
+    enum_type_di_node: &'ll DIType,
+    variant_field_infos: &[VariantFieldInfo<'ll>],
+    discr_type_di_node: &'ll DIType,
+    tag_field: usize,
+) -> SmallVec<&'ll DIType> {
+    let mut unions_fields = SmallVec::with_capacity(variant_field_infos.len() + 1);
+
+    // We create a field in the union for each variant ...
+    unions_fields.extend(variant_field_infos.into_iter().map(|variant_member_info| {
+        let (file_di_node, line_number) = variant_member_info
+            .source_info
+            .unwrap_or_else(|| (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER));
+
+        let field_name = variant_union_field_name(variant_member_info.variant_index);
+        let (size, align) = size_and_align_of(enum_type_and_layout);
+
+        // We use LLVMRustDIBuilderCreateMemberType() member type directly because
+        // the build_field_di_node() function does not support specifying a source location,
+        // which is something that we don't do anywhere else.
+        unsafe {
+            llvm::LLVMRustDIBuilderCreateMemberType(
+                DIB(cx),
+                enum_type_di_node,
+                field_name.as_ptr().cast(),
+                field_name.len(),
+                file_di_node,
+                line_number,
+                // NOTE: We use the size and align of the entire type, not from variant_layout
+                //       since the later is sometimes smaller (if it has fewer fields).
+                size.bits(),
+                align.bits() as u32,
+                // Union fields are always at offset zero
+                Size::ZERO.bits(),
+                DIFlags::FlagZero,
+                variant_member_info.variant_struct_type_di_node,
+            )
+        }
+    }));
+
+    debug_assert_eq!(
+        cx.size_and_align_of(enum_type_and_layout.field(cx, tag_field).ty),
+        cx.size_and_align_of(super::tag_base_type(cx, enum_type_and_layout))
+    );
+
+    // ... and a field for the discriminant.
+    unions_fields.push(build_field_di_node(
+        cx,
+        enum_type_di_node,
+        "discriminant",
+        cx.size_and_align_of(enum_type_and_layout.field(cx, tag_field).ty),
+        enum_type_and_layout.fields.offset(tag_field),
+        DIFlags::FlagZero,
+        discr_type_di_node,
+    ));
+
+    unions_fields
+}
+
+/// Information about a single field of the top-level DW_TAG_union_type.
+struct VariantFieldInfo<'ll> {
+    variant_index: VariantIdx,
+    variant_struct_type_di_node: &'ll DIType,
+    source_info: Option<(&'ll DIFile, c_uint)>,
+}
+
+fn variant_union_field_name(variant_index: VariantIdx) -> Cow<'static, str> {
+    const PRE_ALLOCATED: [&str; 16] = [
+        "variant0",
+        "variant1",
+        "variant2",
+        "variant3",
+        "variant4",
+        "variant5",
+        "variant6",
+        "variant7",
+        "variant8",
+        "variant9",
+        "variant10",
+        "variant11",
+        "variant12",
+        "variant13",
+        "variant14",
+        "variant15",
+    ];
+
+    PRE_ALLOCATED
+        .get(variant_index.as_usize())
+        .map(|&s| Cow::from(s))
+        .unwrap_or_else(|| format!("variant{}", variant_index.as_usize()).into())
+}
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs
new file mode 100644
index 00000000000..e41f11b34c8
--- /dev/null
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/mod.rs
@@ -0,0 +1,428 @@
+use rustc_codegen_ssa::debuginfo::{
+    type_names::{compute_debuginfo_type_name, cpp_like_debuginfo},
+    wants_c_like_enum_debuginfo,
+};
+use rustc_hir::def::CtorKind;
+use rustc_index::vec::IndexVec;
+use rustc_middle::{
+    bug,
+    mir::{Field, GeneratorLayout, GeneratorSavedLocal},
+    ty::{
+        self,
+        layout::{IntegerExt, LayoutOf, PrimitiveExt, TyAndLayout},
+        util::Discr,
+        AdtDef, GeneratorSubsts, Ty, VariantDef,
+    },
+};
+use rustc_span::Symbol;
+use rustc_target::abi::{HasDataLayout, Integer, Primitive, TagEncoding, VariantIdx, Variants};
+use std::borrow::Cow;
+
+use crate::{
+    common::CodegenCx,
+    debuginfo::{
+        metadata::{
+            build_field_di_node, build_generic_type_param_di_nodes, type_di_node,
+            type_map::{self, Stub},
+            unknown_file_metadata, UNKNOWN_LINE_NUMBER,
+        },
+        utils::{create_DIArray, get_namespace_for_item, DIB},
+    },
+    llvm::{
+        self,
+        debuginfo::{DIFlags, DIType},
+    },
+};
+
+use super::{
+    size_and_align_of,
+    type_map::{DINodeCreationResult, UniqueTypeId},
+    SmallVec,
+};
+
+mod cpp_like;
+mod native;
+
+pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    unique_type_id: UniqueTypeId<'tcx>,
+) -> DINodeCreationResult<'ll> {
+    let enum_type = unique_type_id.expect_ty();
+    let &ty::Adt(enum_adt_def, _) = enum_type.kind() else {
+        bug!("build_enum_type_di_node() called with non-enum type: `{:?}`", enum_type)
+        };
+
+    let enum_type_and_layout = cx.layout_of(enum_type);
+
+    if wants_c_like_enum_debuginfo(enum_type_and_layout) {
+        return build_c_style_enum_di_node(cx, enum_adt_def, enum_type_and_layout);
+    }
+
+    if cpp_like_debuginfo(cx.tcx) {
+        cpp_like::build_enum_type_di_node(cx, unique_type_id)
+    } else {
+        native::build_enum_type_di_node(cx, unique_type_id)
+    }
+}
+
+pub(super) fn build_generator_di_node<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    unique_type_id: UniqueTypeId<'tcx>,
+) -> DINodeCreationResult<'ll> {
+    if cpp_like_debuginfo(cx.tcx) {
+        cpp_like::build_generator_di_node(cx, unique_type_id)
+    } else {
+        native::build_generator_di_node(cx, unique_type_id)
+    }
+}
+
+/// Build the debuginfo node for a C-style enum, i.e. an enum the variants of which have no fields.
+///
+/// The resulting debuginfo will be a DW_TAG_enumeration_type.
+fn build_c_style_enum_di_node<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    enum_adt_def: AdtDef<'tcx>,
+    enum_type_and_layout: TyAndLayout<'tcx>,
+) -> DINodeCreationResult<'ll> {
+    let containing_scope = get_namespace_for_item(cx, enum_adt_def.did());
+    DINodeCreationResult {
+        di_node: build_enumeration_type_di_node(
+            cx,
+            &compute_debuginfo_type_name(cx.tcx, enum_type_and_layout.ty, false),
+            tag_base_type(cx, enum_type_and_layout),
+            &mut enum_adt_def.discriminants(cx.tcx).map(|(variant_index, discr)| {
+                (discr, Cow::from(enum_adt_def.variant(variant_index).name.as_str()))
+            }),
+            containing_scope,
+        ),
+        already_stored_in_typemap: false,
+    }
+}
+
+/// Extract the type with which we want to describe the tag of the given enum or generator.
+fn tag_base_type<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    enum_type_and_layout: TyAndLayout<'tcx>,
+) -> Ty<'tcx> {
+    debug_assert!(match enum_type_and_layout.ty.kind() {
+        ty::Generator(..) => true,
+        ty::Adt(adt_def, _) => adt_def.is_enum(),
+        _ => false,
+    });
+
+    // FIXME(mw): Why are niche and regular tags treated differently? Because we want to preserve
+    //            the sign?
+    match enum_type_and_layout.layout.variants() {
+        // A single-variant enum has no discriminant.
+        Variants::Single { .. } => {
+            bug!("tag_base_type() called for enum without tag: {:?}", enum_type_and_layout)
+        }
+
+        Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, tag, .. } => {
+            match tag.value {
+                Primitive::Int(t, _) => t,
+                Primitive::F32 => Integer::I32,
+                Primitive::F64 => Integer::I64,
+                Primitive::Pointer => {
+                    // If the niche is the NULL value of a reference, then `discr_enum_ty` will be
+                    // a RawPtr. CodeView doesn't know what to do with enums whose base type is a
+                    // pointer so we fix this up to just be `usize`.
+                    cx.data_layout().ptr_sized_integer()
+                }
+            }
+            .to_ty(cx.tcx, false)
+        }
+
+        Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, .. } => {
+            tag.value.to_ty(cx.tcx)
+        }
+    }
+}
+
+/// This is a helper function. FIXME: elaborate docs.
+fn build_enumeration_type_di_node<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    type_name: &str,
+    base_type: Ty<'tcx>,
+    variants: &mut dyn Iterator<Item = (Discr<'tcx>, Cow<'tcx, str>)>,
+    containing_scope: &'ll DIType,
+) -> &'ll DIType {
+    let enumerator_di_nodes: SmallVec<Option<&'ll DIType>> = variants
+        .map(|(discr, variant_name)| {
+            let is_unsigned = match discr.ty.kind() {
+                ty::Int(_) => false,
+                ty::Uint(_) => true,
+                _ => bug!("build_enumeration_type_di_node() called with non-integer tag type."),
+            };
+            unsafe {
+                Some(llvm::LLVMRustDIBuilderCreateEnumerator(
+                    DIB(cx),
+                    variant_name.as_ptr().cast(),
+                    variant_name.len(),
+                    // FIXME: what if enumeration has i128 discriminant?
+                    discr.val as i64,
+                    is_unsigned,
+                ))
+            }
+        })
+        .collect();
+
+    let (size, align) = cx.size_and_align_of(base_type);
+
+    unsafe {
+        llvm::LLVMRustDIBuilderCreateEnumerationType(
+            DIB(cx),
+            containing_scope,
+            type_name.as_ptr().cast(),
+            type_name.len(),
+            unknown_file_metadata(cx),
+            UNKNOWN_LINE_NUMBER,
+            size.bits(),
+            align.bits() as u32,
+            create_DIArray(DIB(cx), &enumerator_di_nodes[..]),
+            type_di_node(cx, base_type),
+            true,
+        )
+    }
+}
+
+/// Build the debuginfo node for the struct type describing a single variant of an enum.
+///
+/// ```txt
+///       DW_TAG_structure_type              (top-level type for enum)
+///         DW_TAG_variant_part              (variant part)
+///           DW_AT_discr                    (reference to discriminant DW_TAG_member)
+///           DW_TAG_member                  (discriminant member)
+///           DW_TAG_variant                 (variant 1)
+///           DW_TAG_variant                 (variant 2)
+///           DW_TAG_variant                 (variant 3)
+///  --->   DW_TAG_structure_type            (type of variant 1)
+///  --->   DW_TAG_structure_type            (type of variant 2)
+///  --->   DW_TAG_structure_type            (type of variant 3)
+/// ```
+///
+/// In CPP-like mode, we have the exact same descriptions for each variant too:
+///
+/// ```txt
+///       DW_TAG_union_type              (top-level type for enum)
+///         DW_TAG_member                    (member for variant 1)
+///         DW_TAG_member                    (member for variant 2)
+///         DW_TAG_member                    (member for variant 3)
+///  --->   DW_TAG_structure_type            (type of variant 1)
+///  --->   DW_TAG_structure_type            (type of variant 2)
+///  --->   DW_TAG_structure_type            (type of variant 3)
+///         DW_TAG_enumeration_type          (type of tag)
+/// ```
+///
+/// The node looks like:
+///
+/// ```txt
+/// DW_TAG_structure_type
+///   DW_AT_name                  <name-of-variant>
+///   DW_AT_byte_size             0x00000010
+///   DW_AT_alignment             0x00000008
+///   DW_TAG_member
+///     DW_AT_name                  <name-of-field-0>
+///     DW_AT_type                  <0x0000018e>
+///     DW_AT_alignment             0x00000004
+///     DW_AT_data_member_location  4
+///   DW_TAG_member
+///     DW_AT_name                  <name-of-field-1>
+///     DW_AT_type                  <0x00000195>
+///     DW_AT_alignment             0x00000008
+///     DW_AT_data_member_location  8
+///   ...
+/// ```
+///
+/// The type of a variant is always a struct type with the name of the variant
+/// and a DW_TAG_member for each field (but not the discriminant).
+fn build_enum_variant_struct_type_di_node<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    enum_type: Ty<'tcx>,
+    enum_type_di_node: &'ll DIType,
+    variant_index: VariantIdx,
+    variant_def: &VariantDef,
+    variant_layout: TyAndLayout<'tcx>,
+) -> &'ll DIType {
+    debug_assert_eq!(variant_layout.ty, enum_type);
+
+    type_map::build_type_with_children(
+        cx,
+        type_map::stub(
+            cx,
+            Stub::Struct,
+            UniqueTypeId::for_enum_variant_struct_type(cx.tcx, enum_type, variant_index),
+            variant_def.name.as_str(),
+            // NOTE: We use size and align of enum_type, not from variant_layout:
+            cx.size_and_align_of(enum_type),
+            Some(enum_type_di_node),
+            DIFlags::FlagZero,
+        ),
+        |cx, struct_type_di_node| {
+            (0..variant_layout.fields.count())
+                .map(|field_index| {
+                    let field_name = if variant_def.ctor_kind != CtorKind::Fn {
+                        // Fields have names
+                        Cow::from(variant_def.fields[field_index].name.as_str())
+                    } else {
+                        // Tuple-like
+                        super::tuple_field_name(field_index)
+                    };
+
+                    let field_layout = variant_layout.field(cx, field_index);
+
+                    build_field_di_node(
+                        cx,
+                        struct_type_di_node,
+                        &field_name,
+                        (field_layout.size, field_layout.align.abi),
+                        variant_layout.fields.offset(field_index),
+                        DIFlags::FlagZero,
+                        type_di_node(cx, field_layout.ty),
+                    )
+                })
+                .collect()
+        },
+        |cx| build_generic_type_param_di_nodes(cx, enum_type),
+    )
+    .di_node
+}
+
+/// Build the struct type for describing a single generator state.
+/// See [build_generator_variant_struct_type_di_node].
+///
+/// ```txt
+///
+///       DW_TAG_structure_type              (top-level type for enum)
+///         DW_TAG_variant_part              (variant part)
+///           DW_AT_discr                    (reference to discriminant DW_TAG_member)
+///           DW_TAG_member                  (discriminant member)
+///           DW_TAG_variant                 (variant 1)
+///           DW_TAG_variant                 (variant 2)
+///           DW_TAG_variant                 (variant 3)
+///  --->   DW_TAG_structure_type            (type of variant 1)
+///  --->   DW_TAG_structure_type            (type of variant 2)
+///  --->   DW_TAG_structure_type            (type of variant 3)
+///
+/// ```
+pub fn build_generator_variant_struct_type_di_node<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    variant_index: VariantIdx,
+    generator_type_and_layout: TyAndLayout<'tcx>,
+    generator_type_di_node: &'ll DIType,
+    generator_layout: &GeneratorLayout<'tcx>,
+    state_specific_upvar_names: &IndexVec<GeneratorSavedLocal, Option<Symbol>>,
+    common_upvar_names: &[String],
+) -> &'ll DIType {
+    let variant_name = GeneratorSubsts::variant_name(variant_index);
+    let unique_type_id = UniqueTypeId::for_enum_variant_struct_type(
+        cx.tcx,
+        generator_type_and_layout.ty,
+        variant_index,
+    );
+
+    let variant_layout = generator_type_and_layout.for_variant(cx, variant_index);
+
+    let generator_substs = match generator_type_and_layout.ty.kind() {
+        ty::Generator(_, substs, _) => substs.as_generator(),
+        _ => unreachable!(),
+    };
+
+    type_map::build_type_with_children(
+        cx,
+        type_map::stub(
+            cx,
+            Stub::Struct,
+            unique_type_id,
+            &variant_name,
+            size_and_align_of(generator_type_and_layout),
+            Some(generator_type_di_node),
+            DIFlags::FlagZero,
+        ),
+        |cx, variant_struct_type_di_node| {
+            // Fields that just belong to this variant/state
+            let state_specific_fields: SmallVec<_> = (0..variant_layout.fields.count())
+                .map(|field_index| {
+                    let generator_saved_local = generator_layout.variant_fields[variant_index]
+                        [Field::from_usize(field_index)];
+                    let field_name_maybe = state_specific_upvar_names[generator_saved_local];
+                    let field_name = field_name_maybe
+                        .as_ref()
+                        .map(|s| Cow::from(s.as_str()))
+                        .unwrap_or_else(|| super::tuple_field_name(field_index));
+
+                    let field_type = variant_layout.field(cx, field_index).ty;
+
+                    build_field_di_node(
+                        cx,
+                        variant_struct_type_di_node,
+                        &field_name,
+                        cx.size_and_align_of(field_type),
+                        variant_layout.fields.offset(field_index),
+                        DIFlags::FlagZero,
+                        type_di_node(cx, field_type),
+                    )
+                })
+                .collect();
+
+            // Fields that are common to all states
+            let common_fields: SmallVec<_> = generator_substs
+                .prefix_tys()
+                .enumerate()
+                .map(|(index, upvar_ty)| {
+                    build_field_di_node(
+                        cx,
+                        variant_struct_type_di_node,
+                        &common_upvar_names[index],
+                        cx.size_and_align_of(upvar_ty),
+                        generator_type_and_layout.fields.offset(index),
+                        DIFlags::FlagZero,
+                        type_di_node(cx, upvar_ty),
+                    )
+                })
+                .collect();
+
+            state_specific_fields.into_iter().chain(common_fields.into_iter()).collect()
+        },
+        |cx| build_generic_type_param_di_nodes(cx, generator_type_and_layout.ty),
+    )
+    .di_node
+}
+
+/// Returns the discriminant value corresponding to the variant index.
+///
+/// Will return `None` if there is less than two variants (because then the enum won't have)
+/// a tag, and if this is the dataful variant of a niche-layout enum (because then there is no
+/// single discriminant value).
+fn compute_discriminant_value<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    enum_type_and_layout: TyAndLayout<'tcx>,
+    variant_index: VariantIdx,
+) -> Option<u64> {
+    match enum_type_and_layout.layout.variants() {
+        &Variants::Single { .. } => None,
+        &Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => Some(
+            enum_type_and_layout.ty.discriminant_for_variant(cx.tcx, variant_index).unwrap().val
+                as u64,
+        ),
+        &Variants::Multiple {
+            tag_encoding: TagEncoding::Niche { ref niche_variants, niche_start, dataful_variant },
+            tag,
+            ..
+        } => {
+            if variant_index == dataful_variant {
+                None
+            } else {
+                let value = (variant_index.as_u32() as u128)
+                    .wrapping_sub(niche_variants.start().as_u32() as u128)
+                    .wrapping_add(niche_start);
+                let value = tag.value.size(cx).truncate(value);
+                // NOTE(eddyb) do *NOT* remove this assert, until
+                // we pass the full 128-bit value to LLVM, otherwise
+                // truncation will be silent and remain undetected.
+                assert_eq!(value as u64 as u128, value);
+                Some(value as u64)
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs
new file mode 100644
index 00000000000..12b8cfb4812
--- /dev/null
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs
@@ -0,0 +1,441 @@
+use std::borrow::Cow;
+
+use crate::{
+    common::CodegenCx,
+    debuginfo::{
+        metadata::{
+            closure_saved_names_of_captured_variables,
+            enums::tag_base_type,
+            file_metadata, generator_layout_and_saved_local_names, size_and_align_of, type_di_node,
+            type_map::{self, Stub, StubInfo, UniqueTypeId},
+            unknown_file_metadata, DINodeCreationResult, SmallVec, NO_GENERICS,
+            UNKNOWN_LINE_NUMBER,
+        },
+        utils::{create_DIArray, get_namespace_for_item, DIB},
+    },
+    llvm::{
+        self,
+        debuginfo::{DIFile, DIFlags, DIType},
+    },
+};
+use libc::c_uint;
+use rustc_codegen_ssa::{
+    debuginfo::{type_names::compute_debuginfo_type_name, wants_c_like_enum_debuginfo},
+    traits::ConstMethods,
+};
+use rustc_middle::{
+    bug,
+    ty::{
+        self,
+        layout::{LayoutOf, TyAndLayout},
+    },
+};
+use rustc_target::abi::{Size, TagEncoding, VariantIdx, Variants};
+use smallvec::smallvec;
+
+/// Build the debuginfo node for an enum type. The listing below shows how such a
+/// type looks like at the LLVM IR/DWARF level. It is a `DW_TAG_structure_type`
+/// with a single `DW_TAG_variant_part` that in turn contains a `DW_TAG_variant`
+/// for each variant of the enum. The variant-part also contains a single member
+/// describing the discriminant, and a nested struct type for each of the variants.
+///
+/// ```txt
+///  ---> DW_TAG_structure_type              (top-level type for enum)
+///         DW_TAG_variant_part              (variant part)
+///           DW_AT_discr                    (reference to discriminant DW_TAG_member)
+///           DW_TAG_member                  (discriminant member)
+///           DW_TAG_variant                 (variant 1)
+///           DW_TAG_variant                 (variant 2)
+///           DW_TAG_variant                 (variant 3)
+///         DW_TAG_structure_type            (type of variant 1)
+///         DW_TAG_structure_type            (type of variant 2)
+///         DW_TAG_structure_type            (type of variant 3)
+/// ```
+pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    unique_type_id: UniqueTypeId<'tcx>,
+) -> DINodeCreationResult<'ll> {
+    let enum_type = unique_type_id.expect_ty();
+    let &ty::Adt(enum_adt_def, _) = enum_type.kind() else {
+        bug!("build_enum_type_di_node() called with non-enum type: `{:?}`", enum_type)
+        };
+
+    let containing_scope = get_namespace_for_item(cx, enum_adt_def.did());
+    let enum_type_and_layout = cx.layout_of(enum_type);
+    let enum_type_name = compute_debuginfo_type_name(cx.tcx, enum_type, false);
+
+    debug_assert!(!wants_c_like_enum_debuginfo(enum_type_and_layout));
+
+    type_map::build_type_with_children(
+        cx,
+        type_map::stub(
+            cx,
+            Stub::Struct,
+            unique_type_id,
+            &enum_type_name,
+            size_and_align_of(enum_type_and_layout),
+            Some(containing_scope),
+            DIFlags::FlagZero,
+        ),
+        |cx, enum_type_di_node| {
+            // Build the struct type for each variant. These will be referenced by the
+            // DW_TAG_variant DIEs inside of the DW_TAG_variant_part DIE.
+            // We also called the names for the corresponding DW_TAG_variant DIEs here.
+            let variant_member_infos: SmallVec<_> = enum_adt_def
+                .variant_range()
+                .map(|variant_index| VariantMemberInfo {
+                    variant_index,
+                    variant_name: Cow::from(enum_adt_def.variant(variant_index).name.as_str()),
+                    variant_struct_type_di_node: super::build_enum_variant_struct_type_di_node(
+                        cx,
+                        enum_type,
+                        enum_type_di_node,
+                        variant_index,
+                        enum_adt_def.variant(variant_index),
+                        enum_type_and_layout.for_variant(cx, variant_index),
+                    ),
+                    source_info: None,
+                })
+                .collect();
+
+            smallvec![build_enum_variant_part_di_node(
+                cx,
+                enum_type_and_layout,
+                enum_type_di_node,
+                &variant_member_infos[..],
+            )]
+        },
+        // We don't seem to be emitting generic args on the enum type, it seems. Rather
+        // they get attached to the struct type of each variant.
+        NO_GENERICS,
+    )
+}
+
+/// Build the debuginfo node for a generator environment. It looks the same as the debuginfo for
+/// an enum. See [build_enum_type_di_node] for more information.
+///
+/// ```txt
+///
+///  ---> DW_TAG_structure_type              (top-level type for the generator)
+///         DW_TAG_variant_part              (variant part)
+///           DW_AT_discr                    (reference to discriminant DW_TAG_member)
+///           DW_TAG_member                  (discriminant member)
+///           DW_TAG_variant                 (variant 1)
+///           DW_TAG_variant                 (variant 2)
+///           DW_TAG_variant                 (variant 3)
+///         DW_TAG_structure_type            (type of variant 1)
+///         DW_TAG_structure_type            (type of variant 2)
+///         DW_TAG_structure_type            (type of variant 3)
+///
+/// ```
+pub(super) fn build_generator_di_node<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    unique_type_id: UniqueTypeId<'tcx>,
+) -> DINodeCreationResult<'ll> {
+    let generator_type = unique_type_id.expect_ty();
+    let &ty::Generator(generator_def_id, _, _ ) = generator_type.kind() else {
+        bug!("build_generator_di_node() called with non-generator type: `{:?}`", generator_type)
+        };
+
+    let containing_scope = get_namespace_for_item(cx, generator_def_id);
+    let generator_type_and_layout = cx.layout_of(generator_type);
+
+    debug_assert!(!wants_c_like_enum_debuginfo(generator_type_and_layout));
+
+    let generator_type_name = compute_debuginfo_type_name(cx.tcx, generator_type, false);
+
+    type_map::build_type_with_children(
+        cx,
+        type_map::stub(
+            cx,
+            Stub::Struct,
+            unique_type_id,
+            &generator_type_name,
+            size_and_align_of(generator_type_and_layout),
+            Some(containing_scope),
+            DIFlags::FlagZero,
+        ),
+        |cx, generator_type_di_node| {
+            let (generator_layout, state_specific_upvar_names) =
+                generator_layout_and_saved_local_names(cx.tcx, generator_def_id);
+
+            let Variants::Multiple { tag_encoding: TagEncoding::Direct, ref variants, .. } = generator_type_and_layout.variants else {
+                bug!(
+                    "Encountered generator with non-direct-tag layout: {:?}",
+                    generator_type_and_layout
+                )
+            };
+
+            let common_upvar_names =
+                closure_saved_names_of_captured_variables(cx.tcx, generator_def_id);
+
+            // Build variant struct types
+            let variant_struct_type_di_nodes: SmallVec<_> = variants
+                .indices()
+                .map(|variant_index| {
+                    // FIXME: This is problematic because just a number is not a valid identifier.
+                    //        GeneratorSubsts::variant_name(variant_index), would be consistent
+                    //        with enums?
+                    let variant_name = format!("{}", variant_index.as_usize()).into();
+
+                    let span = generator_layout.variant_source_info[variant_index].span;
+                    let source_info = if !span.is_dummy() {
+                        let loc = cx.lookup_debug_loc(span.lo());
+                        Some((file_metadata(cx, &loc.file), loc.line))
+                    } else {
+                        None
+                    };
+
+                    VariantMemberInfo {
+                        variant_index,
+                        variant_name,
+                        variant_struct_type_di_node:
+                            super::build_generator_variant_struct_type_di_node(
+                                cx,
+                                variant_index,
+                                generator_type_and_layout,
+                                generator_type_di_node,
+                                generator_layout,
+                                &state_specific_upvar_names,
+                                &common_upvar_names,
+                            ),
+                        source_info,
+                    }
+                })
+                .collect();
+
+            smallvec![build_enum_variant_part_di_node(
+                cx,
+                generator_type_and_layout,
+                generator_type_di_node,
+                &variant_struct_type_di_nodes[..],
+            )]
+        },
+        // We don't seem to be emitting generic args on the generator type, it seems. Rather
+        // they get attached to the struct type of each variant.
+        NO_GENERICS,
+    )
+}
+
+/// Builds the DW_TAG_variant_part of an enum or generator debuginfo node:
+///
+/// ```txt
+///       DW_TAG_structure_type              (top-level type for enum)
+/// --->    DW_TAG_variant_part              (variant part)
+///           DW_AT_discr                    (reference to discriminant DW_TAG_member)
+///           DW_TAG_member                  (discriminant member)
+///           DW_TAG_variant                 (variant 1)
+///           DW_TAG_variant                 (variant 2)
+///           DW_TAG_variant                 (variant 3)
+///         DW_TAG_structure_type            (type of variant 1)
+///         DW_TAG_structure_type            (type of variant 2)
+///         DW_TAG_structure_type            (type of variant 3)
+/// ```
+fn build_enum_variant_part_di_node<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    enum_type_and_layout: TyAndLayout<'tcx>,
+    enum_type_di_node: &'ll DIType,
+    variant_member_infos: &[VariantMemberInfo<'_, 'll>],
+) -> &'ll DIType {
+    let tag_member_di_node =
+        build_discr_member_di_node(cx, enum_type_and_layout, enum_type_di_node);
+
+    let variant_part_unique_type_id =
+        UniqueTypeId::for_enum_variant_part(cx.tcx, enum_type_and_layout.ty);
+
+    let stub = StubInfo::new(
+        cx,
+        variant_part_unique_type_id,
+        |cx, variant_part_unique_type_id_str| unsafe {
+            let variant_part_name = "";
+            llvm::LLVMRustDIBuilderCreateVariantPart(
+                DIB(cx),
+                enum_type_di_node,
+                variant_part_name.as_ptr().cast(),
+                variant_part_name.len(),
+                unknown_file_metadata(cx),
+                UNKNOWN_LINE_NUMBER,
+                enum_type_and_layout.size.bits(),
+                enum_type_and_layout.align.abi.bits() as u32,
+                DIFlags::FlagZero,
+                tag_member_di_node,
+                create_DIArray(DIB(cx), &[]),
+                variant_part_unique_type_id_str.as_ptr().cast(),
+                variant_part_unique_type_id_str.len(),
+            )
+        },
+    );
+
+    type_map::build_type_with_children(
+        cx,
+        stub,
+        |cx, variant_part_di_node| {
+            variant_member_infos
+                .iter()
+                .map(|variant_member_info| {
+                    build_enum_variant_member_di_node(
+                        cx,
+                        enum_type_and_layout,
+                        variant_part_di_node,
+                        variant_member_info,
+                    )
+                })
+                .collect()
+        },
+        NO_GENERICS,
+    )
+    .di_node
+}
+
+/// Builds the DW_TAG_member describing where we can find the tag of an enum.
+/// Returns `None` if the enum does not have a tag.
+///
+/// ```txt
+///
+///       DW_TAG_structure_type              (top-level type for enum)
+///         DW_TAG_variant_part              (variant part)
+///           DW_AT_discr                    (reference to discriminant DW_TAG_member)
+/// --->      DW_TAG_member                  (discriminant member)
+///           DW_TAG_variant                 (variant 1)
+///           DW_TAG_variant                 (variant 2)
+///           DW_TAG_variant                 (variant 3)
+///         DW_TAG_structure_type            (type of variant 1)
+///         DW_TAG_structure_type            (type of variant 2)
+///         DW_TAG_structure_type            (type of variant 3)
+///
+/// ```
+fn build_discr_member_di_node<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    enum_or_generator_type_and_layout: TyAndLayout<'tcx>,
+    enum_or_generator_type_di_node: &'ll DIType,
+) -> Option<&'ll DIType> {
+    let tag_name = match enum_or_generator_type_and_layout.ty.kind() {
+        ty::Generator(..) => "__state",
+        _ => "",
+    };
+
+    // NOTE: This is actually wrong. This will become a member of
+    //       of the DW_TAG_variant_part. But, due to LLVM's API, that
+    //       can only be constructed with this DW_TAG_member already in created.
+    //       In LLVM IR the wrong scope will be listed but when DWARF is
+    //       generated from it, the DW_TAG_member will be a child the
+    //       DW_TAG_variant_part.
+    let containing_scope = enum_or_generator_type_di_node;
+
+    match enum_or_generator_type_and_layout.layout.variants() {
+        // A single-variant enum has no discriminant.
+        &Variants::Single { .. } => None,
+
+        &Variants::Multiple { tag_field, .. } => {
+            let tag_base_type = tag_base_type(cx, enum_or_generator_type_and_layout);
+            let (size, align) = cx.size_and_align_of(tag_base_type);
+
+            unsafe {
+                Some(llvm::LLVMRustDIBuilderCreateMemberType(
+                    DIB(cx),
+                    containing_scope,
+                    tag_name.as_ptr().cast(),
+                    tag_name.len(),
+                    unknown_file_metadata(cx),
+                    UNKNOWN_LINE_NUMBER,
+                    size.bits(),
+                    align.bits() as u32,
+                    enum_or_generator_type_and_layout.fields.offset(tag_field).bits(),
+                    DIFlags::FlagArtificial,
+                    type_di_node(cx, tag_base_type),
+                ))
+            }
+        }
+    }
+}
+
+/// Build the debuginfo node for `DW_TAG_variant`:
+///
+/// ```txt
+///       DW_TAG_structure_type              (top-level type for enum)
+///         DW_TAG_variant_part              (variant part)
+///           DW_AT_discr                    (reference to discriminant DW_TAG_member)
+///           DW_TAG_member                  (discriminant member)
+///  --->     DW_TAG_variant                 (variant 1)
+///  --->     DW_TAG_variant                 (variant 2)
+///  --->     DW_TAG_variant                 (variant 3)
+///         DW_TAG_structure_type            (type of variant 1)
+///         DW_TAG_structure_type            (type of variant 2)
+///         DW_TAG_structure_type            (type of variant 3)
+/// ```
+///
+/// This node looks like:
+///
+/// ```txt
+/// DW_TAG_variant
+///   DW_AT_discr_value           0
+///   DW_TAG_member
+///     DW_AT_name                  None
+///     DW_AT_type                  <0x000002a1>
+///     DW_AT_alignment             0x00000002
+///     DW_AT_data_member_location  0
+/// ```
+///
+/// The DW_AT_discr_value is optional, and is omitted if
+///   - This is the only variant of a univariant enum (i.e. their is no discriminant)
+///   - This is the "dataful" variant of a niche-layout enum
+///     (where only the other variants are identified by a single value)
+///
+/// There is only ever a single member, the type of which is a struct that describes the
+/// fields of the variant (excluding the discriminant). The name of the member is the name
+/// of the variant as given in the source code. The DW_AT_data_member_location is always
+/// zero.
+///
+/// Note that the LLVM DIBuilder API is a bit unintuitive here. The DW_TAG_variant subtree
+/// (including the DW_TAG_member) is built by a single call to
+/// `LLVMRustDIBuilderCreateVariantMemberType()`.
+fn build_enum_variant_member_di_node<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    enum_type_and_layout: TyAndLayout<'tcx>,
+    variant_part_di_node: &'ll DIType,
+    variant_member_info: &VariantMemberInfo<'_, 'll>,
+) -> &'ll DIType {
+    let variant_index = variant_member_info.variant_index;
+    let discr_value = super::compute_discriminant_value(cx, enum_type_and_layout, variant_index);
+
+    let (file_di_node, line_number) = variant_member_info
+        .source_info
+        .unwrap_or_else(|| (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER));
+
+    unsafe {
+        llvm::LLVMRustDIBuilderCreateVariantMemberType(
+            DIB(cx),
+            variant_part_di_node,
+            variant_member_info.variant_name.as_ptr().cast(),
+            variant_member_info.variant_name.len(),
+            file_di_node,
+            line_number,
+            enum_type_and_layout.size.bits(), // FIXME: Unused?
+            enum_type_and_layout.align.abi.bits() as u32, // FIXME: Unused?
+            Size::ZERO.bits(),                // FIXME: Unused?
+            discr_value.map(|v| cx.const_u64(v)),
+            DIFlags::FlagZero,
+            variant_member_info.variant_struct_type_di_node,
+        )
+    }
+}
+
+/// Information needed for building a `DW_TAG_variant`:
+///
+/// ```txt
+///       DW_TAG_structure_type              (top-level type for enum)
+///         DW_TAG_variant_part              (variant part)
+///           DW_AT_discr                    (reference to discriminant DW_TAG_member)
+///           DW_TAG_member                  (discriminant member)
+///  --->     DW_TAG_variant                 (variant 1)
+///  --->     DW_TAG_variant                 (variant 2)
+///  --->     DW_TAG_variant                 (variant 3)
+///         DW_TAG_structure_type            (type of variant 1)
+///         DW_TAG_structure_type            (type of variant 2)
+///         DW_TAG_structure_type            (type of variant 3)
+struct VariantMemberInfo<'a, 'll> {
+    variant_index: VariantIdx,
+    variant_name: Cow<'a, str>,
+    variant_struct_type_di_node: &'ll DIType,
+    source_info: Option<(&'ll DIFile, c_uint)>,
+}
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs
new file mode 100644
index 00000000000..26f0647f888
--- /dev/null
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs
@@ -0,0 +1,271 @@
+use std::cell::RefCell;
+
+use rustc_data_structures::{
+    fingerprint::Fingerprint,
+    fx::FxHashMap,
+    stable_hasher::{HashStable, NodeIdHashingMode, StableHasher},
+};
+use rustc_middle::{
+    bug,
+    ty::{ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt},
+};
+use rustc_target::abi::{Align, Size, VariantIdx};
+
+use crate::{
+    common::CodegenCx,
+    debuginfo::utils::{create_DIArray, debug_context, DIB},
+    llvm::{
+        self,
+        debuginfo::{DIFlags, DIScope, DIType},
+    },
+};
+
+use super::{unknown_file_metadata, SmallVec, UNKNOWN_LINE_NUMBER};
+
+mod private {
+    // This type cannot be constructed outside of this module because
+    // it has a private field. We make use of this in order to prevent
+    // `UniqueTypeId` from being constructed directly, without asserting
+    // the preconditions.
+    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, HashStable)]
+    pub struct HiddenZst;
+}
+
+/// A unique identifier for anything that we create a debuginfo node for.
+/// The types it contains are expected to already be normalized (which
+/// is debug_asserted in the constructors).
+///
+/// Note that there are some things that only show up in debuginfo, like
+/// the separate type descriptions for each enum variant. These get an ID
+/// too because they have their own debuginfo node in LLVM IR.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, HashStable)]
+pub(super) enum UniqueTypeId<'tcx> {
+    /// The ID of a regular type as it shows up at the language level.
+    Ty(Ty<'tcx>, private::HiddenZst),
+    /// The ID for the single DW_TAG_variant_part nested inside the top-level
+    /// DW_TAG_structure_type that describes enums and generators.
+    VariantPart(Ty<'tcx>, private::HiddenZst),
+    /// The ID for the artificial struct type describing a single enum variant.
+    VariantStructType(Ty<'tcx>, VariantIdx, private::HiddenZst),
+    /// The ID of the artificial type we create for VTables.
+    VTableTy(Ty<'tcx>, Option<PolyExistentialTraitRef<'tcx>>, private::HiddenZst),
+}
+
+impl<'tcx> UniqueTypeId<'tcx> {
+    pub fn for_ty(tcx: TyCtxt<'tcx>, t: Ty<'tcx>) -> Self {
+        debug_assert_eq!(t, tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t));
+        UniqueTypeId::Ty(t, private::HiddenZst)
+    }
+
+    pub fn for_enum_variant_part(tcx: TyCtxt<'tcx>, enum_ty: Ty<'tcx>) -> Self {
+        debug_assert_eq!(enum_ty, tcx.normalize_erasing_regions(ParamEnv::reveal_all(), enum_ty));
+        UniqueTypeId::VariantPart(enum_ty, private::HiddenZst)
+    }
+
+    pub fn for_enum_variant_struct_type(
+        tcx: TyCtxt<'tcx>,
+        enum_ty: Ty<'tcx>,
+        variant_idx: VariantIdx,
+    ) -> Self {
+        debug_assert_eq!(enum_ty, tcx.normalize_erasing_regions(ParamEnv::reveal_all(), enum_ty));
+        UniqueTypeId::VariantStructType(enum_ty, variant_idx, private::HiddenZst)
+    }
+
+    pub fn for_vtable_ty(
+        tcx: TyCtxt<'tcx>,
+        self_type: Ty<'tcx>,
+        implemented_trait: Option<PolyExistentialTraitRef<'tcx>>,
+    ) -> Self {
+        debug_assert_eq!(
+            self_type,
+            tcx.normalize_erasing_regions(ParamEnv::reveal_all(), self_type)
+        );
+        debug_assert_eq!(
+            implemented_trait,
+            tcx.normalize_erasing_regions(ParamEnv::reveal_all(), implemented_trait)
+        );
+        UniqueTypeId::VTableTy(self_type, implemented_trait, private::HiddenZst)
+    }
+
+    /// Generates a string version of this [UniqueTypeId], which can be used as the `UniqueId`
+    /// argument of the various `LLVMRustDIBuilderCreate*Type()` methods.
+    ///
+    /// Right now this takes the form of a hex-encoded opaque hash value.
+    pub fn generate_unique_id_string(self, tcx: TyCtxt<'tcx>) -> String {
+        let mut hasher = StableHasher::new();
+        let mut hcx = tcx.create_stable_hashing_context();
+        hcx.while_hashing_spans(false, |hcx| {
+            hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
+                self.hash_stable(hcx, &mut hasher);
+            });
+        });
+        hasher.finish::<Fingerprint>().to_hex()
+    }
+
+    pub fn expect_ty(self) -> Ty<'tcx> {
+        match self {
+            UniqueTypeId::Ty(ty, _) => ty,
+            _ => bug!("Expected `UniqueTypeId::Ty` but found `{:?}`", self),
+        }
+    }
+}
+
+/// The `TypeMap` is where the debug context holds the type metadata nodes
+/// created so far. The debuginfo nodes are identified by `UniqueTypeId`.
+#[derive(Default)]
+pub(crate) struct TypeMap<'ll, 'tcx> {
+    pub(super) unique_id_to_di_node: RefCell<FxHashMap<UniqueTypeId<'tcx>, &'ll DIType>>,
+}
+
+impl<'ll, 'tcx> TypeMap<'ll, 'tcx> {
+    /// Adds a `UniqueTypeId` to metadata mapping to the `TypeMap`. The method will
+    /// fail if the mapping already exists.
+    pub(super) fn insert(&self, unique_type_id: UniqueTypeId<'tcx>, metadata: &'ll DIType) {
+        if self.unique_id_to_di_node.borrow_mut().insert(unique_type_id, metadata).is_some() {
+            bug!("type metadata for unique ID '{:?}' is already in the `TypeMap`!", unique_type_id);
+        }
+    }
+
+    pub(super) fn di_node_for_unique_id(
+        &self,
+        unique_type_id: UniqueTypeId<'tcx>,
+    ) -> Option<&'ll DIType> {
+        self.unique_id_to_di_node.borrow().get(&unique_type_id).cloned()
+    }
+}
+
+pub struct DINodeCreationResult<'ll> {
+    pub di_node: &'ll DIType,
+    pub already_stored_in_typemap: bool,
+}
+
+impl<'ll> DINodeCreationResult<'ll> {
+    pub fn new(di_node: &'ll DIType, already_stored_in_typemap: bool) -> Self {
+        DINodeCreationResult { di_node, already_stored_in_typemap }
+    }
+}
+
+#[allow(dead_code)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum Stub<'ll> {
+    Struct,
+    Union,
+    VtableTy { vtable_holder: &'ll DIType },
+}
+
+pub struct StubInfo<'ll, 'tcx> {
+    metadata: &'ll DIType,
+    unique_type_id: UniqueTypeId<'tcx>,
+}
+
+impl<'ll, 'tcx> StubInfo<'ll, 'tcx> {
+    pub(super) fn new(
+        cx: &CodegenCx<'ll, 'tcx>,
+        unique_type_id: UniqueTypeId<'tcx>,
+        build: impl FnOnce(&CodegenCx<'ll, 'tcx>, /* unique_type_id_str: */ &str) -> &'ll DIType,
+    ) -> StubInfo<'ll, 'tcx> {
+        let unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx);
+        let di_node = build(cx, &unique_type_id_str);
+        StubInfo { metadata: di_node, unique_type_id }
+    }
+}
+
+/// Create a stub debuginfo node onto which fields and nested types can be attached.
+pub(super) fn stub<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    kind: Stub<'ll>,
+    unique_type_id: UniqueTypeId<'tcx>,
+    name: &str,
+    (size, align): (Size, Align),
+    containing_scope: Option<&'ll DIScope>,
+    flags: DIFlags,
+) -> StubInfo<'ll, 'tcx> {
+    let empty_array = create_DIArray(DIB(cx), &[]);
+    let unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx);
+
+    let metadata = match kind {
+        Stub::Struct | Stub::VtableTy { .. } => {
+            let vtable_holder = match kind {
+                Stub::VtableTy { vtable_holder } => Some(vtable_holder),
+                _ => None,
+            };
+            unsafe {
+                llvm::LLVMRustDIBuilderCreateStructType(
+                    DIB(cx),
+                    containing_scope,
+                    name.as_ptr().cast(),
+                    name.len(),
+                    unknown_file_metadata(cx),
+                    UNKNOWN_LINE_NUMBER,
+                    size.bits(),
+                    align.bits() as u32,
+                    flags,
+                    None,
+                    empty_array,
+                    0,
+                    vtable_holder,
+                    unique_type_id_str.as_ptr().cast(),
+                    unique_type_id_str.len(),
+                )
+            }
+        }
+        Stub::Union => unsafe {
+            llvm::LLVMRustDIBuilderCreateUnionType(
+                DIB(cx),
+                containing_scope,
+                name.as_ptr().cast(),
+                name.len(),
+                unknown_file_metadata(cx),
+                UNKNOWN_LINE_NUMBER,
+                size.bits(),
+                align.bits() as u32,
+                flags,
+                Some(empty_array),
+                0,
+                unique_type_id_str.as_ptr().cast(),
+                unique_type_id_str.len(),
+            )
+        },
+    };
+    StubInfo { metadata, unique_type_id }
+}
+
+/// This function enables creating debuginfo nodes that can recursively refer to themselves.
+/// It will first insert the given stub into the type map and only then execute the `members`
+/// and `generics` closures passed in. These closures have access to the stub so they can
+/// directly attach fields to them. If build the type of a field transitively refers back
+/// to the type currently being built, the stub will already be found in the type map,
+/// which effectively breaks the recursion cycle.
+pub(super) fn build_type_with_children<'ll, 'tcx>(
+    cx: &CodegenCx<'ll, 'tcx>,
+    stub_info: StubInfo<'ll, 'tcx>,
+    members: impl FnOnce(&CodegenCx<'ll, 'tcx>, &'ll DIType) -> SmallVec<&'ll DIType>,
+    generics: impl FnOnce(&CodegenCx<'ll, 'tcx>) -> SmallVec<&'ll DIType>,
+) -> DINodeCreationResult<'ll> {
+    debug_assert_eq!(
+        debug_context(cx).type_map.di_node_for_unique_id(stub_info.unique_type_id),
+        None
+    );
+
+    debug_context(cx).type_map.insert(stub_info.unique_type_id, stub_info.metadata);
+
+    let members: SmallVec<_> =
+        members(cx, stub_info.metadata).into_iter().map(|node| Some(node)).collect();
+    let generics: SmallVec<Option<&'ll DIType>> =
+        generics(cx).into_iter().map(|node| Some(node)).collect();
+
+    if !(members.is_empty() && generics.is_empty()) {
+        unsafe {
+            let members_array = create_DIArray(DIB(cx), &members[..]);
+            let generics_array = create_DIArray(DIB(cx), &generics[..]);
+            llvm::LLVMRustDICompositeTypeReplaceArrays(
+                DIB(cx),
+                stub_info.metadata,
+                Some(members_array),
+                Some(generics_array),
+            );
+        }
+    }
+
+    DINodeCreationResult { di_node: stub_info.metadata, already_stored_in_typemap: true }
+}
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
index 34013b5f737..6cb8f7863a9 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
@@ -2,7 +2,7 @@
 
 use rustc_codegen_ssa::mir::debuginfo::VariableKind::*;
 
-use self::metadata::{file_metadata, type_metadata, TypeMap};
+use self::metadata::{file_metadata, type_di_node};
 use self::metadata::{UNKNOWN_COLUMN_NUMBER, UNKNOWN_LINE_NUMBER};
 use self::namespace::mangled_name_of_instance;
 use self::utils::{create_DIArray, is_node_local_to_unit, DIB};
@@ -20,7 +20,7 @@ use crate::value::Value;
 use rustc_codegen_ssa::debuginfo::type_names;
 use rustc_codegen_ssa::mir::debuginfo::{DebugScope, FunctionDebugContext, VariableKind};
 use rustc_codegen_ssa::traits::*;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::Lrc;
 use rustc_hir::def_id::{DefId, DefIdMap};
 use rustc_index::vec::IndexVec;
@@ -32,7 +32,7 @@ use rustc_session::config::{self, DebugInfo};
 use rustc_session::Session;
 use rustc_span::symbol::Symbol;
 use rustc_span::{self, BytePos, Pos, SourceFile, SourceFileAndLine, Span};
-use rustc_target::abi::{Primitive, Size};
+use rustc_target::abi::Size;
 
 use libc::c_uint;
 use smallvec::SmallVec;
@@ -48,7 +48,7 @@ mod namespace;
 mod utils;
 
 pub use self::create_scope_map::compute_mir_scopes;
-pub use self::metadata::create_global_var_metadata;
+pub use self::metadata::build_global_var_di_node;
 pub use self::metadata::extend_scope_to_file;
 
 #[allow(non_upper_case_globals)]
@@ -57,24 +57,18 @@ const DW_TAG_auto_variable: c_uint = 0x100;
 const DW_TAG_arg_variable: c_uint = 0x101;
 
 /// A context object for maintaining all state needed by the debuginfo module.
-pub struct CrateDebugContext<'a, 'tcx> {
-    llcontext: &'a llvm::Context,
-    llmod: &'a llvm::Module,
-    builder: &'a mut DIBuilder<'a>,
-    created_files: RefCell<FxHashMap<(Option<String>, Option<String>), &'a DIFile>>,
-    created_enum_disr_types: RefCell<FxHashMap<(DefId, Primitive), &'a DIType>>,
-
-    type_map: TypeMap<'a, 'tcx>,
-    namespace_map: RefCell<DefIdMap<&'a DIScope>>,
-
-    recursion_marker_type: OnceCell<&'a DIType>,
-
-    // This collection is used to assert that composite types (structs, enums,
-    // ...) have their members only set once:
-    composite_types_completed: RefCell<FxHashSet<&'a DIType>>,
+pub struct CodegenUnitDebugContext<'ll, 'tcx> {
+    llcontext: &'ll llvm::Context,
+    llmod: &'ll llvm::Module,
+    builder: &'ll mut DIBuilder<'ll>,
+    created_files: RefCell<FxHashMap<(Option<String>, Option<String>), &'ll DIFile>>,
+
+    type_map: metadata::TypeMap<'ll, 'tcx>,
+    namespace_map: RefCell<DefIdMap<&'ll DIScope>>,
+    recursion_marker_type: OnceCell<&'ll DIType>,
 }
 
-impl Drop for CrateDebugContext<'_, '_> {
+impl Drop for CodegenUnitDebugContext<'_, '_> {
     fn drop(&mut self) {
         unsafe {
             llvm::LLVMRustDIBuilderDispose(&mut *(self.builder as *mut _));
@@ -82,22 +76,20 @@ impl Drop for CrateDebugContext<'_, '_> {
     }
 }
 
-impl<'a, 'tcx> CrateDebugContext<'a, 'tcx> {
-    pub fn new(llmod: &'a llvm::Module) -> Self {
-        debug!("CrateDebugContext::new");
+impl<'ll, 'tcx> CodegenUnitDebugContext<'ll, 'tcx> {
+    pub fn new(llmod: &'ll llvm::Module) -> Self {
+        debug!("CodegenUnitDebugContext::new");
         let builder = unsafe { llvm::LLVMRustDIBuilderCreate(llmod) };
         // DIBuilder inherits context from the module, so we'd better use the same one
         let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) };
-        CrateDebugContext {
+        CodegenUnitDebugContext {
             llcontext,
             llmod,
             builder,
             created_files: Default::default(),
-            created_enum_disr_types: Default::default(),
             type_map: Default::default(),
             namespace_map: RefCell::new(Default::default()),
             recursion_marker_type: OnceCell::new(),
-            composite_types_completed: Default::default(),
         }
     }
 
@@ -415,7 +407,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
             signature.push(if fn_abi.ret.is_ignore() {
                 None
             } else {
-                Some(type_metadata(cx, fn_abi.ret.layout.ty))
+                Some(type_di_node(cx, fn_abi.ret.layout.ty))
             });
 
             // Arguments types
@@ -440,11 +432,11 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
                         }
                         _ => t,
                     };
-                    Some(type_metadata(cx, t))
+                    Some(type_di_node(cx, t))
                 }));
             } else {
                 signature
-                    .extend(fn_abi.args.iter().map(|arg| Some(type_metadata(cx, arg.layout.ty))));
+                    .extend(fn_abi.args.iter().map(|arg| Some(type_di_node(cx, arg.layout.ty))));
             }
 
             create_DIArray(DIB(cx), &signature[..])
@@ -467,7 +459,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
                         if let GenericArgKind::Type(ty) = kind.unpack() {
                             let actual_type =
                                 cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty);
-                            let actual_type_metadata = type_metadata(cx, actual_type);
+                            let actual_type_metadata = type_di_node(cx, actual_type);
                             let name = name.as_str();
                             Some(unsafe {
                                 Some(llvm::LLVMRustDIBuilderCreateTemplateTypeParameter(
@@ -522,7 +514,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
                             if cx.sess().opts.debuginfo == DebugInfo::Full
                                 && !impl_self_ty.needs_subst()
                             {
-                                Some(type_metadata(cx, impl_self_ty))
+                                Some(type_di_node(cx, impl_self_ty))
                             } else {
                                 Some(namespace::item_namespace(cx, def.did()))
                             }
@@ -569,7 +561,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
         trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
         vtable: Self::Value,
     ) {
-        metadata::create_vtable_metadata(self, ty, trait_ref, vtable)
+        metadata::create_vtable_di_node(self, ty, trait_ref, vtable)
     }
 
     fn extend_scope_to_file(
@@ -597,7 +589,7 @@ impl<'ll, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
         let loc = self.lookup_debug_loc(span.lo());
         let file_metadata = file_metadata(self, &loc.file);
 
-        let type_metadata = type_metadata(self, variable_type);
+        let type_metadata = type_di_node(self, variable_type);
 
         let (argument_index, dwarf_tag) = match variable_kind {
             ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable),
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs
index fa75463067f..fe9851cfa56 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs
@@ -1,7 +1,7 @@
 // Utility Functions.
 
 use super::namespace::item_namespace;
-use super::CrateDebugContext;
+use super::CodegenUnitDebugContext;
 
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::layout::{HasParamEnv, LayoutOf};
@@ -35,7 +35,7 @@ pub fn create_DIArray<'ll>(
 #[inline]
 pub fn debug_context<'a, 'll, 'tcx>(
     cx: &'a CodegenCx<'ll, 'tcx>,
-) -> &'a CrateDebugContext<'ll, 'tcx> {
+) -> &'a CodegenUnitDebugContext<'ll, 'tcx> {
     cx.dbg_cx.as_ref().unwrap()
 }
 
diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs b/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs
index d1a0cf78d6a..6e3f4f0b8ef 100644
--- a/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/debuginfo/mod.rs
@@ -1,2 +1,34 @@
+use rustc_middle::ty::{self, layout::TyAndLayout};
+use rustc_target::abi::Size;
+
 // FIXME(eddyb) find a place for this (or a way to replace it).
 pub mod type_names;
+
+/// Returns true if we want to generate a DW_TAG_enumeration_type description for
+/// this instead of a DW_TAG_struct_type with DW_TAG_variant_part.
+///
+/// NOTE: This is somewhat inconsistent right now: For empty enums and enums with a single
+///       fieldless variant, we generate DW_TAG_struct_type, although a
+///       DW_TAG_enumeration_type would be a better fit.
+pub fn wants_c_like_enum_debuginfo<'tcx>(enum_type_and_layout: TyAndLayout<'tcx>) -> bool {
+    match enum_type_and_layout.ty.kind() {
+        ty::Adt(adt_def, _) => {
+            if !adt_def.is_enum() {
+                return false;
+            }
+
+            match adt_def.variants().len() {
+                0 => false,
+                1 => {
+                    // Univariant enums unless they are zero-sized
+                    enum_type_and_layout.size != Size::ZERO && adt_def.all_fields().count() == 0
+                }
+                _ => {
+                    // Enums with more than one variant if they have no fields
+                    adt_def.all_fields().count() == 0
+                }
+            }
+        }
+        _ => false,
+    }
+}
diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
index 67df64e9b1b..6a122addf22 100644
--- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
+++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
@@ -18,13 +18,15 @@ use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathD
 use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Mutability};
 use rustc_middle::ty::layout::IntegerExt;
 use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
-use rustc_middle::ty::{self, AdtDef, ExistentialProjection, Ty, TyCtxt};
+use rustc_middle::ty::{self, AdtDef, ExistentialProjection, ParamEnv, Ty, TyCtxt};
 use rustc_query_system::ich::NodeIdHashingMode;
 use rustc_target::abi::{Integer, TagEncoding, Variants};
 use smallvec::SmallVec;
 
 use std::fmt::Write;
 
+use crate::debuginfo::wants_c_like_enum_debuginfo;
+
 // Compute the name of the type as it should be stored in debuginfo. Does not do
 // any caching, i.e., calling the function twice with the same type will also do
 // the work twice. The `qualified` parameter only affects the first level of the
@@ -71,7 +73,9 @@ fn push_debuginfo_type_name<'tcx>(
         ty::Float(float_ty) => output.push_str(float_ty.name_str()),
         ty::Foreign(def_id) => push_item_name(tcx, def_id, qualified, output),
         ty::Adt(def, substs) => {
-            if def.is_enum() && cpp_like_debuginfo {
+            let ty_and_layout = tcx.layout_of(ParamEnv::reveal_all().and(t)).expect("layout error");
+
+            if def.is_enum() && cpp_like_debuginfo && !wants_c_like_enum_debuginfo(ty_and_layout) {
                 msvc_enum_fallback(tcx, t, def, substs, output, visited);
             } else {
                 push_item_name(tcx, def.did(), qualified, output);
diff --git a/src/test/codegen/async-fn-debug-msvc.rs b/src/test/codegen/async-fn-debug-msvc.rs
index a90c85a5449..b10e662b5bb 100644
--- a/src/test/codegen/async-fn-debug-msvc.rs
+++ b/src/test/codegen/async-fn-debug-msvc.rs
@@ -16,8 +16,7 @@ async fn async_fn_test() {
 
 // FIXME: No way to reliably check the filename.
 
-// CHECK-DAG:  [[ASYNC_FN:!.*]] = !DINamespace(name: "async_fn_test"
-// CHECK-DAG:  [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "async_fn_env$0"
+// CHECK-DAG:  [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "async_fn_env$0", {{.*}}, align: {{32|64}},
 // CHECK:      {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]],
 // For brevity, we only check the struct name and members of the last variant.
 // CHECK-SAME: file: [[FILE:![0-9]*]], line: 11,
@@ -40,10 +39,10 @@ async fn async_fn_test() {
 // CHECK-SAME: baseType: [[VARIANT:![0-9]*]]
 // CHECK-NOT:  flags: DIFlagArtificial
 // CHECK-SAME: )
-// CHECK:      [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]],
+// CHECK:      [[VARIANT]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]],
 // CHECK-NOT:  flags: DIFlagArtificial
 // CHECK-SAME: )
-// CHECK:      {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]]
+// CHECK:      {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[VARIANT]]
 // CHECK-NOT:  flags: DIFlagArtificial
 // CHECK-SAME: )
 // CHECK:      {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "discriminant", scope: [[GEN]],
diff --git a/src/test/codegen/async-fn-debug.rs b/src/test/codegen/async-fn-debug.rs
index 8fbd2765fd7..9f6058a71b3 100644
--- a/src/test/codegen/async-fn-debug.rs
+++ b/src/test/codegen/async-fn-debug.rs
@@ -18,7 +18,7 @@ async fn async_fn_test() {
 
 // CHECK-DAG:  [[ASYNC_FN:!.*]] = !DINamespace(name: "async_fn_test"
 // CHECK-DAG:  [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}", scope: [[ASYNC_FN]]
-// CHECK:      [[VARIANT:!.*]] = !DICompositeType(tag: DW_TAG_variant_part, scope: [[ASYNC_FN]],
+// CHECK:      [[VARIANT:!.*]] = !DICompositeType(tag: DW_TAG_variant_part, scope: [[GEN]],
 // CHECK-NOT:  flags: DIFlagArtificial
 // CHECK-SAME: discriminator: [[DISC:![0-9]*]]
 // CHECK:      {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "0", scope: [[VARIANT]],
@@ -50,7 +50,7 @@ async fn async_fn_test() {
 // CHECK:      {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]]
 // CHECK-NOT:  flags: DIFlagArtificial
 // CHECK-SAME: )
-// CHECK:      [[DISC]] = !DIDerivedType(tag: DW_TAG_member, name: "__state", scope: [[ASYNC_FN]],
+// CHECK:      [[DISC]] = !DIDerivedType(tag: DW_TAG_member, name: "__state", scope: [[GEN]],
 // CHECK-SAME: flags: DIFlagArtificial
 
 fn main() {
diff --git a/src/test/codegen/generator-debug-msvc.rs b/src/test/codegen/generator-debug-msvc.rs
index fb8b9e09fd2..a6e56a6bd57 100644
--- a/src/test/codegen/generator-debug-msvc.rs
+++ b/src/test/codegen/generator-debug-msvc.rs
@@ -20,7 +20,6 @@ fn generator_test() -> impl Generator<Yield = i32, Return = ()> {
 
 // FIXME: No way to reliably check the filename.
 
-// CHECK-DAG:  [[GEN_FN:!.*]] = !DINamespace(name: "generator_test"
 // CHECK-DAG:  [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "generator_env$0"
 // CHECK:      {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]],
 // For brevity, we only check the struct name and members of the last variant.
@@ -44,10 +43,10 @@ fn generator_test() -> impl Generator<Yield = i32, Return = ()> {
 // CHECK-SAME: baseType: [[VARIANT:![0-9]*]]
 // CHECK-NOT:  flags: DIFlagArtificial
 // CHECK-SAME: )
-// CHECK:      [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]],
+// CHECK:      [[VARIANT]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]],
 // CHECK-NOT:  flags: DIFlagArtificial
 // CHECK-SAME: )
-// CHECK:      {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]]
+// CHECK:      {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[VARIANT]]
 // CHECK-NOT:  flags: DIFlagArtificial
 // CHECK-SAME: )
 // CHECK:      {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "discriminant", scope: [[GEN]],
diff --git a/src/test/codegen/generator-debug.rs b/src/test/codegen/generator-debug.rs
index e777fe3af63..3ec860f2cbc 100644
--- a/src/test/codegen/generator-debug.rs
+++ b/src/test/codegen/generator-debug.rs
@@ -22,7 +22,7 @@ fn generator_test() -> impl Generator<Yield = i32, Return = ()> {
 
 // CHECK-DAG:  [[GEN_FN:!.*]] = !DINamespace(name: "generator_test"
 // CHECK-DAG:  [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{generator_env#0}", scope: [[GEN_FN]]
-// CHECK:      [[VARIANT:!.*]] = !DICompositeType(tag: DW_TAG_variant_part, scope: [[GEN_FN]],
+// CHECK:      [[VARIANT:!.*]] = !DICompositeType(tag: DW_TAG_variant_part, scope: [[GEN]],
 // CHECK-NOT:  flags: DIFlagArtificial
 // CHECK-SAME: discriminator: [[DISC:![0-9]*]]
 // CHECK:      {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "0", scope: [[VARIANT]],
@@ -54,7 +54,7 @@ fn generator_test() -> impl Generator<Yield = i32, Return = ()> {
 // CHECK:      {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]]
 // CHECK-NOT:  flags: DIFlagArtificial
 // CHECK-SAME: )
-// CHECK:      [[DISC]] = !DIDerivedType(tag: DW_TAG_member, name: "__state", scope: [[GEN_FN]],
+// CHECK:      [[DISC]] = !DIDerivedType(tag: DW_TAG_member, name: "__state", scope: [[GEN]],
 // CHECK-SAME: flags: DIFlagArtificial
 
 fn main() {
diff --git a/src/test/debuginfo/msvc-pretty-enums.rs b/src/test/debuginfo/msvc-pretty-enums.rs
index 642694355ad..a153a9a4228 100644
--- a/src/test/debuginfo/msvc-pretty-enums.rs
+++ b/src/test/debuginfo/msvc-pretty-enums.rs
@@ -4,14 +4,14 @@
 // cdb-command: g
 
 // cdb-command: dx a
-// cdb-check:a                :  Some({...}) [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum> >, 2, 16, Some>]
-// cdb-check:    [<Raw View>]     [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum> >, 2, 16, Some>]
+// cdb-check:a                :  Some({...}) [Type: enum$<core::option::Option<msvc_pretty_enums::CStyleEnum>, 2, 16, Some>]
+// cdb-check:    [<Raw View>]     [Type: enum$<core::option::Option<msvc_pretty_enums::CStyleEnum>, 2, 16, Some>]
 // cdb-check:    [variant]        :  Some
 // cdb-check:    [+0x000] __0              : Low (0x2) [Type: msvc_pretty_enums::CStyleEnum]
 
 // cdb-command: dx b
-// cdb-check:b                : None [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum> >, 2, 16, Some>]
-// cdb-check:    [<Raw View>]     [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum> >, 2, 16, Some>]
+// cdb-check:b                : None [Type: enum$<core::option::Option<msvc_pretty_enums::CStyleEnum>, 2, 16, Some>]
+// cdb-check:    [<Raw View>]     [Type: enum$<core::option::Option<msvc_pretty_enums::CStyleEnum>, 2, 16, Some>]
 // cdb-check:    [variant]        : None
 
 // cdb-command: dx c
@@ -78,7 +78,7 @@ pub enum NicheLayoutEnum {
     Tag2,
 }
 
-pub enum Empty { }
+pub enum Empty {}
 
 fn main() {
     let a = Some(CStyleEnum::Low);
@@ -97,4 +97,6 @@ fn main() {
     zzz(); // #break
 }
 
-fn zzz() { () }
+fn zzz() {
+    ()
+}
diff --git a/src/test/debuginfo/type-names.rs b/src/test/debuginfo/type-names.rs
index c9692128562..52841d50f64 100644
--- a/src/test/debuginfo/type-names.rs
+++ b/src/test/debuginfo/type-names.rs
@@ -33,10 +33,10 @@
 // gdb-check:type = type_names::mod1::Enum2
 
 // gdb-command:whatis generic_enum_1
-// gdb-check:type = type_names::mod1::mod2::Enum3
+// gdb-check:type = type_names::mod1::mod2::Enum3<type_names::mod1::Struct2>
 
 // gdb-command:whatis generic_enum_2
-// gdb-check:type = type_names::mod1::mod2::Enum3
+// gdb-check:type = type_names::mod1::mod2::Enum3<type_names::Struct1>
 
 // TUPLES
 // gdb-command:whatis tuple1
@@ -159,10 +159,10 @@
 
 // FOREIGN TYPES
 // gdb-command:whatis foreign1
-// gdb-check:type = *mut ForeignType1
+// gdb-check:type = *mut type_names::{extern#0}::ForeignType1
 
 // gdb-command:whatis foreign2
-// gdb-check:type = *mut ForeignType2
+// gdb-check:type = *mut type_names::mod1::{extern#0}::ForeignType2
 
 // === CDB TESTS ==================================================================================
 
@@ -178,9 +178,9 @@
 // cdb-command:dv /t *_enum_*
 // cdb-check:union enum$<type_names::Enum1> simple_enum_1 = [...]
 // cdb-check:union enum$<type_names::Enum1> simple_enum_2 = [...]
-// cdb-check:type_names::mod1::Enum2 simple_enum_3 = [...]
-// cdb-check:type_names::mod1::mod2::Enum3 generic_enum_1 = [...]
-// cdb-check:type_names::mod1::mod2::Enum3 generic_enum_2 = [...]
+// cdb-check:union enum$<type_names::mod1::Enum2> simple_enum_3 = [...]
+// cdb-check:union enum$<type_names::mod1::mod2::Enum3<type_names::mod1::Struct2> > generic_enum_1 = [...]
+// cdb-check:union enum$<type_names::mod1::mod2::Enum3<type_names::Struct1> > generic_enum_2 = [...]
 
 // TUPLES
 // cdb-command:dv /t tuple*
@@ -258,8 +258,8 @@
 
 // FOREIGN TYPES
 // cdb-command:dv /t foreign*
-// cdb-check:struct ForeignType2 * foreign2 = [...]
-// cdb-check:struct ForeignType1 * foreign1 = [...]
+// cdb-check:struct type_names::mod1::extern$0::ForeignType2 * foreign2 = [...]
+// cdb-check:struct type_names::extern$0::ForeignType1 * foreign1 = [...]
 
 #![allow(unused_variables)]
 #![feature(omit_gdb_pretty_printer_section)]
@@ -283,7 +283,6 @@ extern "C" {
 }
 
 mod mod1 {
-    pub use self::Enum2::{Variant1, Variant2};
     pub struct Struct2;
 
     pub enum Enum2 {
@@ -367,14 +366,14 @@ fn main() {
     // Enums
     let simple_enum_1 = Variant1;
     let simple_enum_2 = Variant2(0);
-    let simple_enum_3 = mod1::Variant2(Struct1);
+    let simple_enum_3 = mod1::Enum2::Variant2(Struct1);
 
     let generic_enum_1: mod1::mod2::Enum3<mod1::Struct2> = mod1::mod2::Variant1;
     let generic_enum_2 = mod1::mod2::Variant2(Struct1);
 
     // Tuples
     let tuple1 = (8u32, Struct1, mod1::mod2::Variant2(mod1::Struct2));
-    let tuple2 = ((Struct1, mod1::mod2::Struct3), mod1::Variant1, 'x');
+    let tuple2 = ((Struct1, mod1::mod2::Struct3), mod1::Enum2::Variant1, 'x');
 
     // Box
     let box1 = (Box::new(1f32), 0i32);
@@ -404,7 +403,7 @@ fn main() {
 
     let vec1 = vec![0_usize, 2, 3];
     let slice1 = &*vec1;
-    let vec2 = vec![mod1::Variant2(Struct1)];
+    let vec2 = vec![mod1::Enum2::Variant2(Struct1)];
     let slice2 = &*vec2;
 
     // Trait Objects