zig icon indicating copy to clipboard operation
zig copied to clipboard

Packed union field ptr at comptime hits unreachable

Open Vexu opened this issue 1 year ago • 1 comments

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.

Vexu avatar Mar 23 '24 18:03 Vexu

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);
}

Vexu avatar Mar 23 '24 19:03 Vexu