| author | |
| committer | |
| log | 4c012756643ee1f18740ac88180635defa8dc6f2 |
| tree | 7486469829e8d6a42d82a879094b2311cf837a4e |
| parent | 91b3769b032a8a0e2cf9ff4f213290dd29016cdc |
| parent | 4f7aecd3485a291af63a0d8d05759c08bd683eae |
| signature |
forbid unused bits in packed unions10 files changed, 188 insertions(+), 121 deletions(-)
doc/langref.html.in+1| ... | ... | @@ -2496,6 +2496,7 @@ or |
| 2496 | 2496 | {#header_open|packed union#} |
| 2497 | 2497 | <p>A {#syntax#}packed union{#endsyntax#} has well-defined in-memory layout and is eligible |
| 2498 | 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 | 2500 | {#header_close#} |
| 2500 | 2501 | |
| 2501 | 2502 | {#header_open|Anonymous Union Literals#} |
lib/std/meta.zig-7| ... | ... | @@ -1193,13 +1193,6 @@ test hasUniqueRepresentation { |
| 1193 | 1193 | |
| 1194 | 1194 | try testing.expect(hasUniqueRepresentation(TestStruct6)); |
| 1195 | 1195 | |
| 1196 | const TestUnion1 = packed union { | |
| 1197 | a: u32, | |
| 1198 | b: u16, | |
| 1199 | }; | |
| 1200 | ||
| 1201 | try testing.expect(!hasUniqueRepresentation(TestUnion1)); | |
| 1202 | ||
| 1203 | 1196 | const TestUnion2 = extern union { |
| 1204 | 1197 | a: u32, |
| 1205 | 1198 | b: u16, |
src/Sema.zig+55-21| ... | ... | @@ -35614,6 +35614,12 @@ fn unionFields( |
| 35614 | 35614 | if (small.any_aligned_fields) |
| 35615 | 35615 | try field_aligns.ensureTotalCapacityPrecise(sema.arena, fields_len); |
| 35616 | 35616 | |
| 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 | 35623 | const bits_per_field = 4; |
| 35618 | 35624 | const fields_per_u32 = 32 / bits_per_field; |
| 35619 | 35625 | const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; |
| ... | ... | @@ -35622,6 +35628,7 @@ fn unionFields( |
| 35622 | 35628 | var cur_bit_bag: u32 = undefined; |
| 35623 | 35629 | var field_i: u32 = 0; |
| 35624 | 35630 | var last_tag_val: ?Value = null; |
| 35631 | const layout = union_type.flagsUnordered(ip).layout; | |
| 35625 | 35632 | while (field_i < fields_len) : (field_i += 1) { |
| 35626 | 35633 | if (field_i % fields_per_u32 == 0) { |
| 35627 | 35634 | cur_bit_bag = zir.extra[bit_bag_index]; |
| ... | ... | @@ -35773,31 +35780,45 @@ fn unionFields( |
| 35773 | 35780 | }; |
| 35774 | 35781 | return sema.failWithOwnedErrorMsg(&block_scope, msg); |
| 35775 | 35782 | } |
| 35776 | const layout = union_type.flagsUnordered(ip).layout; | |
| 35777 | if (layout == .@"extern" and | |
| 35778 | !try sema.validateExternType(field_ty, .union_field)) | |
| 35779 | { | |
| 35780 | const msg = msg: { | |
| 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); | |
| 35783 | switch (layout) { | |
| 35784 | .@"extern" => if (!try sema.validateExternType(field_ty, .union_field)) { | |
| 35785 | const msg = msg: { | |
| 35786 | const msg = try sema.errMsg(type_src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); | |
| 35787 | errdefer msg.destroy(sema.gpa); | |
| 35783 | 35788 | |
| 35784 | try sema.explainWhyTypeIsNotExtern(msg, type_src, field_ty, .union_field); | |
| 35789 | try sema.explainWhyTypeIsNotExtern(msg, type_src, field_ty, .union_field); | |
| 35785 | 35790 | |
| 35786 | try sema.addDeclaredHereNote(msg, field_ty); | |
| 35787 | break :msg msg; | |
| 35788 | }; | |
| 35789 | return sema.failWithOwnedErrorMsg(&block_scope, msg); | |
| 35790 | } else if (layout == .@"packed" and !try sema.validatePackedType(field_ty)) { | |
| 35791 | const msg = msg: { | |
| 35792 | const msg = try sema.errMsg(type_src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); | |
| 35793 | errdefer msg.destroy(sema.gpa); | |
| 35791 | try sema.addDeclaredHereNote(msg, field_ty); | |
| 35792 | break :msg msg; | |
| 35793 | }; | |
| 35794 | return sema.failWithOwnedErrorMsg(&block_scope, msg); | |
| 35795 | }, | |
| 35796 | .@"packed" => { | |
| 35797 | if (!try sema.validatePackedType(field_ty)) { | |
| 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); | |
| 35794 | 35801 | |
| 35795 | try sema.explainWhyTypeIsNotPacked(msg, type_src, field_ty); | |
| 35802 | try sema.explainWhyTypeIsNotPacked(msg, type_src, field_ty); | |
| 35796 | 35803 | |
| 35797 | try sema.addDeclaredHereNote(msg, field_ty); | |
| 35798 | break :msg msg; | |
| 35799 | }; | |
| 35800 | return sema.failWithOwnedErrorMsg(&block_scope, msg); | |
| 35804 | try sema.addDeclaredHereNote(msg, field_ty); | |
| 35805 | break :msg msg; | |
| 35806 | }; | |
| 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 | } |
| 35802 | 35823 | |
| 35803 | 35824 | field_types.appendAssumeCapacity(field_ty.toIntern()); |
| ... | ... | @@ -35815,6 +35836,19 @@ fn unionFields( |
| 35815 | 35836 | union_type.setFieldTypes(ip, field_types.items); |
| 35816 | 35837 | union_type.setFieldAligns(ip, field_aligns.items); |
| 35817 | 35838 | |
| 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 | 35852 | if (explicit_tags_seen.len > 0) { |
| 35819 | 35853 | const tag_ty = union_type.tagTypeUnordered(ip); |
| 35820 | 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 | 225 | // using ptrCast not to depend on unitialised memory state |
| 226 | 226 | |
| 227 | 227 | var union0: packed union { |
| 228 | p: Piece, | |
| 228 | p: packed struct(u8) { | |
| 229 | a: Piece, | |
| 230 | b: u4, | |
| 231 | }, | |
| 229 | 232 | int: u8, |
| 230 | 233 | } = .{ .int = 0 }; |
| 231 | 234 | union0.int = 0b11111011; |
| 232 | try expect(union0.p.type == .PAWN); | |
| 233 | try expect(union0.p.color == .BLACK); | |
| 235 | try expect(union0.p.a.type == .PAWN); | |
| 236 | try expect(union0.p.a.color == .BLACK); | |
| 234 | 237 | |
| 235 | 238 | var union1: union { |
| 236 | p: Piece, | |
| 239 | p: packed struct(u8) { | |
| 240 | a: Piece, | |
| 241 | b: u4, | |
| 242 | }, | |
| 237 | 243 | int: u8, |
| 238 | } = .{ .p = .{ .color = .WHITE, .type = .KING } }; | |
| 239 | @as(*u8, @ptrCast(&union1.p)).* = 0b11111011; | |
| 240 | try expect(union1.p.type == .PAWN); | |
| 241 | try expect(union1.p.color == .BLACK); | |
| 244 | } = .{ .p = .{ .a = .{ .color = .WHITE, .type = .KING }, .b = 0 } }; | |
| 245 | @as(*u8, @ptrCast(&union1.p.a)).* = 0b11111011; | |
| 246 | try expect(union1.p.a.type == .PAWN); | |
| 247 | try expect(union1.p.a.color == .BLACK); | |
| 242 | 248 | |
| 243 | 249 | var pieces: [3]Piece = undefined; |
| 244 | 250 | @as(*u8, @ptrCast(&pieces[1])).* = 0b11111011; |
test/behavior/export_keyword.zig+5-2| ... | ... | @@ -19,7 +19,10 @@ const PackedStruct = packed struct { |
| 19 | 19 | b: u8, |
| 20 | 20 | }; |
| 21 | 21 | const PackedUnion = packed union { |
| 22 | a: u8, | |
| 22 | a: packed struct(u32) { | |
| 23 | a: u8, | |
| 24 | b: u24 = 0, | |
| 25 | }, | |
| 23 | 26 | b: u32, |
| 24 | 27 | }; |
| 25 | 28 | |
| ... | ... | @@ -29,7 +32,7 @@ test "packed struct, enum, union parameters in extern function" { |
| 29 | 32 | testPackedStuff(&(PackedStruct{ |
| 30 | 33 | .a = 1, |
| 31 | 34 | .b = 2, |
| 32 | }), &(PackedUnion{ .a = 1 })); | |
| 35 | }), &(PackedUnion{ .a = .{ .a = 1 } })); | |
| 33 | 36 | } |
| 34 | 37 | |
| 35 | 38 | export 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 | 1755 | test "@fieldParentPtr packed union" { |
| 1756 | 1756 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 1757 | 1757 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 1758 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 1758 | 1759 | if (builtin.target.cpu.arch.endian() == .big) return error.SkipZigTest; // TODO |
| 1759 | 1760 | |
| 1760 | 1761 | const C = packed union { |
| 1761 | a: bool, | |
| 1762 | a: packed struct(u32) { | |
| 1763 | a: bool, | |
| 1764 | b: u31 = 0, | |
| 1765 | }, | |
| 1762 | 1766 | b: f32, |
| 1763 | c: packed struct { x: u8 }, | |
| 1767 | c: packed struct(u32) { | |
| 1768 | x: u8, | |
| 1769 | b: u24 = 0, | |
| 1770 | }, | |
| 1764 | 1771 | d: i32, |
| 1765 | 1772 | }; |
| 1766 | 1773 | |
| 1767 | 1774 | { |
| 1768 | const c: C = .{ .a = false }; | |
| 1775 | const c: C = .{ .a = .{ .a = false } }; | |
| 1769 | 1776 | const pcf = &c.a; |
| 1770 | 1777 | const pc: *const C = @alignCast(@fieldParentPtr("a", pcf)); |
| 1771 | 1778 | try expect(pc == &c); |
| 1772 | 1779 | } |
| 1773 | 1780 | { |
| 1774 | const c: C = .{ .a = false }; | |
| 1781 | const c: C = .{ .a = .{ .a = false } }; | |
| 1775 | 1782 | const pcf = &c.a; |
| 1776 | 1783 | var pc: *const C = undefined; |
| 1777 | 1784 | pc = @alignCast(@fieldParentPtr("a", pcf)); |
| 1778 | 1785 | try expect(pc == &c); |
| 1779 | 1786 | } |
| 1780 | 1787 | { |
| 1781 | const c: C = .{ .a = false }; | |
| 1788 | const c: C = .{ .a = .{ .a = false } }; | |
| 1782 | 1789 | var pcf: @TypeOf(&c.a) = undefined; |
| 1783 | 1790 | pcf = &c.a; |
| 1784 | 1791 | var pc: *const C = undefined; |
| ... | ... | @@ -1787,7 +1794,7 @@ test "@fieldParentPtr packed union" { |
| 1787 | 1794 | } |
| 1788 | 1795 | { |
| 1789 | 1796 | var c: C = undefined; |
| 1790 | c = .{ .a = false }; | |
| 1797 | c = .{ .a = .{ .a = false } }; | |
| 1791 | 1798 | var pcf: @TypeOf(&c.a) = undefined; |
| 1792 | 1799 | pcf = &c.a; |
| 1793 | 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 | 1223 | test "bitcasting a packed struct at comptime and using the result" { |
| 1224 | 1224 | comptime { |
| 1225 | 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 | 1233 | y: u1, |
| 1228 | 1234 | |
| 1229 | 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 | 59 | |
| 60 | 60 | fn testFlagsInPackedUnionAtOffset() !void { |
| 61 | 61 | const FlagBits = packed union { |
| 62 | base_flags: packed union { | |
| 63 | flags: packed struct(u4) { | |
| 64 | enable_1: bool = true, | |
| 65 | enable_2: bool = false, | |
| 66 | enable_3: bool = false, | |
| 67 | enable_4: bool = false, | |
| 62 | base_flags: packed struct(u12) { | |
| 63 | a: packed union { | |
| 64 | flags: packed struct(u4) { | |
| 65 | enable_1: bool = true, | |
| 66 | enable_2: 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 | 74 | adv_flags: packed struct(u12) { |
| 72 | 75 | pad: u8 = 0, |
test/behavior/union.zig+69-69| ... | ... | @@ -223,7 +223,7 @@ test "packed union generates correctly aligned type" { |
| 223 | 223 | |
| 224 | 224 | const U = packed union { |
| 225 | 225 | f1: *const fn () error{TestUnexpectedResult}!void, |
| 226 | f2: u32, | |
| 226 | f2: usize, | |
| 227 | 227 | }; |
| 228 | 228 | var foo = [_]U{ |
| 229 | 229 | U{ .f1 = doTest }, |
| ... | ... | @@ -356,10 +356,10 @@ test "simple union(enum(u32))" { |
| 356 | 356 | |
| 357 | 357 | const PackedPtrOrInt = packed union { |
| 358 | 358 | ptr: *u8, |
| 359 | int: u64, | |
| 359 | int: usize, | |
| 360 | 360 | }; |
| 361 | 361 | test "packed union size" { |
| 362 | comptime assert(@sizeOf(PackedPtrOrInt) == 8); | |
| 362 | comptime assert(@sizeOf(PackedPtrOrInt) == @sizeOf(usize)); | |
| 363 | 363 | } |
| 364 | 364 | |
| 365 | 365 | const ZeroBits = union { |
| ... | ... | @@ -1337,7 +1337,7 @@ test "packed union in packed struct" { |
| 1337 | 1337 | |
| 1338 | 1338 | const S = packed struct { |
| 1339 | 1339 | nested: packed union { |
| 1340 | val: u16, | |
| 1340 | val: u32, | |
| 1341 | 1341 | foo: u32, |
| 1342 | 1342 | }, |
| 1343 | 1343 | bar: u16, |
| ... | ... | @@ -1415,25 +1415,6 @@ test "union reassignment can use previous value" { |
| 1415 | 1415 | try expect(a.b == 32); |
| 1416 | 1416 | } |
| 1417 | 1417 | |
| 1418 | test "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 | ||
| 1437 | 1418 | test "reinterpreting enum value inside packed union" { |
| 1438 | 1419 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 1439 | 1420 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| ... | ... | @@ -1632,15 +1613,15 @@ test "memset packed union" { |
| 1632 | 1613 | |
| 1633 | 1614 | const U = packed union { |
| 1634 | 1615 | a: u32, |
| 1635 | b: u8, | |
| 1616 | b: u32, | |
| 1636 | 1617 | }; |
| 1637 | 1618 | |
| 1638 | 1619 | const S = struct { |
| 1639 | 1620 | fn doTheTest() !void { |
| 1640 | 1621 | var u: U = undefined; |
| 1641 | @memset(std.mem.asBytes(&u), 42); | |
| 1622 | @memset(@as([]u8, @ptrCast(&u)), 42); | |
| 1642 | 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 | }; |
| 1646 | 1627 | |
| ... | ... | @@ -1732,10 +1713,19 @@ test "reinterpret packed union" { |
| 1732 | 1713 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 1733 | 1714 | |
| 1734 | 1715 | const U = packed union { |
| 1735 | foo: u8, | |
| 1736 | bar: u29, | |
| 1716 | foo: packed struct(u64) { | |
| 1717 | a: u8, | |
| 1718 | b: u56, | |
| 1719 | }, | |
| 1720 | bar: packed struct(u64) { | |
| 1721 | a: u29, | |
| 1722 | b: u35, | |
| 1723 | }, | |
| 1737 | 1724 | baz: u64, |
| 1738 | qux: u12, | |
| 1725 | qux: packed struct(u64) { | |
| 1726 | a: u12, | |
| 1727 | b: u52, | |
| 1728 | }, | |
| 1739 | 1729 | }; |
| 1740 | 1730 | |
| 1741 | 1731 | const S = struct { |
| ... | ... | @@ -1745,16 +1735,16 @@ test "reinterpret packed union" { |
| 1745 | 1735 | var u: U = undefined; |
| 1746 | 1736 | @memset(std.mem.asBytes(&u), 0); |
| 1747 | 1737 | u.baz = 0xbbbbbbbb; |
| 1748 | u.qux = 0xe2a; | |
| 1738 | u.qux.a = 0xe2a; | |
| 1749 | 1739 | break :blk u; |
| 1750 | 1740 | }; |
| 1751 | 1741 | |
| 1752 | try expectEqual(@as(u8, 0x2a), u.foo); | |
| 1753 | try expectEqual(@as(u12, 0xe2a), u.qux); | |
| 1742 | try expectEqual(@as(u8, 0x2a), u.foo.a); | |
| 1743 | try expectEqual(@as(u12, 0xe2a), u.qux.a); | |
| 1754 | 1744 | |
| 1755 | 1745 | // https://github.com/ziglang/zig/issues/17360 |
| 1756 | 1746 | if (@inComptime()) { |
| 1757 | try expectEqual(@as(u29, 0x1bbbbe2a), u.bar); | |
| 1747 | try expectEqual(@as(u29, 0x1bbbbe2a), u.bar.a); | |
| 1758 | 1748 | try expectEqual(@as(u64, 0xbbbbbe2a), u.baz); |
| 1759 | 1749 | } |
| 1760 | 1750 | } |
| ... | ... | @@ -1762,31 +1752,31 @@ test "reinterpret packed union" { |
| 1762 | 1752 | { |
| 1763 | 1753 | // Union initialization |
| 1764 | 1754 | var u: U = .{ .baz = 0 }; // ensure all bits are defined |
| 1765 | u.qux = 0xe2a; | |
| 1766 | try expectEqual(@as(u8, 0x2a), u.foo); | |
| 1767 | try expectEqual(@as(u12, 0xe2a), u.qux); | |
| 1768 | try expectEqual(@as(u29, 0xe2a), u.bar & 0xfff); | |
| 1755 | u.qux.a = 0xe2a; | |
| 1756 | try expectEqual(@as(u8, 0x2a), u.foo.a); | |
| 1757 | try expectEqual(@as(u12, 0xe2a), u.qux.a); | |
| 1758 | try expectEqual(@as(u29, 0xe2a), u.bar.a & 0xfff); | |
| 1769 | 1759 | try expectEqual(@as(u64, 0xe2a), u.baz & 0xfff); |
| 1770 | 1760 | |
| 1771 | 1761 | // Writing to a larger field |
| 1772 | 1762 | u.baz = 0xbbbbbbbb; |
| 1773 | try expectEqual(@as(u8, 0xbb), u.foo); | |
| 1774 | try expectEqual(@as(u12, 0xbbb), u.qux); | |
| 1775 | try expectEqual(@as(u29, 0x1bbbbbbb), u.bar); | |
| 1763 | try expectEqual(@as(u8, 0xbb), u.foo.a); | |
| 1764 | try expectEqual(@as(u12, 0xbbb), u.qux.a); | |
| 1765 | try expectEqual(@as(u29, 0x1bbbbbbb), u.bar.a); | |
| 1776 | 1766 | try expectEqual(@as(u64, 0xbbbbbbbb), u.baz); |
| 1777 | 1767 | |
| 1778 | 1768 | // Writing to the same field |
| 1779 | 1769 | u.baz = 0xcccccccc; |
| 1780 | try expectEqual(@as(u8, 0xcc), u.foo); | |
| 1781 | try expectEqual(@as(u12, 0xccc), u.qux); | |
| 1782 | try expectEqual(@as(u29, 0x0ccccccc), u.bar); | |
| 1770 | try expectEqual(@as(u8, 0xcc), u.foo.a); | |
| 1771 | try expectEqual(@as(u12, 0xccc), u.qux.a); | |
| 1772 | try expectEqual(@as(u29, 0x0ccccccc), u.bar.a); | |
| 1783 | 1773 | try expectEqual(@as(u64, 0xcccccccc), u.baz); |
| 1784 | 1774 | |
| 1785 | 1775 | // Writing to a smaller field |
| 1786 | u.foo = 0xdd; | |
| 1787 | try expectEqual(@as(u8, 0xdd), u.foo); | |
| 1788 | try expectEqual(@as(u12, 0xcdd), u.qux); | |
| 1789 | try expectEqual(@as(u29, 0x0cccccdd), u.bar); | |
| 1776 | u.foo.a = 0xdd; | |
| 1777 | try expectEqual(@as(u8, 0xdd), u.foo.a); | |
| 1778 | try expectEqual(@as(u12, 0xcdd), u.qux.a); | |
| 1779 | try expectEqual(@as(u29, 0x0cccccdd), u.bar.a); | |
| 1790 | 1780 | try expectEqual(@as(u64, 0xccccccdd), u.baz); |
| 1791 | 1781 | } |
| 1792 | 1782 | } |
| ... | ... | @@ -1807,7 +1797,10 @@ test "reinterpret packed union inside packed struct" { |
| 1807 | 1797 | |
| 1808 | 1798 | const U = packed union { |
| 1809 | 1799 | a: u7, |
| 1810 | b: u1, | |
| 1800 | b: packed struct(u7) { | |
| 1801 | a: u1, | |
| 1802 | b: u6, | |
| 1803 | }, | |
| 1811 | 1804 | }; |
| 1812 | 1805 | |
| 1813 | 1806 | const V = packed struct { |
| ... | ... | @@ -1818,18 +1811,18 @@ test "reinterpret packed union inside packed struct" { |
| 1818 | 1811 | const S = struct { |
| 1819 | 1812 | fn doTheTest() !void { |
| 1820 | 1813 | var v: V = undefined; |
| 1821 | @memset(std.mem.asBytes(&v), 0x55); | |
| 1822 | try expectEqual(@as(u7, 0x55), v.lo.a); | |
| 1823 | try expectEqual(@as(u1, 1), v.lo.b); | |
| 1824 | try expectEqual(@as(u7, 0x2a), v.hi.a); | |
| 1825 | try expectEqual(@as(u1, 0), v.hi.b); | |
| 1826 | ||
| 1827 | v.lo.b = 0; | |
| 1828 | try expectEqual(@as(u7, 0x54), v.lo.a); | |
| 1829 | try expectEqual(@as(u1, 0), v.lo.b); | |
| 1830 | v.hi.b = 1; | |
| 1831 | try expectEqual(@as(u7, 0x2b), v.hi.a); | |
| 1832 | try expectEqual(@as(u1, 1), v.hi.b); | |
| 1814 | @memset(@as([]u8, @ptrCast(&v)), 0x55); | |
| 1815 | try expect(@as(u7, 0x55) == v.lo.a); | |
| 1816 | try expect(@as(u1, 1) == v.lo.b.a); | |
| 1817 | try expect(@as(u7, 0x2a) == v.hi.a); | |
| 1818 | try expect(@as(u1, 0) == v.hi.b.a); | |
| 1819 | ||
| 1820 | v.lo.b.a = 0; | |
| 1821 | try expect(@as(u7, 0x54) == v.lo.a); | |
| 1822 | try expect(@as(u1, 0) == v.lo.b.a); | |
| 1823 | v.hi.b.a = 1; | |
| 1824 | try expect(@as(u7, 0x2b) == v.hi.a); | |
| 1825 | try expect(@as(u1, 1) == v.hi.b.a); | |
| 1833 | 1826 | } |
| 1834 | 1827 | }; |
| 1835 | 1828 | |
| ... | ... | @@ -1869,8 +1862,9 @@ test "inner struct initializer uses packed union layout" { |
| 1869 | 1862 | a: packed struct { |
| 1870 | 1863 | x: u32 = @alignOf(U) + 1, |
| 1871 | 1864 | }, |
| 1872 | b: packed struct { | |
| 1865 | b: packed struct(u32) { | |
| 1873 | 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 | 1892 | }; |
| 1899 | 1893 | |
| 1900 | 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 | }; |
| 1903 | 1897 | |
| 1904 | 1898 | const s: S = .{}; |
| ... | ... | @@ -1913,17 +1907,20 @@ test "packed union initialized via reintepreted struct field initializer" { |
| 1913 | 1907 | |
| 1914 | 1908 | const U = packed union { |
| 1915 | 1909 | a: u32, |
| 1916 | b: u8, | |
| 1910 | b: packed struct(u32) { | |
| 1911 | a: u8, | |
| 1912 | b: u24, | |
| 1913 | }, | |
| 1917 | 1914 | }; |
| 1918 | 1915 | |
| 1919 | 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 | }; |
| 1922 | 1919 | |
| 1923 | 1920 | var s: S = .{}; |
| 1924 | 1921 | _ = &s; |
| 1925 | 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 | } |
| 1928 | 1925 | |
| 1929 | 1926 | test "store of comptime reinterpreted memory to extern union" { |
| ... | ... | @@ -1938,7 +1935,7 @@ test "store of comptime reinterpreted memory to extern union" { |
| 1938 | 1935 | |
| 1939 | 1936 | const reinterpreted = comptime b: { |
| 1940 | 1937 | var u: U = undefined; |
| 1941 | u = std.mem.bytesAsValue(U, &bytes).*; | |
| 1938 | u = @as(*align(1) const U, @ptrCast(&bytes)).*; | |
| 1942 | 1939 | break :b u; |
| 1943 | 1940 | }; |
| 1944 | 1941 | |
| ... | ... | @@ -1955,19 +1952,22 @@ test "store of comptime reinterpreted memory to packed union" { |
| 1955 | 1952 | |
| 1956 | 1953 | const U = packed union { |
| 1957 | 1954 | a: u32, |
| 1958 | b: u8, | |
| 1955 | b: packed struct(u32) { | |
| 1956 | a: u8, | |
| 1957 | b: u24, | |
| 1958 | }, | |
| 1959 | 1959 | }; |
| 1960 | 1960 | |
| 1961 | 1961 | const reinterpreted = comptime b: { |
| 1962 | 1962 | var u: U = undefined; |
| 1963 | u = std.mem.bytesAsValue(U, &bytes).*; | |
| 1963 | u = @as(*align(1) const U, @ptrCast(&bytes)).*; | |
| 1964 | 1964 | break :b u; |
| 1965 | 1965 | }; |
| 1966 | 1966 | |
| 1967 | 1967 | var u: U = reinterpreted; |
| 1968 | 1968 | _ = &u; |
| 1969 | 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 | } |
| 1972 | 1972 | |
| 1973 | 1973 | test "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 @@ |
| 1 | export 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 |