authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-03-04 10:51:45+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-03-10 10:26:15+00:00
logea7e34224a8c3c0551e17a60fd849efee82447f7
tree96379f798486f191a84fac86c91cfb6cd2daf934
parent0f3c883245f27870472978f738d6e81958e15c52
signaturelock-open Commit is signed but in an unrecognized format.

compiler: don't call `getUnionLayout` on packed unions


4 files changed, 168 insertions(+), 172 deletions(-)

src/Type.zig+4
......@@ -1571,6 +1571,7 @@ pub fn externUnionBackingType(ty: Type, pt: Zcu.PerThread) !Type {
15711571 }
15721572}
15731573
1574/// Asserts that `ty` is a non-packed union type.
15741575pub fn unionGetLayout(ty: Type, zcu: *const Zcu) Zcu.UnionLayout {
15751576 assertHasLayout(ty, zcu);
15761577 const union_obj = zcu.intern_pool.loadUnionType(ty.toIntern());
......@@ -2689,7 +2690,10 @@ pub fn arrayBase(ty: Type, zcu: *const Zcu) struct { Type, u64 } {
26892690 return .{ cur_ty, cur_len };
26902691}
26912692
2693/// Asserts that `loaded_union.layout` is not `.@"packed"`.
26922694pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) Zcu.UnionLayout {
2695 assert(loaded_union.layout != .@"packed");
2696
26932697 const ip = &zcu.intern_pool;
26942698 var most_aligned_field: u32 = 0;
26952699 var most_aligned_field_align: InternPool.Alignment = .@"1";
src/codegen/llvm.zig+63-68
......@@ -2369,6 +2369,30 @@ pub const Object = struct {
23692369 const line = ty.typeDeclSrcLine(zcu).? + 1;
23702370
23712371 const enum_tag_ty: Type = .fromInterned(union_type.enum_tag_type);
2372
2373 if (union_type.layout == .@"packed") {
2374 const bitpack_field = try o.builder.debugMemberType(
2375 try o.builder.metadataString("bits"),
2376 null, // file
2377 ty_fwd_ref,
2378 0, // line
2379 try o.getDebugType(pt, .fromInterned(union_type.packed_backing_int_type)),
2380 ty.abiSize(zcu) * 8,
2381 ty.abiAlignment(zcu).toByteUnits().? * 8,
2382 0, // offset
2383 );
2384 return o.builder.debugStructType(
2385 name,
2386 file,
2387 scope,
2388 line,
2389 null, // underlying type
2390 ty.abiSize(zcu) * 8,
2391 ty.abiAlignment(zcu).toByteUnits().? * 8,
2392 try o.builder.metadataTuple(&.{bitpack_field}),
2393 );
2394 }
2395
23722396 const layout = Type.getUnionLayout(union_type, zcu);
23732397
23742398 if (layout.payload_size == 0) {
......@@ -2411,10 +2435,7 @@ pub const Object = struct {
24112435 const field_ty = union_type.field_types.get(ip)[field_index];
24122436
24132437 const field_size = Type.fromInterned(field_ty).abiSize(zcu);
2414 const field_align: InternPool.Alignment = switch (union_type.layout) {
2415 .@"packed" => .none,
2416 .auto, .@"extern" => ty.explicitFieldAlignment(field_index, zcu),
2417 };
2438 const field_align: InternPool.Alignment = ty.explicitFieldAlignment(field_index, zcu);
24182439
24192440 const field_name = enum_tag_ty.enumFieldName(field_index, zcu);
24202441 fields.appendAssumeCapacity(try o.builder.debugMemberType(
......@@ -3318,7 +3339,6 @@ pub const Object = struct {
33183339 if (o.type_map.get(t.toIntern())) |value| return value;
33193340
33203341 const union_obj = ip.loadUnionType(t.toIntern());
3321 const layout = Type.getUnionLayout(union_obj, zcu);
33223342
33233343 if (union_obj.layout == .@"packed") {
33243344 const int_ty = try o.lowerType(pt, .fromInterned(union_obj.packed_backing_int_type));
......@@ -3326,6 +3346,8 @@ pub const Object = struct {
33263346 return int_ty;
33273347 }
33283348
3349 const layout = Type.getUnionLayout(union_obj, zcu);
3350
33293351 if (layout.payload_size == 0) {
33303352 const enum_tag_ty = try o.lowerType(pt, .fromInterned(union_obj.enum_tag_type));
33313353 try o.type_map.put(o.gpa, t.toIntern(), enum_tag_ty);
......@@ -6760,7 +6782,7 @@ pub const FuncGen = struct {
67606782 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
67616783 const struct_ptr = try self.resolveInst(struct_field.struct_operand);
67626784 const struct_ptr_ty = self.typeOf(struct_field.struct_operand);
6763 return self.fieldPtr(inst, struct_ptr, struct_ptr_ty, struct_field.field_index);
6785 return self.fieldPtr(struct_ptr, struct_ptr_ty, struct_field.field_index);
67646786 }
67656787
67666788 fn airStructFieldPtrIndex(
......@@ -6771,7 +6793,7 @@ pub const FuncGen = struct {
67716793 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
67726794 const struct_ptr = try self.resolveInst(ty_op.operand);
67736795 const struct_ptr_ty = self.typeOf(ty_op.operand);
6774 return self.fieldPtr(inst, struct_ptr, struct_ptr_ty, field_index);
6796 return self.fieldPtr(struct_ptr, struct_ptr_ty, field_index);
67756797 }
67766798
67776799 fn airStructFieldVal(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
......@@ -10704,18 +10726,11 @@ pub const FuncGen = struct {
1070410726 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
1070510727 const union_ty = self.typeOfIndex(inst);
1070610728 const union_llvm_ty = try o.lowerType(pt, union_ty);
10707 const layout = union_ty.unionGetLayout(zcu);
1070810729 const union_obj = zcu.typeToUnion(union_ty).?;
1070910730
10710 if (union_obj.layout == .@"packed") {
10711 const big_bits = union_ty.bitSize(zcu);
10712 const int_llvm_ty = try o.builder.intType(@intCast(big_bits));
10713 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[extra.field_index]);
10714 const non_int_val = try self.resolveInst(extra.init);
10715 const small_int_ty = try o.builder.intType(@intCast(field_ty.bitSize(zcu)));
10716 const small_int_val = try self.wip.cast(.bitcast, non_int_val, small_int_ty, "");
10717 return self.wip.conv(.unsigned, small_int_val, int_llvm_ty, "");
10718 }
10731 assert(union_obj.layout != .@"packed");
10732
10733 const layout = Type.getUnionLayout(union_obj, zcu);
1071910734
1072010735 const tag_int_val = blk: {
1072110736 const tag_ty = union_ty.unionTagTypeHypothetical(zcu);
......@@ -11051,65 +11066,45 @@ pub const FuncGen = struct {
1105111066
1105211067 fn fieldPtr(
1105311068 self: *FuncGen,
11054 inst: Air.Inst.Index,
11055 struct_ptr: Builder.Value,
11056 struct_ptr_ty: Type,
11069 aggregate_ptr: Builder.Value,
11070 aggregate_ptr_ty: Type,
1105711071 field_index: u32,
1105811072 ) !Builder.Value {
1105911073 const o = self.ng.object;
1106011074 const pt = self.ng.pt;
1106111075 const zcu = pt.zcu;
11062 const struct_ty = struct_ptr_ty.childType(zcu);
11063 switch (struct_ty.zigTypeTag(zcu)) {
11064 .@"struct" => switch (struct_ty.containerLayout(zcu)) {
11065 .@"packed" => {
11066 const result_ty = self.typeOfIndex(inst);
11067 const result_ty_info = result_ty.ptrInfo(zcu);
11068 const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(zcu);
11069 const struct_type = zcu.typeToStruct(struct_ty).?;
11070
11071 if (result_ty_info.packed_offset.host_size != 0) {
11072 // From LLVM's perspective, a pointer to a packed struct and a pointer
11073 // to a field of a packed struct are the same. The difference is in the
11074 // Zig pointer type which provides information for how to mask and shift
11075 // out the relevant bits when accessing the pointee.
11076 return struct_ptr;
11077 }
11078
11079 // We have a pointer to a packed struct field that happens to be byte-aligned.
11080 // Offset our operand pointer by the correct number of bytes.
11081 const byte_offset = @divExact(zcu.structPackedFieldBitOffset(struct_type, field_index) + struct_ptr_ty_info.packed_offset.bit_offset, 8);
11082 if (byte_offset == 0) return struct_ptr;
11083 const usize_ty = try o.lowerType(pt, Type.usize);
11084 const llvm_index = try o.builder.intValue(usize_ty, byte_offset);
11085 return self.wip.gep(.inbounds, .i8, struct_ptr, &.{llvm_index}, "");
11086 },
11087 else => {
11088 if (!struct_ty.hasRuntimeBits(zcu)) {
11089 return struct_ptr;
11090 }
11091 const struct_llvm_ty = try o.lowerType(pt, struct_ty);
11092 if (o.llvmFieldIndex(struct_ty, field_index)) |llvm_field_index| {
11093 return self.wip.gepStruct(struct_llvm_ty, struct_ptr, llvm_field_index, "");
11094 } else {
11095 // If we found no index then this means this is a zero sized field at the
11096 // end of the struct. Treat our struct pointer as an array of two and get
11097 // the index to the element at index `1` to get a pointer to the end of
11098 // the struct.
11099 const llvm_index = try o.builder.intValue(
11100 try o.lowerType(pt, Type.usize),
11101 @intFromBool(struct_ty.hasRuntimeBits(zcu)),
11102 );
11103 return self.wip.gep(.inbounds, struct_llvm_ty, struct_ptr, &.{llvm_index}, "");
11104 }
11105 },
11076 const aggregate_ty = aggregate_ptr_ty.childType(zcu);
11077 if (aggregate_ty.containerLayout(zcu) == .@"packed") {
11078 // A pointer to a bitpack field is equivalent to a pointer to the whole bitpack; the
11079 // bit offset is represented in the pointer *type*.
11080 return aggregate_ptr;
11081 }
11082 switch (aggregate_ty.zigTypeTag(zcu)) {
11083 .@"struct" => {
11084 if (!aggregate_ty.hasRuntimeBits(zcu)) {
11085 return aggregate_ptr;
11086 }
11087 const struct_llvm_ty = try o.lowerType(pt, aggregate_ty);
11088 if (o.llvmFieldIndex(aggregate_ty, field_index)) |llvm_field_index| {
11089 return self.wip.gepStruct(struct_llvm_ty, aggregate_ptr, llvm_field_index, "");
11090 } else {
11091 // If we found no index then this means this is a zero sized field at the
11092 // end of the struct. Treat our struct pointer as an array of two and get
11093 // the index to the element at index `1` to get a pointer to the end of
11094 // the struct.
11095 const llvm_index = try o.builder.intValue(
11096 try o.lowerType(pt, Type.usize),
11097 @intFromBool(aggregate_ty.hasRuntimeBits(zcu)),
11098 );
11099 return self.wip.gep(.inbounds, struct_llvm_ty, aggregate_ptr, &.{llvm_index}, "");
11100 }
1110611101 },
1110711102 .@"union" => {
11108 const layout = struct_ty.unionGetLayout(zcu);
11109 if (layout.payload_size == 0 or struct_ty.containerLayout(zcu) == .@"packed") return struct_ptr;
11103 const layout = aggregate_ty.unionGetLayout(zcu);
11104 if (layout.payload_size == 0) return aggregate_ptr;
1111011105 const payload_index = @intFromBool(layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align));
11111 const union_llvm_ty = try o.lowerType(pt, struct_ty);
11112 return self.wip.gepStruct(union_llvm_ty, struct_ptr, payload_index, "");
11106 const union_llvm_ty = try o.lowerType(pt, aggregate_ty);
11107 return self.wip.gepStruct(union_llvm_ty, aggregate_ptr, payload_index, "");
1111311108 },
1111411109 else => unreachable,
1111511110 }
src/codegen/spirv/CodeGen.zig+26-46
......@@ -4519,30 +4519,7 @@ fn unionInit(
45194519 const layout = cg.unionLayout(ty);
45204520 const payload_ty: Type = .fromInterned(union_ty.field_types.get(ip)[active_field]);
45214521
4522 if (union_ty.layout == .@"packed") {
4523 if (!payload_ty.hasRuntimeBits(zcu)) {
4524 const int_ty = try pt.intType(.unsigned, @intCast(ty.bitSize(zcu)));
4525 return cg.constInt(int_ty, 0);
4526 }
4527
4528 assert(payload != null);
4529 if (payload_ty.isInt(zcu)) {
4530 if (ty.bitSize(zcu) == payload_ty.bitSize(zcu)) {
4531 return cg.bitCast(ty, payload_ty, payload.?);
4532 }
4533
4534 const trunc = try cg.buildConvert(ty, .{ .ty = payload_ty, .value = .{ .singleton = payload.? } });
4535 return try trunc.materialize(cg);
4536 }
4537
4538 const payload_int_ty = try pt.intType(.unsigned, @intCast(payload_ty.bitSize(zcu)));
4539 const payload_int = if (payload_ty.ip_index == .bool_type)
4540 try cg.convertToIndirect(payload_ty, payload.?)
4541 else
4542 try cg.bitCast(payload_int_ty, payload_ty, payload.?);
4543 const trunc = try cg.buildConvert(ty, .{ .ty = payload_int_ty, .value = .{ .singleton = payload_int } });
4544 return try trunc.materialize(cg);
4545 }
4522 assert(union_ty.layout != .@"packed");
45464523
45474524 const tag_int = if (layout.tag_size != 0) blk: {
45484525 const tag_val = try pt.enumValueFieldIndex(tag_ty, active_field);
......@@ -4761,33 +4738,36 @@ fn structFieldPtr(
47614738 },
47624739 .@"struct" => switch (object_ty.containerLayout(zcu)) {
47634740 .@"packed" => return cg.todo("implement field access for packed structs", .{}),
4764 else => {
4741 .auto, .@"extern" => {
47654742 return try cg.accessChain(result_ty_id, object_ptr, &.{field_index});
47664743 },
47674744 },
4768 .@"union" => {
4769 const layout = cg.unionLayout(object_ty);
4770 if (!layout.has_payload) {
4771 // Asked to get a pointer to a zero-sized field. Just lower this
4772 // to undefined, there is no reason to make it be a valid pointer.
4773 return try cg.module.constUndef(result_ty_id);
4774 }
4745 .@"union" => switch (object_ty.containerLayout(zcu)) {
4746 .@"packed" => return cg.todo("implement field access for packed unions", .{}),
4747 .auto, .@"extern" => {
4748 const layout = cg.unionLayout(object_ty);
4749 if (!layout.has_payload) {
4750 // Asked to get a pointer to a zero-sized field. Just lower this
4751 // to undefined, there is no reason to make it be a valid pointer.
4752 return try cg.module.constUndef(result_ty_id);
4753 }
47754754
4776 const storage_class = cg.module.storageClass(object_ptr_ty.ptrAddressSpace(zcu));
4777 const layout_payload_ty_id = try cg.resolveType(layout.payload_ty, .indirect);
4778 const pl_ptr_ty_id = try cg.module.ptrType(layout_payload_ty_id, storage_class);
4779 const pl_ptr_id = blk: {
4780 if (object_ty.containerLayout(zcu) == .@"packed") break :blk object_ptr;
4781 break :blk try cg.accessChain(pl_ptr_ty_id, object_ptr, &.{layout.payload_index});
4782 };
4755 const storage_class = cg.module.storageClass(object_ptr_ty.ptrAddressSpace(zcu));
4756 const layout_payload_ty_id = try cg.resolveType(layout.payload_ty, .indirect);
4757 const pl_ptr_ty_id = try cg.module.ptrType(layout_payload_ty_id, storage_class);
4758 const pl_ptr_id = blk: {
4759 if (object_ty.containerLayout(zcu) == .@"packed") break :blk object_ptr;
4760 break :blk try cg.accessChain(pl_ptr_ty_id, object_ptr, &.{layout.payload_index});
4761 };
47834762
4784 const active_pl_ptr_id = cg.module.allocId();
4785 try cg.body.emit(cg.module.gpa, .OpBitcast, .{
4786 .id_result_type = result_ty_id,
4787 .id_result = active_pl_ptr_id,
4788 .operand = pl_ptr_id,
4789 });
4790 return active_pl_ptr_id;
4763 const active_pl_ptr_id = cg.module.allocId();
4764 try cg.body.emit(cg.module.gpa, .OpBitcast, .{
4765 .id_result_type = result_ty_id,
4766 .id_result = active_pl_ptr_id,
4767 .operand = pl_ptr_id,
4768 });
4769 return active_pl_ptr_id;
4770 },
47914771 },
47924772 else => unreachable,
47934773 }
src/link/Dwarf.zig+75-58
......@@ -4009,69 +4009,86 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co
40094009 .union_type => {
40104010 const loaded_union = ip.loadUnionType(value_index);
40114011 const file = loaded_union.zir_index.resolveFile(ip);
4012 const need_terminator: bool = if (loaded_union.name_nav.unwrap()) |nav_index| t: {
4013 const nav = ip.getNav(nav_index);
4014 const decl_inst = nav.srcInst(ip).resolve(ip).?;
4015 const decl = zcu.fileByIndex(file).zir.?.getDeclaration(decl_inst);
4016 try wip_nav.declCommon(.{
4017 .decl = .decl_union,
4018 .generic_decl = .generic_decl_const,
4019 .decl_instance = .decl_instance_union,
4020 }, &nav, file, &decl);
4021 break :t true;
4022 } else t: {
4023 const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, file);
4024 try wip_nav.abbrevCode(if (loaded_union.field_types.len > 0) .union_type else .empty_union_type);
4025 try diw.writeUleb128(file_gop.index);
4026 try wip_nav.strp(loaded_union.name.toSlice(ip));
4027 break :t loaded_union.field_types.len > 0;
4028 };
4029 const union_layout = Type.getUnionLayout(loaded_union, zcu);
4030 try diw.writeUleb128(union_layout.abi_size);
4031 try diw.writeUleb128(union_layout.abi_align.toByteUnits().?);
4032 const loaded_tag = ip.loadEnumType(loaded_union.enum_tag_type);
4033 if (loaded_union.has_runtime_tag) {
4034 try wip_nav.abbrevCode(.tagged_union);
4035 try wip_nav.infoSectionOffset(
4036 .debug_info,
4037 wip_nav.unit,
4038 wip_nav.entry,
4039 @intCast(diw.end + dwarf.sectionOffsetBytes()),
4040 );
4041 {
4042 try wip_nav.abbrevCode(.generated_field);
4043 try wip_nav.strp("tag");
4044 try wip_nav.refType(.fromInterned(loaded_union.enum_tag_type));
4045 try diw.writeUleb128(union_layout.tagOffset());
4046
4047 for (0..loaded_union.field_types.len) |field_index| {
4048 try wip_nav.enumConstValue(loaded_tag, .{
4049 .sdata = .signed_tagged_union_field,
4050 .udata = .unsigned_tagged_union_field,
4051 .block = .big_tagged_union_field,
4052 }, field_index);
4012 switch (loaded_union.layout) {
4013 .auto, .@"extern" => {
4014 const need_terminator: bool = if (loaded_union.name_nav.unwrap()) |nav_index| t: {
4015 const nav = ip.getNav(nav_index);
4016 const decl_inst = nav.srcInst(ip).resolve(ip).?;
4017 const decl = zcu.fileByIndex(file).zir.?.getDeclaration(decl_inst);
4018 try wip_nav.declCommon(.{
4019 .decl = .decl_union,
4020 .generic_decl = .generic_decl_const,
4021 .decl_instance = .decl_instance_union,
4022 }, &nav, file, &decl);
4023 break :t true;
4024 } else t: {
4025 const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, file);
4026 try wip_nav.abbrevCode(if (loaded_union.field_types.len > 0) .union_type else .empty_union_type);
4027 try diw.writeUleb128(file_gop.index);
4028 try wip_nav.strp(loaded_union.name.toSlice(ip));
4029 break :t loaded_union.field_types.len > 0;
4030 };
4031 const union_layout = Type.getUnionLayout(loaded_union, zcu);
4032 try diw.writeUleb128(union_layout.abi_size);
4033 try diw.writeUleb128(union_layout.abi_align.toByteUnits().?);
4034 const loaded_tag = ip.loadEnumType(loaded_union.enum_tag_type);
4035 if (loaded_union.has_runtime_tag) {
4036 try wip_nav.abbrevCode(.tagged_union);
4037 try wip_nav.infoSectionOffset(
4038 .debug_info,
4039 wip_nav.unit,
4040 wip_nav.entry,
4041 @intCast(diw.end + dwarf.sectionOffsetBytes()),
4042 );
40534043 {
4054 try wip_nav.abbrevCode(.struct_field);
4055 try wip_nav.strp(loaded_tag.field_names.get(ip)[field_index].toSlice(ip));
4056 const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]);
4057 try wip_nav.refType(field_type);
4058 try diw.writeUleb128(union_layout.payloadOffset());
4059 try diw.writeUleb128(loaded_union.field_aligns.getOrNone(ip, field_index).toByteUnits() orelse
4060 if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?);
4044 try wip_nav.abbrevCode(.generated_field);
4045 try wip_nav.strp("tag");
4046 try wip_nav.refType(.fromInterned(loaded_union.enum_tag_type));
4047 try diw.writeUleb128(union_layout.tagOffset());
4048
4049 for (0..loaded_union.field_types.len) |field_index| {
4050 try wip_nav.enumConstValue(loaded_tag, .{
4051 .sdata = .signed_tagged_union_field,
4052 .udata = .unsigned_tagged_union_field,
4053 .block = .big_tagged_union_field,
4054 }, field_index);
4055 {
4056 try wip_nav.abbrevCode(.struct_field);
4057 try wip_nav.strp(loaded_tag.field_names.get(ip)[field_index].toSlice(ip));
4058 const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]);
4059 try wip_nav.refType(field_type);
4060 try diw.writeUleb128(union_layout.payloadOffset());
4061 try diw.writeUleb128(loaded_union.field_aligns.getOrNone(ip, field_index).toByteUnits() orelse
4062 if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?);
4063 }
4064 try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
4065 }
40614066 }
40624067 try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
4068 } else for (0..loaded_union.field_types.len) |field_index| {
4069 try wip_nav.abbrevCode(.untagged_union_field);
4070 try wip_nav.strp(loaded_tag.field_names.get(ip)[field_index].toSlice(ip));
4071 const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]);
4072 try wip_nav.refType(field_type);
4073 try diw.writeUleb128(loaded_union.field_aligns.getOrNone(ip, field_index).toByteUnits() orelse
4074 if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?);
40634075 }
4064 }
4065 try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
4066 } else for (0..loaded_union.field_types.len) |field_index| {
4067 try wip_nav.abbrevCode(.untagged_union_field);
4068 try wip_nav.strp(loaded_tag.field_names.get(ip)[field_index].toSlice(ip));
4069 const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]);
4070 try wip_nav.refType(field_type);
4071 try diw.writeUleb128(loaded_union.field_aligns.getOrNone(ip, field_index).toByteUnits() orelse
4072 if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?);
4076 if (need_terminator) try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
4077 },
4078 .@"packed" => {
4079 // TODO: debug info for packed unions
4080 try wip_nav.abbrevCode(.numeric_type);
4081 try wip_nav.strp(loaded_union.name.toSlice(ip));
4082 const backing_int_ty: Type = .fromInterned(loaded_union.packed_backing_int_type);
4083 const int_info = backing_int_ty.intInfo(zcu);
4084 try diw.writeByte(switch (int_info.signedness) {
4085 inline .signed, .unsigned => |signedness| @field(DW.ATE, @tagName(signedness)),
4086 });
4087 try diw.writeUleb128(int_info.bits);
4088 try diw.writeUleb128(backing_int_ty.abiSize(zcu));
4089 try diw.writeUleb128(backing_int_ty.abiAlignment(zcu).toByteUnits().?);
4090 },
40734091 }
4074 if (need_terminator) try diw.writeUleb128(@intFromEnum(AbbrevCode.null));
40754092 },
40764093 .enum_type => {
40774094 const loaded_enum = ip.loadEnumType(value_index);