authorgravatar for justus@klausecker.deJustus Klausecker <justus@klausecker.de> 2026-05-02 11:36:50+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-05-02 20:05:33+02:00
log1f22b2cbb2d9cdc4b7e436328e7018f473a57388
tree3b04f651011cc5a61ae1c8799b7a8663b47d46da
parent0db721ec2e4cfa5f91dc5589bab93c25353823ad

LowerZon: fix `packed` containers

Since `packed` containers are now internally represented by a `bitpack`, they need special handling on initialization: they need to be either bitpacked or bitcasted to their backing integer. `Sema` already did this, but `LowerZon` didn't yet.

4 files changed, 69 insertions(+), 10 deletions(-)

src/Sema/LowerZon.zig+34-10
......@@ -748,7 +748,8 @@ fn lowerTuple(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.
748748
749749fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
750750 const pt = self.sema.pt;
751 const comp = pt.zcu.comp;
751 const zcu = pt.zcu;
752 const comp = zcu.comp;
752753 const gpa = comp.gpa;
753754 const io = comp.io;
754755 const ip = &pt.zcu.intern_pool;
......@@ -807,7 +808,28 @@ fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool
807808 if (value.* == .none) return self.fail(node, "missing field '{f}'", .{name.fmt(ip)});
808809 }
809810
810 return (try self.sema.pt.aggregateValue(res_ty, field_values)).toIntern();
811 const result: Value = switch (struct_info.layout) {
812 .auto, .@"extern" => try pt.aggregateValue(res_ty, field_values),
813 .@"packed" => result: {
814 const arena = self.sema.arena;
815 const buf = try arena.alloc(u8, @intCast((res_ty.bitSize(zcu) + 7) / 8));
816 var bit_offset: u16 = 0;
817 for (field_values) |field_ip| {
818 const field_val: Value = .fromInterned(field_ip);
819 field_val.writeToPackedMemory(zcu, buf, bit_offset) catch |err| switch (err) {
820 error.ReinterpretDeclRef => unreachable, // bitpack fields cannot be pointers
821 error.OutOfMemory => |e| return e,
822 };
823 bit_offset += @intCast(field_val.typeOf(zcu).bitSize(zcu));
824 }
825 assert(bit_offset == res_ty.bitSize(zcu));
826 break :result Value.readFromPackedMemory(res_ty, pt, buf, 0, arena) catch |err| switch (err) {
827 error.IllDefinedMemoryLayout => unreachable, // bitpacks have well-defined layout
828 error.OutOfMemory => |e| return e,
829 };
830 },
831 };
832 return result.toIntern();
811833}
812834
813835fn lowerSlice(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
......@@ -944,22 +966,24 @@ fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.
944966 };
945967 const tag = try self.sema.pt.enumValueFieldIndex(.fromInterned(union_info.enum_tag_type), name_index);
946968 const field_type: Type = .fromInterned(union_info.field_types.get(ip)[name_index]);
947 const val = if (maybe_field_node) |field_node| b: {
969 const val: Value = if (maybe_field_node) |field_node| b: {
948970 if (field_type.toIntern() == .void_type) {
949971 return self.fail(field_node, "expected type 'void'", .{});
950972 }
951 break :b try self.lowerExprKnownResTy(field_node, field_type);
973 break :b .fromInterned(try self.lowerExprKnownResTy(field_node, field_type));
952974 } else b: {
953975 if (field_type.toIntern() != .void_type) {
954976 return error.WrongType;
955977 }
956 break :b .void_value;
978 break :b .void;
957979 };
958 return ip.getUnion(gpa, io, self.sema.pt.tid, .{
959 .ty = res_ty.toIntern(),
960 .tag = tag.toIntern(),
961 .val = val,
962 });
980 const result: Value = switch (union_info.layout) {
981 .auto, .@"extern" => try pt.unionValue(res_ty, tag, val),
982 .@"packed" => try self.sema.bitCastVal(val, res_ty, 0, 0, 0) orelse {
983 unreachable; // `null` is only possible if the input value contains a pointer, which a packed union cannot.
984 },
985 };
986 return result.toIntern();
963987}
964988
965989fn lowerVector(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
test/behavior/zon.zig+27
......@@ -571,3 +571,30 @@ test "build.zig.zon" {
571571
572572 try expectEqual(.{ "build.zig", "build.zig.zon", "src" }, build.paths);
573573}
574
575test "packed" {
576 {
577 const U = packed union {
578 x: f32,
579 y: u32,
580 };
581
582 const u: U = @import("zon/packed_union.zon");
583
584 try expectEqual(0.5, u.x);
585 try expectEqual(@as(u32, @bitCast(@as(f32, 0.5))), u.y);
586 }
587 {
588 const S = packed struct {
589 x: u3,
590 y: f32,
591 z: i7,
592 };
593
594 const s: S = @import("zon/packed_struct.zon");
595
596 try expectEqual(2, s.x);
597 try expectEqual(0.5, s.y);
598 try expectEqual(-10, s.z);
599 }
600}
test/behavior/zon/packed_struct.zon created+5
......@@ -0,0 +1,5 @@
1.{
2 .x = 2,
3 .y = 0.5,
4 .z = -10,
5}
test/behavior/zon/packed_union.zon created+3
......@@ -0,0 +1,3 @@
1.{
2 .x = 0.5,
3}