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

forbid unused bits in packed unions
This commit is contained in:
Andrew Kelley 2025-09-06 12:08:31 -07:00 committed by GitHub
commit 4c01275664
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 187 additions and 120 deletions

View File

@ -2496,6 +2496,7 @@ or
{#header_open|packed union#} {#header_open|packed union#}
<p>A {#syntax#}packed union{#endsyntax#} has well-defined in-memory layout and is eligible <p>A {#syntax#}packed union{#endsyntax#} has well-defined in-memory layout and is eligible
to be in a {#link|packed struct#}.</p> to be in a {#link|packed struct#}.</p>
<p>All fields in a packed union must have the same {#link|@bitSizeOf#}.</p>
{#header_close#} {#header_close#}
{#header_open|Anonymous Union Literals#} {#header_open|Anonymous Union Literals#}

View File

@ -1193,13 +1193,6 @@ test hasUniqueRepresentation {
try testing.expect(hasUniqueRepresentation(TestStruct6)); try testing.expect(hasUniqueRepresentation(TestStruct6));
const TestUnion1 = packed union {
a: u32,
b: u16,
};
try testing.expect(!hasUniqueRepresentation(TestUnion1));
const TestUnion2 = extern union { const TestUnion2 = extern union {
a: u32, a: u32,
b: u16, b: u16,

View File

@ -35614,6 +35614,12 @@ fn unionFields(
if (small.any_aligned_fields) if (small.any_aligned_fields)
try field_aligns.ensureTotalCapacityPrecise(sema.arena, fields_len); 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 bits_per_field = 4;
const fields_per_u32 = 32 / bits_per_field; const fields_per_u32 = 32 / bits_per_field;
const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; 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 cur_bit_bag: u32 = undefined;
var field_i: u32 = 0; var field_i: u32 = 0;
var last_tag_val: ?Value = null; var last_tag_val: ?Value = null;
const layout = union_type.flagsUnordered(ip).layout;
while (field_i < fields_len) : (field_i += 1) { while (field_i < fields_len) : (field_i += 1) {
if (field_i % fields_per_u32 == 0) { if (field_i % fields_per_u32 == 0) {
cur_bit_bag = zir.extra[bit_bag_index]; cur_bit_bag = zir.extra[bit_bag_index];
@ -35773,31 +35780,45 @@ fn unionFields(
}; };
return sema.failWithOwnedErrorMsg(&block_scope, msg); return sema.failWithOwnedErrorMsg(&block_scope, msg);
} }
const layout = union_type.flagsUnordered(ip).layout; switch (layout) {
if (layout == .@"extern" and .@"extern" => if (!try sema.validateExternType(field_ty, .union_field)) {
!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)});
const msg = msg: { errdefer msg.destroy(sema.gpa);
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); try sema.addDeclaredHereNote(msg, field_ty);
break :msg msg; break :msg msg;
}; };
return sema.failWithOwnedErrorMsg(&block_scope, msg); return sema.failWithOwnedErrorMsg(&block_scope, msg);
} else if (layout == .@"packed" and !try sema.validatePackedType(field_ty)) { },
const msg = msg: { .@"packed" => {
const msg = try sema.errMsg(type_src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)}); if (!try sema.validatePackedType(field_ty)) {
errdefer msg.destroy(sema.gpa); 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); try sema.addDeclaredHereNote(msg, field_ty);
break :msg msg; break :msg msg;
}; };
return sema.failWithOwnedErrorMsg(&block_scope, 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()); field_types.appendAssumeCapacity(field_ty.toIntern());
@ -35815,6 +35836,19 @@ fn unionFields(
union_type.setFieldTypes(ip, field_types.items); union_type.setFieldTypes(ip, field_types.items);
union_type.setFieldAligns(ip, field_aligns.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) { if (explicit_tags_seen.len > 0) {
const tag_ty = union_type.tagTypeUnordered(ip); const tag_ty = union_type.tagTypeUnordered(ip);
const tag_info = ip.loadEnumType(tag_ty); const tag_info = ip.loadEnumType(tag_ty);

View File

@ -225,20 +225,26 @@ test "load non byte-sized value in union" {
// using ptrCast not to depend on unitialised memory state // using ptrCast not to depend on unitialised memory state
var union0: packed union { var union0: packed union {
p: Piece, p: packed struct(u8) {
a: Piece,
b: u4,
},
int: u8, int: u8,
} = .{ .int = 0 }; } = .{ .int = 0 };
union0.int = 0b11111011; union0.int = 0b11111011;
try expect(union0.p.type == .PAWN); try expect(union0.p.a.type == .PAWN);
try expect(union0.p.color == .BLACK); try expect(union0.p.a.color == .BLACK);
var union1: union { var union1: union {
p: Piece, p: packed struct(u8) {
a: Piece,
b: u4,
},
int: u8, int: u8,
} = .{ .p = .{ .color = .WHITE, .type = .KING } }; } = .{ .p = .{ .a = .{ .color = .WHITE, .type = .KING }, .b = 0 } };
@as(*u8, @ptrCast(&union1.p)).* = 0b11111011; @as(*u8, @ptrCast(&union1.p.a)).* = 0b11111011;
try expect(union1.p.type == .PAWN); try expect(union1.p.a.type == .PAWN);
try expect(union1.p.color == .BLACK); try expect(union1.p.a.color == .BLACK);
var pieces: [3]Piece = undefined; var pieces: [3]Piece = undefined;
@as(*u8, @ptrCast(&pieces[1])).* = 0b11111011; @as(*u8, @ptrCast(&pieces[1])).* = 0b11111011;

View File

@ -19,7 +19,10 @@ const PackedStruct = packed struct {
b: u8, b: u8,
}; };
const PackedUnion = packed union { const PackedUnion = packed union {
a: u8, a: packed struct(u32) {
a: u8,
b: u24 = 0,
},
b: u32, b: u32,
}; };
@ -29,7 +32,7 @@ test "packed struct, enum, union parameters in extern function" {
testPackedStuff(&(PackedStruct{ testPackedStuff(&(PackedStruct{
.a = 1, .a = 1,
.b = 2, .b = 2,
}), &(PackedUnion{ .a = 1 })); }), &(PackedUnion{ .a = .{ .a = 1 } }));
} }
export fn testPackedStuff(a: *const PackedStruct, b: *const PackedUnion) void { export fn testPackedStuff(a: *const PackedStruct, b: *const PackedUnion) void {

View File

@ -1755,30 +1755,37 @@ test "@fieldParentPtr extern union" {
test "@fieldParentPtr packed union" { test "@fieldParentPtr packed union" {
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_riscv64) 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 if (builtin.target.cpu.arch.endian() == .big) return error.SkipZigTest; // TODO
const C = packed union { const C = packed union {
a: bool, a: packed struct(u32) {
a: bool,
b: u31 = 0,
},
b: f32, b: f32,
c: packed struct { x: u8 }, c: packed struct(u32) {
x: u8,
b: u24 = 0,
},
d: i32, d: i32,
}; };
{ {
const c: C = .{ .a = false }; const c: C = .{ .a = .{ .a = false } };
const pcf = &c.a; const pcf = &c.a;
const pc: *const C = @alignCast(@fieldParentPtr("a", pcf)); const pc: *const C = @alignCast(@fieldParentPtr("a", pcf));
try expect(pc == &c); try expect(pc == &c);
} }
{ {
const c: C = .{ .a = false }; const c: C = .{ .a = .{ .a = false } };
const pcf = &c.a; const pcf = &c.a;
var pc: *const C = undefined; var pc: *const C = undefined;
pc = @alignCast(@fieldParentPtr("a", pcf)); pc = @alignCast(@fieldParentPtr("a", pcf));
try expect(pc == &c); try expect(pc == &c);
} }
{ {
const c: C = .{ .a = false }; const c: C = .{ .a = .{ .a = false } };
var pcf: @TypeOf(&c.a) = undefined; var pcf: @TypeOf(&c.a) = undefined;
pcf = &c.a; pcf = &c.a;
var pc: *const C = undefined; var pc: *const C = undefined;
@ -1787,7 +1794,7 @@ test "@fieldParentPtr packed union" {
} }
{ {
var c: C = undefined; var c: C = undefined;
c = .{ .a = false }; c = .{ .a = .{ .a = false } };
var pcf: @TypeOf(&c.a) = undefined; var pcf: @TypeOf(&c.a) = undefined;
pcf = &c.a; pcf = &c.a;
var pc: *C = undefined; var pc: *C = undefined;

View File

@ -1223,7 +1223,13 @@ test "load flag from packed struct in union" {
test "bitcasting a packed struct at comptime and using the result" { test "bitcasting a packed struct at comptime and using the result" {
comptime { comptime {
const Struct = packed struct { 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, y: u1,
pub fn bitcast(fd: u64) @This() { pub fn bitcast(fd: u64) @This() {

View File

@ -59,14 +59,17 @@ test "flags in packed union at offset" {
fn testFlagsInPackedUnionAtOffset() !void { fn testFlagsInPackedUnionAtOffset() !void {
const FlagBits = packed union { const FlagBits = packed union {
base_flags: packed union { base_flags: packed struct(u12) {
flags: packed struct(u4) { a: packed union {
enable_1: bool = true, flags: packed struct(u4) {
enable_2: bool = false, enable_1: bool = true,
enable_3: bool = false, enable_2: bool = false,
enable_4: bool = false, enable_3: bool = false,
enable_4: bool = false,
},
bits: u4,
}, },
bits: u4, pad: u8 = 0,
}, },
adv_flags: packed struct(u12) { adv_flags: packed struct(u12) {
pad: u8 = 0, pad: u8 = 0,

View File

@ -223,7 +223,7 @@ test "packed union generates correctly aligned type" {
const U = packed union { const U = packed union {
f1: *const fn () error{TestUnexpectedResult}!void, f1: *const fn () error{TestUnexpectedResult}!void,
f2: u32, f2: usize,
}; };
var foo = [_]U{ var foo = [_]U{
U{ .f1 = doTest }, U{ .f1 = doTest },
@ -356,10 +356,10 @@ test "simple union(enum(u32))" {
const PackedPtrOrInt = packed union { const PackedPtrOrInt = packed union {
ptr: *u8, ptr: *u8,
int: u64, int: usize,
}; };
test "packed union size" { test "packed union size" {
comptime assert(@sizeOf(PackedPtrOrInt) == 8); comptime assert(@sizeOf(PackedPtrOrInt) == @sizeOf(usize));
} }
const ZeroBits = union { const ZeroBits = union {
@ -1337,7 +1337,7 @@ test "packed union in packed struct" {
const S = packed struct { const S = packed struct {
nested: packed union { nested: packed union {
val: u16, val: u32,
foo: u32, foo: u32,
}, },
bar: u16, bar: u16,
@ -1415,25 +1415,6 @@ test "union reassignment can use previous value" {
try expect(a.b == 32); 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" { test "reinterpreting enum value inside packed union" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
@ -1632,15 +1613,15 @@ test "memset packed union" {
const U = packed union { const U = packed union {
a: u32, a: u32,
b: u8, b: u32,
}; };
const S = struct { const S = struct {
fn doTheTest() !void { fn doTheTest() !void {
var u: U = undefined; 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(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; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
const U = packed union { const U = packed union {
foo: u8, foo: packed struct(u64) {
bar: u29, a: u8,
b: u56,
},
bar: packed struct(u64) {
a: u29,
b: u35,
},
baz: u64, baz: u64,
qux: u12, qux: packed struct(u64) {
a: u12,
b: u52,
},
}; };
const S = struct { const S = struct {
@ -1745,16 +1735,16 @@ test "reinterpret packed union" {
var u: U = undefined; var u: U = undefined;
@memset(std.mem.asBytes(&u), 0); @memset(std.mem.asBytes(&u), 0);
u.baz = 0xbbbbbbbb; u.baz = 0xbbbbbbbb;
u.qux = 0xe2a; u.qux.a = 0xe2a;
break :blk u; break :blk u;
}; };
try expectEqual(@as(u8, 0x2a), u.foo); try expectEqual(@as(u8, 0x2a), u.foo.a);
try expectEqual(@as(u12, 0xe2a), u.qux); try expectEqual(@as(u12, 0xe2a), u.qux.a);
// https://github.com/ziglang/zig/issues/17360 // https://github.com/ziglang/zig/issues/17360
if (@inComptime()) { if (@inComptime()) {
try expectEqual(@as(u29, 0x1bbbbe2a), u.bar); try expectEqual(@as(u29, 0x1bbbbe2a), u.bar.a);
try expectEqual(@as(u64, 0xbbbbbe2a), u.baz); try expectEqual(@as(u64, 0xbbbbbe2a), u.baz);
} }
} }
@ -1762,31 +1752,31 @@ test "reinterpret packed union" {
{ {
// Union initialization // Union initialization
var u: U = .{ .baz = 0 }; // ensure all bits are defined var u: U = .{ .baz = 0 }; // ensure all bits are defined
u.qux = 0xe2a; u.qux.a = 0xe2a;
try expectEqual(@as(u8, 0x2a), u.foo); try expectEqual(@as(u8, 0x2a), u.foo.a);
try expectEqual(@as(u12, 0xe2a), u.qux); try expectEqual(@as(u12, 0xe2a), u.qux.a);
try expectEqual(@as(u29, 0xe2a), u.bar & 0xfff); try expectEqual(@as(u29, 0xe2a), u.bar.a & 0xfff);
try expectEqual(@as(u64, 0xe2a), u.baz & 0xfff); try expectEqual(@as(u64, 0xe2a), u.baz & 0xfff);
// Writing to a larger field // Writing to a larger field
u.baz = 0xbbbbbbbb; u.baz = 0xbbbbbbbb;
try expectEqual(@as(u8, 0xbb), u.foo); try expectEqual(@as(u8, 0xbb), u.foo.a);
try expectEqual(@as(u12, 0xbbb), u.qux); try expectEqual(@as(u12, 0xbbb), u.qux.a);
try expectEqual(@as(u29, 0x1bbbbbbb), u.bar); try expectEqual(@as(u29, 0x1bbbbbbb), u.bar.a);
try expectEqual(@as(u64, 0xbbbbbbbb), u.baz); try expectEqual(@as(u64, 0xbbbbbbbb), u.baz);
// Writing to the same field // Writing to the same field
u.baz = 0xcccccccc; u.baz = 0xcccccccc;
try expectEqual(@as(u8, 0xcc), u.foo); try expectEqual(@as(u8, 0xcc), u.foo.a);
try expectEqual(@as(u12, 0xccc), u.qux); try expectEqual(@as(u12, 0xccc), u.qux.a);
try expectEqual(@as(u29, 0x0ccccccc), u.bar); try expectEqual(@as(u29, 0x0ccccccc), u.bar.a);
try expectEqual(@as(u64, 0xcccccccc), u.baz); try expectEqual(@as(u64, 0xcccccccc), u.baz);
// Writing to a smaller field // Writing to a smaller field
u.foo = 0xdd; u.foo.a = 0xdd;
try expectEqual(@as(u8, 0xdd), u.foo); try expectEqual(@as(u8, 0xdd), u.foo.a);
try expectEqual(@as(u12, 0xcdd), u.qux); try expectEqual(@as(u12, 0xcdd), u.qux.a);
try expectEqual(@as(u29, 0x0cccccdd), u.bar); try expectEqual(@as(u29, 0x0cccccdd), u.bar.a);
try expectEqual(@as(u64, 0xccccccdd), u.baz); try expectEqual(@as(u64, 0xccccccdd), u.baz);
} }
} }
@ -1807,7 +1797,10 @@ test "reinterpret packed union inside packed struct" {
const U = packed union { const U = packed union {
a: u7, a: u7,
b: u1, b: packed struct(u7) {
a: u1,
b: u6,
},
}; };
const V = packed struct { const V = packed struct {
@ -1818,18 +1811,18 @@ test "reinterpret packed union inside packed struct" {
const S = struct { const S = struct {
fn doTheTest() !void { fn doTheTest() !void {
var v: V = undefined; var v: V = undefined;
@memset(std.mem.asBytes(&v), 0x55); @memset(@as([]u8, @ptrCast(&v)), 0x55);
try expectEqual(@as(u7, 0x55), v.lo.a); try expect(@as(u7, 0x55) == v.lo.a);
try expectEqual(@as(u1, 1), v.lo.b); try expect(@as(u1, 1) == v.lo.b.a);
try expectEqual(@as(u7, 0x2a), v.hi.a); try expect(@as(u7, 0x2a) == v.hi.a);
try expectEqual(@as(u1, 0), v.hi.b); try expect(@as(u1, 0) == v.hi.b.a);
v.lo.b = 0; v.lo.b.a = 0;
try expectEqual(@as(u7, 0x54), v.lo.a); try expect(@as(u7, 0x54) == v.lo.a);
try expectEqual(@as(u1, 0), v.lo.b); try expect(@as(u1, 0) == v.lo.b.a);
v.hi.b = 1; v.hi.b.a = 1;
try expectEqual(@as(u7, 0x2b), v.hi.a); try expect(@as(u7, 0x2b) == v.hi.a);
try expectEqual(@as(u1, 1), v.hi.b); try expect(@as(u1, 1) == v.hi.b.a);
} }
}; };
@ -1869,8 +1862,9 @@ test "inner struct initializer uses packed union layout" {
a: packed struct { a: packed struct {
x: u32 = @alignOf(U) + 1, x: u32 = @alignOf(U) + 1,
}, },
b: packed struct { b: packed struct(u32) {
y: u16 = @sizeOf(U) + 2, 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 { const S = extern struct {
u: U = std.mem.bytesAsValue(U, &bytes).*, u: U = @as(*align(1) const U, @ptrCast(&bytes)).*,
}; };
const s: S = .{}; const s: S = .{};
@ -1913,17 +1907,20 @@ test "packed union initialized via reintepreted struct field initializer" {
const U = packed union { const U = packed union {
a: u32, a: u32,
b: u8, b: packed struct(u32) {
a: u8,
b: u24,
},
}; };
const S = packed struct { const S = packed struct {
u: U = std.mem.bytesAsValue(U, &bytes).*, u: U = @as(*align(1) const U, @ptrCast(&bytes)).*,
}; };
var s: S = .{}; var s: S = .{};
_ = &s; _ = &s;
try expect(s.u.a == littleToNativeEndian(u32, 0xddccbbaa)); 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" { 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: { const reinterpreted = comptime b: {
var u: U = undefined; var u: U = undefined;
u = std.mem.bytesAsValue(U, &bytes).*; u = @as(*align(1) const U, @ptrCast(&bytes)).*;
break :b u; break :b u;
}; };
@ -1955,19 +1952,22 @@ test "store of comptime reinterpreted memory to packed union" {
const U = packed union { const U = packed union {
a: u32, a: u32,
b: u8, b: packed struct(u32) {
a: u8,
b: u24,
},
}; };
const reinterpreted = comptime b: { const reinterpreted = comptime b: {
var u: U = undefined; var u: U = undefined;
u = std.mem.bytesAsValue(U, &bytes).*; u = @as(*align(1) const U, @ptrCast(&bytes)).*;
break :b u; break :b u;
}; };
var u: U = reinterpreted; var u: U = reinterpreted;
_ = &u; _ = &u;
try expect(u.a == littleToNativeEndian(u32, 0xddccbbaa)); 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" { test "union field is a pointer to an aligned version of itself" {

View File

@ -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