authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-09-06 12:08:31-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-09-06 12:08:31-07:00
log4c012756643ee1f18740ac88180635defa8dc6f2
tree7486469829e8d6a42d82a879094b2311cf837a4e
parent91b3769b032a8a0e2cf9ff4f213290dd29016cdc
parent4f7aecd3485a291af63a0d8d05759c08bd683eae
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #25163 from ziglang/packed-union-unused

forbid unused bits in packed unions

10 files changed, 188 insertions(+), 121 deletions(-)

doc/langref.html.in+1
...@@ -2496,6 +2496,7 @@ or...@@ -2496,6 +2496,7 @@ or
2496 {#header_open|packed union#}2496 {#header_open|packed union#}
2497 <p>A {#syntax#}packed union{#endsyntax#} has well-defined in-memory layout and is eligible2497 <p>A {#syntax#}packed union{#endsyntax#} has well-defined in-memory layout and is eligible
2498 to be in a {#link|packed struct#}.</p>2498 to be in a {#link|packed struct#}.</p>
2499 <p>All fields in a packed union must have the same {#link|@bitSizeOf#}.</p>
2499 {#header_close#}2500 {#header_close#}
25002501
2501 {#header_open|Anonymous Union Literals#}2502 {#header_open|Anonymous Union Literals#}
lib/std/meta.zig-7
...@@ -1193,13 +1193,6 @@ test hasUniqueRepresentation {...@@ -1193,13 +1193,6 @@ test hasUniqueRepresentation {
11931193
1194 try testing.expect(hasUniqueRepresentation(TestStruct6));1194 try testing.expect(hasUniqueRepresentation(TestStruct6));
11951195
1196 const TestUnion1 = packed union {
1197 a: u32,
1198 b: u16,
1199 };
1200
1201 try testing.expect(!hasUniqueRepresentation(TestUnion1));
1202
1203 const TestUnion2 = extern union {1196 const TestUnion2 = extern union {
1204 a: u32,1197 a: u32,
1205 b: u16,1198 b: u16,
src/Sema.zig+55-21
...@@ -35614,6 +35614,12 @@ fn unionFields(...@@ -35614,6 +35614,12 @@ fn unionFields(
35614 if (small.any_aligned_fields)35614 if (small.any_aligned_fields)
35615 try field_aligns.ensureTotalCapacityPrecise(sema.arena, fields_len);35615 try field_aligns.ensureTotalCapacityPrecise(sema.arena, fields_len);
3561635616
35617 var max_bits: u64 = 0;
35618 var min_bits: u64 = std.math.maxInt(u64);
35619 var max_bits_src: LazySrcLoc = undefined;
35620 var min_bits_src: LazySrcLoc = undefined;
35621 var max_bits_ty: Type = undefined;
35622 var min_bits_ty: Type = undefined;
35617 const bits_per_field = 4;35623 const bits_per_field = 4;
35618 const fields_per_u32 = 32 / bits_per_field;35624 const fields_per_u32 = 32 / bits_per_field;
35619 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;35625 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
...@@ -35622,6 +35628,7 @@ fn unionFields(...@@ -35622,6 +35628,7 @@ fn unionFields(
35622 var cur_bit_bag: u32 = undefined;35628 var cur_bit_bag: u32 = undefined;
35623 var field_i: u32 = 0;35629 var field_i: u32 = 0;
35624 var last_tag_val: ?Value = null;35630 var last_tag_val: ?Value = null;
35631 const layout = union_type.flagsUnordered(ip).layout;
35625 while (field_i < fields_len) : (field_i += 1) {35632 while (field_i < fields_len) : (field_i += 1) {
35626 if (field_i % fields_per_u32 == 0) {35633 if (field_i % fields_per_u32 == 0) {
35627 cur_bit_bag = zir.extra[bit_bag_index];35634 cur_bit_bag = zir.extra[bit_bag_index];
...@@ -35773,31 +35780,45 @@ fn unionFields(...@@ -35773,31 +35780,45 @@ fn unionFields(
35773 };35780 };
35774 return sema.failWithOwnedErrorMsg(&block_scope, msg);35781 return sema.failWithOwnedErrorMsg(&block_scope, msg);
35775 }35782 }
35776 const layout = union_type.flagsUnordered(ip).layout;35783 switch (layout) {
35777 if (layout == .@"extern" and35784 .@"extern" => if (!try sema.validateExternType(field_ty, .union_field)) {
35778 !try sema.validateExternType(field_ty, .union_field))35785 const msg = msg: {
35779 {35786 const msg = try sema.errMsg(type_src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
35780 const msg = msg: {35787 errdefer msg.destroy(sema.gpa);
35781 const msg = try sema.errMsg(type_src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
35782 errdefer msg.destroy(sema.gpa);
3578335788
35784 try sema.explainWhyTypeIsNotExtern(msg, type_src, field_ty, .union_field);35789 try sema.explainWhyTypeIsNotExtern(msg, type_src, field_ty, .union_field);
3578535790
35786 try sema.addDeclaredHereNote(msg, field_ty);35791 try sema.addDeclaredHereNote(msg, field_ty);
35787 break :msg msg;35792 break :msg msg;
35788 };35793 };
35789 return sema.failWithOwnedErrorMsg(&block_scope, msg);35794 return sema.failWithOwnedErrorMsg(&block_scope, msg);
35790 } else if (layout == .@"packed" and !try sema.validatePackedType(field_ty)) {35795 },
35791 const msg = msg: {35796 .@"packed" => {
35792 const msg = try sema.errMsg(type_src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});35797 if (!try sema.validatePackedType(field_ty)) {
35793 errdefer msg.destroy(sema.gpa);35798 const msg = msg: {
35799 const msg = try sema.errMsg(type_src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
35800 errdefer msg.destroy(sema.gpa);
3579435801
35795 try sema.explainWhyTypeIsNotPacked(msg, type_src, field_ty);35802 try sema.explainWhyTypeIsNotPacked(msg, type_src, field_ty);
3579635803
35797 try sema.addDeclaredHereNote(msg, field_ty);35804 try sema.addDeclaredHereNote(msg, field_ty);
35798 break :msg msg;35805 break :msg msg;
35799 };35806 };
35800 return sema.failWithOwnedErrorMsg(&block_scope, msg);35807 return sema.failWithOwnedErrorMsg(&block_scope, msg);
35808 }
35809 const field_bits = try field_ty.bitSizeSema(pt);
35810 if (field_bits >= max_bits) {
35811 max_bits = field_bits;
35812 max_bits_src = type_src;
35813 max_bits_ty = field_ty;
35814 }
35815 if (field_bits <= min_bits) {
35816 min_bits = field_bits;
35817 min_bits_src = type_src;
35818 min_bits_ty = field_ty;
35819 }
35820 },
35821 .auto => {},
35801 }35822 }
3580235823
35803 field_types.appendAssumeCapacity(field_ty.toIntern());35824 field_types.appendAssumeCapacity(field_ty.toIntern());
...@@ -35815,6 +35836,19 @@ fn unionFields(...@@ -35815,6 +35836,19 @@ fn unionFields(
35815 union_type.setFieldTypes(ip, field_types.items);35836 union_type.setFieldTypes(ip, field_types.items);
35816 union_type.setFieldAligns(ip, field_aligns.items);35837 union_type.setFieldAligns(ip, field_aligns.items);
3581735838
35839 if (layout == .@"packed" and fields_len != 0 and min_bits != max_bits) {
35840 const msg = msg: {
35841 const msg = try sema.errMsg(src, "packed union has fields with mismatching bit sizes", .{});
35842 errdefer msg.destroy(sema.gpa);
35843 try sema.errNote(min_bits_src, msg, "{d} bits here", .{min_bits});
35844 try sema.addDeclaredHereNote(msg, min_bits_ty);
35845 try sema.errNote(max_bits_src, msg, "{d} bits here", .{max_bits});
35846 try sema.addDeclaredHereNote(msg, max_bits_ty);
35847 break :msg msg;
35848 };
35849 return sema.failWithOwnedErrorMsg(&block_scope, msg);
35850 }
35851
35818 if (explicit_tags_seen.len > 0) {35852 if (explicit_tags_seen.len > 0) {
35819 const tag_ty = union_type.tagTypeUnordered(ip);35853 const tag_ty = union_type.tagTypeUnordered(ip);
35820 const tag_info = ip.loadEnumType(tag_ty);35854 const tag_info = ip.loadEnumType(tag_ty);
test/behavior/cast_int.zig+14-8
...@@ -225,20 +225,26 @@ test "load non byte-sized value in union" {...@@ -225,20 +225,26 @@ test "load non byte-sized value in union" {
225 // using ptrCast not to depend on unitialised memory state225 // using ptrCast not to depend on unitialised memory state
226226
227 var union0: packed union {227 var union0: packed union {
228 p: Piece,228 p: packed struct(u8) {
229 a: Piece,
230 b: u4,
231 },
229 int: u8,232 int: u8,
230 } = .{ .int = 0 };233 } = .{ .int = 0 };
231 union0.int = 0b11111011;234 union0.int = 0b11111011;
232 try expect(union0.p.type == .PAWN);235 try expect(union0.p.a.type == .PAWN);
233 try expect(union0.p.color == .BLACK);236 try expect(union0.p.a.color == .BLACK);
234237
235 var union1: union {238 var union1: union {
236 p: Piece,239 p: packed struct(u8) {
240 a: Piece,
241 b: u4,
242 },
237 int: u8,243 int: u8,
238 } = .{ .p = .{ .color = .WHITE, .type = .KING } };244 } = .{ .p = .{ .a = .{ .color = .WHITE, .type = .KING }, .b = 0 } };
239 @as(*u8, @ptrCast(&union1.p)).* = 0b11111011;245 @as(*u8, @ptrCast(&union1.p.a)).* = 0b11111011;
240 try expect(union1.p.type == .PAWN);246 try expect(union1.p.a.type == .PAWN);
241 try expect(union1.p.color == .BLACK);247 try expect(union1.p.a.color == .BLACK);
242248
243 var pieces: [3]Piece = undefined;249 var pieces: [3]Piece = undefined;
244 @as(*u8, @ptrCast(&pieces[1])).* = 0b11111011;250 @as(*u8, @ptrCast(&pieces[1])).* = 0b11111011;
test/behavior/export_keyword.zig+5-2
...@@ -19,7 +19,10 @@ const PackedStruct = packed struct {...@@ -19,7 +19,10 @@ const PackedStruct = packed struct {
19 b: u8,19 b: u8,
20};20};
21const PackedUnion = packed union {21const PackedUnion = packed union {
22 a: u8,22 a: packed struct(u32) {
23 a: u8,
24 b: u24 = 0,
25 },
23 b: u32,26 b: u32,
24};27};
2528
...@@ -29,7 +32,7 @@ test "packed struct, enum, union parameters in extern function" {...@@ -29,7 +32,7 @@ test "packed struct, enum, union parameters in extern function" {
29 testPackedStuff(&(PackedStruct{32 testPackedStuff(&(PackedStruct{
30 .a = 1,33 .a = 1,
31 .b = 2,34 .b = 2,
32 }), &(PackedUnion{ .a = 1 }));35 }), &(PackedUnion{ .a = .{ .a = 1 } }));
33}36}
3437
35export fn testPackedStuff(a: *const PackedStruct, b: *const PackedUnion) void {38export fn testPackedStuff(a: *const PackedStruct, b: *const PackedUnion) void {
test/behavior/field_parent_ptr.zig+13-6
...@@ -1755,30 +1755,37 @@ test "@fieldParentPtr extern union" {...@@ -1755,30 +1755,37 @@ test "@fieldParentPtr extern union" {
1755test "@fieldParentPtr packed union" {1755test "@fieldParentPtr packed union" {
1756 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;1756 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1757 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;1757 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1758 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1758 if (builtin.target.cpu.arch.endian() == .big) return error.SkipZigTest; // TODO1759 if (builtin.target.cpu.arch.endian() == .big) return error.SkipZigTest; // TODO
17591760
1760 const C = packed union {1761 const C = packed union {
1761 a: bool,1762 a: packed struct(u32) {
1763 a: bool,
1764 b: u31 = 0,
1765 },
1762 b: f32,1766 b: f32,
1763 c: packed struct { x: u8 },1767 c: packed struct(u32) {
1768 x: u8,
1769 b: u24 = 0,
1770 },
1764 d: i32,1771 d: i32,
1765 };1772 };
17661773
1767 {1774 {
1768 const c: C = .{ .a = false };1775 const c: C = .{ .a = .{ .a = false } };
1769 const pcf = &c.a;1776 const pcf = &c.a;
1770 const pc: *const C = @alignCast(@fieldParentPtr("a", pcf));1777 const pc: *const C = @alignCast(@fieldParentPtr("a", pcf));
1771 try expect(pc == &c);1778 try expect(pc == &c);
1772 }1779 }
1773 {1780 {
1774 const c: C = .{ .a = false };1781 const c: C = .{ .a = .{ .a = false } };
1775 const pcf = &c.a;1782 const pcf = &c.a;
1776 var pc: *const C = undefined;1783 var pc: *const C = undefined;
1777 pc = @alignCast(@fieldParentPtr("a", pcf));1784 pc = @alignCast(@fieldParentPtr("a", pcf));
1778 try expect(pc == &c);1785 try expect(pc == &c);
1779 }1786 }
1780 {1787 {
1781 const c: C = .{ .a = false };1788 const c: C = .{ .a = .{ .a = false } };
1782 var pcf: @TypeOf(&c.a) = undefined;1789 var pcf: @TypeOf(&c.a) = undefined;
1783 pcf = &c.a;1790 pcf = &c.a;
1784 var pc: *const C = undefined;1791 var pc: *const C = undefined;
...@@ -1787,7 +1794,7 @@ test "@fieldParentPtr packed union" {...@@ -1787,7 +1794,7 @@ test "@fieldParentPtr packed union" {
1787 }1794 }
1788 {1795 {
1789 var c: C = undefined;1796 var c: C = undefined;
1790 c = .{ .a = false };1797 c = .{ .a = .{ .a = false } };
1791 var pcf: @TypeOf(&c.a) = undefined;1798 var pcf: @TypeOf(&c.a) = undefined;
1792 pcf = &c.a;1799 pcf = &c.a;
1793 var pc: *C = undefined;1800 var pc: *C = undefined;
test/behavior/packed-struct.zig+7-1
...@@ -1223,7 +1223,13 @@ test "load flag from packed struct in union" {...@@ -1223,7 +1223,13 @@ test "load flag from packed struct in union" {
1223test "bitcasting a packed struct at comptime and using the result" {1223test "bitcasting a packed struct at comptime and using the result" {
1224 comptime {1224 comptime {
1225 const Struct = packed struct {1225 const Struct = packed struct {
1226 x: packed union { a: u63, b: i32 },1226 x: packed union {
1227 a: u63,
1228 b: packed struct(u63) {
1229 a: i32,
1230 b: u31 = 0,
1231 },
1232 },
1227 y: u1,1233 y: u1,
12281234
1229 pub fn bitcast(fd: u64) @This() {1235 pub fn bitcast(fd: u64) @This() {
test/behavior/packed-union.zig+10-7
...@@ -59,14 +59,17 @@ test "flags in packed union at offset" {...@@ -59,14 +59,17 @@ test "flags in packed union at offset" {
5959
60fn testFlagsInPackedUnionAtOffset() !void {60fn testFlagsInPackedUnionAtOffset() !void {
61 const FlagBits = packed union {61 const FlagBits = packed union {
62 base_flags: packed union {62 base_flags: packed struct(u12) {
63 flags: packed struct(u4) {63 a: packed union {
64 enable_1: bool = true,64 flags: packed struct(u4) {
65 enable_2: bool = false,65 enable_1: bool = true,
66 enable_3: bool = false,66 enable_2: bool = false,
67 enable_4: bool = false,67 enable_3: bool = false,
68 enable_4: bool = false,
69 },
70 bits: u4,
68 },71 },
69 bits: u4,72 pad: u8 = 0,
70 },73 },
71 adv_flags: packed struct(u12) {74 adv_flags: packed struct(u12) {
72 pad: u8 = 0,75 pad: u8 = 0,
test/behavior/union.zig+69-69
...@@ -223,7 +223,7 @@ test "packed union generates correctly aligned type" {...@@ -223,7 +223,7 @@ test "packed union generates correctly aligned type" {
223223
224 const U = packed union {224 const U = packed union {
225 f1: *const fn () error{TestUnexpectedResult}!void,225 f1: *const fn () error{TestUnexpectedResult}!void,
226 f2: u32,226 f2: usize,
227 };227 };
228 var foo = [_]U{228 var foo = [_]U{
229 U{ .f1 = doTest },229 U{ .f1 = doTest },
...@@ -356,10 +356,10 @@ test "simple union(enum(u32))" {...@@ -356,10 +356,10 @@ test "simple union(enum(u32))" {
356356
357const PackedPtrOrInt = packed union {357const PackedPtrOrInt = packed union {
358 ptr: *u8,358 ptr: *u8,
359 int: u64,359 int: usize,
360};360};
361test "packed union size" {361test "packed union size" {
362 comptime assert(@sizeOf(PackedPtrOrInt) == 8);362 comptime assert(@sizeOf(PackedPtrOrInt) == @sizeOf(usize));
363}363}
364364
365const ZeroBits = union {365const ZeroBits = union {
...@@ -1337,7 +1337,7 @@ test "packed union in packed struct" {...@@ -1337,7 +1337,7 @@ test "packed union in packed struct" {
13371337
1338 const S = packed struct {1338 const S = packed struct {
1339 nested: packed union {1339 nested: packed union {
1340 val: u16,1340 val: u32,
1341 foo: u32,1341 foo: u32,
1342 },1342 },
1343 bar: u16,1343 bar: u16,
...@@ -1415,25 +1415,6 @@ test "union reassignment can use previous value" {...@@ -1415,25 +1415,6 @@ test "union reassignment can use previous value" {
1415 try expect(a.b == 32);1415 try expect(a.b == 32);
1416}1416}
14171417
1418test "packed union with zero-bit field" {
1419 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1420 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1421 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1422
1423 const S = packed struct {
1424 nested: packed union {
1425 zero: void,
1426 sized: u32,
1427 },
1428 bar: u32,
1429
1430 fn doTest(self: @This()) !void {
1431 try expect(self.bar == 42);
1432 }
1433 };
1434 try S.doTest(.{ .nested = .{ .zero = {} }, .bar = 42 });
1435}
1436
1437test "reinterpreting enum value inside packed union" {1418test "reinterpreting enum value inside packed union" {
1438 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;1419 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1439 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1420 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
...@@ -1632,15 +1613,15 @@ test "memset packed union" {...@@ -1632,15 +1613,15 @@ test "memset packed union" {
16321613
1633 const U = packed union {1614 const U = packed union {
1634 a: u32,1615 a: u32,
1635 b: u8,1616 b: u32,
1636 };1617 };
16371618
1638 const S = struct {1619 const S = struct {
1639 fn doTheTest() !void {1620 fn doTheTest() !void {
1640 var u: U = undefined;1621 var u: U = undefined;
1641 @memset(std.mem.asBytes(&u), 42);1622 @memset(@as([]u8, @ptrCast(&u)), 42);
1642 try expectEqual(@as(u32, 0x2a2a2a2a), u.a);1623 try expectEqual(@as(u32, 0x2a2a2a2a), u.a);
1643 try expectEqual(@as(u8, 0x2a), u.b);1624 try expectEqual(@as(u32, 0x2a2a2a2a), u.b);
1644 }1625 }
1645 };1626 };
16461627
...@@ -1732,10 +1713,19 @@ test "reinterpret packed union" {...@@ -1732,10 +1713,19 @@ test "reinterpret packed union" {
1732 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;1713 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
17331714
1734 const U = packed union {1715 const U = packed union {
1735 foo: u8,1716 foo: packed struct(u64) {
1736 bar: u29,1717 a: u8,
1718 b: u56,
1719 },
1720 bar: packed struct(u64) {
1721 a: u29,
1722 b: u35,
1723 },
1737 baz: u64,1724 baz: u64,
1738 qux: u12,1725 qux: packed struct(u64) {
1726 a: u12,
1727 b: u52,
1728 },
1739 };1729 };
17401730
1741 const S = struct {1731 const S = struct {
...@@ -1745,16 +1735,16 @@ test "reinterpret packed union" {...@@ -1745,16 +1735,16 @@ test "reinterpret packed union" {
1745 var u: U = undefined;1735 var u: U = undefined;
1746 @memset(std.mem.asBytes(&u), 0);1736 @memset(std.mem.asBytes(&u), 0);
1747 u.baz = 0xbbbbbbbb;1737 u.baz = 0xbbbbbbbb;
1748 u.qux = 0xe2a;1738 u.qux.a = 0xe2a;
1749 break :blk u;1739 break :blk u;
1750 };1740 };
17511741
1752 try expectEqual(@as(u8, 0x2a), u.foo);1742 try expectEqual(@as(u8, 0x2a), u.foo.a);
1753 try expectEqual(@as(u12, 0xe2a), u.qux);1743 try expectEqual(@as(u12, 0xe2a), u.qux.a);
17541744
1755 // https://github.com/ziglang/zig/issues/173601745 // https://github.com/ziglang/zig/issues/17360
1756 if (@inComptime()) {1746 if (@inComptime()) {
1757 try expectEqual(@as(u29, 0x1bbbbe2a), u.bar);1747 try expectEqual(@as(u29, 0x1bbbbe2a), u.bar.a);
1758 try expectEqual(@as(u64, 0xbbbbbe2a), u.baz);1748 try expectEqual(@as(u64, 0xbbbbbe2a), u.baz);
1759 }1749 }
1760 }1750 }
...@@ -1762,31 +1752,31 @@ test "reinterpret packed union" {...@@ -1762,31 +1752,31 @@ test "reinterpret packed union" {
1762 {1752 {
1763 // Union initialization1753 // Union initialization
1764 var u: U = .{ .baz = 0 }; // ensure all bits are defined1754 var u: U = .{ .baz = 0 }; // ensure all bits are defined
1765 u.qux = 0xe2a;1755 u.qux.a = 0xe2a;
1766 try expectEqual(@as(u8, 0x2a), u.foo);1756 try expectEqual(@as(u8, 0x2a), u.foo.a);
1767 try expectEqual(@as(u12, 0xe2a), u.qux);1757 try expectEqual(@as(u12, 0xe2a), u.qux.a);
1768 try expectEqual(@as(u29, 0xe2a), u.bar & 0xfff);1758 try expectEqual(@as(u29, 0xe2a), u.bar.a & 0xfff);
1769 try expectEqual(@as(u64, 0xe2a), u.baz & 0xfff);1759 try expectEqual(@as(u64, 0xe2a), u.baz & 0xfff);
17701760
1771 // Writing to a larger field1761 // Writing to a larger field
1772 u.baz = 0xbbbbbbbb;1762 u.baz = 0xbbbbbbbb;
1773 try expectEqual(@as(u8, 0xbb), u.foo);1763 try expectEqual(@as(u8, 0xbb), u.foo.a);
1774 try expectEqual(@as(u12, 0xbbb), u.qux);1764 try expectEqual(@as(u12, 0xbbb), u.qux.a);
1775 try expectEqual(@as(u29, 0x1bbbbbbb), u.bar);1765 try expectEqual(@as(u29, 0x1bbbbbbb), u.bar.a);
1776 try expectEqual(@as(u64, 0xbbbbbbbb), u.baz);1766 try expectEqual(@as(u64, 0xbbbbbbbb), u.baz);
17771767
1778 // Writing to the same field1768 // Writing to the same field
1779 u.baz = 0xcccccccc;1769 u.baz = 0xcccccccc;
1780 try expectEqual(@as(u8, 0xcc), u.foo);1770 try expectEqual(@as(u8, 0xcc), u.foo.a);
1781 try expectEqual(@as(u12, 0xccc), u.qux);1771 try expectEqual(@as(u12, 0xccc), u.qux.a);
1782 try expectEqual(@as(u29, 0x0ccccccc), u.bar);1772 try expectEqual(@as(u29, 0x0ccccccc), u.bar.a);
1783 try expectEqual(@as(u64, 0xcccccccc), u.baz);1773 try expectEqual(@as(u64, 0xcccccccc), u.baz);
17841774
1785 // Writing to a smaller field1775 // Writing to a smaller field
1786 u.foo = 0xdd;1776 u.foo.a = 0xdd;
1787 try expectEqual(@as(u8, 0xdd), u.foo);1777 try expectEqual(@as(u8, 0xdd), u.foo.a);
1788 try expectEqual(@as(u12, 0xcdd), u.qux);1778 try expectEqual(@as(u12, 0xcdd), u.qux.a);
1789 try expectEqual(@as(u29, 0x0cccccdd), u.bar);1779 try expectEqual(@as(u29, 0x0cccccdd), u.bar.a);
1790 try expectEqual(@as(u64, 0xccccccdd), u.baz);1780 try expectEqual(@as(u64, 0xccccccdd), u.baz);
1791 }1781 }
1792 }1782 }
...@@ -1807,7 +1797,10 @@ test "reinterpret packed union inside packed struct" {...@@ -1807,7 +1797,10 @@ test "reinterpret packed union inside packed struct" {
18071797
1808 const U = packed union {1798 const U = packed union {
1809 a: u7,1799 a: u7,
1810 b: u1,1800 b: packed struct(u7) {
1801 a: u1,
1802 b: u6,
1803 },
1811 };1804 };
18121805
1813 const V = packed struct {1806 const V = packed struct {
...@@ -1818,18 +1811,18 @@ test "reinterpret packed union inside packed struct" {...@@ -1818,18 +1811,18 @@ test "reinterpret packed union inside packed struct" {
1818 const S = struct {1811 const S = struct {
1819 fn doTheTest() !void {1812 fn doTheTest() !void {
1820 var v: V = undefined;1813 var v: V = undefined;
1821 @memset(std.mem.asBytes(&v), 0x55);1814 @memset(@as([]u8, @ptrCast(&v)), 0x55);
1822 try expectEqual(@as(u7, 0x55), v.lo.a);1815 try expect(@as(u7, 0x55) == v.lo.a);
1823 try expectEqual(@as(u1, 1), v.lo.b);1816 try expect(@as(u1, 1) == v.lo.b.a);
1824 try expectEqual(@as(u7, 0x2a), v.hi.a);1817 try expect(@as(u7, 0x2a) == v.hi.a);
1825 try expectEqual(@as(u1, 0), v.hi.b);1818 try expect(@as(u1, 0) == v.hi.b.a);
18261819
1827 v.lo.b = 0;1820 v.lo.b.a = 0;
1828 try expectEqual(@as(u7, 0x54), v.lo.a);1821 try expect(@as(u7, 0x54) == v.lo.a);
1829 try expectEqual(@as(u1, 0), v.lo.b);1822 try expect(@as(u1, 0) == v.lo.b.a);
1830 v.hi.b = 1;1823 v.hi.b.a = 1;
1831 try expectEqual(@as(u7, 0x2b), v.hi.a);1824 try expect(@as(u7, 0x2b) == v.hi.a);
1832 try expectEqual(@as(u1, 1), v.hi.b);1825 try expect(@as(u1, 1) == v.hi.b.a);
1833 }1826 }
1834 };1827 };
18351828
...@@ -1869,8 +1862,9 @@ test "inner struct initializer uses packed union layout" {...@@ -1869,8 +1862,9 @@ test "inner struct initializer uses packed union layout" {
1869 a: packed struct {1862 a: packed struct {
1870 x: u32 = @alignOf(U) + 1,1863 x: u32 = @alignOf(U) + 1,
1871 },1864 },
1872 b: packed struct {1865 b: packed struct(u32) {
1873 y: u16 = @sizeOf(U) + 2,1866 y: u16 = @sizeOf(U) + 2,
1867 padding: u16 = 0,
1874 },1868 },
1875 };1869 };
1876 };1870 };
...@@ -1898,7 +1892,7 @@ test "extern union initialized via reintepreted struct field initializer" {...@@ -1898,7 +1892,7 @@ test "extern union initialized via reintepreted struct field initializer" {
1898 };1892 };
18991893
1900 const S = extern struct {1894 const S = extern struct {
1901 u: U = std.mem.bytesAsValue(U, &bytes).*,1895 u: U = @as(*align(1) const U, @ptrCast(&bytes)).*,
1902 };1896 };
19031897
1904 const s: S = .{};1898 const s: S = .{};
...@@ -1913,17 +1907,20 @@ test "packed union initialized via reintepreted struct field initializer" {...@@ -1913,17 +1907,20 @@ test "packed union initialized via reintepreted struct field initializer" {
19131907
1914 const U = packed union {1908 const U = packed union {
1915 a: u32,1909 a: u32,
1916 b: u8,1910 b: packed struct(u32) {
1911 a: u8,
1912 b: u24,
1913 },
1917 };1914 };
19181915
1919 const S = packed struct {1916 const S = packed struct {
1920 u: U = std.mem.bytesAsValue(U, &bytes).*,1917 u: U = @as(*align(1) const U, @ptrCast(&bytes)).*,
1921 };1918 };
19221919
1923 var s: S = .{};1920 var s: S = .{};
1924 _ = &s;1921 _ = &s;
1925 try expect(s.u.a == littleToNativeEndian(u32, 0xddccbbaa));1922 try expect(s.u.a == littleToNativeEndian(u32, 0xddccbbaa));
1926 try expect(s.u.b == if (endian == .little) 0xaa else 0xdd);1923 try expect(s.u.b.a == if (endian == .little) 0xaa else 0xdd);
1927}1924}
19281925
1929test "store of comptime reinterpreted memory to extern union" {1926test "store of comptime reinterpreted memory to extern union" {
...@@ -1938,7 +1935,7 @@ test "store of comptime reinterpreted memory to extern union" {...@@ -1938,7 +1935,7 @@ test "store of comptime reinterpreted memory to extern union" {
19381935
1939 const reinterpreted = comptime b: {1936 const reinterpreted = comptime b: {
1940 var u: U = undefined;1937 var u: U = undefined;
1941 u = std.mem.bytesAsValue(U, &bytes).*;1938 u = @as(*align(1) const U, @ptrCast(&bytes)).*;
1942 break :b u;1939 break :b u;
1943 };1940 };
19441941
...@@ -1955,19 +1952,22 @@ test "store of comptime reinterpreted memory to packed union" {...@@ -1955,19 +1952,22 @@ test "store of comptime reinterpreted memory to packed union" {
19551952
1956 const U = packed union {1953 const U = packed union {
1957 a: u32,1954 a: u32,
1958 b: u8,1955 b: packed struct(u32) {
1956 a: u8,
1957 b: u24,
1958 },
1959 };1959 };
19601960
1961 const reinterpreted = comptime b: {1961 const reinterpreted = comptime b: {
1962 var u: U = undefined;1962 var u: U = undefined;
1963 u = std.mem.bytesAsValue(U, &bytes).*;1963 u = @as(*align(1) const U, @ptrCast(&bytes)).*;
1964 break :b u;1964 break :b u;
1965 };1965 };
19661966
1967 var u: U = reinterpreted;1967 var u: U = reinterpreted;
1968 _ = &u;1968 _ = &u;
1969 try expect(u.a == littleToNativeEndian(u32, 0xddccbbaa));1969 try expect(u.a == littleToNativeEndian(u32, 0xddccbbaa));
1970 try expect(u.b == if (endian == .little) 0xaa else 0xdd);1970 try expect(u.b.a == if (endian == .little) 0xaa else 0xdd);
1971}1971}
19721972
1973test "union field is a pointer to an aligned version of itself" {1973test "union field is a pointer to an aligned version of itself" {
test/cases/compile_errors/packed_union_fields_mismatch.zig created+14
...@@ -0,0 +1,14 @@
1export fn entry1() void {
2 _ = packed union {
3 a: u1,
4 b: u2,
5 };
6}
7
8// error
9// backend=stage2
10// target=native
11//
12// :2:16: error: packed union has fields with mismatching bit sizes
13// :3:12: note: 1 bits here
14// :4:12: note: 2 bits here