Commit 359bbdd574

Tau <jonathan.haehne@hotmail.com>
2024-06-21 00:53:11
llvm: encode variables as DW_TAG_imported_declaration
Now we get working global variable lookup in GDB! LLDB still re-mangles, and it looks like we can't do much about that for now. Also: translate non-owning type declarations into typedefs.
1 parent 876258a
Changed files (3)
src
src/codegen/llvm/Builder.zig
@@ -7652,6 +7652,8 @@ pub const Metadata = enum(u32) {
         derived_pointer_type,
         derived_member_type,
         derived_static_member_type,
+        derived_typedef,
+        imported_declaration,
         subroutine_type,
         enumerator_unsigned,
         enumerator_signed_positive,
@@ -7699,6 +7701,8 @@ pub const Metadata = enum(u32) {
                 .derived_pointer_type,
                 .derived_member_type,
                 .derived_static_member_type,
+                .derived_typedef,
+                .imported_declaration,
                 .subroutine_type,
                 .enumerator_unsigned,
                 .enumerator_signed_positive,
@@ -7816,6 +7820,7 @@ pub const Metadata = enum(u32) {
         producer: MetadataString,
         enums: Metadata,
         globals: Metadata,
+        imports: Metadata,
     };
 
     pub const Subprogram = struct {
@@ -7943,6 +7948,14 @@ pub const Metadata = enum(u32) {
         }
     };
 
+    pub const ImportedEntity = struct {
+        name: MetadataString,
+        file: Metadata,
+        scope: Metadata,
+        line: u32,
+        entity: Metadata,
+    };
+
     pub const SubroutineType = struct {
         types_tuple: Metadata,
     };
@@ -8232,6 +8245,7 @@ pub const Metadata = enum(u32) {
                 DIBasicType,
                 DICompositeType,
                 DIDerivedType,
+                DIImportedEntity,
                 DISubroutineType,
                 DIEnumerator,
                 DISubrange,
@@ -9969,7 +9983,7 @@ pub fn printUnbuffered(
                         .enums = extra.enums,
                         .retainedTypes = null,
                         .globals = extra.globals,
-                        .imports = null,
+                        .imports = extra.imports,
                         .macros = null,
                         .dwoId = null,
                         .splitDebugInlining = false,
@@ -10110,16 +10124,18 @@ pub fn printUnbuffered(
                 .derived_pointer_type,
                 .derived_member_type,
                 .derived_static_member_type,
+                .derived_typedef,
                 => |kind| {
                     const extra = self.metadataExtraData(Metadata.DerivedType, metadata_item.data);
                     try metadata_formatter.specialized(.@"!", .DIDerivedType, .{
                         .tag = @as(enum {
                             DW_TAG_pointer_type,
                             DW_TAG_member,
+                            DW_TAG_typedef,
                         }, switch (kind) {
                             .derived_pointer_type => .DW_TAG_pointer_type,
-                            .derived_member_type,
-                            .derived_static_member_type => .DW_TAG_member,
+                            .derived_member_type, .derived_static_member_type => .DW_TAG_member,
+                            .derived_typedef => .DW_TAG_typedef,
                             else => unreachable,
                         }),
                         .name = switch (extra.name) {
@@ -10142,6 +10158,22 @@ pub fn printUnbuffered(
                         .annotations = null,
                     }, writer);
                 },
+                .imported_declaration => {
+                    const extra = self.metadataExtraData(Metadata.ImportedEntity, metadata_item.data);
+
+                    try metadata_formatter.specialized(.@"!", .DIImportedEntity, .{
+                        .tag = .DW_TAG_imported_declaration,
+                        .scope = extra.scope,
+                        .entity = extra.entity,
+                        .file = extra.file,
+                        .line = extra.line,
+                        .name = switch (extra.name) {
+                            .none => null,
+                            else => extra.name,
+                        },
+                        .elements = null,
+                    }, writer);
+                },
                 .subroutine_type => {
                     const extra = self.metadataExtraData(Metadata.SubroutineType, metadata_item.data);
                     try metadata_formatter.specialized(.@"!", .DISubroutineType, .{
@@ -11747,10 +11779,11 @@ pub fn debugCompileUnit(
     producer: MetadataString,
     enums: Metadata,
     globals: Metadata,
+    imports: Metadata,
     options: Metadata.CompileUnit.Options,
 ) Allocator.Error!Metadata {
     try self.ensureUnusedMetadataCapacity(1, Metadata.CompileUnit, 0);
-    return self.debugCompileUnitAssumeCapacity(file, producer, enums, globals, options);
+    return self.debugCompileUnitAssumeCapacity(file, producer, enums, globals, imports, options);
 }
 
 pub fn debugSubprogram(
@@ -12005,6 +12038,53 @@ pub fn debugMemberType(
     );
 }
 
+pub fn debugTypedef(
+    self: *Builder,
+    name: MetadataString,
+    file: Metadata,
+    scope: Metadata,
+    line: u32,
+    underlying_type: Metadata,
+    align_in_bits: u64,
+) Allocator.Error!Metadata {
+    try self.ensureUnusedMetadataCapacity(1, Metadata.DerivedType, 0);
+
+    assert(!self.strip);
+    return self.metadataSimpleAssumeCapacity(.derived_typedef, Metadata.DerivedType{
+        .name = name,
+        .file = file,
+        .scope = scope,
+        .line = line,
+        .underlying_type = underlying_type,
+        .size_in_bits_lo = 0,
+        .size_in_bits_hi = 0,
+        .align_in_bits_lo = @truncate(align_in_bits),
+        .align_in_bits_hi = @truncate(align_in_bits >> 32),
+        .offset_in_bits_lo = 0,
+        .offset_in_bits_hi = 0,
+    });
+}
+
+pub fn debugImportDeclaration(
+    self: *Builder,
+    name: MetadataString,
+    file: Metadata,
+    scope: Metadata,
+    line: u32,
+    entity: Metadata,
+) Allocator.Error!Metadata {
+    try self.ensureUnusedMetadataCapacity(1, Metadata.ImportedEntity, 0);
+
+    assert(!self.strip);
+    return self.metadataSimpleAssumeCapacity(.imported_declaration, Metadata.ImportedEntity{
+        .name = name,
+        .file = file,
+        .scope = scope,
+        .line = line,
+        .entity = entity,
+    });
+}
+
 pub fn debugSubroutineType(
     self: *Builder,
     types_tuple: Metadata,
@@ -12241,6 +12321,7 @@ pub fn debugCompileUnitAssumeCapacity(
     producer: MetadataString,
     enums: Metadata,
     globals: Metadata,
+    imports: Metadata,
     options: Metadata.CompileUnit.Options,
 ) Metadata {
     assert(!self.strip);
@@ -12251,6 +12332,7 @@ pub fn debugCompileUnitAssumeCapacity(
             .producer = producer,
             .enums = enums,
             .globals = globals,
+            .imports = imports,
         },
     );
 }
@@ -12538,22 +12620,19 @@ fn debugMemberTypeAssumeCapacity(
     static: bool,
 ) Metadata {
     assert(!self.strip);
-    return self.metadataSimpleAssumeCapacity(
-        if (static) .derived_static_member_type else .derived_member_type,
-        Metadata.DerivedType{
-            .name = name,
-            .file = file,
-            .scope = scope,
-            .line = line,
-            .underlying_type = underlying_type,
-            .size_in_bits_lo = @truncate(size_in_bits),
-            .size_in_bits_hi = @truncate(size_in_bits >> 32),
-            .align_in_bits_lo = @truncate(align_in_bits),
-            .align_in_bits_hi = @truncate(align_in_bits >> 32),
-            .offset_in_bits_lo = @truncate(offset_in_bits),
-            .offset_in_bits_hi = @truncate(offset_in_bits >> 32),
-        }
-    );
+    return self.metadataSimpleAssumeCapacity(if (static) .derived_static_member_type else .derived_member_type, Metadata.DerivedType{
+        .name = name,
+        .file = file,
+        .scope = scope,
+        .line = line,
+        .underlying_type = underlying_type,
+        .size_in_bits_lo = @truncate(size_in_bits),
+        .size_in_bits_hi = @truncate(size_in_bits >> 32),
+        .align_in_bits_lo = @truncate(align_in_bits),
+        .align_in_bits_hi = @truncate(align_in_bits >> 32),
+        .offset_in_bits_lo = @truncate(offset_in_bits),
+        .offset_in_bits_hi = @truncate(offset_in_bits >> 32),
+    });
 }
 
 fn debugSubroutineTypeAssumeCapacity(
@@ -13850,6 +13929,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
                             },
                             .enums = extra.enums,
                             .globals = extra.globals,
+                            .imports = extra.imports,
                         }, metadata_adapter);
                     },
                     .subprogram,
@@ -13945,13 +14025,14 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
                     .derived_pointer_type,
                     .derived_member_type,
                     .derived_static_member_type,
+                    .derived_typedef,
                     => |kind| {
                         const extra = self.metadataExtraData(Metadata.DerivedType, data);
                         try metadata_block.writeAbbrevAdapted(MetadataBlock.DerivedType{
                             .tag = switch (kind) {
                                 .derived_pointer_type => DW.TAG.pointer_type,
-                                .derived_member_type,
-                                .derived_static_member_type => DW.TAG.member,
+                                .derived_member_type, .derived_static_member_type => DW.TAG.member,
+                                .derived_typedef => DW.TAG.typedef,
                                 else => unreachable,
                             },
                             .name = extra.name,
@@ -13967,6 +14048,17 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
                             },
                         }, metadata_adapter);
                     },
+                    .imported_declaration => {
+                        const extra = self.metadataExtraData(Metadata.ImportedEntity, data);
+                        try metadata_block.writeAbbrevAdapted(MetadataBlock.ImportedEntity{
+                            .tag = DW.TAG.imported_declaration,
+                            .scope = extra.scope,
+                            .entity = extra.entity,
+                            .line = extra.line,
+                            .name = extra.name,
+                            .file = extra.file,
+                        }, metadata_adapter);
+                    },
                     .subroutine_type => {
                         const extra = self.metadataExtraData(Metadata.SubroutineType, data);
 
src/codegen/llvm/ir.zig
@@ -649,6 +649,7 @@ pub const MetadataBlock = struct {
         BasicType,
         CompositeType,
         DerivedType,
+        ImportedEntity,
         SubroutineType,
         Enumerator,
         Subrange,
@@ -706,7 +707,7 @@ pub const MetadataBlock = struct {
             .{ .literal = 0 }, // retained types
             .{ .literal = 0 }, // subprograms
             MetadataAbbrev, // globals
-            .{ .literal = 0 }, // imported entities
+            MetadataAbbrev, // imported entities
             .{ .literal = 0 }, // DWO ID
             .{ .literal = 0 }, // macros
             .{ .literal = 0 }, // split debug inlining
@@ -722,6 +723,7 @@ pub const MetadataBlock = struct {
         is_optimized: bool,
         enums: Builder.Metadata,
         globals: Builder.Metadata,
+        imports: Builder.Metadata,
     };
 
     pub const Subprogram = struct {
@@ -879,6 +881,27 @@ pub const MetadataBlock = struct {
         flags: Builder.Metadata.DIFlags,
     };
 
+    pub const ImportedEntity = struct {
+        pub const ops = [_]AbbrevOp{
+            .{ .literal = 31 },
+            .{ .literal = 0 }, // is distinct
+            .{ .fixed = 32 }, // tag
+            MetadataAbbrev, // scope
+            MetadataAbbrev, // entity
+            LineAbbrev, // line
+            MetadataAbbrev, // name
+            MetadataAbbrev, // file
+            .{ .literal = 0 }, // elements
+        };
+
+        tag: u32,
+        scope: Builder.Metadata,
+        entity: Builder.Metadata,
+        line: u32,
+        name: Builder.MetadataString,
+        file: Builder.Metadata,
+    };
+
     pub const SubroutineType = struct {
         pub const ops = [_]AbbrevOp{
             .{ .literal = 19 },
src/codegen/llvm.zig
@@ -814,14 +814,17 @@ pub const Object = struct {
 
     debug_enums_fwd_ref: Builder.Metadata,
     debug_globals_fwd_ref: Builder.Metadata,
+    debug_imports_fwd_ref: Builder.Metadata,
 
     debug_enums: std.ArrayListUnmanaged(Builder.Metadata),
     debug_globals: std.ArrayListUnmanaged(Builder.Metadata),
+    debug_imports: std.ArrayListUnmanaged(Builder.Metadata),
 
     debug_file_map: std.AutoHashMapUnmanaged(*const Zcu.File, Builder.Metadata),
     debug_type_map: std.AutoHashMapUnmanaged(Type, Builder.Metadata),
 
-    debug_unresolved_namespace_scopes: std.AutoArrayHashMapUnmanaged(InternPool.NamespaceIndex, Builder.Metadata),
+    // The value says whether this namespace's type is runtime-required.
+    debug_unresolved_namespace_scopes: std.AutoArrayHashMapUnmanaged(Type, bool),
 
     target: std.Target,
     /// Ideally we would use `llvm_module.getNamedFunction` to go from *Decl to LLVM function,
@@ -884,7 +887,7 @@ pub const Object = struct {
 
         builder.data_layout = try builder.fmt("{}", .{DataLayoutBuilder{ .target = target }});
 
-        const debug_compile_unit, const debug_enums_fwd_ref, const debug_globals_fwd_ref =
+        const debug_compile_unit, const debug_enums_fwd_ref, const debug_globals_fwd_ref, const debug_imports_fwd_ref =
             if (!builder.strip)
         debug_info: {
             // We fully resolve all paths at this point to avoid lack of
@@ -916,6 +919,7 @@ pub const Object = struct {
 
             const debug_enums_fwd_ref = try builder.debugForwardReference();
             const debug_globals_fwd_ref = try builder.debugForwardReference();
+            const debug_imports_fwd_ref = try builder.debugForwardReference();
 
             const debug_compile_unit = try builder.debugCompileUnit(
                 debug_file,
@@ -928,6 +932,7 @@ pub const Object = struct {
                 }),
                 debug_enums_fwd_ref,
                 debug_globals_fwd_ref,
+                debug_imports_fwd_ref,
                 .{ .optimized = comp.root_mod.optimize_mode != .Debug },
             );
 
@@ -983,8 +988,8 @@ pub const Object = struct {
             }
 
             try builder.debugNamed(try builder.metadataString("llvm.dbg.cu"), &.{debug_compile_unit});
-            break :debug_info .{ debug_compile_unit, debug_enums_fwd_ref, debug_globals_fwd_ref };
-        } else .{.none} ** 3;
+            break :debug_info .{ debug_compile_unit, debug_enums_fwd_ref, debug_globals_fwd_ref, debug_imports_fwd_ref };
+        } else .{.none} ** 4;
 
         const obj = try arena.create(Object);
         obj.* = .{
@@ -997,8 +1002,10 @@ pub const Object = struct {
             .debug_compile_unit = debug_compile_unit,
             .debug_enums_fwd_ref = debug_enums_fwd_ref,
             .debug_globals_fwd_ref = debug_globals_fwd_ref,
+            .debug_imports_fwd_ref = debug_imports_fwd_ref,
             .debug_enums = .{},
             .debug_globals = .{},
+            .debug_imports = .{},
             .debug_file_map = .{},
             .debug_type_map = .{},
             .debug_unresolved_namespace_scopes = .{},
@@ -1151,6 +1158,11 @@ pub const Object = struct {
                     self.debug_globals_fwd_ref,
                     try self.builder.debugTuple(self.debug_globals.items),
                 );
+
+                self.builder.debugForwardReferenceSetType(
+                    self.debug_imports_fwd_ref,
+                    try self.builder.debugTuple(self.debug_imports.items),
+                );
             }
         }
 
@@ -1634,7 +1646,7 @@ pub const Object = struct {
 
             const line_number = decl.navSrcLine(zcu) + 1;
             const is_internal_linkage = decl.val.getExternFunc(zcu) == null;
-            const debug_decl_type = try o.lowerDebugType(decl.typeOf(zcu));
+            const debug_decl_type = try o.lowerDebugType(decl.typeOf(zcu), true);
 
             const subprogram = try o.builder.debugSubprogram(
                 file,
@@ -1904,6 +1916,7 @@ pub const Object = struct {
     pub fn lowerDebugType(
         o: *Object,
         ty: Type,
+        required_by_runtime: bool,
     ) Allocator.Error!Builder.Metadata {
         assert(!o.builder.strip);
 
@@ -1913,8 +1926,13 @@ pub const Object = struct {
         const zcu = pt.zcu;
         const ip = &zcu.intern_pool;
 
-        if (o.debug_type_map.get(ty)) |debug_type| return debug_type;
-
+        if (o.debug_type_map.get(ty)) |debug_type| {
+            if (required_by_runtime) {
+                if (o.debug_unresolved_namespace_scopes.getEntry(ty)) |entry|
+                    entry.value_ptr.* = true;
+            }
+            return debug_type;
+        }
 
         switch (ty.zigTypeTag(zcu)) {
             .Void,
@@ -1988,7 +2006,7 @@ pub const Object = struct {
                             },
                         },
                     });
-                    const debug_ptr_type = try o.lowerDebugType(bland_ptr_ty);
+                    const debug_ptr_type = try o.lowerDebugType(bland_ptr_ty, required_by_runtime);
                     try o.debug_type_map.put(gpa, ty, debug_ptr_type);
                     return debug_ptr_type;
                 }
@@ -2019,7 +2037,7 @@ pub const Object = struct {
                         .none, // File
                         debug_fwd_ref,
                         0, // Line
-                        try o.lowerDebugType(ptr_ty),
+                        try o.lowerDebugType(ptr_ty, required_by_runtime),
                         ptr_size * 8,
                         (ptr_align.toByteUnits() orelse 0) * 8,
                         0, // Offset
@@ -2030,7 +2048,7 @@ pub const Object = struct {
                         .none, // File
                         debug_fwd_ref,
                         0, // Line
-                        try o.lowerDebugType(len_ty),
+                        try o.lowerDebugType(len_ty, required_by_runtime),
                         len_size * 8,
                         (len_align.toByteUnits() orelse 0) * 8,
                         len_offset * 8,
@@ -2059,7 +2077,7 @@ pub const Object = struct {
                     return debug_slice_type;
                 }
 
-                const debug_elem_ty = try o.lowerDebugType(Type.fromInterned(ptr_info.child));
+                const debug_elem_ty = try o.lowerDebugType(Type.fromInterned(ptr_info.child), required_by_runtime);
 
                 const name = try o.allocTypeName(ty);
                 defer gpa.free(name);
@@ -2089,7 +2107,7 @@ pub const Object = struct {
                     .none, // File
                     .none, // Scope
                     0, // Line
-                    try o.lowerDebugType(ty.childType(zcu)),
+                    try o.lowerDebugType(ty.childType(zcu), required_by_runtime),
                     ty.abiSize(pt) * 8,
                     (ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
                     try o.builder.debugTuple(&.{
@@ -2124,7 +2142,7 @@ pub const Object = struct {
                         try o.builder.metadataString("bool"),
                         1,
                     ),
-                    else => try o.lowerDebugType(ty.childType(zcu)),
+                    else => try o.lowerDebugType(ty.childType(zcu), required_by_runtime),
                 };
 
                 const debug_vector_type = try o.builder.debugVectorType(
@@ -2166,7 +2184,7 @@ pub const Object = struct {
                 try o.debug_type_map.put(gpa, ty, debug_fwd_ref);
 
                 if (ty.optionalReprIsPayload(zcu)) {
-                    const debug_optional_type = try o.lowerDebugType(child_ty);
+                    const debug_optional_type = try o.lowerDebugType(child_ty, required_by_runtime);
 
                     o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_optional_type);
 
@@ -2189,7 +2207,7 @@ pub const Object = struct {
                     .none, // File
                     debug_fwd_ref,
                     0, // Line
-                    try o.lowerDebugType(child_ty),
+                    try o.lowerDebugType(child_ty, required_by_runtime),
                     payload_size * 8,
                     (payload_align.toByteUnits() orelse 0) * 8,
                     0, // Offset
@@ -2200,7 +2218,7 @@ pub const Object = struct {
                     .none,
                     debug_fwd_ref,
                     0,
-                    try o.lowerDebugType(non_null_ty),
+                    try o.lowerDebugType(non_null_ty, required_by_runtime),
                     non_null_size * 8,
                     (non_null_align.toByteUnits() orelse 0) * 8,
                     non_null_offset * 8,
@@ -2232,7 +2250,7 @@ pub const Object = struct {
                 const payload_ty = ty.errorUnionPayload(zcu);
                 if (!payload_ty.hasRuntimeBitsIgnoreComptime(pt)) {
                     // TODO: Maybe remove?
-                    const debug_error_union_type = try o.lowerDebugType(Type.anyerror);
+                    const debug_error_union_type = try o.lowerDebugType(Type.anyerror, required_by_runtime);
                     try o.debug_type_map.put(gpa, ty, debug_error_union_type);
                     return debug_error_union_type;
                 }
@@ -2269,7 +2287,7 @@ pub const Object = struct {
                     .none, // File
                     debug_fwd_ref,
                     0, // Line
-                    try o.lowerDebugType(Type.anyerror),
+                    try o.lowerDebugType(Type.anyerror, required_by_runtime),
                     error_size * 8,
                     (error_align.toByteUnits() orelse 0) * 8,
                     error_offset * 8,
@@ -2279,7 +2297,7 @@ pub const Object = struct {
                     .none, // File
                     debug_fwd_ref,
                     0, // Line
-                    try o.lowerDebugType(payload_ty),
+                    try o.lowerDebugType(payload_ty, required_by_runtime),
                     payload_size * 8,
                     (payload_align.toByteUnits() orelse 0) * 8,
                     payload_offset * 8,
@@ -2321,21 +2339,21 @@ pub const Object = struct {
                 if (Type.fromInterned(fn_info.return_type).hasRuntimeBitsIgnoreComptime(pt)) {
                     const sret = firstParamSRet(fn_info, pt, target);
                     const ret_ty = if (sret) Type.void else Type.fromInterned(fn_info.return_type);
-                    debug_param_types.appendAssumeCapacity(try o.lowerDebugType(ret_ty));
+                    debug_param_types.appendAssumeCapacity(try o.lowerDebugType(ret_ty, required_by_runtime));
 
                     if (sret) {
                         const ptr_ty = try pt.singleMutPtrType(Type.fromInterned(fn_info.return_type));
-                        debug_param_types.appendAssumeCapacity(try o.lowerDebugType(ptr_ty));
+                        debug_param_types.appendAssumeCapacity(try o.lowerDebugType(ptr_ty, required_by_runtime));
                     }
                 } else {
-                    debug_param_types.appendAssumeCapacity(try o.lowerDebugType(Type.void));
+                    debug_param_types.appendAssumeCapacity(try o.lowerDebugType(Type.void, required_by_runtime));
                 }
 
                 if (Type.fromInterned(fn_info.return_type).isError(zcu) and
                     zcu.comp.config.any_error_tracing)
                 {
                     const ptr_ty = try pt.singleMutPtrType(try o.getStackTraceType());
-                    debug_param_types.appendAssumeCapacity(try o.lowerDebugType(ptr_ty));
+                    debug_param_types.appendAssumeCapacity(try o.lowerDebugType(ptr_ty, required_by_runtime));
                 }
 
                 for (0..fn_info.param_types.len) |i| {
@@ -2344,9 +2362,9 @@ pub const Object = struct {
 
                     if (isByRef(param_ty, pt)) {
                         const ptr_ty = try pt.singleMutPtrType(param_ty);
-                        debug_param_types.appendAssumeCapacity(try o.lowerDebugType(ptr_ty));
+                        debug_param_types.appendAssumeCapacity(try o.lowerDebugType(ptr_ty, required_by_runtime));
                     } else {
-                        debug_param_types.appendAssumeCapacity(try o.lowerDebugType(param_ty));
+                        debug_param_types.appendAssumeCapacity(try o.lowerDebugType(param_ty, required_by_runtime));
                     }
                 }
 
@@ -2367,421 +2385,419 @@ pub const Object = struct {
             .Frame => @panic("TODO implement lowerDebugType for Frame types"),
             .AnyFrame => @panic("TODO implement lowerDebugType for AnyFrame types"),
             // These are the types that need a correct scope.
-            .Enum,
-            .Struct,
-            .Union,
-            .Opaque => {}
+            .Enum, .Struct, .Union, .Opaque => {},
         }
+        const fwd_ref = try o.builder.debugForwardReference();
+        try o.debug_type_map.put(gpa, ty, fwd_ref);
+        try o.debug_unresolved_namespace_scopes.put(gpa, ty, required_by_runtime);
 
+        return fwd_ref;
+    }
 
-        const owner_decl_index = ty.getOwnerDeclOrNull(zcu);
-        const owner_decl: ?*Zcu.Decl =
-            if (owner_decl_index) |owner| zcu.declPtr(owner) else null;
+    fn genNamespaces(o: *Object) !void {
+        const gpa = o.gpa;
+        const pt = o.pt;
+        const zcu = pt.zcu;
+        const ip = &zcu.intern_pool;
 
-        const file = if (owner_decl) |owner|
-            try o.getDebugFile(zcu.namespacePtr(owner.src_namespace).fileScope(zcu)) else .none;
-        const scope = if (owner_decl) |owner|
-            try o.namespaceToDebugScope(owner.src_namespace) else o.debug_compile_unit;
-        const line = if (owner_decl) |owner| owner.typeSrcLine(zcu) + 1 else 0;
+        var fields: std.ArrayListUnmanaged(Builder.Metadata) = .{};
+        defer fields.deinit(gpa);
 
+        const unresolved = &o.debug_unresolved_namespace_scopes;
+        var unresolved_i: usize = 0;
+        while (unresolved_i < unresolved.count()) : (unresolved_i += 1) {
+            const ty = unresolved.keys()[unresolved_i];
+            const required_by_runtime = unresolved.values()[unresolved_i];
 
-        const name = if (owner_decl) |owner| owner.name.toSlice(ip) else try o.allocTypeName(ty);
-        defer if (owner_decl == null) gpa.free(name);
+            const owner_decl_index = ty.getOwnerDeclOrNull(zcu);
+            const owner_decl: ?*Zcu.Decl =
+                if (owner_decl_index) |owner| ip.declPtr(owner) else null;
 
-        switch (ty.zigTypeTag(zcu)) {
-            .Enum => {
-                if (!ty.hasRuntimeBitsIgnoreComptime(pt)) {
-                    const debug_enum_type = try o.makeEmptyNamespaceDebugType(owner_decl_index.?);
-                    try o.debug_type_map.put(gpa, ty, debug_enum_type);
-                    return debug_enum_type;
-                }
+            const file = if (owner_decl) |owner|
+                try o.getDebugFile(zcu.namespacePtr(owner.src_namespace).fileScope(zcu))
+            else
+                .none;
+            const scope = if (owner_decl) |owner|
+                try o.namespaceToDebugScope(owner.src_namespace)
+            else
+                o.debug_compile_unit;
+            const line = if (owner_decl) |owner| owner.typeSrcLine(zcu) + 1 else 0;
 
-                const enum_type = ip.loadEnumType(ty.toIntern());
+            const name = if (owner_decl) |owner| owner.name.toSlice(ip) else try o.allocTypeName(ty);
+            defer if (owner_decl == null) gpa.free(name);
 
-                const enumerators = try gpa.alloc(Builder.Metadata, enum_type.names.len);
-                defer gpa.free(enumerators);
+            const fwd_ref = o.debug_type_map.get(ty).?;
 
-                const int_ty = Type.fromInterned(enum_type.tag_ty);
-                const int_info = ty.intInfo(zcu);
-                assert(int_info.bits != 0);
+            fields.clearRetainingCapacity();
 
-                for (enum_type.names.get(ip), 0..) |field_name_ip, i| {
-                    var bigint_space: Value.BigIntSpace = undefined;
-                    const bigint = if (enum_type.values.len != 0)
-                        Value.fromInterned(enum_type.values.get(ip)[i]).toBigInt(&bigint_space, pt)
-                    else
-                        std.math.big.int.Mutable.init(&bigint_space.limbs, i).toConst();
+            const ns = if (ty.getNamespace(zcu)) |n| n.unwrap() else null;
+            if (ns) |ns_id| {
+                const namespace = ip.namespacePtr(ns_id);
+                try fields.ensureUnusedCapacity(gpa, namespace.decls.keys().len);
 
-                    enumerators[i] = try o.builder.debugEnumerator(
-                        try o.builder.metadataString(field_name_ip.toSlice(ip)),
-                        int_info.signedness == .unsigned,
-                        int_info.bits,
-                        bigint,
-                    );
-                }
+                for (namespace.decls.keys()) |decl_id| {
+                    const decl = ip.declPtr(decl_id);
+                    const decl_name = decl.name.toSlice(ip);
 
-                const debug_enum_type = try o.builder.debugEnumerationType(
-                    try o.builder.metadataString(name),
-                    file,
-                    scope,
-                    line,
-                    try o.lowerDebugType(int_ty),
-                    ty.abiSize(pt) * 8,
-                    (ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
-                    try o.builder.debugTuple(enumerators),
-                );
+                    if (!decl.has_tv) continue;
+                    if (decl.kind != .named) continue;
+                    if (decl.analysis != .complete) continue;
 
-                try o.debug_type_map.put(gpa, ty, debug_enum_type);
-                try o.debug_enums.append(gpa, debug_enum_type);
-                return debug_enum_type;
-            },
-            .Opaque => {
-                if (ty.toIntern() == .anyopaque_type) {
-                    const debug_opaque_type = try o.builder.debugSignedType(
-                        try o.builder.metadataString("anyopaque"),
-                        0,
-                    );
-                    try o.debug_type_map.put(gpa, ty, debug_opaque_type);
-                    return debug_opaque_type;
-                }
+                    const decl_line = decl.typeSrcLine(zcu) + 1;
 
-                const debug_opaque_type = try o.builder.debugStructType(
-                    try o.builder.metadataString(name),
-                    file,
-                    scope,
-                    line,
-                    .none, // Underlying type
-                    0, // Size
-                    0, // Align
-                    .none, // Fields
-                );
-                try o.debug_type_map.put(gpa, ty, debug_opaque_type);
-                return debug_opaque_type;
-            },
-            .Struct => {
-                if (zcu.typeToPackedStruct(ty)) |struct_type| {
-                    const backing_int_ty = struct_type.backingIntTypeUnordered(ip);
-                    if (backing_int_ty != .none) {
-                        const info = Type.fromInterned(backing_int_ty).intInfo(zcu);
-                        const builder_name = try o.builder.metadataString(name);
-                        const debug_int_type = switch (info.signedness) {
-                            .signed => try o.builder.debugSignedType(builder_name, ty.abiSize(pt) * 8),
-                            .unsigned => try o.builder.debugUnsignedType(builder_name, ty.abiSize(pt) * 8),
-                        };
-                        try o.debug_type_map.put(gpa, ty, debug_int_type);
-                        return debug_int_type;
+                    if (decl.val.typeOf(zcu).ip_index == .type_type) {
+                        const nested_type = decl.val.toType();
+                        // If this decl is the owner of the type, it will
+                        // already have been declared as a direct child and
+                        // will not need to be typedef'd.
+                        if (nested_type.getOwnerDeclOrNull(zcu)) |owner| {
+                            if (owner == decl_id) continue;
+                        }
+
+                        fields.appendAssumeCapacity(try o.builder.debugTypedef(
+                            try o.builder.metadataString(decl_name),
+                            try o.getDebugFile(namespace.fileScope(zcu)),
+                            fwd_ref,
+                            decl_line,
+                            try o.lowerDebugType(nested_type, false),
+                            0, // Align
+                        ));
+                    } else if (decl.val.getVariable(zcu)) |v| {
+                        fields.appendAssumeCapacity(try o.builder.debugStaticMemberType(
+                            try o.builder.metadataString(decl_name),
+                            try o.getDebugFile(namespace.fileScope(zcu)),
+                            fwd_ref,
+                            decl_line,
+                            try o.lowerDebugType(Type.fromInterned(v.ty), false),
+                        ));
                     }
                 }
+            }
 
-                switch (ip.indexToKey(ty.toIntern())) {
-                    .anon_struct_type => |tuple| {
-                        var fields: std.ArrayListUnmanaged(Builder.Metadata) = .{};
-                        defer fields.deinit(gpa);
-
-                        try fields.ensureUnusedCapacity(gpa, tuple.types.len);
-
-                        comptime assert(struct_layout_version == 2);
-                        var offset: u64 = 0;
-
-                        const debug_fwd_ref = try o.builder.debugForwardReference();
-
-                        for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, field_val, i| {
-                            if (field_val != .none or !Type.fromInterned(field_ty).hasRuntimeBits(pt)) continue;
-
-                            const field_size = Type.fromInterned(field_ty).abiSize(pt);
-                            const field_align = Type.fromInterned(field_ty).abiAlignment(pt);
-                            const field_offset = field_align.forward(offset);
-                            offset = field_offset + field_size;
-
-                            const field_name = if (tuple.names.len != 0)
-                                tuple.names.get(ip)[i].toSlice(ip)
-                            else
-                                try std.fmt.allocPrintZ(gpa, "{d}", .{i});
-                            defer if (tuple.names.len == 0) gpa.free(field_name);
-
-                            fields.appendAssumeCapacity(try o.builder.debugMemberType(
-                                try o.builder.metadataString(field_name),
-                                .none, // File
-                                debug_fwd_ref,
-                                0,
-                                try o.lowerDebugType(Type.fromInterned(field_ty)),
-                                field_size * 8,
-                                (field_align.toByteUnits() orelse 0) * 8,
-                                field_offset * 8,
-                            ));
-                        }
-
-                        const debug_struct_type = try o.builder.debugStructType(
-                            try o.builder.metadataString(name),
-                            file,
-                            scope,
-                            0, // Line
-                            .none, // Underlying type
-                            ty.abiSize(pt) * 8,
-                            (ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
-                            try o.builder.debugTuple(fields.items),
-                        );
+            if (!required_by_runtime) {
+                const res = try o.makeNamespaceDebugType(owner_decl_index.?, fields.items);
+                o.builder.debugForwardReferenceSetType(fwd_ref, res);
+                continue;
+            }
 
-                        o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_struct_type);
+            const res = switch (ty.zigTypeTag(zcu)) {
+                .Enum => res: {
+                    if (!ty.hasRuntimeBitsIgnoreComptime(pt)) {
+                        break :res try o.makeNamespaceDebugType(owner_decl_index.?, fields.items);
+                    }
 
-                        try o.debug_type_map.put(gpa, ty, debug_struct_type);
-                        return debug_struct_type;
-                    },
-                    .struct_type => {
-                        if (!ip.loadStructType(ty.toIntern()).haveFieldTypes(ip)) {
-                            // This can happen if a struct type makes it all the way to
-                            // flush() without ever being instantiated or referenced (even
-                            // via pointer). The only reason we are hearing about it now is
-                            // that it is being used as a namespace to put other debug types
-                            // into. Therefore we can satisfy this by making an empty namespace,
-                            // rather than changing the frontend to unnecessarily resolve the
-                            // struct field types.
-                            const debug_struct_type = try o.makeEmptyNamespaceDebugType(owner_decl_index.?);
-                            try o.debug_type_map.put(gpa, ty, debug_struct_type);
-                            return debug_struct_type;
-                        }
-                    },
-                    else => {},
-                }
+                    const enum_type = ip.loadEnumType(ty.toIntern());
 
-                if (!ty.hasRuntimeBitsIgnoreComptime(pt)) {
-                    const debug_struct_type = try o.makeEmptyNamespaceDebugType(owner_decl_index.?);
-                    try o.debug_type_map.put(gpa, ty, debug_struct_type);
-                    return debug_struct_type;
-                }
+                    const enumerators = try gpa.alloc(Builder.Metadata, enum_type.names.len);
+                    defer gpa.free(enumerators);
 
-                const struct_type = zcu.typeToStruct(ty).?;
+                    const int_ty = Type.fromInterned(enum_type.tag_ty);
+                    const int_info = ty.intInfo(zcu);
+                    assert(int_info.bits != 0);
 
-                var fields: std.ArrayListUnmanaged(Builder.Metadata) = .{};
-                defer fields.deinit(gpa);
+                    for (enum_type.names.get(ip), 0..) |field_name_ip, i| {
+                        var bigint_space: Value.BigIntSpace = undefined;
+                        const bigint = if (enum_type.values.len != 0)
+                            Value.fromInterned(enum_type.values.get(ip)[i]).toBigInt(&bigint_space, pt)
+                        else
+                            std.math.big.int.Mutable.init(&bigint_space.limbs, i).toConst();
 
-                try fields.ensureUnusedCapacity(gpa, struct_type.field_types.len);
+                        enumerators[i] = try o.builder.debugEnumerator(
+                            try o.builder.metadataString(field_name_ip.toSlice(ip)),
+                            int_info.signedness == .unsigned,
+                            int_info.bits,
+                            bigint,
+                        );
+                    }
 
-                const debug_fwd_ref = try o.builder.debugForwardReference();
+                    const debug_enum_type = try o.builder.debugEnumerationType(
+                        try o.builder.metadataString(name),
+                        file,
+                        scope,
+                        line,
+                        try o.lowerDebugType(int_ty, required_by_runtime),
+                        ty.abiSize(pt) * 8,
+                        (ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
+                        try o.builder.debugTuple(enumerators),
+                    );
 
-                // Set as forward reference while the type is lowered in case it references itself
-                try o.debug_type_map.put(gpa, ty, debug_fwd_ref);
+                    try o.debug_enums.append(gpa, debug_enum_type);
+                    break :res debug_enum_type;
+                },
+                .Opaque => res: {
+                    if (ty.toIntern() == .anyopaque_type) {
+                        break :res try o.builder.debugSignedType(
+                            try o.builder.metadataString("anyopaque"),
+                            0,
+                        );
+                    }
 
-                comptime assert(struct_layout_version == 2);
-                var it = struct_type.iterateRuntimeOrder(ip);
-                while (it.next()) |field_index| {
-                    const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]);
-                    if (!field_ty.hasRuntimeBitsIgnoreComptime(pt)) continue;
-                    const field_size = field_ty.abiSize(pt);
-                    const field_align = pt.structFieldAlignment(
-                        struct_type.fieldAlign(ip, field_index),
-                        field_ty,
-                        struct_type.layout,
+                    const debug_opaque_type = try o.builder.debugStructType(
+                        try o.builder.metadataString(name),
+                        file,
+                        scope,
+                        line,
+                        .none, // Underlying type
+                        0, // Size
+                        0, // Align
+                        .none, // Fields
                     );
-                    const field_offset = ty.structFieldOffset(field_index, pt);
+                    break :res debug_opaque_type;
+                },
+                .Struct => res: {
+                    if (zcu.typeToPackedStruct(ty)) |struct_type| {
+                        const backing_int_ty = struct_type.backingIntTypeUnordered(ip);
+                        if (backing_int_ty != .none) {
+                            const info = Type.fromInterned(backing_int_ty).intInfo(zcu);
+                            const builder_name = try o.builder.metadataString(name);
+                            const debug_int_type = switch (info.signedness) {
+                                .signed => try o.builder.debugSignedType(builder_name, ty.abiSize(pt) * 8),
+                                .unsigned => try o.builder.debugUnsignedType(builder_name, ty.abiSize(pt) * 8),
+                            };
+                            break :res debug_int_type;
+                        }
+                    }
 
-                    const field_name = struct_type.fieldName(ip, field_index).unwrap() orelse
-                        try ip.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
+                    switch (ip.indexToKey(ty.toIntern())) {
+                        .anon_struct_type => |tuple| {
+                            try fields.ensureUnusedCapacity(gpa, tuple.types.len);
+
+                            comptime assert(struct_layout_version == 2);
+                            var offset: u64 = 0;
+
+                            for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, field_val, i| {
+                                if (field_val != .none or !Type.fromInterned(field_ty).hasRuntimeBits(pt)) continue;
+
+                                const field_size = Type.fromInterned(field_ty).abiSize(pt);
+                                const field_align = Type.fromInterned(field_ty).abiAlignment(pt);
+                                const field_offset = field_align.forward(offset);
+                                offset = field_offset + field_size;
+
+                                const field_name = if (tuple.names.len != 0)
+                                    tuple.names.get(ip)[i].toSlice(ip)
+                                else
+                                    try std.fmt.allocPrintZ(gpa, "{d}", .{i});
+                                defer if (tuple.names.len == 0) gpa.free(field_name);
+
+                                fields.appendAssumeCapacity(try o.builder.debugMemberType(
+                                    try o.builder.metadataString(field_name),
+                                    .none, // File
+                                    fwd_ref,
+                                    0,
+                                    try o.lowerDebugType(Type.fromInterned(field_ty), required_by_runtime),
+                                    field_size * 8,
+                                    (field_align.toByteUnits() orelse 0) * 8,
+                                    field_offset * 8,
+                                ));
+                            }
 
-                    fields.appendAssumeCapacity(try o.builder.debugMemberType(
-                        try o.builder.metadataString(field_name.toSlice(ip)),
-                        file,
-                        debug_fwd_ref,
-                        0, // Line
-                        try o.lowerDebugType(field_ty),
-                        field_size * 8,
-                        (field_align.toByteUnits() orelse 0) * 8,
-                        field_offset * 8,
-                    ));
-                }
+                            const debug_struct_type = try o.builder.debugStructType(
+                                try o.builder.metadataString(name),
+                                file,
+                                scope,
+                                0, // Line
+                                .none, // Underlying type
+                                ty.abiSize(pt) * 8,
+                                (ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
+                                try o.builder.debugTuple(fields.items),
+                            );
 
-                const debug_struct_type = try o.builder.debugStructType(
-                    try o.builder.metadataString(name),
-                    file,
-                    scope,
-                    line,
-                    .none, // Underlying type
-                    ty.abiSize(pt) * 8,
-                    (ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
-                    try o.builder.debugTuple(fields.items),
-                );
+                            break :res debug_struct_type;
+                        },
+                        else => {},
+                    }
 
-                o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_struct_type);
+                    if (!ty.hasRuntimeBitsIgnoreComptime(pt)) {
+                        break :res try o.makeNamespaceDebugType(owner_decl_index.?, fields.items);
+                    }
+                    const struct_type = zcu.typeToStruct(ty).?;
 
-                // Set to real type now that it has been lowered fully
-                const map_ptr = o.debug_type_map.getPtr(ty) orelse unreachable;
-                map_ptr.* = debug_struct_type;
+                    if (!struct_type.haveLayout(ip) or !struct_type.haveFieldTypes(ip)) {
+                        break :res try o.makeNamespaceDebugType(owner_decl_index.?, fields.items);
+                    }
 
-                return debug_struct_type;
-            },
-            .Union => {
-                const union_type = ip.loadUnionType(ty.toIntern());
-                if (!union_type.haveFieldTypes(ip) or
-                    !ty.hasRuntimeBitsIgnoreComptime(pt) or
-                    !union_type.haveLayout(ip))
-                {
-                    const debug_union_type = try o.makeEmptyNamespaceDebugType(owner_decl_index.?);
-                    try o.debug_type_map.put(gpa, ty, debug_union_type);
-                    return debug_union_type;
-                }
+                    try fields.ensureUnusedCapacity(gpa, struct_type.field_types.len);
 
-                const layout = pt.getUnionLayout(union_type);
+                    comptime assert(struct_layout_version == 2);
+                    var it = struct_type.iterateRuntimeOrder(ip);
+                    while (it.next()) |field_index| {
+                        const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]);
+                        if (!field_ty.hasRuntimeBitsIgnoreComptime(pt)) continue;
+                        const field_size = field_ty.abiSize(pt);
+                        const field_align = pt.structFieldAlignment(
+                            struct_type.fieldAlign(ip, field_index),
+                            field_ty,
+                            struct_type.layout,
+                        );
+                        const field_offset = ty.structFieldOffset(field_index, pt);
 
-                const debug_fwd_ref = try o.builder.debugForwardReference();
+                        const field_name = struct_type.fieldName(ip, field_index).unwrap() orelse
+                            try ip.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
 
-                // Set as forward reference while the type is lowered in case it references itself
-                try o.debug_type_map.put(gpa, ty, debug_fwd_ref);
+                        fields.appendAssumeCapacity(try o.builder.debugMemberType(
+                            try o.builder.metadataString(field_name.toSlice(ip)),
+                            file,
+                            fwd_ref,
+                            0, // Line
+                            try o.lowerDebugType(field_ty, required_by_runtime),
+                            field_size * 8,
+                            (field_align.toByteUnits() orelse 0) * 8,
+                            field_offset * 8,
+                        ));
+                    }
 
-                if (layout.payload_size == 0) {
-                    const debug_union_type = try o.builder.debugStructType(
+                    const debug_struct_type = try o.builder.debugStructType(
                         try o.builder.metadataString(name),
                         file,
                         scope,
-                        0, // Line
+                        line,
                         .none, // Underlying type
                         ty.abiSize(pt) * 8,
                         (ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
-                        try o.builder.debugTuple(
-                            &.{try o.lowerDebugType(Type.fromInterned(union_type.enum_tag_ty))},
-                        ),
+                        try o.builder.debugTuple(fields.items),
                     );
 
-                    // Set to real type now that it has been lowered fully
-                    const map_ptr = o.debug_type_map.getPtr(ty) orelse unreachable;
-                    map_ptr.* = debug_union_type;
-
-                    return debug_union_type;
-                }
-
-                var fields: std.ArrayListUnmanaged(Builder.Metadata) = .{};
-                defer fields.deinit(gpa);
-
-                try fields.ensureUnusedCapacity(gpa, union_type.loadTagType(ip).names.len);
-
-                const debug_union_fwd_ref = if (layout.tag_size == 0)
-                    debug_fwd_ref
-                else
-                    try o.builder.debugForwardReference();
+                    break :res debug_struct_type;
+                },
+                .Union => res: {
+                    const union_type = ip.loadUnionType(ty.toIntern());
+                    if (!union_type.haveFieldTypes(ip) or
+                        !ty.hasRuntimeBitsIgnoreComptime(pt) or
+                        !union_type.haveLayout(ip))
+                    {
+                        break :res try o.makeNamespaceDebugType(owner_decl_index.?, fields.items);
+                    }
 
-                const tag_type = union_type.loadTagType(ip);
+                    const layout = pt.getUnionLayout(union_type);
 
-                for (0..tag_type.names.len) |field_index| {
-                    const field_ty = union_type.field_types.get(ip)[field_index];
-                    if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(pt)) continue;
+                    if (layout.payload_size == 0) {
+                        const debug_union_type = try o.builder.debugStructType(
+                            try o.builder.metadataString(name),
+                            file,
+                            scope,
+                            0, // Line
+                            .none, // Underlying type
+                            ty.abiSize(pt) * 8,
+                            (ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
+                            try o.builder.debugTuple(
+                                &.{try o.lowerDebugType(Type.fromInterned(union_type.enum_tag_ty), required_by_runtime)},
+                            ),
+                        );
 
-                    const field_size = Type.fromInterned(field_ty).abiSize(pt);
-                    const field_align: InternPool.Alignment = switch (union_type.flagsUnordered(ip).layout) {
-                        .@"packed" => .none,
-                        .auto, .@"extern" => pt.unionFieldNormalAlignment(union_type, @intCast(field_index)),
-                    };
+                        break :res debug_union_type;
+                    }
 
-                    const field_name = tag_type.names.get(ip)[field_index];
-                    fields.appendAssumeCapacity(try o.builder.debugMemberType(
-                        try o.builder.metadataString(field_name.toSlice(ip)),
-                        file,
-                        debug_union_fwd_ref,
-                        0, // Line
-                        try o.lowerDebugType(Type.fromInterned(field_ty)),
-                        field_size * 8,
-                        (field_align.toByteUnits() orelse 0) * 8,
-                        0, // Offset
-                    ));
-                }
+                    try fields.ensureUnusedCapacity(gpa, union_type.loadTagType(ip).names.len);
 
-                var union_name_buf: ?[:0]const u8 = null;
-                defer if (union_name_buf) |buf| gpa.free(buf);
-                const union_name = if (layout.tag_size == 0) name else name: {
-                    union_name_buf = try std.fmt.allocPrintZ(gpa, "{s}:Payload", .{name});
-                    break :name union_name_buf.?;
-                };
+                    const debug_union_fwd_ref = if (layout.tag_size == 0)
+                        fwd_ref
+                    else
+                        try o.builder.debugForwardReference();
 
-                const debug_union_type = try o.builder.debugUnionType(
-                    try o.builder.metadataString(union_name),
-                    file,
-                    scope,
-                    line,
-                    .none, // Underlying type
-                    ty.abiSize(pt) * 8,
-                    (ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
-                    try o.builder.debugTuple(fields.items),
-                );
+                    const tag_type = union_type.loadTagType(ip);
 
-                o.builder.debugForwardReferenceSetType(debug_union_fwd_ref, debug_union_type);
+                    for (0..tag_type.names.len) |field_index| {
+                        const field_ty = union_type.field_types.get(ip)[field_index];
+                        if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(pt)) continue;
 
-                if (layout.tag_size == 0) {
-                    // Set to real type now that it has been lowered fully
-                    const map_ptr = o.debug_type_map.getPtr(ty) orelse unreachable;
-                    map_ptr.* = debug_union_type;
+                        const field_size = Type.fromInterned(field_ty).abiSize(pt);
+                        const field_align: InternPool.Alignment = switch (union_type.flagsUnordered(ip).layout) {
+                            .@"packed" => .none,
+                            .auto, .@"extern" => pt.unionFieldNormalAlignment(union_type, @intCast(field_index)),
+                        };
 
-                    return debug_union_type;
-                }
+                        const field_name = tag_type.names.get(ip)[field_index];
+                        fields.appendAssumeCapacity(try o.builder.debugMemberType(
+                            try o.builder.metadataString(field_name.toSlice(ip)),
+                            file,
+                            debug_union_fwd_ref,
+                            0, // Line
+                            try o.lowerDebugType(Type.fromInterned(field_ty), required_by_runtime),
+                            field_size * 8,
+                            (field_align.toByteUnits() orelse 0) * 8,
+                            0, // Offset
+                        ));
+                    }
 
-                var tag_offset: u64 = undefined;
-                var payload_offset: u64 = undefined;
-                if (layout.tag_align.compare(.gte, layout.payload_align)) {
-                    tag_offset = 0;
-                    payload_offset = layout.payload_align.forward(layout.tag_size);
-                } else {
-                    payload_offset = 0;
-                    tag_offset = layout.tag_align.forward(layout.payload_size);
-                }
+                    var union_name_buf: ?[:0]const u8 = null;
+                    defer if (union_name_buf) |buf| gpa.free(buf);
+                    const union_name = if (layout.tag_size == 0) name else name: {
+                        union_name_buf = try std.fmt.allocPrintZ(gpa, "{s}:Payload", .{name});
+                        break :name union_name_buf.?;
+                    };
 
-                const debug_tag_type = try o.builder.debugMemberType(
-                    try o.builder.metadataString("tag"),
-                    file, // File
-                    debug_fwd_ref,
-                    0, // Line
-                    try o.lowerDebugType(Type.fromInterned(union_type.enum_tag_ty)),
-                    layout.tag_size * 8,
-                    (layout.tag_align.toByteUnits() orelse 0) * 8,
-                    tag_offset * 8,
-                );
+                    const debug_union_type = try o.builder.debugUnionType(
+                        try o.builder.metadataString(union_name),
+                        file,
+                        scope,
+                        line,
+                        .none, // Underlying type
+                        ty.abiSize(pt) * 8,
+                        (ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
+                        try o.builder.debugTuple(fields.items),
+                    );
 
-                const debug_payload_type = try o.builder.debugMemberType(
-                    try o.builder.metadataString("payload"),
-                    file,
-                    debug_fwd_ref,
-                    0, // Line
-                    debug_union_type,
-                    layout.payload_size * 8,
-                    (layout.payload_align.toByteUnits() orelse 0) * 8,
-                    payload_offset * 8,
-                );
+                    if (layout.tag_size == 0) {
+                        break :res debug_union_type;
+                    }
 
-                const full_fields: [2]Builder.Metadata =
-                    if (layout.tag_align.compare(.gte, layout.payload_align))
-                    .{ debug_tag_type, debug_payload_type }
-                else
-                    .{ debug_payload_type, debug_tag_type };
+                    o.builder.debugForwardReferenceSetType(debug_union_fwd_ref, debug_union_type);
 
-                const debug_tagged_union_type = try o.builder.debugStructType(
-                    try o.builder.metadataString(name),
-                    file, // File
-                    scope,
-                    line,
-                    .none, // Underlying type
-                    ty.abiSize(pt) * 8,
-                    (ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
-                    try o.builder.debugTuple(&full_fields),
-                );
+                    var tag_offset: u64 = undefined;
+                    var payload_offset: u64 = undefined;
+                    if (layout.tag_align.compare(.gte, layout.payload_align)) {
+                        tag_offset = 0;
+                        payload_offset = layout.payload_align.forward(layout.tag_size);
+                    } else {
+                        payload_offset = 0;
+                        tag_offset = layout.tag_align.forward(layout.payload_size);
+                    }
 
-                o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_tagged_union_type);
+                    const debug_tag_type = try o.builder.debugMemberType(
+                        try o.builder.metadataString("tag"),
+                        file, // File
+                        fwd_ref,
+                        0, // Line
+                        try o.lowerDebugType(Type.fromInterned(union_type.enum_tag_ty), required_by_runtime),
+                        layout.tag_size * 8,
+                        (layout.tag_align.toByteUnits() orelse 0) * 8,
+                        tag_offset * 8,
+                    );
 
-                // Set to real type now that it has been lowered fully
-                const map_ptr = o.debug_type_map.getPtr(ty) orelse unreachable;
-                map_ptr.* = debug_tagged_union_type;
+                    const debug_payload_type = try o.builder.debugMemberType(
+                        try o.builder.metadataString("payload"),
+                        file,
+                        fwd_ref,
+                        0, // Line
+                        debug_union_type,
+                        layout.payload_size * 8,
+                        (layout.payload_align.toByteUnits() orelse 0) * 8,
+                        payload_offset * 8,
+                    );
 
-                return debug_tagged_union_type;
-            },
-            else => unreachable, // Handled above.
-        }
-    }
+                    const full_fields: [2]Builder.Metadata =
+                        if (layout.tag_align.compare(.gte, layout.payload_align))
+                        .{ debug_tag_type, debug_payload_type }
+                    else
+                        .{ debug_payload_type, debug_tag_type };
 
-    fn genNamespaces(o: *Object) !void {
-        var i: usize = 0;
-        while (i < o.debug_unresolved_namespace_scopes.count()) : (i += 1) {
-            const namespace_index = o.debug_unresolved_namespace_scopes.keys()[i];
-            const fwd_ref = o.debug_unresolved_namespace_scopes.values()[i];
+                    const debug_tagged_union_type = try o.builder.debugStructType(
+                        try o.builder.metadataString(name),
+                        file, // File
+                        scope,
+                        line,
+                        .none, // Underlying type
+                        ty.abiSize(pt) * 8,
+                        (ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
+                        try o.builder.debugTuple(&full_fields),
+                    );
 
-            const namespace = o.pt.zcu.namespacePtr(namespace_index);
-            const debug_type = try o.lowerDebugType(namespace.getType(o.pt.zcu));
+                    break :res debug_tagged_union_type;
+                },
+                else => unreachable, // Handled above.
+            };
 
-            o.builder.debugForwardReferenceSetType(fwd_ref, debug_type);
+            o.builder.debugForwardReferenceSetType(fwd_ref, res);
         }
     }
 
@@ -2791,14 +2807,10 @@ pub const Object = struct {
         const file_scope = namespace.fileScope(zcu);
         if (namespace.parent == .none) return try o.getDebugFile(file_scope);
 
-        const gop = try o.debug_unresolved_namespace_scopes.getOrPut(o.gpa, namespace_index);
-
-        if (!gop.found_existing) gop.value_ptr.* = try o.builder.debugForwardReference();
-
-        return gop.value_ptr.*;
+        return o.lowerDebugType(zcu.declPtr(namespace.decl_index).val.toType(), false);
     }
 
-    fn makeEmptyNamespaceDebugType(o: *Object, decl_index: InternPool.DeclIndex) !Builder.Metadata {
+    fn makeNamespaceDebugType(o: *Object, decl_index: InternPool.DeclIndex, fields: []const Builder.Metadata) !Builder.Metadata {
         const zcu = o.pt.zcu;
         const decl = zcu.declPtr(decl_index);
         const file_scope = zcu.namespacePtr(decl.src_namespace).fileScope(zcu);
@@ -2810,7 +2822,7 @@ pub const Object = struct {
             .none,
             0,
             0,
-            .none,
+            if (fields.len == 0) .none else try o.builder.debugTuple(fields),
         );
     }
 
@@ -4723,6 +4735,7 @@ pub const DeclGen = struct {
 
             if (!owner_mod.strip) {
                 const debug_file = try o.getDebugFile(file_scope);
+                const debug_scope = try o.namespaceToDebugScope(decl.src_namespace);
 
                 const linkage_name = try o.builder.metadataStringFromStrtabString(variable_index.name(&o.builder));
 
@@ -4730,10 +4743,10 @@ pub const DeclGen = struct {
                     // Imitate a C++ static member variable since neither
                     // GDB or LLDB can really cope with regular variables
                     // directly inside a struct type.
-                    const ty = try o.lowerDebugType(decl.typeOf(zcu));
+                    const ty = try o.lowerDebugType(decl.typeOf(zcu), true);
                     const name = try o.builder.metadataString(decl.name.toSlice(ip));
 
-                    break :blk try o.builder.debugGlobalVar(
+                    const variable = try o.builder.debugGlobalVar(
                         name,
                         linkage_name,
                         debug_file,
@@ -4742,15 +4755,25 @@ pub const DeclGen = struct {
                         ty,
                         variable_index,
                         .none,
-                        .internal,
+                        .external,
                     );
+
+                    try o.debug_imports.append(o.gpa, try o.builder.debugImportDeclaration(
+                        name,
+                        debug_file,
+                        debug_scope,
+                        line_number,
+                        variable,
+                    ));
+
+                    break :blk variable;
                 } else try o.builder.debugGlobalVar(
                     linkage_name,
                     linkage_name,
                     debug_file,
                     debug_file,
                     line_number,
-                    try o.lowerDebugType(decl.typeOf(zcu)),
+                    try o.lowerDebugType(decl.typeOf(zcu), true),
                     variable_index,
                     .none,
                     .external,
@@ -5209,7 +5232,7 @@ pub const FuncGen = struct {
                 try o.builder.metadataString(decl.fqn.toSlice(&zcu.intern_pool)),
                 line_number,
                 line_number + func.lbrace_line,
-                try o.lowerDebugType(fn_ty),
+                try o.lowerDebugType(fn_ty, true),
                 .{
                     .di_flags = .{ .StaticMember = true },
                     .sp_flags = .{
@@ -6759,7 +6782,7 @@ pub const FuncGen = struct {
             self.file,
             self.scope,
             self.prev_dbg_line,
-            try o.lowerDebugType(ptr_ty.childType(mod)),
+            try o.lowerDebugType(ptr_ty.childType(mod), true),
         );
 
         _ = try self.wip.callIntrinsic(
@@ -6792,7 +6815,7 @@ pub const FuncGen = struct {
             self.file,
             self.scope,
             self.prev_dbg_line,
-            try o.lowerDebugType(operand_ty),
+            try o.lowerDebugType(operand_ty, true),
         );
 
         const pt = o.pt;
@@ -8906,7 +8929,7 @@ pub const FuncGen = struct {
             self.file,
             self.scope,
             lbrace_line,
-            try o.lowerDebugType(inst_ty),
+            try o.lowerDebugType(inst_ty, true),
             @intCast(self.arg_index),
         );