commit 1f22b2cbb2d9cdc4b7e436328e7018f473a57388 (tree)
parent 0db721ec2e4cfa5f91dc5589bab93c25353823ad
Author: Justus Klausecker <justus@klausecker.de>
Date: Sat, 2 May 2026 11:36:50 +0200
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.
Diffstat:
4 files changed, 69 insertions(+), 10 deletions(-)
diff --git a/src/Sema/LowerZon.zig b/src/Sema/LowerZon.zig
@@ -748,7 +748,8 @@ fn lowerTuple(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.
fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
const pt = self.sema.pt;
- const comp = pt.zcu.comp;
+ const zcu = pt.zcu;
+ const comp = zcu.comp;
const gpa = comp.gpa;
const io = comp.io;
const ip = &pt.zcu.intern_pool;
@@ -807,7 +808,28 @@ fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool
if (value.* == .none) return self.fail(node, "missing field '{f}'", .{name.fmt(ip)});
}
- return (try self.sema.pt.aggregateValue(res_ty, field_values)).toIntern();
+ const result: Value = switch (struct_info.layout) {
+ .auto, .@"extern" => try pt.aggregateValue(res_ty, field_values),
+ .@"packed" => result: {
+ const arena = self.sema.arena;
+ const buf = try arena.alloc(u8, @intCast((res_ty.bitSize(zcu) + 7) / 8));
+ var bit_offset: u16 = 0;
+ for (field_values) |field_ip| {
+ const field_val: Value = .fromInterned(field_ip);
+ field_val.writeToPackedMemory(zcu, buf, bit_offset) catch |err| switch (err) {
+ error.ReinterpretDeclRef => unreachable, // bitpack fields cannot be pointers
+ error.OutOfMemory => |e| return e,
+ };
+ bit_offset += @intCast(field_val.typeOf(zcu).bitSize(zcu));
+ }
+ assert(bit_offset == res_ty.bitSize(zcu));
+ break :result Value.readFromPackedMemory(res_ty, pt, buf, 0, arena) catch |err| switch (err) {
+ error.IllDefinedMemoryLayout => unreachable, // bitpacks have well-defined layout
+ error.OutOfMemory => |e| return e,
+ };
+ },
+ };
+ return result.toIntern();
}
fn 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.
};
const tag = try self.sema.pt.enumValueFieldIndex(.fromInterned(union_info.enum_tag_type), name_index);
const field_type: Type = .fromInterned(union_info.field_types.get(ip)[name_index]);
- const val = if (maybe_field_node) |field_node| b: {
+ const val: Value = if (maybe_field_node) |field_node| b: {
if (field_type.toIntern() == .void_type) {
return self.fail(field_node, "expected type 'void'", .{});
}
- break :b try self.lowerExprKnownResTy(field_node, field_type);
+ break :b .fromInterned(try self.lowerExprKnownResTy(field_node, field_type));
} else b: {
if (field_type.toIntern() != .void_type) {
return error.WrongType;
}
- break :b .void_value;
+ break :b .void;
};
- return ip.getUnion(gpa, io, self.sema.pt.tid, .{
- .ty = res_ty.toIntern(),
- .tag = tag.toIntern(),
- .val = val,
- });
+ const result: Value = switch (union_info.layout) {
+ .auto, .@"extern" => try pt.unionValue(res_ty, tag, val),
+ .@"packed" => try self.sema.bitCastVal(val, res_ty, 0, 0, 0) orelse {
+ unreachable; // `null` is only possible if the input value contains a pointer, which a packed union cannot.
+ },
+ };
+ return result.toIntern();
}
fn lowerVector(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
diff --git a/test/behavior/zon.zig b/test/behavior/zon.zig
@@ -571,3 +571,30 @@ test "build.zig.zon" {
try expectEqual(.{ "build.zig", "build.zig.zon", "src" }, build.paths);
}
+
+test "packed" {
+ {
+ const U = packed union {
+ x: f32,
+ y: u32,
+ };
+
+ const u: U = @import("zon/packed_union.zon");
+
+ try expectEqual(0.5, u.x);
+ try expectEqual(@as(u32, @bitCast(@as(f32, 0.5))), u.y);
+ }
+ {
+ const S = packed struct {
+ x: u3,
+ y: f32,
+ z: i7,
+ };
+
+ const s: S = @import("zon/packed_struct.zon");
+
+ try expectEqual(2, s.x);
+ try expectEqual(0.5, s.y);
+ try expectEqual(-10, s.z);
+ }
+}
diff --git a/test/behavior/zon/packed_struct.zon b/test/behavior/zon/packed_struct.zon
@@ -0,0 +1,5 @@
+.{
+ .x = 2,
+ .y = 0.5,
+ .z = -10,
+}
diff --git a/test/behavior/zon/packed_union.zon b/test/behavior/zon/packed_union.zon
@@ -0,0 +1,3 @@
+.{
+ .x = 0.5,
+}