From c699286dd3f59d4efe1ff294193c516f945f7687 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 22 Feb 2025 11:47:22 -0500 Subject: [PATCH] Dwarf: fix lowering of comptime-only optional pointer `null` values Closes #22974 --- src/Type.zig | 14 +--- src/Value.zig | 2 +- src/link/Dwarf.zig | 153 +++++++++++++++++++++---------------- test/behavior/optional.zig | 11 +++ 4 files changed, 104 insertions(+), 76 deletions(-) diff --git a/src/Type.zig b/src/Type.zig index 883c752ef10a..74e1497b8132 100644 --- a/src/Type.zig +++ b/src/Type.zig @@ -806,7 +806,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; } @@ -823,15 +823,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, diff --git a/src/Value.zig b/src/Value.zig index f9f552aa0f42..be2c73c3e96d 100644 --- a/src/Value.zig +++ b/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. diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index e2a189e8e754..16b8575ee513 100644 --- a/src/link/Dwarf.zig +++ b/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; diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index 1f8acfaa3909..36e5ba579cd4 100644 --- a/test/behavior/optional.zig +++ b/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); + } +}