authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-02-08 14:40:18+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-03-10 10:26:10+00:00
logf9183edf08214d3ff014cfe26a5f218385ef5ccc
tree1ab0d86f271aea82294e7a69cc16610232567d59
parent031d109310fe1f7e68314069ddf5d0d1dd342098
signaturelock-open Commit is signed but in an unrecognized format.

tests: update for accepted language change

`packed struct`s and `packed union`s can no longer contain pointer fields. There are a few reasons for this, but in particular, binary formats do not typically support the relocation types we would need to lower such values into static memory. See the proposal at https://github.com/ziglang/zig/issues/24657 for details.

7 files changed, 33 insertions(+), 182 deletions(-)

test/behavior/bitcast.zig-29
...@@ -511,35 +511,6 @@ test "@bitCast of packed struct of bools all false" {...@@ -511,35 +511,6 @@ test "@bitCast of packed struct of bools all false" {
511 try expect(@as(u8, @as(u4, @bitCast(p))) == 0);511 try expect(@as(u8, @as(u4, @bitCast(p))) == 0);
512}512}
513513
514test "@bitCast of packed struct containing pointer" {
515 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
516 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
517 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
518 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
519 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://discourse.llvm.org/t/rfc-remove-most-constant-expressions/63179
520
521 const S = struct {
522 const A = packed struct {
523 ptr: *const u32,
524 };
525
526 const B = packed struct {
527 ptr: *const i32,
528 };
529
530 fn doTheTest() !void {
531 const x: u32 = 123;
532 var a: A = undefined;
533 a = .{ .ptr = &x };
534 const b: B = @bitCast(a);
535 try expect(b.ptr.* == 123);
536 }
537 };
538
539 try S.doTheTest();
540 try comptime S.doTheTest();
541}
542
543test "@bitCast of extern struct containing pointer" {514test "@bitCast of extern struct containing pointer" {
544 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;515 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
545 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO516 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
test/behavior/packed-struct.zig-98
...@@ -438,27 +438,6 @@ test "nested packed struct field pointers" {...@@ -438,27 +438,6 @@ test "nested packed struct field pointers" {
438 try expectEqual(6, ptr_p1_b.*);438 try expectEqual(6, ptr_p1_b.*);
439}439}
440440
441test "load pointer from packed struct" {
442 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
443 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
444 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
445 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
446 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
447
448 const A = struct {
449 index: u16,
450 };
451 const B = packed struct {
452 x: *A,
453 y: u32,
454 };
455 var a: A = .{ .index = 123 };
456 const b_list: []const B = &.{.{ .x = &a, .y = 99 }};
457 for (b_list) |b| {
458 try expect(b.x.index == 123);
459 }
460}
461
462test "@intFromPtr on a packed struct field" {441test "@intFromPtr on a packed struct field" {
463 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;442 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
464 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO443 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -601,19 +580,6 @@ test "packed struct fields modification" {...@@ -601,19 +580,6 @@ test "packed struct fields modification" {
601 try expect(@as(u16, @bitCast(Small.p)) == 0x1313);580 try expect(@as(u16, @bitCast(Small.p)) == 0x1313);
602}581}
603582
604test "optional pointer in packed struct" {
605 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
606 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
607 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
608 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
609 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
610
611 const T = packed struct { ptr: ?*const u8 };
612 var n: u8 = 0;
613 const x = T{ .ptr = &n };
614 try expect(x.ptr.? == &n);
615}
616
617test "nested packed struct field access test" {583test "nested packed struct field access test" {
618 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO584 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
619 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO packed structs larger than 64 bits585 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO packed structs larger than 64 bits
...@@ -1042,48 +1008,6 @@ test "packed struct acts as a namespace" {...@@ -1042,48 +1008,6 @@ test "packed struct acts as a namespace" {
1042 try expect(foo == .fizz);1008 try expect(foo == .fizz);
1043}1009}
10441010
1045test "pointer loaded correctly from packed struct" {
1046 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1047 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1048 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1049 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
1050 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1051
1052 if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // crashes MSVC
1053
1054 const RAM = struct {
1055 data: [0xFFFF + 1]u8,
1056 fn new() !@This() {
1057 return .{ .data = [_]u8{0} ** 0x10000 };
1058 }
1059 fn get(self: *@This(), addr: u16) u8 {
1060 return self.data[addr];
1061 }
1062 };
1063
1064 const CPU = packed struct {
1065 interrupts: bool,
1066 ram: *RAM,
1067 fn new(ram: *RAM) !@This() {
1068 return .{
1069 .ram = ram,
1070 .interrupts = false,
1071 };
1072 }
1073 fn tick(self: *@This()) !void {
1074 const queued_interrupts = self.ram.get(0xFFFF) & self.ram.get(0xFF0F);
1075 if (self.interrupts and queued_interrupts != 0) {
1076 self.interrupts = false;
1077 }
1078 }
1079 };
1080
1081 var ram = try RAM.new();
1082 var cpu = try CPU.new(&ram);
1083 try cpu.tick();
1084 try std.testing.expect(cpu.interrupts == false);
1085}
1086
1087test "assignment to non-byte-aligned field in packed struct" {1011test "assignment to non-byte-aligned field in packed struct" {
1088 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;1012 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1089 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1013 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -1227,13 +1151,6 @@ test "2-byte packed struct argument in C calling convention" {...@@ -1227,13 +1151,6 @@ test "2-byte packed struct argument in C calling convention" {
1227 }1151 }
1228}1152}
12291153
1230test "packed struct contains optional pointer" {
1231 const foo: packed struct {
1232 a: ?*@This() = null,
1233 } = .{};
1234 try expect(foo.a == null);
1235}
1236
1237test "packed struct equality" {1154test "packed struct equality" {
1238 const Foo = packed struct {1155 const Foo = packed struct {
1239 a: u4,1156 a: u4,
...@@ -1297,21 +1214,6 @@ test "assign packed struct initialized with RLS to packed struct literal field"...@@ -1297,21 +1214,6 @@ test "assign packed struct initialized with RLS to packed struct literal field"
1297 try expect(outer.x == x);1214 try expect(outer.x == x);
1298}1215}
12991216
1300test "byte-aligned packed relocation" {
1301 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
1302 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
1303 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1304 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
1305 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1306
1307 const S = struct {
1308 var global: u8 align(2) = 0;
1309 var packed_value: packed struct { x: u8, y: *align(2) u8 } = .{ .x = 111, .y = &global };
1310 };
1311 try expect(S.packed_value.x == 111);
1312 try expect(S.packed_value.y == &S.global);
1313}
1314
1315test "packed struct store of comparison result" {1217test "packed struct store of comparison result" {
1316 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;1218 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1317 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;1219 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
test/behavior/packed-union.zig-12
...@@ -177,15 +177,3 @@ test "assigning to non-active field at comptime" {...@@ -177,15 +177,3 @@ test "assigning to non-active field at comptime" {
177 test_bits.bits = .{};177 test_bits.bits = .{};
178 }178 }
179}179}
180
181test "comptime packed union of pointers" {
182 const U = packed union {
183 a: *const u32,
184 b: *const [1]u32,
185 };
186
187 const x: u32 = 123;
188 const u: U = .{ .a = &x };
189
190 comptime assert(u.b[0] == 123);
191}
test/behavior/union.zig+5-25
...@@ -217,26 +217,6 @@ test "union with specified enum tag" {...@@ -217,26 +217,6 @@ test "union with specified enum tag" {
217 try comptime doTest();217 try comptime doTest();
218}218}
219219
220test "packed union generates correctly aligned type" {
221 // This test will be removed after the following accepted proposal is implemented:
222 // https://github.com/ziglang/zig/issues/24657
223 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
224 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
225 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
226 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
227 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
228
229 const U = packed union {
230 f1: *const fn () error{TestUnexpectedResult}!void,
231 f2: usize,
232 };
233 var foo = [_]U{
234 U{ .f1 = doTest },
235 U{ .f2 = 0 },
236 };
237 try foo[0].f1();
238}
239
240fn doTest() error{TestUnexpectedResult}!void {220fn doTest() error{TestUnexpectedResult}!void {
241 try expect((try bar(Payload{ .A = 1234 })) == -10);221 try expect((try bar(Payload{ .A = 1234 })) == -10);
242}222}
...@@ -359,12 +339,12 @@ test "simple union(enum(u32))" {...@@ -359,12 +339,12 @@ test "simple union(enum(u32))" {
359 try expect(@intFromEnum(@as(Tag(MultipleChoice), x)) == 60);339 try expect(@intFromEnum(@as(Tag(MultipleChoice), x)) == 60);
360}340}
361341
362const PackedPtrOrInt = packed union {
363 ptr: *u8,
364 int: usize,
365};
366test "packed union size" {342test "packed union size" {
367 comptime assert(@sizeOf(PackedPtrOrInt) == @sizeOf(usize));343 const U = packed union {
344 signed: isize,
345 unsigned: usize,
346 };
347 comptime assert(@sizeOf(U) == @sizeOf(usize));
368}348}
369349
370const ZeroBits = union {350const ZeroBits = union {
test/cases/compile_errors/packed_struct_with_fields_of_not_allowed_types.zig+8
...@@ -76,6 +76,11 @@ export fn entry14() void {...@@ -76,6 +76,11 @@ export fn entry14() void {
76 x: E,76 x: E,
77 });77 });
78}78}
79export fn entry15() void {
80 _ = @sizeOf(packed struct {
81 x: *const u32,
82 });
83}
7984
80// error85// error
81//86//
...@@ -103,3 +108,6 @@ export fn entry14() void {...@@ -103,3 +108,6 @@ export fn entry14() void {
103// :70:12: note: types are not available at runtime108// :70:12: note: types are not available at runtime
104// :76:12: error: packed structs cannot contain fields of type 'tmp.entry14.E'109// :76:12: error: packed structs cannot contain fields of type 'tmp.entry14.E'
105// :74:15: note: enum declared here110// :74:15: note: enum declared here
111// :81:12: error: packed structs cannot contain fields of type '*const u32'
112// :81:12: note: pointers cannot be directly bitpacked
113// :81:12: note: consider using 'usize' and '@intFromPtr'
test/cases/compile_errors/packed_union_with_automatic_layout_field.zig deleted-18
...@@ -1,18 +0,0 @@
1const Foo = struct {
2 a: u32,
3 b: f32,
4};
5const Payload = packed union {
6 A: Foo,
7 B: bool,
8};
9export fn entry() void {
10 const a: Payload = .{ .B = true };
11 _ = a;
12}
13
14// error
15//
16// :6:8: error: packed unions cannot contain fields of type 'tmp.Foo'
17// :6:8: note: only packed structs layout are allowed in packed types
18// :1:13: note: struct declared here
test/cases/compile_errors/packed_union_with_fields_of_not_allowed_types.zig created+20
...@@ -0,0 +1,20 @@
1export fn entry0() void {
2 _ = @sizeOf(packed union {
3 foo: struct { a: u32 },
4 bar: bool,
5 });
6}
7export fn entry1() void {
8 _ = @sizeOf(packed union {
9 x: *const u32,
10 });
11}
12
13// error
14//
15// :3:14: error: packed unions cannot contain fields of type 'packed_union_with_fields_of_not_allowed_types.entry0__union_180__struct_182'
16// :3:14: note: non-packed structs do not have a bit-packed representation
17// :3:14: note: struct declared here
18// :9:12: error: packed unions cannot contain fields of type '*const u32'
19// :9:12: note: pointers cannot be directly bitpacked
20// :9:12: note: consider using 'usize' and '@intFromPtr'