Commit 220f80e71d

Jacob Young <jacobly0@users.noreply.github.com>
2025-02-22 17:47:22
Dwarf: fix lowering of comptime-only optional pointer `null` values
Closes #22974
1 parent 4b0f77c
Changed files (4)
src/link/Dwarf.zig
@@ -3248,75 +3248,72 @@ fn updateLazyType(
         },
         .opt_type => |opt_child_type_index| {
             const opt_child_type: Type = .fromInterned(opt_child_type_index);
+            const opt_repr = optRepr(opt_child_type, zcu);
             try wip_nav.abbrevCode(.generated_union_type);
             try wip_nav.strp(name);
             try uleb128(diw, ty.abiSize(zcu));
             try uleb128(diw, ty.abiAlignment(zcu).toByteUnits().?);
-            if (opt_child_type.isNoReturn(zcu)) {
-                try wip_nav.abbrevCode(.generated_field);
-                try wip_nav.strp("null");
-                try wip_nav.refType(.null);
-                try uleb128(diw, 0);
-            } else {
-                try wip_nav.abbrevCode(.tagged_union);
-                try wip_nav.infoSectionOffset(
-                    .debug_info,
-                    wip_nav.unit,
-                    wip_nav.entry,
-                    @intCast(wip_nav.debug_info.items.len + dwarf.sectionOffsetBytes()),
-                );
-                {
+            switch (opt_repr) {
+                .opv_null => {
                     try wip_nav.abbrevCode(.generated_field);
-                    try wip_nav.strp("has_value");
-                    const repr: enum { unpacked, error_set, pointer } = switch (opt_child_type_index) {
-                        .anyerror_type => .error_set,
-                        else => switch (ip.indexToKey(opt_child_type_index)) {
-                            else => .unpacked,
-                            .error_set_type, .inferred_error_set_type => .error_set,
-                            .ptr_type => |ptr_type| if (ptr_type.flags.is_allowzero) .unpacked else .pointer,
-                        },
-                    };
-                    switch (repr) {
-                        .unpacked => {
-                            try wip_nav.refType(.bool);
-                            try uleb128(diw, if (opt_child_type.hasRuntimeBits(zcu))
-                                opt_child_type.abiSize(zcu)
-                            else
-                                0);
-                        },
-                        .error_set => {
-                            try wip_nav.refType(.fromInterned(try pt.intern(.{ .int_type = .{
-                                .signedness = .unsigned,
-                                .bits = zcu.errorSetBits(),
-                            } })));
-                            try uleb128(diw, 0);
-                        },
-                        .pointer => {
-                            try wip_nav.refType(.usize);
-                            try uleb128(diw, 0);
-                        },
-                    }
-
-                    try wip_nav.abbrevCode(.unsigned_tagged_union_field);
+                    try wip_nav.strp("null");
+                    try wip_nav.refType(.null);
                     try uleb128(diw, 0);
+                },
+                .unpacked, .error_set, .pointer => {
+                    try wip_nav.abbrevCode(.tagged_union);
+                    try wip_nav.infoSectionOffset(
+                        .debug_info,
+                        wip_nav.unit,
+                        wip_nav.entry,
+                        @intCast(wip_nav.debug_info.items.len + dwarf.sectionOffsetBytes()),
+                    );
                     {
                         try wip_nav.abbrevCode(.generated_field);
-                        try wip_nav.strp("null");
-                        try wip_nav.refType(.null);
-                        try uleb128(diw, 0);
-                    }
-                    try uleb128(diw, @intFromEnum(AbbrevCode.null));
+                        try wip_nav.strp("has_value");
+                        switch (opt_repr) {
+                            .opv_null => unreachable,
+                            .unpacked => {
+                                try wip_nav.refType(.bool);
+                                try uleb128(diw, if (opt_child_type.hasRuntimeBits(zcu))
+                                    opt_child_type.abiSize(zcu)
+                                else
+                                    0);
+                            },
+                            .error_set => {
+                                try wip_nav.refType(.fromInterned(try pt.intern(.{ .int_type = .{
+                                    .signedness = .unsigned,
+                                    .bits = zcu.errorSetBits(),
+                                } })));
+                                try uleb128(diw, 0);
+                            },
+                            .pointer => {
+                                try wip_nav.refType(.usize);
+                                try uleb128(diw, 0);
+                            },
+                        }
 
-                    try wip_nav.abbrevCode(.tagged_union_default_field);
-                    {
-                        try wip_nav.abbrevCode(.generated_field);
-                        try wip_nav.strp("?");
-                        try wip_nav.refType(opt_child_type);
+                        try wip_nav.abbrevCode(.unsigned_tagged_union_field);
                         try uleb128(diw, 0);
+                        {
+                            try wip_nav.abbrevCode(.generated_field);
+                            try wip_nav.strp("null");
+                            try wip_nav.refType(.null);
+                            try uleb128(diw, 0);
+                        }
+                        try uleb128(diw, @intFromEnum(AbbrevCode.null));
+
+                        try wip_nav.abbrevCode(.tagged_union_default_field);
+                        {
+                            try wip_nav.abbrevCode(.generated_field);
+                            try wip_nav.strp("?");
+                            try wip_nav.refType(opt_child_type);
+                            try uleb128(diw, 0);
+                        }
+                        try uleb128(diw, @intFromEnum(AbbrevCode.null));
                     }
                     try uleb128(diw, @intFromEnum(AbbrevCode.null));
-                }
-                try uleb128(diw, @intFromEnum(AbbrevCode.null));
+                },
             }
             try uleb128(diw, @intFromEnum(AbbrevCode.null));
         },
@@ -3850,22 +3847,31 @@ fn updateLazyValue(
             try uleb128(diw, @intFromEnum(AbbrevCode.null));
         },
         .opt => |opt| {
-            const child_type: Type = .fromInterned(ip.indexToKey(opt.ty).opt_type);
+            const opt_child_type: Type = .fromInterned(ip.indexToKey(opt.ty).opt_type);
             try wip_nav.abbrevCode(.aggregate_comptime_value);
             try wip_nav.refType(.fromInterned(opt.ty));
             {
                 try wip_nav.abbrevCode(.comptime_value_field_runtime_bits);
                 try wip_nav.strp("has_value");
-                if (Type.fromInterned(opt.ty).optionalReprIsPayload(zcu)) {
-                    try wip_nav.blockValue(src_loc, .fromInterned(opt.val));
-                } else {
-                    try uleb128(diw, 1);
-                    try diw.writeByte(@intFromBool(opt.val != .none));
+                switch (optRepr(opt_child_type, zcu)) {
+                    .opv_null => try uleb128(diw, 0),
+                    .unpacked => try wip_nav.blockValue(src_loc, .makeBool(opt.val != .none)),
+                    .error_set => try wip_nav.blockValue(src_loc, .fromInterned(value_index)),
+                    .pointer => if (opt_child_type.comptimeOnly(zcu)) {
+                        var buf: [8]u8 = undefined;
+                        const bytes = buf[0..@divExact(zcu.getTarget().ptrBitWidth(), 8)];
+                        dwarf.writeInt(bytes, switch (opt.val) {
+                            .none => 0,
+                            else => opt_child_type.ptrAlignment(zcu).toByteUnits().?,
+                        });
+                        try uleb128(diw, bytes.len);
+                        try diw.writeAll(bytes);
+                    } else try wip_nav.blockValue(src_loc, .fromInterned(value_index)),
                 }
             }
             if (opt.val != .none) child_field: {
-                const has_runtime_bits = child_type.hasRuntimeBits(zcu);
-                const has_comptime_state = child_type.comptimeOnly(zcu) and try child_type.onePossibleValue(pt) == null;
+                const has_runtime_bits = opt_child_type.hasRuntimeBits(zcu);
+                const has_comptime_state = opt_child_type.comptimeOnly(zcu) and try opt_child_type.onePossibleValue(pt) == null;
                 try wip_nav.abbrevCode(if (has_comptime_state)
                     .comptime_value_field_comptime_state
                 else if (has_runtime_bits)
@@ -3995,6 +4001,23 @@ fn updateLazyValue(
     try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.items);
 }
 
+fn optRepr(opt_child_type: Type, zcu: *const Zcu) enum {
+    unpacked,
+    opv_null,
+    error_set,
+    pointer,
+} {
+    if (opt_child_type.isNoReturn(zcu)) return .opv_null;
+    return switch (opt_child_type.toIntern()) {
+        .anyerror_type => .error_set,
+        else => switch (zcu.intern_pool.indexToKey(opt_child_type.toIntern())) {
+            else => .unpacked,
+            .error_set_type, .inferred_error_set_type => .error_set,
+            .ptr_type => |ptr_type| if (ptr_type.flags.is_allowzero) .unpacked else .pointer,
+        },
+    };
+}
+
 pub fn updateContainerType(dwarf: *Dwarf, pt: Zcu.PerThread, type_index: InternPool.Index) UpdateError!void {
     const zcu = pt.zcu;
     const ip = &zcu.intern_pool;
src/Type.zig
@@ -808,7 +808,7 @@ pub fn isNoReturn(ty: Type, zcu: *const Zcu) bool {
     return zcu.intern_pool.isNoReturn(ty.toIntern());
 }
 
-/// Returns `none` if the pointer is naturally aligned and the element type is 0-bit.
+/// Never returns `none`. Asserts that all necessary type resolution is already done.
 pub fn ptrAlignment(ty: Type, zcu: *Zcu) Alignment {
     return ptrAlignmentInner(ty, .normal, zcu, {}) catch unreachable;
 }
@@ -825,15 +825,9 @@ pub fn ptrAlignmentInner(
 ) !Alignment {
     return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
         .ptr_type => |ptr_type| {
-            if (ptr_type.flags.alignment != .none)
-                return ptr_type.flags.alignment;
-
-            if (strat == .sema) {
-                const res = try Type.fromInterned(ptr_type.child).abiAlignmentInner(.sema, zcu, tid);
-                return res.scalar;
-            }
-
-            return Type.fromInterned(ptr_type.child).abiAlignment(zcu);
+            if (ptr_type.flags.alignment != .none) return ptr_type.flags.alignment;
+            const res = try Type.fromInterned(ptr_type.child).abiAlignmentInner(strat.toLazy(), zcu, tid);
+            return res.scalar;
         },
         .opt_type => |child| Type.fromInterned(child).ptrAlignmentInner(strat, zcu, tid),
         else => unreachable,
src/Value.zig
@@ -3690,7 +3690,7 @@ pub const generic_poison_type: Value = .{ .ip_index = .generic_poison_type };
 pub const empty_tuple: Value = .{ .ip_index = .empty_tuple };
 
 pub fn makeBool(x: bool) Value {
-    return if (x) Value.true else Value.false;
+    return if (x) .true else .false;
 }
 
 /// `parent_ptr` must be a single-pointer to some optional.
test/behavior/optional.zig
@@ -656,3 +656,14 @@ test "result location initialization of optional with OPV payload" {
     _ = &c;
     try expectEqual(0, (c orelse return error.TestFailed).x);
 }
+
+test "global comptime only optional" {
+    const S = struct {
+        const @"null": ?*type = null;
+        const @"void": ?*const type = &void;
+    };
+    comptime {
+        assert(S.null == null);
+        assert(S.void.?.* == void);
+    }
+}