Commit 876258abe4

Tau <jonathan.haehne@hotmail.com>
2024-06-20 19:10:26
llvm: set precise scopes on namespace types and variables
This will allow accessing non-local declarations from debuggers, which, AFAICT, was impossible before. Getting scopes right already works for type declarations and functions, but will need some fiddling for variables: For those, I tried imitating what Clang does for static member variables, but LLDB tries to re-mangle those and then fails at lookup, while GDB outright crashes. Hopefully I can find some other dwarven incantation to do the right thing.
1 parent ebd9efa
Changed files (3)
src
src/codegen/llvm/Builder.zig
@@ -7651,6 +7651,7 @@ pub const Metadata = enum(u32) {
         composite_vector_type,
         derived_pointer_type,
         derived_member_type,
+        derived_static_member_type,
         subroutine_type,
         enumerator_unsigned,
         enumerator_signed_positive,
@@ -7663,6 +7664,7 @@ pub const Metadata = enum(u32) {
         parameter,
         global_var,
         @"global_var local",
+        @"global_var decl",
         global_var_expression,
         constant,
 
@@ -7696,6 +7698,7 @@ pub const Metadata = enum(u32) {
                 .composite_vector_type,
                 .derived_pointer_type,
                 .derived_member_type,
+                .derived_static_member_type,
                 .subroutine_type,
                 .enumerator_unsigned,
                 .enumerator_signed_positive,
@@ -7707,6 +7710,7 @@ pub const Metadata = enum(u32) {
                 .parameter,
                 .global_var,
                 .@"global_var local",
+                .@"global_var decl",
                 .global_var_expression,
                 => false,
             };
@@ -7860,6 +7864,7 @@ pub const Metadata = enum(u32) {
             }
         };
 
+        scope: Metadata,
         file: Metadata,
         name: MetadataString,
         linkage_name: MetadataString,
@@ -7990,8 +7995,10 @@ pub const Metadata = enum(u32) {
     };
 
     pub const GlobalVar = struct {
-        pub const Options = struct {
-            local: bool,
+        pub const Options = enum {
+            internal,
+            internal_decl,
+            external,
         };
 
         name: MetadataString,
@@ -8001,6 +8008,7 @@ pub const Metadata = enum(u32) {
         line: u32,
         ty: Metadata,
         variable: Variable.Index,
+        declaration: Metadata,
     };
 
     pub const GlobalVarExpression = struct {
@@ -9985,7 +9993,7 @@ pub fn printUnbuffered(
                     try metadata_formatter.specialized(.@"distinct !", .DISubprogram, .{
                         .name = extra.name,
                         .linkageName = extra.linkage_name,
-                        .scope = extra.file,
+                        .scope = extra.scope,
                         .file = extra.file,
                         .line = extra.line,
                         .type = extra.ty,
@@ -10079,8 +10087,8 @@ pub fn printUnbuffered(
                             else => extra.name,
                         },
                         .scope = extra.scope,
-                        .file = null,
-                        .line = null,
+                        .file = extra.file,
+                        .line = extra.line,
                         .baseType = extra.underlying_type,
                         .size = extra.bitSize(),
                         .@"align" = extra.bitAlign(),
@@ -10101,6 +10109,7 @@ pub fn printUnbuffered(
                 },
                 .derived_pointer_type,
                 .derived_member_type,
+                .derived_static_member_type,
                 => |kind| {
                     const extra = self.metadataExtraData(Metadata.DerivedType, metadata_item.data);
                     try metadata_formatter.specialized(.@"!", .DIDerivedType, .{
@@ -10109,7 +10118,8 @@ pub fn printUnbuffered(
                             DW_TAG_member,
                         }, switch (kind) {
                             .derived_pointer_type => .DW_TAG_pointer_type,
-                            .derived_member_type => .DW_TAG_member,
+                            .derived_member_type,
+                            .derived_static_member_type => .DW_TAG_member,
                             else => unreachable,
                         }),
                         .name = switch (extra.name) {
@@ -10126,7 +10136,7 @@ pub fn printUnbuffered(
                             0 => null,
                             else => |bit_offset| bit_offset,
                         },
-                        .flags = null,
+                        .flags = null, // TODO staticness
                         .extraData = null,
                         .dwarfAddressSpace = null,
                         .annotations = null,
@@ -10246,6 +10256,7 @@ pub fn printUnbuffered(
                 },
                 .global_var,
                 .@"global_var local",
+                .@"global_var decl",
                 => |kind| {
                     const extra = self.metadataExtraData(Metadata.GlobalVar, metadata_item.data);
                     try metadata_formatter.specialized(.@"distinct !", .DIGlobalVariable, .{
@@ -10255,12 +10266,8 @@ pub fn printUnbuffered(
                         .file = extra.file,
                         .line = extra.line,
                         .type = extra.ty,
-                        .isLocal = switch (kind) {
-                            .global_var => false,
-                            .@"global_var local" => true,
-                            else => unreachable,
-                        },
-                        .isDefinition = true,
+                        .isLocal = kind != .global_var,
+                        .isDefinition = kind != .@"global_var decl",
                         .declaration = null,
                         .templateParams = null,
                         .@"align" = null,
@@ -11749,6 +11756,7 @@ pub fn debugCompileUnit(
 pub fn debugSubprogram(
     self: *Builder,
     file: Metadata,
+    scope: Metadata,
     name: MetadataString,
     linkage_name: MetadataString,
     line: u32,
@@ -11760,6 +11768,7 @@ pub fn debugSubprogram(
     try self.ensureUnusedMetadataCapacity(1, Metadata.Subprogram, 0);
     return self.debugSubprogramAssumeCapacity(
         file,
+        scope,
         name,
         linkage_name,
         line,
@@ -11949,6 +11958,28 @@ pub fn debugPointerType(
     );
 }
 
+pub fn debugStaticMemberType(
+    self: *Builder,
+    name: MetadataString,
+    file: Metadata,
+    scope: Metadata,
+    line: u32,
+    underlying_type: Metadata,
+) Allocator.Error!Metadata {
+    try self.ensureUnusedMetadataCapacity(1, Metadata.DerivedType, 0);
+    return self.debugMemberTypeAssumeCapacity(
+        name,
+        file,
+        scope,
+        line,
+        underlying_type,
+        0,
+        0,
+        0,
+        true,
+    );
+}
+
 pub fn debugMemberType(
     self: *Builder,
     name: MetadataString,
@@ -11970,6 +12001,7 @@ pub fn debugMemberType(
         size_in_bits,
         align_in_bits,
         offset_in_bits,
+        false,
     );
 }
 
@@ -12063,6 +12095,7 @@ pub fn debugGlobalVar(
     line: u32,
     ty: Metadata,
     variable: Variable.Index,
+    declaration: Metadata,
     options: Metadata.GlobalVar.Options,
 ) Allocator.Error!Metadata {
     try self.ensureUnusedMetadataCapacity(1, Metadata.GlobalVar, 0);
@@ -12074,6 +12107,7 @@ pub fn debugGlobalVar(
         line,
         ty,
         variable,
+        declaration,
         options,
     );
 }
@@ -12224,6 +12258,7 @@ pub fn debugCompileUnitAssumeCapacity(
 fn debugSubprogramAssumeCapacity(
     self: *Builder,
     file: Metadata,
+    scope: Metadata,
     name: MetadataString,
     linkage_name: MetadataString,
     line: u32,
@@ -12237,6 +12272,7 @@ fn debugSubprogramAssumeCapacity(
         @as(u3, @truncate(@as(u32, @bitCast(options.sp_flags)) >> 2)));
     return self.metadataDistinctAssumeCapacity(tag, Metadata.Subprogram{
         .file = file,
+        .scope = scope,
         .name = name,
         .linkage_name = linkage_name,
         .line = line,
@@ -12499,21 +12535,25 @@ fn debugMemberTypeAssumeCapacity(
     size_in_bits: u64,
     align_in_bits: u64,
     offset_in_bits: u64,
+    static: bool,
 ) Metadata {
     assert(!self.strip);
-    return self.metadataSimpleAssumeCapacity(.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(
@@ -12769,11 +12809,16 @@ fn debugGlobalVarAssumeCapacity(
     line: u32,
     ty: Metadata,
     variable: Variable.Index,
+    declaration: Metadata,
     options: Metadata.GlobalVar.Options,
 ) Metadata {
     assert(!self.strip);
     return self.metadataDistinctAssumeCapacity(
-        if (options.local) .@"global_var local" else .global_var,
+        switch (options) {
+            .internal => .@"global_var local",
+            .internal_decl => .@"global_var decl",
+            .external => .global_var,
+        },
         Metadata.GlobalVar{
             .name = name,
             .linkage_name = linkage_name,
@@ -12782,6 +12827,7 @@ fn debugGlobalVarAssumeCapacity(
             .line = line,
             .ty = ty,
             .variable = variable,
+            .declaration = declaration,
         },
     );
 }
@@ -13818,7 +13864,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
                         const extra = self.metadataExtraData(Metadata.Subprogram, data);
 
                         try metadata_block.writeAbbrevAdapted(MetadataBlock.Subprogram{
-                            .scope = extra.file,
+                            .scope = extra.scope,
                             .name = extra.name,
                             .linkage_name = extra.linkage_name,
                             .file = extra.file,
@@ -13898,12 +13944,14 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
                     },
                     .derived_pointer_type,
                     .derived_member_type,
+                    .derived_static_member_type,
                     => |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 => DW.TAG.member,
+                                .derived_member_type,
+                                .derived_static_member_type => DW.TAG.member,
                                 else => unreachable,
                             },
                             .name = extra.name,
@@ -13914,6 +13962,9 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
                             .size_in_bits = extra.bitSize(),
                             .align_in_bits = extra.bitAlign(),
                             .offset_in_bits = extra.bitOffset(),
+                            .flags = .{
+                                .StaticMember = kind == .derived_static_member_type,
+                            },
                         }, metadata_adapter);
                     },
                     .subroutine_type => {
@@ -14041,6 +14092,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
                     },
                     .global_var,
                     .@"global_var local",
+                    .@"global_var decl",
                     => |kind| {
                         const extra = self.metadataExtraData(Metadata.GlobalVar, data);
                         try metadata_block.writeAbbrevAdapted(MetadataBlock.GlobalVar{
@@ -14051,6 +14103,8 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
                             .line = extra.line,
                             .ty = extra.ty,
                             .local = kind == .@"global_var local",
+                            .defined = kind != .@"global_var decl",
+                            .declaration = extra.declaration,
                         }, metadata_adapter);
                     },
                     .global_var_expression => {
src/codegen/llvm/ir.zig
@@ -694,7 +694,7 @@ pub const MetadataBlock = struct {
         pub const ops = [_]AbbrevOp{
             .{ .literal = 20 },
             .{ .literal = 1 }, // is distinct
-            .{ .literal = std.dwarf.LANG.C99 }, // source language
+            .{ .literal = std.dwarf.LANG.C_plus_plus_11 }, // source language
             MetadataAbbrev, // file
             MetadataAbbrev, // producer
             .{ .fixed = 1 }, // isOptimized
@@ -863,7 +863,7 @@ pub const MetadataBlock = struct {
             .{ .vbr = 6 }, // size in bits
             .{ .vbr = 6 }, // align in bits
             .{ .vbr = 6 }, // offset in bits
-            .{ .literal = 0 }, // flags
+            .{ .fixed = 32 }, // flags
             .{ .literal = 0 }, // extra data
         };
 
@@ -876,6 +876,7 @@ pub const MetadataBlock = struct {
         size_in_bits: u64,
         align_in_bits: u64,
         offset_in_bits: u64,
+        flags: Builder.Metadata.DIFlags,
     };
 
     pub const SubroutineType = struct {
@@ -1002,8 +1003,8 @@ pub const MetadataBlock = struct {
             LineAbbrev, // line
             MetadataAbbrev, // type
             .{ .fixed = 1 }, // local
-            .{ .literal = 1 }, // defined
-            .{ .literal = 0 }, // static data members declaration
+            .{ .fixed = 1 }, // defined
+            MetadataAbbrev, // static data members declaration
             .{ .literal = 0 }, // template params
             .{ .literal = 0 }, // align in bits
             .{ .literal = 0 }, // annotations
@@ -1016,6 +1017,8 @@ pub const MetadataBlock = struct {
         line: u32,
         ty: Builder.Metadata,
         local: bool,
+        defined: bool,
+        declaration: Builder.Metadata,
     };
 
     pub const GlobalVarExpression = struct {
src/codegen/llvm.zig
@@ -1140,18 +1140,7 @@ pub const Object = struct {
             try self.genModuleLevelAssembly();
 
             if (!self.builder.strip) {
-                {
-                    var i: usize = 0;
-                    while (i < self.debug_unresolved_namespace_scopes.count()) : (i += 1) {
-                        const namespace_index = self.debug_unresolved_namespace_scopes.keys()[i];
-                        const fwd_ref = self.debug_unresolved_namespace_scopes.values()[i];
-
-                        const namespace = zcu.namespacePtr(namespace_index);
-                        const debug_type = try self.lowerDebugType(namespace.getType(zcu));
-
-                        self.builder.debugForwardReferenceSetType(fwd_ref, debug_type);
-                    }
-                }
+                try self.genNamespaces();
 
                 self.builder.debugForwardReferenceSetType(
                     self.debug_enums_fwd_ref,
@@ -1641,6 +1630,7 @@ pub const Object = struct {
 
         const file, const subprogram = if (!wip.strip) debug_info: {
             const file = try o.getDebugFile(file_scope);
+            const scope = try o.namespaceToDebugScope(decl.src_namespace);
 
             const line_number = decl.navSrcLine(zcu) + 1;
             const is_internal_linkage = decl.val.getExternFunc(zcu) == null;
@@ -1648,6 +1638,7 @@ pub const Object = struct {
 
             const subprogram = try o.builder.debugSubprogram(
                 file,
+                scope,
                 try o.builder.metadataString(decl.name.toSlice(ip)),
                 try o.builder.metadataStringFromStrtabString(function_index.name(&o.builder)),
                 line_number,
@@ -1924,6 +1915,7 @@ pub const Object = struct {
 
         if (o.debug_type_map.get(ty)) |debug_type| return debug_type;
 
+
         switch (ty.zigTypeTag(zcu)) {
             .Void,
             .NoReturn,
@@ -1938,9 +1930,9 @@ pub const Object = struct {
             .Int => {
                 const info = ty.intInfo(zcu);
                 assert(info.bits != 0);
-                const name = try o.allocTypeName(ty);
-                defer gpa.free(name);
-                const builder_name = try o.builder.metadataString(name);
+                const int_name = try o.allocTypeName(ty);
+                defer gpa.free(int_name);
+                const builder_name = try o.builder.metadataString(int_name);
                 const debug_bits = ty.abiSize(pt) * 8; // lldb cannot handle non-byte sized types
                 const debug_int_type = switch (info.signedness) {
                     .signed => try o.builder.debugSignedType(builder_name, debug_bits),
@@ -1949,68 +1941,12 @@ pub const Object = struct {
                 try o.debug_type_map.put(gpa, ty, debug_int_type);
                 return debug_int_type;
             },
-            .Enum => {
-                const owner_decl_index = ty.getOwnerDecl(zcu);
-                const owner_decl = zcu.declPtr(owner_decl_index);
-
-                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 enum_type = ip.loadEnumType(ty.toIntern());
-
-                const enumerators = try gpa.alloc(Builder.Metadata, enum_type.names.len);
-                defer gpa.free(enumerators);
-
-                const int_ty = Type.fromInterned(enum_type.tag_ty);
-                const int_info = ty.intInfo(zcu);
-                assert(int_info.bits != 0);
-
-                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();
-
-                    enumerators[i] = try o.builder.debugEnumerator(
-                        try o.builder.metadataString(field_name_ip.toSlice(ip)),
-                        int_info.signedness == .unsigned,
-                        int_info.bits,
-                        bigint,
-                    );
-                }
-
-                const file_scope = zcu.namespacePtr(owner_decl.src_namespace).fileScope(zcu);
-                const file = try o.getDebugFile(file_scope);
-                const scope = try o.namespaceToDebugScope(owner_decl.src_namespace);
-
-                const name = try o.allocTypeName(ty);
-                defer gpa.free(name);
-
-                const debug_enum_type = try o.builder.debugEnumerationType(
-                    try o.builder.metadataString(name),
-                    file,
-                    scope,
-                    owner_decl.typeSrcLine(zcu) + 1, // Line
-                    try o.lowerDebugType(int_ty),
-                    ty.abiSize(pt) * 8,
-                    (ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
-                    try o.builder.debugTuple(enumerators),
-                );
-
-                try o.debug_type_map.put(gpa, ty, debug_enum_type);
-                try o.debug_enums.append(gpa, debug_enum_type);
-                return debug_enum_type;
-            },
             .Float => {
                 const bits = ty.floatBits(target);
-                const name = try o.allocTypeName(ty);
-                defer gpa.free(name);
+                const float_name = try o.allocTypeName(ty);
+                defer gpa.free(float_name);
                 const debug_float_type = try o.builder.debugFloatType(
-                    try o.builder.metadataString(name),
+                    try o.builder.metadataString(float_name),
                     bits,
                 );
                 try o.debug_type_map.put(gpa, ty, debug_float_type);
@@ -2068,6 +2004,7 @@ pub const Object = struct {
 
                     const name = try o.allocTypeName(ty);
                     defer gpa.free(name);
+
                     const line = 0;
 
                     const ptr_size = ptr_ty.abiSize(pt);
@@ -2146,34 +2083,6 @@ pub const Object = struct {
 
                 return debug_ptr_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 name = try o.allocTypeName(ty);
-                defer gpa.free(name);
-                const owner_decl_index = ty.getOwnerDecl(zcu);
-                const owner_decl = zcu.declPtr(owner_decl_index);
-                const file_scope = zcu.namespacePtr(owner_decl.src_namespace).fileScope(zcu);
-                const debug_opaque_type = try o.builder.debugStructType(
-                    try o.builder.metadataString(name),
-                    try o.getDebugFile(file_scope),
-                    try o.namespaceToDebugScope(owner_decl.src_namespace),
-                    owner_decl.typeSrcLine(zcu) + 1, // 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;
-            },
             .Array => {
                 const debug_array_type = try o.builder.debugArrayType(
                     .none, // Name
@@ -2203,9 +2112,9 @@ pub const Object = struct {
                     .Int => blk: {
                         const info = elem_ty.intInfo(zcu);
                         assert(info.bits != 0);
-                        const name = try o.allocTypeName(ty);
-                        defer gpa.free(name);
-                        const builder_name = try o.builder.metadataString(name);
+                        const vec_name = try o.allocTypeName(ty);
+                        defer gpa.free(vec_name);
+                        const builder_name = try o.builder.metadataString(vec_name);
                         break :blk switch (info.signedness) {
                             .signed => try o.builder.debugSignedType(builder_name, info.bits),
                             .unsigned => try o.builder.debugUnsignedType(builder_name, info.bits),
@@ -2240,6 +2149,7 @@ pub const Object = struct {
             .Optional => {
                 const name = try o.allocTypeName(ty);
                 defer gpa.free(name);
+
                 const child_ty = ty.optionalChild(zcu);
                 if (!child_ty.hasRuntimeBitsIgnoreComptime(pt)) {
                     const debug_bool_type = try o.builder.debugBoolType(
@@ -2399,10 +2309,156 @@ pub const Object = struct {
                 try o.debug_type_map.put(gpa, ty, debug_error_set);
                 return debug_error_set;
             },
-            .Struct => {
-                const name = try o.allocTypeName(ty);
-                defer gpa.free(name);
+            .Fn => {
+                const fn_info = zcu.typeToFunc(ty).?;
+
+                var debug_param_types = std.ArrayList(Builder.Metadata).init(gpa);
+                defer debug_param_types.deinit();
+
+                try debug_param_types.ensureUnusedCapacity(3 + fn_info.param_types.len);
+
+                // Return type goes first.
+                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));
+
+                    if (sret) {
+                        const ptr_ty = try pt.singleMutPtrType(Type.fromInterned(fn_info.return_type));
+                        debug_param_types.appendAssumeCapacity(try o.lowerDebugType(ptr_ty));
+                    }
+                } else {
+                    debug_param_types.appendAssumeCapacity(try o.lowerDebugType(Type.void));
+                }
+
+                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));
+                }
+
+                for (0..fn_info.param_types.len) |i| {
+                    const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[i]);
+                    if (!param_ty.hasRuntimeBitsIgnoreComptime(pt)) continue;
+
+                    if (isByRef(param_ty, pt)) {
+                        const ptr_ty = try pt.singleMutPtrType(param_ty);
+                        debug_param_types.appendAssumeCapacity(try o.lowerDebugType(ptr_ty));
+                    } else {
+                        debug_param_types.appendAssumeCapacity(try o.lowerDebugType(param_ty));
+                    }
+                }
+
+                const debug_function_type = try o.builder.debugSubroutineType(
+                    try o.builder.debugTuple(debug_param_types.items),
+                );
+
+                try o.debug_type_map.put(gpa, ty, debug_function_type);
+                return debug_function_type;
+            },
+            .ComptimeInt => unreachable,
+            .ComptimeFloat => unreachable,
+            .Type => unreachable,
+            .Undefined => unreachable,
+            .Null => unreachable,
+            .EnumLiteral => unreachable,
 
+            .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 => {}
+        }
+
+
+        const owner_decl_index = ty.getOwnerDeclOrNull(zcu);
+        const owner_decl: ?*Zcu.Decl =
+            if (owner_decl_index) |owner| zcu.declPtr(owner) else null;
+
+        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 name = if (owner_decl) |owner| owner.name.toSlice(ip) else try o.allocTypeName(ty);
+        defer if (owner_decl == null) gpa.free(name);
+
+        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 enum_type = ip.loadEnumType(ty.toIntern());
+
+                const enumerators = try gpa.alloc(Builder.Metadata, enum_type.names.len);
+                defer gpa.free(enumerators);
+
+                const int_ty = Type.fromInterned(enum_type.tag_ty);
+                const int_info = ty.intInfo(zcu);
+                assert(int_info.bits != 0);
+
+                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();
+
+                    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_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),
+                );
+
+                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 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) {
@@ -2457,8 +2513,8 @@ pub const Object = struct {
 
                         const debug_struct_type = try o.builder.debugStructType(
                             try o.builder.metadataString(name),
-                            .none, // File
-                            o.debug_compile_unit, // Scope
+                            file,
+                            scope,
                             0, // Line
                             .none, // Underlying type
                             ty.abiSize(pt) * 8,
@@ -2480,8 +2536,7 @@ pub const Object = struct {
                             // into. Therefore we can satisfy this by making an empty namespace,
                             // rather than changing the frontend to unnecessarily resolve the
                             // struct field types.
-                            const owner_decl_index = ty.getOwnerDecl(zcu);
-                            const debug_struct_type = try o.makeEmptyNamespaceDebugType(owner_decl_index);
+                            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;
                         }
@@ -2490,8 +2545,7 @@ pub const Object = struct {
                 }
 
                 if (!ty.hasRuntimeBitsIgnoreComptime(pt)) {
-                    const owner_decl_index = ty.getOwnerDecl(zcu);
-                    const debug_struct_type = try o.makeEmptyNamespaceDebugType(owner_decl_index);
+                    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;
                 }
@@ -2526,7 +2580,7 @@ pub const Object = struct {
 
                     fields.appendAssumeCapacity(try o.builder.debugMemberType(
                         try o.builder.metadataString(field_name.toSlice(ip)),
-                        .none, // File
+                        file,
                         debug_fwd_ref,
                         0, // Line
                         try o.lowerDebugType(field_ty),
@@ -2538,9 +2592,9 @@ pub const Object = struct {
 
                 const debug_struct_type = try o.builder.debugStructType(
                     try o.builder.metadataString(name),
-                    .none, // File
-                    o.debug_compile_unit, // Scope
-                    0, // Line
+                    file,
+                    scope,
+                    line,
                     .none, // Underlying type
                     ty.abiSize(pt) * 8,
                     (ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
@@ -2556,17 +2610,12 @@ pub const Object = struct {
                 return debug_struct_type;
             },
             .Union => {
-                const owner_decl_index = ty.getOwnerDecl(zcu);
-
-                const name = try o.allocTypeName(ty);
-                defer gpa.free(name);
-
                 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);
+                    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;
                 }
@@ -2581,8 +2630,8 @@ pub const Object = struct {
                 if (layout.payload_size == 0) {
                     const debug_union_type = try o.builder.debugStructType(
                         try o.builder.metadataString(name),
-                        .none, // File
-                        o.debug_compile_unit, // Scope
+                        file,
+                        scope,
                         0, // Line
                         .none, // Underlying type
                         ty.abiSize(pt) * 8,
@@ -2624,7 +2673,7 @@ pub const Object = struct {
                     const field_name = tag_type.names.get(ip)[field_index];
                     fields.appendAssumeCapacity(try o.builder.debugMemberType(
                         try o.builder.metadataString(field_name.toSlice(ip)),
-                        .none, // File
+                        file,
                         debug_union_fwd_ref,
                         0, // Line
                         try o.lowerDebugType(Type.fromInterned(field_ty)),
@@ -2643,9 +2692,9 @@ pub const Object = struct {
 
                 const debug_union_type = try o.builder.debugUnionType(
                     try o.builder.metadataString(union_name),
-                    .none, // File
-                    o.debug_compile_unit, // Scope
-                    0, // Line
+                    file,
+                    scope,
+                    line,
                     .none, // Underlying type
                     ty.abiSize(pt) * 8,
                     (ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
@@ -2674,7 +2723,7 @@ pub const Object = struct {
 
                 const debug_tag_type = try o.builder.debugMemberType(
                     try o.builder.metadataString("tag"),
-                    .none, // File
+                    file, // File
                     debug_fwd_ref,
                     0, // Line
                     try o.lowerDebugType(Type.fromInterned(union_type.enum_tag_ty)),
@@ -2685,7 +2734,7 @@ pub const Object = struct {
 
                 const debug_payload_type = try o.builder.debugMemberType(
                     try o.builder.metadataString("payload"),
-                    .none, // File
+                    file,
                     debug_fwd_ref,
                     0, // Line
                     debug_union_type,
@@ -2702,9 +2751,9 @@ pub const Object = struct {
 
                 const debug_tagged_union_type = try o.builder.debugStructType(
                     try o.builder.metadataString(name),
-                    .none, // File
-                    o.debug_compile_unit, // Scope
-                    0, // Line
+                    file, // File
+                    scope,
+                    line,
                     .none, // Underlying type
                     ty.abiSize(pt) * 8,
                     (ty.abiAlignment(pt).toByteUnits() orelse 0) * 8,
@@ -2719,63 +2768,20 @@ pub const Object = struct {
 
                 return debug_tagged_union_type;
             },
-            .Fn => {
-                const fn_info = zcu.typeToFunc(ty).?;
-
-                var debug_param_types = std.ArrayList(Builder.Metadata).init(gpa);
-                defer debug_param_types.deinit();
-
-                try debug_param_types.ensureUnusedCapacity(3 + fn_info.param_types.len);
-
-                // Return type goes first.
-                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));
-
-                    if (sret) {
-                        const ptr_ty = try pt.singleMutPtrType(Type.fromInterned(fn_info.return_type));
-                        debug_param_types.appendAssumeCapacity(try o.lowerDebugType(ptr_ty));
-                    }
-                } else {
-                    debug_param_types.appendAssumeCapacity(try o.lowerDebugType(Type.void));
-                }
-
-                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));
-                }
-
-                for (0..fn_info.param_types.len) |i| {
-                    const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[i]);
-                    if (!param_ty.hasRuntimeBitsIgnoreComptime(pt)) continue;
-
-                    if (isByRef(param_ty, pt)) {
-                        const ptr_ty = try pt.singleMutPtrType(param_ty);
-                        debug_param_types.appendAssumeCapacity(try o.lowerDebugType(ptr_ty));
-                    } else {
-                        debug_param_types.appendAssumeCapacity(try o.lowerDebugType(param_ty));
-                    }
-                }
+            else => unreachable, // Handled above.
+        }
+    }
 
-                const debug_function_type = try o.builder.debugSubroutineType(
-                    try o.builder.debugTuple(debug_param_types.items),
-                );
+    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];
 
-                try o.debug_type_map.put(gpa, ty, debug_function_type);
-                return debug_function_type;
-            },
-            .ComptimeInt => unreachable,
-            .ComptimeFloat => unreachable,
-            .Type => unreachable,
-            .Undefined => unreachable,
-            .Null => unreachable,
-            .EnumLiteral => unreachable,
+            const namespace = o.pt.zcu.namespacePtr(namespace_index);
+            const debug_type = try o.lowerDebugType(namespace.getType(o.pt.zcu));
 
-            .Frame => @panic("TODO implement lowerDebugType for Frame types"),
-            .AnyFrame => @panic("TODO implement lowerDebugType for AnyFrame types"),
+            o.builder.debugForwardReferenceSetType(fwd_ref, debug_type);
         }
     }
 
@@ -4718,17 +4724,37 @@ pub const DeclGen = struct {
             if (!owner_mod.strip) {
                 const debug_file = try o.getDebugFile(file_scope);
 
-                const debug_global_var = try o.builder.debugGlobalVar(
-                    try o.builder.metadataString(decl.name.toSlice(ip)), // Name
-                    try o.builder.metadataStringFromStrtabString(variable_index.name(&o.builder)), // Linkage name
-                    debug_file, // File
-                    debug_file, // Scope
+                const linkage_name = try o.builder.metadataStringFromStrtabString(variable_index.name(&o.builder));
+
+                const debug_global_var = if (!decl.isExtern(zcu)) blk: {
+                    // 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 name = try o.builder.metadataString(decl.name.toSlice(ip));
+
+                    break :blk try o.builder.debugGlobalVar(
+                        name,
+                        linkage_name,
+                        debug_file,
+                        debug_file,
+                        line_number,
+                        ty,
+                        variable_index,
+                        .none,
+                        .internal,
+                    );
+                } else try o.builder.debugGlobalVar(
+                    linkage_name,
+                    linkage_name,
+                    debug_file,
+                    debug_file,
                     line_number,
                     try o.lowerDebugType(decl.typeOf(zcu)),
                     variable_index,
-                    .{ .local = !decl.isExtern(zcu) },
+                    .none,
+                    .external,
                 );
-
                 const debug_expression = try o.builder.debugExpression(&.{});
 
                 const debug_global_var_expression = try o.builder.debugGlobalVarExpression(
@@ -5178,6 +5204,7 @@ pub const FuncGen = struct {
 
             self.scope = try o.builder.debugSubprogram(
                 self.file,
+                self.file, // TODO Get the correct scope into here—self.scope is the function's *inner* scope.
                 try o.builder.metadataString(decl.name.toSlice(&zcu.intern_pool)),
                 try o.builder.metadataString(decl.fqn.toSlice(&zcu.intern_pool)),
                 line_number,