zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

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:
Msrc/Sema/LowerZon.zig | 44++++++++++++++++++++++++++++++++++----------
Mtest/behavior/zon.zig | 27+++++++++++++++++++++++++++
Atest/behavior/zon/packed_struct.zon | 5+++++
Atest/behavior/zon/packed_union.zon | 3+++
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, +}