From 1f22b2cbb2d9cdc4b7e436328e7018f473a57388 Mon Sep 17 00:00:00 2001 From: Justus Klausecker Date: Sat, 2 May 2026 11:36:50 +0200 Subject: [PATCH] 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. --- src/Sema/LowerZon.zig | 44 ++++++++++++++++++++++------- test/behavior/zon.zig | 27 ++++++++++++++++++ test/behavior/zon/packed_struct.zon | 5 ++++ test/behavior/zon/packed_union.zon | 3 ++ 4 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 test/behavior/zon/packed_struct.zon create mode 100644 test/behavior/zon/packed_union.zon diff --git a/src/Sema/LowerZon.zig b/src/Sema/LowerZon.zig index 96b854885f6ab1083497d9608c9ebdc36a7b2d0e..0ba12a8813477ce26016c34443c2ea3825630740 100644 --- 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 index 8a43b4894e3e9a200b855c52fc96b52f3a19e742..6de22701d5bd644b492ba321bdc9d78b3fa3b87b 100644 --- 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 new file mode 100644 index 0000000000000000000000000000000000000000..31996ea9fca4962b9130b8e30465d503d7d847d3 --- /dev/null +++ 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 new file mode 100644 index 0000000000000000000000000000000000000000..9911dfd9b101fd97c49968ef96702e824d462afc --- /dev/null +++ b/test/behavior/zon/packed_union.zon @@ -0,0 +1,3 @@ +.{ + .x = 0.5, +} -- 2.54.0