| 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,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 eligible | 2497 | <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#} |
| 2500 | 2501 | ||
| 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 { |
| 1193 | 1193 | ||
| 1194 | try testing.expect(hasUniqueRepresentation(TestStruct6)); | 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 | 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); |
| 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 | 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" and | 35784 | .@"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); | ||
| 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); | 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); | ||
| 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); | 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 | } |
| 35802 | 35823 | ||
| 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); |
| 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 | 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 state | 225 | // using ptrCast not to depend on unitialised memory state |
| 226 | 226 | ||
| 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); |
| 234 | 237 | ||
| 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); |
| 242 | 248 | ||
| 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 | }; |
| 21 | const PackedUnion = packed union { | 21 | const 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 | }; |
| 25 | 28 | ||
| ... | @@ -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 | } |
| 34 | 37 | ||
| 35 | export fn testPackedStuff(a: *const PackedStruct, b: *const PackedUnion) void { | 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,30 +1755,37 @@ test "@fieldParentPtr extern union" { |
| 1755 | test "@fieldParentPtr packed union" { | 1755 | test "@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; // TODO | 1759 | if (builtin.target.cpu.arch.endian() == .big) return error.SkipZigTest; // TODO |
| 1759 | 1760 | ||
| 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 | }; |
| 1766 | 1773 | ||
| 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" { |
| 1223 | test "bitcasting a packed struct at comptime and using the result" { | 1223 | test "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, |
| 1228 | 1234 | ||
| 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" { |
| 59 | 59 | ||
| 60 | fn testFlagsInPackedUnionAtOffset() !void { | 60 | fn 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" { |
| 223 | 223 | ||
| 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))" { |
| 356 | 356 | ||
| 357 | const PackedPtrOrInt = packed union { | 357 | const PackedPtrOrInt = packed union { |
| 358 | ptr: *u8, | 358 | ptr: *u8, |
| 359 | int: u64, | 359 | int: usize, |
| 360 | }; | 360 | }; |
| 361 | test "packed union size" { | 361 | test "packed union size" { |
| 362 | comptime assert(@sizeOf(PackedPtrOrInt) == 8); | 362 | comptime assert(@sizeOf(PackedPtrOrInt) == @sizeOf(usize)); |
| 363 | } | 363 | } |
| 364 | 364 | ||
| 365 | const ZeroBits = union { | 365 | const ZeroBits = union { |
| ... | @@ -1337,7 +1337,7 @@ test "packed union in packed struct" { | ... | @@ -1337,7 +1337,7 @@ test "packed union in packed struct" { |
| 1337 | 1337 | ||
| 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 | } |
| 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 | test "reinterpreting enum value inside packed union" { | 1418 | test "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; // TODO | 1420 | 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" { |
| 1632 | 1613 | ||
| 1633 | const U = packed union { | 1614 | const U = packed union { |
| 1634 | a: u32, | 1615 | a: u32, |
| 1635 | b: u8, | 1616 | b: u32, |
| 1636 | }; | 1617 | }; |
| 1637 | 1618 | ||
| 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 | }; |
| 1646 | 1627 | ||
| ... | @@ -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; |
| 1733 | 1714 | ||
| 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 | }; |
| 1740 | 1730 | ||
| 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 | }; |
| 1751 | 1741 | ||
| 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); |
| 1754 | 1744 | ||
| 1755 | // https://github.com/ziglang/zig/issues/17360 | 1745 | // 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 initialization | 1753 | // Union initialization |
| 1764 | var u: U = .{ .baz = 0 }; // ensure all bits are defined | 1754 | 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); |
| 1770 | 1760 | ||
| 1771 | // Writing to a larger field | 1761 | // 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); |
| 1777 | 1767 | ||
| 1778 | // Writing to the same field | 1768 | // 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); |
| 1784 | 1774 | ||
| 1785 | // Writing to a smaller field | 1775 | // 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" { |
| 1807 | 1797 | ||
| 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 | }; |
| 1812 | 1805 | ||
| 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); |
| 1826 | 1819 | ||
| 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 | }; |
| 1835 | 1828 | ||
| ... | @@ -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 | }; |
| 1899 | 1893 | ||
| 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 | }; |
| 1903 | 1897 | ||
| 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" { |
| 1913 | 1907 | ||
| 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 | }; |
| 1918 | 1915 | ||
| 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 | }; |
| 1922 | 1919 | ||
| 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 | } |
| 1928 | 1925 | ||
| 1929 | test "store of comptime reinterpreted memory to extern union" { | 1926 | test "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" { |
| 1938 | 1935 | ||
| 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 | }; |
| 1944 | 1941 | ||
| ... | @@ -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" { |
| 1955 | 1952 | ||
| 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 | }; |
| 1960 | 1960 | ||
| 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 | }; |
| 1966 | 1966 | ||
| 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 | } |
| 1972 | 1972 | ||
| 1973 | test "union field is a pointer to an aligned version of itself" { | 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 | ||