From 52c6d7a9290d7d6017208e23c6fbac1b8cfeecb8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 5 Sep 2025 18:45:24 -0700 Subject: [PATCH 1/6] Sema: forbid packed unions with mismatched field bit sizes --- src/Sema.zig | 76 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 21 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 007e1a7fef..587c6b2d70 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -35614,6 +35614,12 @@ fn unionFields( if (small.any_aligned_fields) try field_aligns.ensureTotalCapacityPrecise(sema.arena, fields_len); + var max_bits: u64 = 0; + var min_bits: u64 = std.math.maxInt(u64); + var max_bits_src: LazySrcLoc = undefined; + var min_bits_src: LazySrcLoc = undefined; + var max_bits_ty: Type = undefined; + var min_bits_ty: Type = undefined; const bits_per_field = 4; const fields_per_u32 = 32 / bits_per_field; const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; @@ -35622,6 +35628,7 @@ fn unionFields( var cur_bit_bag: u32 = undefined; var field_i: u32 = 0; var last_tag_val: ?Value = null; + const layout = union_type.flagsUnordered(ip).layout; while (field_i < fields_len) : (field_i += 1) { if (field_i % fields_per_u32 == 0) { cur_bit_bag = zir.extra[bit_bag_index]; @@ -35773,31 +35780,45 @@ fn unionFields( }; return sema.failWithOwnedErrorMsg(&block_scope, msg); } - const layout = union_type.flagsUnordered(ip).layout; - if (layout == .@"extern" and - !try sema.validateExternType(field_ty, .union_field)) - { - const msg = msg: { - const msg = try sema.errMsg(type_src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); - errdefer msg.destroy(sema.gpa); + switch (layout) { + .@"extern" => if (!try sema.validateExternType(field_ty, .union_field)) { + const msg = msg: { + const msg = try sema.errMsg(type_src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); + errdefer msg.destroy(sema.gpa); - try sema.explainWhyTypeIsNotExtern(msg, type_src, field_ty, .union_field); + try sema.explainWhyTypeIsNotExtern(msg, type_src, field_ty, .union_field); - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(&block_scope, msg); - } else if (layout == .@"packed" and !try sema.validatePackedType(field_ty)) { - const msg = msg: { - const msg = try sema.errMsg(type_src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); - errdefer msg.destroy(sema.gpa); + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(&block_scope, msg); + }, + .@"packed" => { + if (!try sema.validatePackedType(field_ty)) { + const msg = msg: { + const msg = try sema.errMsg(type_src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); + errdefer msg.destroy(sema.gpa); - try sema.explainWhyTypeIsNotPacked(msg, type_src, field_ty); + try sema.explainWhyTypeIsNotPacked(msg, type_src, field_ty); - try sema.addDeclaredHereNote(msg, field_ty); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(&block_scope, msg); + try sema.addDeclaredHereNote(msg, field_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(&block_scope, msg); + } + const field_bits = try field_ty.bitSizeSema(pt); + if (field_bits >= max_bits) { + max_bits = field_bits; + max_bits_src = type_src; + max_bits_ty = field_ty; + } + if (field_bits <= min_bits) { + min_bits = field_bits; + min_bits_src = type_src; + min_bits_ty = field_ty; + } + }, + .auto => {}, } field_types.appendAssumeCapacity(field_ty.toIntern()); @@ -35815,6 +35836,19 @@ fn unionFields( union_type.setFieldTypes(ip, field_types.items); union_type.setFieldAligns(ip, field_aligns.items); + if (layout == .@"packed" and fields_len != 0 and min_bits != max_bits) { + const msg = msg: { + const msg = try sema.errMsg(src, "packed union has fields with mismatching bit sizes", .{}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(min_bits_src, msg, "{d} bits here", .{min_bits}); + try sema.addDeclaredHereNote(msg, min_bits_ty); + try sema.errNote(max_bits_src, msg, "{d} bits here", .{max_bits}); + try sema.addDeclaredHereNote(msg, max_bits_ty); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(&block_scope, msg); + } + if (explicit_tags_seen.len > 0) { const tag_ty = union_type.tagTypeUnordered(ip); const tag_info = ip.loadEnumType(tag_ty); From b9a63433b71e8b07691da158a136d17e1f04c49b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 5 Sep 2025 19:33:30 -0700 Subject: [PATCH 2/6] behavior tests: update for new requirement packed union fields must all have matching bit sizes --- test/behavior/cast_int.zig | 22 +++-- test/behavior/export_keyword.zig | 7 +- test/behavior/field_parent_ptr.zig | 18 ++-- test/behavior/packed-struct.zig | 8 +- test/behavior/packed-union.zig | 17 ++-- test/behavior/union.zig | 136 ++++++++++++++--------------- 6 files changed, 116 insertions(+), 92 deletions(-) diff --git a/test/behavior/cast_int.zig b/test/behavior/cast_int.zig index 0c4d01f501..8a1fcf79b2 100644 --- a/test/behavior/cast_int.zig +++ b/test/behavior/cast_int.zig @@ -225,20 +225,26 @@ test "load non byte-sized value in union" { // using ptrCast not to depend on unitialised memory state var union0: packed union { - p: Piece, + p: packed struct(u8) { + a: Piece, + b: u4, + }, int: u8, } = .{ .int = 0 }; union0.int = 0b11111011; - try expect(union0.p.type == .PAWN); - try expect(union0.p.color == .BLACK); + try expect(union0.p.a.type == .PAWN); + try expect(union0.p.a.color == .BLACK); var union1: union { - p: Piece, + p: packed struct(u8) { + a: Piece, + b: u4, + }, int: u8, - } = .{ .p = .{ .color = .WHITE, .type = .KING } }; - @as(*u8, @ptrCast(&union1.p)).* = 0b11111011; - try expect(union1.p.type == .PAWN); - try expect(union1.p.color == .BLACK); + } = .{ .p = .{ .a = .{ .color = .WHITE, .type = .KING }, .b = 0 } }; + @as(*u8, @ptrCast(&union1.p.a)).* = 0b11111011; + try expect(union1.p.a.type == .PAWN); + try expect(union1.p.a.color == .BLACK); var pieces: [3]Piece = undefined; @as(*u8, @ptrCast(&pieces[1])).* = 0b11111011; diff --git a/test/behavior/export_keyword.zig b/test/behavior/export_keyword.zig index af2065067e..f6b5126eb2 100644 --- a/test/behavior/export_keyword.zig +++ b/test/behavior/export_keyword.zig @@ -19,7 +19,10 @@ const PackedStruct = packed struct { b: u8, }; const PackedUnion = packed union { - a: u8, + a: packed struct(u32) { + a: u8, + b: u24 = 0, + }, b: u32, }; @@ -29,7 +32,7 @@ test "packed struct, enum, union parameters in extern function" { testPackedStuff(&(PackedStruct{ .a = 1, .b = 2, - }), &(PackedUnion{ .a = 1 })); + }), &(PackedUnion{ .a = .{ .a = 1 } })); } export fn testPackedStuff(a: *const PackedStruct, b: *const PackedUnion) void { diff --git a/test/behavior/field_parent_ptr.zig b/test/behavior/field_parent_ptr.zig index 65050e3df0..0bfec91d84 100644 --- a/test/behavior/field_parent_ptr.zig +++ b/test/behavior/field_parent_ptr.zig @@ -1758,27 +1758,33 @@ test "@fieldParentPtr packed union" { if (builtin.target.cpu.arch.endian() == .big) return error.SkipZigTest; // TODO const C = packed union { - a: bool, + a: packed struct(u32) { + a: bool, + b: u31 = 0, + }, b: f32, - c: packed struct { x: u8 }, + c: packed struct(u32) { + x: u8, + b: u24 = 0, + }, d: i32, }; { - const c: C = .{ .a = false }; + const c: C = .{ .a = .{ .a = false } }; const pcf = &c.a; const pc: *const C = @alignCast(@fieldParentPtr("a", pcf)); try expect(pc == &c); } { - const c: C = .{ .a = false }; + const c: C = .{ .a = .{ .a = false } }; const pcf = &c.a; var pc: *const C = undefined; pc = @alignCast(@fieldParentPtr("a", pcf)); try expect(pc == &c); } { - const c: C = .{ .a = false }; + const c: C = .{ .a = .{ .a = false } }; var pcf: @TypeOf(&c.a) = undefined; pcf = &c.a; var pc: *const C = undefined; @@ -1787,7 +1793,7 @@ test "@fieldParentPtr packed union" { } { var c: C = undefined; - c = .{ .a = false }; + c = .{ .a = .{ .a = false } }; var pcf: @TypeOf(&c.a) = undefined; pcf = &c.a; var pc: *C = undefined; diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index b2b4f89eb8..8352c493a2 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -1223,7 +1223,13 @@ test "load flag from packed struct in union" { test "bitcasting a packed struct at comptime and using the result" { comptime { const Struct = packed struct { - x: packed union { a: u63, b: i32 }, + x: packed union { + a: u63, + b: packed struct(u63) { + a: i32, + b: u31 = 0, + }, + }, y: u1, pub fn bitcast(fd: u64) @This() { diff --git a/test/behavior/packed-union.zig b/test/behavior/packed-union.zig index ad31e590ba..bbb2f612b6 100644 --- a/test/behavior/packed-union.zig +++ b/test/behavior/packed-union.zig @@ -59,14 +59,17 @@ test "flags in packed union at offset" { fn testFlagsInPackedUnionAtOffset() !void { const FlagBits = packed union { - base_flags: packed union { - flags: packed struct(u4) { - enable_1: bool = true, - enable_2: bool = false, - enable_3: bool = false, - enable_4: bool = false, + base_flags: packed struct(u12) { + a: packed union { + flags: packed struct(u4) { + enable_1: bool = true, + enable_2: bool = false, + enable_3: bool = false, + enable_4: bool = false, + }, + bits: u4, }, - bits: u4, + pad: u8 = 0, }, adv_flags: packed struct(u12) { pad: u8 = 0, diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 04d07f91ae..1807cf14cf 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -223,7 +223,7 @@ test "packed union generates correctly aligned type" { const U = packed union { f1: *const fn () error{TestUnexpectedResult}!void, - f2: u32, + f2: usize, }; var foo = [_]U{ U{ .f1 = doTest }, @@ -356,10 +356,10 @@ test "simple union(enum(u32))" { const PackedPtrOrInt = packed union { ptr: *u8, - int: u64, + int: usize, }; test "packed union size" { - comptime assert(@sizeOf(PackedPtrOrInt) == 8); + comptime assert(@sizeOf(PackedPtrOrInt) == @sizeOf(usize)); } const ZeroBits = union { @@ -1337,7 +1337,7 @@ test "packed union in packed struct" { const S = packed struct { nested: packed union { - val: u16, + val: u32, foo: u32, }, bar: u16, @@ -1415,25 +1415,6 @@ test "union reassignment can use previous value" { try expect(a.b == 32); } -test "packed union with zero-bit field" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - - const S = packed struct { - nested: packed union { - zero: void, - sized: u32, - }, - bar: u32, - - fn doTest(self: @This()) !void { - try expect(self.bar == 42); - } - }; - try S.doTest(.{ .nested = .{ .zero = {} }, .bar = 42 }); -} - test "reinterpreting enum value inside packed union" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO @@ -1632,15 +1613,15 @@ test "memset packed union" { const U = packed union { a: u32, - b: u8, + b: u32, }; const S = struct { fn doTheTest() !void { var u: U = undefined; - @memset(std.mem.asBytes(&u), 42); + @memset(@as([]u8, @ptrCast(&u)), 42); try expectEqual(@as(u32, 0x2a2a2a2a), u.a); - try expectEqual(@as(u8, 0x2a), u.b); + try expectEqual(@as(u32, 0x2a2a2a2a), u.b); } }; @@ -1732,10 +1713,19 @@ test "reinterpret packed union" { if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const U = packed union { - foo: u8, - bar: u29, + foo: packed struct(u64) { + a: u8, + b: u56, + }, + bar: packed struct(u64) { + a: u29, + b: u35, + }, baz: u64, - qux: u12, + qux: packed struct(u64) { + a: u12, + b: u52, + }, }; const S = struct { @@ -1745,16 +1735,16 @@ test "reinterpret packed union" { var u: U = undefined; @memset(std.mem.asBytes(&u), 0); u.baz = 0xbbbbbbbb; - u.qux = 0xe2a; + u.qux.a = 0xe2a; break :blk u; }; - try expectEqual(@as(u8, 0x2a), u.foo); - try expectEqual(@as(u12, 0xe2a), u.qux); + try expectEqual(@as(u8, 0x2a), u.foo.a); + try expectEqual(@as(u12, 0xe2a), u.qux.a); // https://github.com/ziglang/zig/issues/17360 if (@inComptime()) { - try expectEqual(@as(u29, 0x1bbbbe2a), u.bar); + try expectEqual(@as(u29, 0x1bbbbe2a), u.bar.a); try expectEqual(@as(u64, 0xbbbbbe2a), u.baz); } } @@ -1762,31 +1752,31 @@ test "reinterpret packed union" { { // Union initialization var u: U = .{ .baz = 0 }; // ensure all bits are defined - u.qux = 0xe2a; - try expectEqual(@as(u8, 0x2a), u.foo); - try expectEqual(@as(u12, 0xe2a), u.qux); - try expectEqual(@as(u29, 0xe2a), u.bar & 0xfff); + u.qux.a = 0xe2a; + try expectEqual(@as(u8, 0x2a), u.foo.a); + try expectEqual(@as(u12, 0xe2a), u.qux.a); + try expectEqual(@as(u29, 0xe2a), u.bar.a & 0xfff); try expectEqual(@as(u64, 0xe2a), u.baz & 0xfff); // Writing to a larger field u.baz = 0xbbbbbbbb; - try expectEqual(@as(u8, 0xbb), u.foo); - try expectEqual(@as(u12, 0xbbb), u.qux); - try expectEqual(@as(u29, 0x1bbbbbbb), u.bar); + try expectEqual(@as(u8, 0xbb), u.foo.a); + try expectEqual(@as(u12, 0xbbb), u.qux.a); + try expectEqual(@as(u29, 0x1bbbbbbb), u.bar.a); try expectEqual(@as(u64, 0xbbbbbbbb), u.baz); // Writing to the same field u.baz = 0xcccccccc; - try expectEqual(@as(u8, 0xcc), u.foo); - try expectEqual(@as(u12, 0xccc), u.qux); - try expectEqual(@as(u29, 0x0ccccccc), u.bar); + try expectEqual(@as(u8, 0xcc), u.foo.a); + try expectEqual(@as(u12, 0xccc), u.qux.a); + try expectEqual(@as(u29, 0x0ccccccc), u.bar.a); try expectEqual(@as(u64, 0xcccccccc), u.baz); // Writing to a smaller field - u.foo = 0xdd; - try expectEqual(@as(u8, 0xdd), u.foo); - try expectEqual(@as(u12, 0xcdd), u.qux); - try expectEqual(@as(u29, 0x0cccccdd), u.bar); + u.foo.a = 0xdd; + try expectEqual(@as(u8, 0xdd), u.foo.a); + try expectEqual(@as(u12, 0xcdd), u.qux.a); + try expectEqual(@as(u29, 0x0cccccdd), u.bar.a); try expectEqual(@as(u64, 0xccccccdd), u.baz); } } @@ -1807,7 +1797,10 @@ test "reinterpret packed union inside packed struct" { const U = packed union { a: u7, - b: u1, + b: packed struct(u7) { + a: u1, + b: u6, + }, }; const V = packed struct { @@ -1818,18 +1811,18 @@ test "reinterpret packed union inside packed struct" { const S = struct { fn doTheTest() !void { var v: V = undefined; - @memset(std.mem.asBytes(&v), 0x55); - try expectEqual(@as(u7, 0x55), v.lo.a); - try expectEqual(@as(u1, 1), v.lo.b); - try expectEqual(@as(u7, 0x2a), v.hi.a); - try expectEqual(@as(u1, 0), v.hi.b); + @memset(@as([]u8, @ptrCast(&v)), 0x55); + try expect(@as(u7, 0x55) == v.lo.a); + try expect(@as(u1, 1) == v.lo.b.a); + try expect(@as(u7, 0x2a) == v.hi.a); + try expect(@as(u1, 0) == v.hi.b.a); - v.lo.b = 0; - try expectEqual(@as(u7, 0x54), v.lo.a); - try expectEqual(@as(u1, 0), v.lo.b); - v.hi.b = 1; - try expectEqual(@as(u7, 0x2b), v.hi.a); - try expectEqual(@as(u1, 1), v.hi.b); + v.lo.b.a = 0; + try expect(@as(u7, 0x54) == v.lo.a); + try expect(@as(u1, 0) == v.lo.b.a); + v.hi.b.a = 1; + try expect(@as(u7, 0x2b) == v.hi.a); + try expect(@as(u1, 1) == v.hi.b.a); } }; @@ -1869,8 +1862,9 @@ test "inner struct initializer uses packed union layout" { a: packed struct { x: u32 = @alignOf(U) + 1, }, - b: packed struct { + b: packed struct(u32) { y: u16 = @sizeOf(U) + 2, + padding: u16 = 0, }, }; }; @@ -1898,7 +1892,7 @@ test "extern union initialized via reintepreted struct field initializer" { }; const S = extern struct { - u: U = std.mem.bytesAsValue(U, &bytes).*, + u: U = @as(*align(1) const U, @ptrCast(&bytes)).*, }; const s: S = .{}; @@ -1913,17 +1907,20 @@ test "packed union initialized via reintepreted struct field initializer" { const U = packed union { a: u32, - b: u8, + b: packed struct(u32) { + a: u8, + b: u24, + }, }; const S = packed struct { - u: U = std.mem.bytesAsValue(U, &bytes).*, + u: U = @as(*align(1) const U, @ptrCast(&bytes)).*, }; var s: S = .{}; _ = &s; try expect(s.u.a == littleToNativeEndian(u32, 0xddccbbaa)); - try expect(s.u.b == if (endian == .little) 0xaa else 0xdd); + try expect(s.u.b.a == if (endian == .little) 0xaa else 0xdd); } test "store of comptime reinterpreted memory to extern union" { @@ -1938,7 +1935,7 @@ test "store of comptime reinterpreted memory to extern union" { const reinterpreted = comptime b: { var u: U = undefined; - u = std.mem.bytesAsValue(U, &bytes).*; + u = @as(*align(1) const U, @ptrCast(&bytes)).*; break :b u; }; @@ -1955,19 +1952,22 @@ test "store of comptime reinterpreted memory to packed union" { const U = packed union { a: u32, - b: u8, + b: packed struct(u32) { + a: u8, + b: u24, + }, }; const reinterpreted = comptime b: { var u: U = undefined; - u = std.mem.bytesAsValue(U, &bytes).*; + u = @as(*align(1) const U, @ptrCast(&bytes)).*; break :b u; }; var u: U = reinterpreted; _ = &u; try expect(u.a == littleToNativeEndian(u32, 0xddccbbaa)); - try expect(u.b == if (endian == .little) 0xaa else 0xdd); + try expect(u.b.a == if (endian == .little) 0xaa else 0xdd); } test "union field is a pointer to an aligned version of itself" { From b074299124e1403d48d9904c6ab855b7c72df9e0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 5 Sep 2025 19:45:07 -0700 Subject: [PATCH 3/6] std: update for new packed union rules --- lib/std/meta.zig | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 65b7d60c18..019b0e3910 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -1193,13 +1193,6 @@ test hasUniqueRepresentation { try testing.expect(hasUniqueRepresentation(TestStruct6)); - const TestUnion1 = packed union { - a: u32, - b: u16, - }; - - try testing.expect(!hasUniqueRepresentation(TestUnion1)); - const TestUnion2 = extern union { a: u32, b: u16, From 8c631ebfeeed9b2338b3f4a280f6679c527f487b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 5 Sep 2025 19:45:18 -0700 Subject: [PATCH 4/6] langref: update for new packed union rules --- doc/langref.html.in | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/langref.html.in b/doc/langref.html.in index 9e2659d977..cfd1554634 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2496,6 +2496,7 @@ or {#header_open|packed union#}

A {#syntax#}packed union{#endsyntax#} has well-defined in-memory layout and is eligible to be in a {#link|packed struct#}.

+

All fields in a packed union must have the same {#link|@bitSizeOf#}.

{#header_close#} {#header_open|Anonymous Union Literals#} From 5dc5bf6a6bc5fbd3fd222f824be415be1a2085bd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 5 Sep 2025 19:55:45 -0700 Subject: [PATCH 5/6] add compile error test case for new error --- .../packed_union_fields_mismatch.zig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 test/cases/compile_errors/packed_union_fields_mismatch.zig diff --git a/test/cases/compile_errors/packed_union_fields_mismatch.zig b/test/cases/compile_errors/packed_union_fields_mismatch.zig new file mode 100644 index 0000000000..b2c1a5fbbc --- /dev/null +++ b/test/cases/compile_errors/packed_union_fields_mismatch.zig @@ -0,0 +1,14 @@ +export fn entry1() void { + _ = packed union { + a: u1, + b: u2, + }; +} + +// error +// backend=stage2 +// target=native +// +// :2:16: error: packed union has fields with mismatching bit sizes +// :3:12: note: 1 bits here +// :4:12: note: 2 bits here From 4f7aecd3485a291af63a0d8d05759c08bd683eae Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 5 Sep 2025 23:47:56 -0700 Subject: [PATCH 6/6] disable failing behavior test on stage2_aarch64 --- test/behavior/field_parent_ptr.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/test/behavior/field_parent_ptr.zig b/test/behavior/field_parent_ptr.zig index 0bfec91d84..4d23f6515e 100644 --- a/test/behavior/field_parent_ptr.zig +++ b/test/behavior/field_parent_ptr.zig @@ -1755,6 +1755,7 @@ test "@fieldParentPtr extern union" { test "@fieldParentPtr packed union" { if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.target.cpu.arch.endian() == .big) return error.SkipZigTest; // TODO const C = packed union {