mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
Merge pull request #25163 from ziglang/packed-union-unused
forbid unused bits in packed unions
This commit is contained in:
commit
4c01275664
@ -2496,6 +2496,7 @@ or
|
||||
{#header_open|packed union#}
|
||||
<p>A {#syntax#}packed union{#endsyntax#} has well-defined in-memory layout and is eligible
|
||||
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_open|Anonymous Union Literals#}
|
||||
|
||||
@ -1193,13 +1193,6 @@ test hasUniqueRepresentation {
|
||||
|
||||
try testing.expect(hasUniqueRepresentation(TestStruct6));
|
||||
|
||||
const TestUnion1 = packed union {
|
||||
a: u32,
|
||||
b: u16,
|
||||
};
|
||||
|
||||
try testing.expect(!hasUniqueRepresentation(TestUnion1));
|
||||
|
||||
const TestUnion2 = extern union {
|
||||
a: u32,
|
||||
b: u16,
|
||||
|
||||
76
src/Sema.zig
76
src/Sema.zig
@ -35614,6 +35614,12 @@ fn unionFields(
|
||||
if (small.any_aligned_fields)
|
||||
try field_aligns.ensureTotalCapacityPrecise(sema.arena, fields_len);
|
||||
|
||||
var max_bits: u64 = 0;
|
||||
var min_bits: u64 = std.math.maxInt(u64);
|
||||
var max_bits_src: LazySrcLoc = undefined;
|
||||
var min_bits_src: LazySrcLoc = undefined;
|
||||
var max_bits_ty: Type = undefined;
|
||||
var min_bits_ty: Type = undefined;
|
||||
const bits_per_field = 4;
|
||||
const fields_per_u32 = 32 / bits_per_field;
|
||||
const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
|
||||
@ -35622,6 +35628,7 @@ fn unionFields(
|
||||
var cur_bit_bag: u32 = undefined;
|
||||
var field_i: u32 = 0;
|
||||
var last_tag_val: ?Value = null;
|
||||
const layout = union_type.flagsUnordered(ip).layout;
|
||||
while (field_i < fields_len) : (field_i += 1) {
|
||||
if (field_i % fields_per_u32 == 0) {
|
||||
cur_bit_bag = zir.extra[bit_bag_index];
|
||||
@ -35773,31 +35780,45 @@ fn unionFields(
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(&block_scope, msg);
|
||||
}
|
||||
const layout = union_type.flagsUnordered(ip).layout;
|
||||
if (layout == .@"extern" and
|
||||
!try sema.validateExternType(field_ty, .union_field))
|
||||
{
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(type_src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
switch (layout) {
|
||||
.@"extern" => if (!try sema.validateExternType(field_ty, .union_field)) {
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(type_src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
|
||||
try sema.explainWhyTypeIsNotExtern(msg, type_src, field_ty, .union_field);
|
||||
try sema.explainWhyTypeIsNotExtern(msg, type_src, field_ty, .union_field);
|
||||
|
||||
try sema.addDeclaredHereNote(msg, field_ty);
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(&block_scope, msg);
|
||||
} else if (layout == .@"packed" and !try sema.validatePackedType(field_ty)) {
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(type_src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
try sema.addDeclaredHereNote(msg, field_ty);
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(&block_scope, msg);
|
||||
},
|
||||
.@"packed" => {
|
||||
if (!try sema.validatePackedType(field_ty)) {
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(type_src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
|
||||
try sema.explainWhyTypeIsNotPacked(msg, type_src, field_ty);
|
||||
try sema.explainWhyTypeIsNotPacked(msg, type_src, field_ty);
|
||||
|
||||
try sema.addDeclaredHereNote(msg, field_ty);
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(&block_scope, msg);
|
||||
try sema.addDeclaredHereNote(msg, field_ty);
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(&block_scope, msg);
|
||||
}
|
||||
const field_bits = try field_ty.bitSizeSema(pt);
|
||||
if (field_bits >= max_bits) {
|
||||
max_bits = field_bits;
|
||||
max_bits_src = type_src;
|
||||
max_bits_ty = field_ty;
|
||||
}
|
||||
if (field_bits <= min_bits) {
|
||||
min_bits = field_bits;
|
||||
min_bits_src = type_src;
|
||||
min_bits_ty = field_ty;
|
||||
}
|
||||
},
|
||||
.auto => {},
|
||||
}
|
||||
|
||||
field_types.appendAssumeCapacity(field_ty.toIntern());
|
||||
@ -35815,6 +35836,19 @@ fn unionFields(
|
||||
union_type.setFieldTypes(ip, field_types.items);
|
||||
union_type.setFieldAligns(ip, field_aligns.items);
|
||||
|
||||
if (layout == .@"packed" and fields_len != 0 and min_bits != max_bits) {
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(src, "packed union has fields with mismatching bit sizes", .{});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
try sema.errNote(min_bits_src, msg, "{d} bits here", .{min_bits});
|
||||
try sema.addDeclaredHereNote(msg, min_bits_ty);
|
||||
try sema.errNote(max_bits_src, msg, "{d} bits here", .{max_bits});
|
||||
try sema.addDeclaredHereNote(msg, max_bits_ty);
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(&block_scope, msg);
|
||||
}
|
||||
|
||||
if (explicit_tags_seen.len > 0) {
|
||||
const tag_ty = union_type.tagTypeUnordered(ip);
|
||||
const tag_info = ip.loadEnumType(tag_ty);
|
||||
|
||||
@ -225,20 +225,26 @@ test "load non byte-sized value in union" {
|
||||
// using ptrCast not to depend on unitialised memory state
|
||||
|
||||
var union0: packed union {
|
||||
p: Piece,
|
||||
p: packed struct(u8) {
|
||||
a: Piece,
|
||||
b: u4,
|
||||
},
|
||||
int: u8,
|
||||
} = .{ .int = 0 };
|
||||
union0.int = 0b11111011;
|
||||
try expect(union0.p.type == .PAWN);
|
||||
try expect(union0.p.color == .BLACK);
|
||||
try expect(union0.p.a.type == .PAWN);
|
||||
try expect(union0.p.a.color == .BLACK);
|
||||
|
||||
var union1: union {
|
||||
p: Piece,
|
||||
p: packed struct(u8) {
|
||||
a: Piece,
|
||||
b: u4,
|
||||
},
|
||||
int: u8,
|
||||
} = .{ .p = .{ .color = .WHITE, .type = .KING } };
|
||||
@as(*u8, @ptrCast(&union1.p)).* = 0b11111011;
|
||||
try expect(union1.p.type == .PAWN);
|
||||
try expect(union1.p.color == .BLACK);
|
||||
} = .{ .p = .{ .a = .{ .color = .WHITE, .type = .KING }, .b = 0 } };
|
||||
@as(*u8, @ptrCast(&union1.p.a)).* = 0b11111011;
|
||||
try expect(union1.p.a.type == .PAWN);
|
||||
try expect(union1.p.a.color == .BLACK);
|
||||
|
||||
var pieces: [3]Piece = undefined;
|
||||
@as(*u8, @ptrCast(&pieces[1])).* = 0b11111011;
|
||||
|
||||
@ -19,7 +19,10 @@ const PackedStruct = packed struct {
|
||||
b: u8,
|
||||
};
|
||||
const PackedUnion = packed union {
|
||||
a: u8,
|
||||
a: packed struct(u32) {
|
||||
a: u8,
|
||||
b: u24 = 0,
|
||||
},
|
||||
b: u32,
|
||||
};
|
||||
|
||||
@ -29,7 +32,7 @@ test "packed struct, enum, union parameters in extern function" {
|
||||
testPackedStuff(&(PackedStruct{
|
||||
.a = 1,
|
||||
.b = 2,
|
||||
}), &(PackedUnion{ .a = 1 }));
|
||||
}), &(PackedUnion{ .a = .{ .a = 1 } }));
|
||||
}
|
||||
|
||||
export fn testPackedStuff(a: *const PackedStruct, b: *const PackedUnion) void {
|
||||
|
||||
@ -1755,30 +1755,37 @@ test "@fieldParentPtr extern union" {
|
||||
test "@fieldParentPtr packed union" {
|
||||
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.target.cpu.arch.endian() == .big) return error.SkipZigTest; // TODO
|
||||
|
||||
const C = packed union {
|
||||
a: bool,
|
||||
a: packed struct(u32) {
|
||||
a: bool,
|
||||
b: u31 = 0,
|
||||
},
|
||||
b: f32,
|
||||
c: packed struct { x: u8 },
|
||||
c: packed struct(u32) {
|
||||
x: u8,
|
||||
b: u24 = 0,
|
||||
},
|
||||
d: i32,
|
||||
};
|
||||
|
||||
{
|
||||
const c: C = .{ .a = false };
|
||||
const c: C = .{ .a = .{ .a = false } };
|
||||
const pcf = &c.a;
|
||||
const pc: *const C = @alignCast(@fieldParentPtr("a", pcf));
|
||||
try expect(pc == &c);
|
||||
}
|
||||
{
|
||||
const c: C = .{ .a = false };
|
||||
const c: C = .{ .a = .{ .a = false } };
|
||||
const pcf = &c.a;
|
||||
var pc: *const C = undefined;
|
||||
pc = @alignCast(@fieldParentPtr("a", pcf));
|
||||
try expect(pc == &c);
|
||||
}
|
||||
{
|
||||
const c: C = .{ .a = false };
|
||||
const c: C = .{ .a = .{ .a = false } };
|
||||
var pcf: @TypeOf(&c.a) = undefined;
|
||||
pcf = &c.a;
|
||||
var pc: *const C = undefined;
|
||||
@ -1787,7 +1794,7 @@ test "@fieldParentPtr packed union" {
|
||||
}
|
||||
{
|
||||
var c: C = undefined;
|
||||
c = .{ .a = false };
|
||||
c = .{ .a = .{ .a = false } };
|
||||
var pcf: @TypeOf(&c.a) = undefined;
|
||||
pcf = &c.a;
|
||||
var pc: *C = undefined;
|
||||
|
||||
@ -1223,7 +1223,13 @@ test "load flag from packed struct in union" {
|
||||
test "bitcasting a packed struct at comptime and using the result" {
|
||||
comptime {
|
||||
const Struct = packed struct {
|
||||
x: packed union { a: u63, b: i32 },
|
||||
x: packed union {
|
||||
a: u63,
|
||||
b: packed struct(u63) {
|
||||
a: i32,
|
||||
b: u31 = 0,
|
||||
},
|
||||
},
|
||||
y: u1,
|
||||
|
||||
pub fn bitcast(fd: u64) @This() {
|
||||
|
||||
@ -59,14 +59,17 @@ test "flags in packed union at offset" {
|
||||
|
||||
fn testFlagsInPackedUnionAtOffset() !void {
|
||||
const FlagBits = packed union {
|
||||
base_flags: packed union {
|
||||
flags: packed struct(u4) {
|
||||
enable_1: bool = true,
|
||||
enable_2: bool = false,
|
||||
enable_3: bool = false,
|
||||
enable_4: bool = false,
|
||||
base_flags: packed struct(u12) {
|
||||
a: packed union {
|
||||
flags: packed struct(u4) {
|
||||
enable_1: bool = true,
|
||||
enable_2: bool = false,
|
||||
enable_3: bool = false,
|
||||
enable_4: bool = false,
|
||||
},
|
||||
bits: u4,
|
||||
},
|
||||
bits: u4,
|
||||
pad: u8 = 0,
|
||||
},
|
||||
adv_flags: packed struct(u12) {
|
||||
pad: u8 = 0,
|
||||
|
||||
@ -223,7 +223,7 @@ test "packed union generates correctly aligned type" {
|
||||
|
||||
const U = packed union {
|
||||
f1: *const fn () error{TestUnexpectedResult}!void,
|
||||
f2: u32,
|
||||
f2: usize,
|
||||
};
|
||||
var foo = [_]U{
|
||||
U{ .f1 = doTest },
|
||||
@ -356,10 +356,10 @@ test "simple union(enum(u32))" {
|
||||
|
||||
const PackedPtrOrInt = packed union {
|
||||
ptr: *u8,
|
||||
int: u64,
|
||||
int: usize,
|
||||
};
|
||||
test "packed union size" {
|
||||
comptime assert(@sizeOf(PackedPtrOrInt) == 8);
|
||||
comptime assert(@sizeOf(PackedPtrOrInt) == @sizeOf(usize));
|
||||
}
|
||||
|
||||
const ZeroBits = union {
|
||||
@ -1337,7 +1337,7 @@ test "packed union in packed struct" {
|
||||
|
||||
const S = packed struct {
|
||||
nested: packed union {
|
||||
val: u16,
|
||||
val: u32,
|
||||
foo: u32,
|
||||
},
|
||||
bar: u16,
|
||||
@ -1415,25 +1415,6 @@ test "union reassignment can use previous value" {
|
||||
try expect(a.b == 32);
|
||||
}
|
||||
|
||||
test "packed union with zero-bit field" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = packed struct {
|
||||
nested: packed union {
|
||||
zero: void,
|
||||
sized: u32,
|
||||
},
|
||||
bar: u32,
|
||||
|
||||
fn doTest(self: @This()) !void {
|
||||
try expect(self.bar == 42);
|
||||
}
|
||||
};
|
||||
try S.doTest(.{ .nested = .{ .zero = {} }, .bar = 42 });
|
||||
}
|
||||
|
||||
test "reinterpreting enum value inside packed union" {
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
@ -1632,15 +1613,15 @@ test "memset packed union" {
|
||||
|
||||
const U = packed union {
|
||||
a: u32,
|
||||
b: u8,
|
||||
b: u32,
|
||||
};
|
||||
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var u: U = undefined;
|
||||
@memset(std.mem.asBytes(&u), 42);
|
||||
@memset(@as([]u8, @ptrCast(&u)), 42);
|
||||
try expectEqual(@as(u32, 0x2a2a2a2a), u.a);
|
||||
try expectEqual(@as(u8, 0x2a), u.b);
|
||||
try expectEqual(@as(u32, 0x2a2a2a2a), u.b);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1732,10 +1713,19 @@ test "reinterpret packed union" {
|
||||
if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
|
||||
|
||||
const U = packed union {
|
||||
foo: u8,
|
||||
bar: u29,
|
||||
foo: packed struct(u64) {
|
||||
a: u8,
|
||||
b: u56,
|
||||
},
|
||||
bar: packed struct(u64) {
|
||||
a: u29,
|
||||
b: u35,
|
||||
},
|
||||
baz: u64,
|
||||
qux: u12,
|
||||
qux: packed struct(u64) {
|
||||
a: u12,
|
||||
b: u52,
|
||||
},
|
||||
};
|
||||
|
||||
const S = struct {
|
||||
@ -1745,16 +1735,16 @@ test "reinterpret packed union" {
|
||||
var u: U = undefined;
|
||||
@memset(std.mem.asBytes(&u), 0);
|
||||
u.baz = 0xbbbbbbbb;
|
||||
u.qux = 0xe2a;
|
||||
u.qux.a = 0xe2a;
|
||||
break :blk u;
|
||||
};
|
||||
|
||||
try expectEqual(@as(u8, 0x2a), u.foo);
|
||||
try expectEqual(@as(u12, 0xe2a), u.qux);
|
||||
try expectEqual(@as(u8, 0x2a), u.foo.a);
|
||||
try expectEqual(@as(u12, 0xe2a), u.qux.a);
|
||||
|
||||
// https://github.com/ziglang/zig/issues/17360
|
||||
if (@inComptime()) {
|
||||
try expectEqual(@as(u29, 0x1bbbbe2a), u.bar);
|
||||
try expectEqual(@as(u29, 0x1bbbbe2a), u.bar.a);
|
||||
try expectEqual(@as(u64, 0xbbbbbe2a), u.baz);
|
||||
}
|
||||
}
|
||||
@ -1762,31 +1752,31 @@ test "reinterpret packed union" {
|
||||
{
|
||||
// Union initialization
|
||||
var u: U = .{ .baz = 0 }; // ensure all bits are defined
|
||||
u.qux = 0xe2a;
|
||||
try expectEqual(@as(u8, 0x2a), u.foo);
|
||||
try expectEqual(@as(u12, 0xe2a), u.qux);
|
||||
try expectEqual(@as(u29, 0xe2a), u.bar & 0xfff);
|
||||
u.qux.a = 0xe2a;
|
||||
try expectEqual(@as(u8, 0x2a), u.foo.a);
|
||||
try expectEqual(@as(u12, 0xe2a), u.qux.a);
|
||||
try expectEqual(@as(u29, 0xe2a), u.bar.a & 0xfff);
|
||||
try expectEqual(@as(u64, 0xe2a), u.baz & 0xfff);
|
||||
|
||||
// Writing to a larger field
|
||||
u.baz = 0xbbbbbbbb;
|
||||
try expectEqual(@as(u8, 0xbb), u.foo);
|
||||
try expectEqual(@as(u12, 0xbbb), u.qux);
|
||||
try expectEqual(@as(u29, 0x1bbbbbbb), u.bar);
|
||||
try expectEqual(@as(u8, 0xbb), u.foo.a);
|
||||
try expectEqual(@as(u12, 0xbbb), u.qux.a);
|
||||
try expectEqual(@as(u29, 0x1bbbbbbb), u.bar.a);
|
||||
try expectEqual(@as(u64, 0xbbbbbbbb), u.baz);
|
||||
|
||||
// Writing to the same field
|
||||
u.baz = 0xcccccccc;
|
||||
try expectEqual(@as(u8, 0xcc), u.foo);
|
||||
try expectEqual(@as(u12, 0xccc), u.qux);
|
||||
try expectEqual(@as(u29, 0x0ccccccc), u.bar);
|
||||
try expectEqual(@as(u8, 0xcc), u.foo.a);
|
||||
try expectEqual(@as(u12, 0xccc), u.qux.a);
|
||||
try expectEqual(@as(u29, 0x0ccccccc), u.bar.a);
|
||||
try expectEqual(@as(u64, 0xcccccccc), u.baz);
|
||||
|
||||
// Writing to a smaller field
|
||||
u.foo = 0xdd;
|
||||
try expectEqual(@as(u8, 0xdd), u.foo);
|
||||
try expectEqual(@as(u12, 0xcdd), u.qux);
|
||||
try expectEqual(@as(u29, 0x0cccccdd), u.bar);
|
||||
u.foo.a = 0xdd;
|
||||
try expectEqual(@as(u8, 0xdd), u.foo.a);
|
||||
try expectEqual(@as(u12, 0xcdd), u.qux.a);
|
||||
try expectEqual(@as(u29, 0x0cccccdd), u.bar.a);
|
||||
try expectEqual(@as(u64, 0xccccccdd), u.baz);
|
||||
}
|
||||
}
|
||||
@ -1807,7 +1797,10 @@ test "reinterpret packed union inside packed struct" {
|
||||
|
||||
const U = packed union {
|
||||
a: u7,
|
||||
b: u1,
|
||||
b: packed struct(u7) {
|
||||
a: u1,
|
||||
b: u6,
|
||||
},
|
||||
};
|
||||
|
||||
const V = packed struct {
|
||||
@ -1818,18 +1811,18 @@ test "reinterpret packed union inside packed struct" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var v: V = undefined;
|
||||
@memset(std.mem.asBytes(&v), 0x55);
|
||||
try expectEqual(@as(u7, 0x55), v.lo.a);
|
||||
try expectEqual(@as(u1, 1), v.lo.b);
|
||||
try expectEqual(@as(u7, 0x2a), v.hi.a);
|
||||
try expectEqual(@as(u1, 0), v.hi.b);
|
||||
@memset(@as([]u8, @ptrCast(&v)), 0x55);
|
||||
try expect(@as(u7, 0x55) == v.lo.a);
|
||||
try expect(@as(u1, 1) == v.lo.b.a);
|
||||
try expect(@as(u7, 0x2a) == v.hi.a);
|
||||
try expect(@as(u1, 0) == v.hi.b.a);
|
||||
|
||||
v.lo.b = 0;
|
||||
try expectEqual(@as(u7, 0x54), v.lo.a);
|
||||
try expectEqual(@as(u1, 0), v.lo.b);
|
||||
v.hi.b = 1;
|
||||
try expectEqual(@as(u7, 0x2b), v.hi.a);
|
||||
try expectEqual(@as(u1, 1), v.hi.b);
|
||||
v.lo.b.a = 0;
|
||||
try expect(@as(u7, 0x54) == v.lo.a);
|
||||
try expect(@as(u1, 0) == v.lo.b.a);
|
||||
v.hi.b.a = 1;
|
||||
try expect(@as(u7, 0x2b) == v.hi.a);
|
||||
try expect(@as(u1, 1) == v.hi.b.a);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1869,8 +1862,9 @@ test "inner struct initializer uses packed union layout" {
|
||||
a: packed struct {
|
||||
x: u32 = @alignOf(U) + 1,
|
||||
},
|
||||
b: packed struct {
|
||||
b: packed struct(u32) {
|
||||
y: u16 = @sizeOf(U) + 2,
|
||||
padding: u16 = 0,
|
||||
},
|
||||
};
|
||||
};
|
||||
@ -1898,7 +1892,7 @@ test "extern union initialized via reintepreted struct field initializer" {
|
||||
};
|
||||
|
||||
const S = extern struct {
|
||||
u: U = std.mem.bytesAsValue(U, &bytes).*,
|
||||
u: U = @as(*align(1) const U, @ptrCast(&bytes)).*,
|
||||
};
|
||||
|
||||
const s: S = .{};
|
||||
@ -1913,17 +1907,20 @@ test "packed union initialized via reintepreted struct field initializer" {
|
||||
|
||||
const U = packed union {
|
||||
a: u32,
|
||||
b: u8,
|
||||
b: packed struct(u32) {
|
||||
a: u8,
|
||||
b: u24,
|
||||
},
|
||||
};
|
||||
|
||||
const S = packed struct {
|
||||
u: U = std.mem.bytesAsValue(U, &bytes).*,
|
||||
u: U = @as(*align(1) const U, @ptrCast(&bytes)).*,
|
||||
};
|
||||
|
||||
var s: S = .{};
|
||||
_ = &s;
|
||||
try expect(s.u.a == littleToNativeEndian(u32, 0xddccbbaa));
|
||||
try expect(s.u.b == if (endian == .little) 0xaa else 0xdd);
|
||||
try expect(s.u.b.a == if (endian == .little) 0xaa else 0xdd);
|
||||
}
|
||||
|
||||
test "store of comptime reinterpreted memory to extern union" {
|
||||
@ -1938,7 +1935,7 @@ test "store of comptime reinterpreted memory to extern union" {
|
||||
|
||||
const reinterpreted = comptime b: {
|
||||
var u: U = undefined;
|
||||
u = std.mem.bytesAsValue(U, &bytes).*;
|
||||
u = @as(*align(1) const U, @ptrCast(&bytes)).*;
|
||||
break :b u;
|
||||
};
|
||||
|
||||
@ -1955,19 +1952,22 @@ test "store of comptime reinterpreted memory to packed union" {
|
||||
|
||||
const U = packed union {
|
||||
a: u32,
|
||||
b: u8,
|
||||
b: packed struct(u32) {
|
||||
a: u8,
|
||||
b: u24,
|
||||
},
|
||||
};
|
||||
|
||||
const reinterpreted = comptime b: {
|
||||
var u: U = undefined;
|
||||
u = std.mem.bytesAsValue(U, &bytes).*;
|
||||
u = @as(*align(1) const U, @ptrCast(&bytes)).*;
|
||||
break :b u;
|
||||
};
|
||||
|
||||
var u: U = reinterpreted;
|
||||
_ = &u;
|
||||
try expect(u.a == littleToNativeEndian(u32, 0xddccbbaa));
|
||||
try expect(u.b == if (endian == .little) 0xaa else 0xdd);
|
||||
try expect(u.b.a == if (endian == .little) 0xaa else 0xdd);
|
||||
}
|
||||
|
||||
test "union field is a pointer to an aligned version of itself" {
|
||||
|
||||
14
test/cases/compile_errors/packed_union_fields_mismatch.zig
Normal file
14
test/cases/compile_errors/packed_union_fields_mismatch.zig
Normal 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
|
||||
Loading…
x
Reference in New Issue
Block a user