zig
zig copied to clipboard
Packed union field ptr at comptime hits unreachable
Discovered in #19347
export fn entry() void {
comptime testFlagsInPackedUnionAtOffset();
}
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,
},
bits: u4,
},
adv_flags: packed struct(u12) {
pad: u8 = 0,
adv: packed union {
flags: packed struct(u4) {
enable_1: bool = true,
enable_2: bool = false,
enable_3: bool = false,
enable_4: bool = false,
},
bits: u4,
},
},
};
var test_bits: FlagBits = .{ .adv_flags = .{ .adv = .{ .flags = .{} } } };
test_bits.adv_flags.adv.bits = 12;
if ((&test_bits.adv_flags.adv.flags.enable_1).* != false) unreachable;
}
Stack trace
thread 92776 panic: reached unreachable code
Analyzing a.zig: a.zig:testFlagsInPackedUnionAtOffset
%91 = dbg_stmt(27, 20)
%92 = field_ptr(%67, "adv_flags") node_offset:42:11 to :42:30
%93 = dbg_stmt(27, 30)
%94 = field_ptr(%92, "adv") node_offset:42:11 to :42:34
%95 = dbg_stmt(27, 34)
%96 = field_ptr(%94, "flags") node_offset:42:11 to :42:40
%97 = dbg_stmt(27, 40)
%98 = field_ptr(%96, "enable_1") node_offset:42:11 to :42:49
%99 = validate_deref(%98) node_offset:42:9 to :42:52
> %100 = load(%98) node_offset:42:9 to :42:52
%101 = cmp_neq(%100, @bool_false) node_offset:42:9 to :42:61
%102 = condbr(%101, {
%104 = dbg_stmt(27, 63)
%105 = unreachable() node_offset:42:63 to :42:74
}, {
%106 = break(%103, @void_value)
}) node_offset:42:5 to :42:74
For full context, use the command
zig ast-check -t a.zig
in a.zig: a.zig:testFlagsInPackedUnionAtOffset
> %103 = block({%91..%102}) node_offset:42:5 to :42:74
in a.zig: a.zig:entry
> %27 = call(.compile_time, %25, []) node_offset:14:14 to :14:46
in a.zig: a.zig:entry
> %24 = block_comptime({%25..%28}) node_offset:14:14 to :14:46
/home/vexu/Documents/zig/zig/src/Value.zig:1770:21: 0xa1f7b69 in fieldValue (zig)
else => unreachable,
^
/home/vexu/Documents/zig/zig/src/Sema.zig:31429:53: 0xb7ecd9e in beginComptimePtrLoad (zig)
.val = try tv.val.fieldValue(mod, field_index),
^
/home/vexu/Documents/zig/zig/src/Sema.zig:38160:44: 0xb2d3d68 in pointerDerefExtra (zig)
const deref = sema.beginComptimePtrLoad(block, src, ptr_val, load_ty) catch |err| switch (err) {
^
/home/vexu/Documents/zig/zig/src/Sema.zig:38131:43: 0xad63302 in pointerDeref (zig)
const res = try sema.pointerDerefExtra(block, src, ptr_val, load_ty);
^
/home/vexu/Documents/zig/zig/src/Sema.zig:32594:34: 0xa825b93 in analyzeLoad (zig)
if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |elem_val| {
^
This test case is modified from the flags in packed union at offset test in behavior/packed-union.zig.
Other similar cases:
test "extern union doesn't trigger field check at comptime" {
const U = extern union {
x: u32,
y: u8,
};
const x = U{ .x = 0x55AAAA55 };
comptime assert((&x.y).* == 0x55);
}
test "memset extern union" {
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
const U = extern union {
foo: u8,
bar: u32,
};
const S = struct {
fn doTheTest() !void {
var u: U = undefined;
@memset(std.mem.asBytes(&u), 0);
try expectEqual(@as(u8, 0), (&u.foo).*);
try expectEqual(@as(u32, 0), u.bar);
}
};
try comptime S.doTheTest();
try S.doTheTest();
}
const endian = builtin.cpu.arch.endian();
fn littleToNativeEndian(comptime T: type, v: T) T {
return if (endian == .little) v else @byteSwap(v);
}
test "extern union initialized via reintepreted struct field initializer" {
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
const bytes = [_]u8{ 0xaa, 0xbb, 0xcc, 0xdd };
const U = extern union {
a: u32,
b: u8,
};
const S = extern struct {
u: U = std.mem.bytesAsValue(U, &bytes).*,
};
const s: S = .{};
try expect(s.u.a == littleToNativeEndian(u32, 0xddccbbaa));
try expect((&s.u.b).* == 0xaa);
}