const std = @import("std");
const builtin = @import("builtin");
const assert = std.debug.assert;
const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual;

test "flags in packed structs" {
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO

    const Flags1 = packed struct {
        // first 8 bits
        b0_0: u1,
        b0_1: u1,
        b0_2: u1,
        b0_3: u1,
        b0_4: u1,
        b0_5: u1,
        b0_6: u1,
        b0_7: u1,

        // 7 more bits
        b1_0: u1,
        b1_1: u1,
        b1_2: u1,
        b1_3: u1,
        b1_4: u1,
        b1_5: u1,
        b1_6: u1,

        // some padding to fill to 24 bits
        _: u9,
    };

    try expectEqual(@sizeOf(u24), @sizeOf(Flags1));
    try expectEqual(24, @bitSizeOf(Flags1));

    const Flags2 = packed struct {
        // byte 0
        b0_0: u1,
        b0_1: u1,
        b0_2: u1,
        b0_3: u1,
        b0_4: u1,
        b0_5: u1,
        b0_6: u1,
        b0_7: u1,

        // partial byte 1 (but not 8 bits)
        b1_0: u1,
        b1_1: u1,
        b1_2: u1,
        b1_3: u1,
        b1_4: u1,
        b1_5: u1,
        b1_6: u1,

        // some padding that should yield @sizeOf(Flags2) == 4
        _: u10,
    };

    try expectEqual(@sizeOf(u25), @sizeOf(Flags2));
    try expectEqual(25, @bitSizeOf(Flags2));

    const Flags3 = packed struct {
        // byte 0
        b0_0: u1,
        b0_1: u1,
        b0_2: u1,
        b0_3: u1,
        b0_4: u1,
        b0_5: u1,
        b0_6: u1,
        b0_7: u1,

        // byte 1
        b1_0: u1,
        b1_1: u1,
        b1_2: u1,
        b1_3: u1,
        b1_4: u1,
        b1_5: u1,
        b1_6: u1,
        b1_7: u1,

        // some padding that should yield @sizeOf(Flags2) == 4
        _: u16, // it works, if the padding is 8-based
    };

    try expectEqual(@sizeOf(u32), @sizeOf(Flags3));
    try expectEqual(32, @bitSizeOf(Flags3));
}

test "consistent size of packed structs" {
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO

    const TxData1 = packed struct { data: u8, _23: u23, full: bool = false };
    const TxData2 = packed struct { data: u9, _22: u22, full: bool = false };

    const register_size_bits = 32;
    const register_size_bytes = @sizeOf(u32);

    try expectEqual(register_size_bits, @bitSizeOf(TxData1));
    try expectEqual(register_size_bytes, @sizeOf(TxData1));

    try expectEqual(register_size_bits, @bitSizeOf(TxData2));
    try expectEqual(register_size_bytes, @sizeOf(TxData2));

    const TxData4 = packed struct { a: u32, b: u24 };
    const TxData6 = packed struct { a: u24, b: u32 };

    const expectedBitSize = 56;
    const expectedByteSize = @sizeOf(u56);

    try expectEqual(expectedBitSize, @bitSizeOf(TxData4));
    try expectEqual(expectedByteSize, @sizeOf(TxData4));

    try expectEqual(expectedBitSize, @bitSizeOf(TxData6));
    try expectEqual(expectedByteSize, @sizeOf(TxData6));
}

test "correct sizeOf and offsets in packed structs" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO

    const PStruct = packed struct {
        bool_a: bool,
        bool_b: bool,
        bool_c: bool,
        bool_d: bool,
        bool_e: bool,
        bool_f: bool,
        u1_a: u1,
        bool_g: bool,
        u1_b: u1,
        u3_a: u3,
        u10_a: u10,
        u10_b: u10,
    };
    try expectEqual(0, @offsetOf(PStruct, "bool_a"));
    try expectEqual(0, @bitOffsetOf(PStruct, "bool_a"));
    try expectEqual(0, @offsetOf(PStruct, "bool_b"));
    try expectEqual(1, @bitOffsetOf(PStruct, "bool_b"));
    try expectEqual(0, @offsetOf(PStruct, "bool_c"));
    try expectEqual(2, @bitOffsetOf(PStruct, "bool_c"));
    try expectEqual(0, @offsetOf(PStruct, "bool_d"));
    try expectEqual(3, @bitOffsetOf(PStruct, "bool_d"));
    try expectEqual(0, @offsetOf(PStruct, "bool_e"));
    try expectEqual(4, @bitOffsetOf(PStruct, "bool_e"));
    try expectEqual(0, @offsetOf(PStruct, "bool_f"));
    try expectEqual(5, @bitOffsetOf(PStruct, "bool_f"));
    try expectEqual(0, @offsetOf(PStruct, "u1_a"));
    try expectEqual(6, @bitOffsetOf(PStruct, "u1_a"));
    try expectEqual(0, @offsetOf(PStruct, "bool_g"));
    try expectEqual(7, @bitOffsetOf(PStruct, "bool_g"));
    try expectEqual(1, @offsetOf(PStruct, "u1_b"));
    try expectEqual(8, @bitOffsetOf(PStruct, "u1_b"));
    try expectEqual(1, @offsetOf(PStruct, "u3_a"));
    try expectEqual(9, @bitOffsetOf(PStruct, "u3_a"));
    try expectEqual(1, @offsetOf(PStruct, "u10_a"));
    try expectEqual(12, @bitOffsetOf(PStruct, "u10_a"));
    try expectEqual(2, @offsetOf(PStruct, "u10_b"));
    try expectEqual(22, @bitOffsetOf(PStruct, "u10_b"));
    try expectEqual(4, @sizeOf(PStruct));

    const s1 = @as(PStruct, @bitCast(@as(u32, 0x12345678)));
    try expectEqual(false, s1.bool_a);
    try expectEqual(false, s1.bool_b);
    try expectEqual(false, s1.bool_c);
    try expectEqual(true, s1.bool_d);
    try expectEqual(true, s1.bool_e);
    try expectEqual(true, s1.bool_f);
    try expectEqual(1, s1.u1_a);
    try expectEqual(false, s1.bool_g);
    try expectEqual(0, s1.u1_b);
    try expectEqual(3, s1.u3_a);
    try expectEqual(0b1101000101, s1.u10_a);
    try expectEqual(0b0001001000, s1.u10_b);

    const s2 = @as(packed struct { x: u1, y: u7, z: u24 }, @bitCast(@as(u32, 0xd5c71ff4)));
    try expectEqual(0, s2.x);
    try expectEqual(0b1111010, s2.y);
    try expectEqual(0xd5c71f, s2.z);
}

test "nested packed structs" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO

    const S1 = packed struct { a: u8, b: u8, c: u8 };

    const S2 = packed struct { d: u8, e: u8, f: u8 };

    const S3 = packed struct { x: S1, y: S2 };
    const S3Padded = packed struct { s3: S3, pad: u16 };

    try expectEqual(48, @bitSizeOf(S3));
    try expectEqual(@sizeOf(u48), @sizeOf(S3));

    try expectEqual(3, @offsetOf(S3, "y"));
    try expectEqual(24, @bitOffsetOf(S3, "y"));

    const s3 = @as(S3Padded, @bitCast(@as(u64, 0xe952d5c71ff4))).s3;
    try expectEqual(0xf4, s3.x.a);
    try expectEqual(0x1f, s3.x.b);
    try expectEqual(0xc7, s3.x.c);
    try expectEqual(0xd5, s3.y.d);
    try expectEqual(0x52, s3.y.e);
    try expectEqual(0xe9, s3.y.f);

    const S4 = packed struct { a: i32, b: i8 };
    const S5 = packed struct { a: i32, b: i8, c: S4 };
    const S6 = packed struct { a: i32, b: S4, c: i8 };

    const expectedBitSize = 80;
    const expectedByteSize = @sizeOf(u80);
    try expectEqual(expectedBitSize, @bitSizeOf(S5));
    try expectEqual(expectedByteSize, @sizeOf(S5));
    try expectEqual(expectedBitSize, @bitSizeOf(S6));
    try expectEqual(expectedByteSize, @sizeOf(S6));

    try expectEqual(5, @offsetOf(S5, "c"));
    try expectEqual(40, @bitOffsetOf(S5, "c"));
    try expectEqual(9, @offsetOf(S6, "c"));
    try expectEqual(72, @bitOffsetOf(S6, "c"));
}

test "regular in irregular packed struct" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;

    const Irregular = packed struct {
        bar: Regular = Regular{},
        _: u24 = 0,
        pub const Regular = packed struct { a: u16 = 0, b: u8 = 0 };
    };

    var foo = Irregular{};
    foo.bar.a = 235;
    foo.bar.b = 42;

    try expectEqual(235, foo.bar.a);
    try expectEqual(42, foo.bar.b);
}

test "nested packed struct unaligned" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO

    const S1 = packed struct {
        a: u4,
        b: u4,
        c: u8,
    };
    const S2 = packed struct {
        base: u8,
        p0: S1,
        bit0: u1,
        p1: packed struct {
            a: u8,
        },
        p2: packed struct {
            a: u7,
            b: u8,
        },
        p3: S1,

        var s: @This() = .{
            .base = 1,
            .p0 = .{ .a = 2, .b = 3, .c = 4 },
            .bit0 = 0,
            .p1 = .{ .a = 5 },
            .p2 = .{ .a = 6, .b = 7 },
            .p3 = .{ .a = 8, .b = 9, .c = 10 },
        };
    };

    try expect(S2.s.base == 1);
    try expect(S2.s.p0.a == 2);
    try expect(S2.s.p0.b == 3);
    try expect(S2.s.p0.c == 4);
    try expect(S2.s.bit0 == 0);
    try expect(S2.s.p1.a == 5);
    try expect(S2.s.p2.a == 6);
    try expect(S2.s.p2.b == 7);
    try expect(S2.s.p3.a == 8);
    try expect(S2.s.p3.b == 9);
    try expect(S2.s.p3.c == 10);

    const S3 = packed struct {
        pad: u8,
        v: u2,
        s: packed struct {
            v: u3,
            s: packed struct {
                v: u2,
                s: packed struct {
                    bit0: u1,
                    byte: u8,
                    bit1: u1,
                },
            },
        },
        var v0: @This() = .{ .pad = 0, .v = 1, .s = .{ .v = 2, .s = .{ .v = 3, .s = .{ .bit0 = 0, .byte = 4, .bit1 = 1 } } } };
    };

    try expect(S3.v0.v == 1);
    try expect(S3.v0.s.v == 2);
    try expect(S3.v0.s.s.v == 3);
    try expect(S3.v0.s.s.s.bit0 == 0);
    try expect(S3.v0.s.s.s.byte == 4);
    try expect(S3.v0.s.s.s.bit1 == 1);
}

test "byte-aligned field pointer offsets" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;

    const S = struct {
        const A = packed struct {
            a: u8,
            b: u8,
            c: u8,
            d: u8,
        };

        const B = packed struct {
            a: u16,
            b: u16,
        };

        fn doTheTest() !void {
            var a: A = .{
                .a = 1,
                .b = 2,
                .c = 3,
                .d = 4,
            };

            comptime assert(@TypeOf(&a.a) == *align(4:0:4) u8);
            comptime assert(@TypeOf(&a.b) == *align(4:8:4) u8);
            comptime assert(@TypeOf(&a.c) == *align(4:16:4) u8);
            comptime assert(@TypeOf(&a.d) == *align(4:24:4) u8);

            try expect(a.a == 1);
            try expect(a.b == 2);
            try expect(a.c == 3);
            try expect(a.d == 4);

            a.a += 1;
            try expect(a.a == 2);
            try expect(a.b == 2);
            try expect(a.c == 3);
            try expect(a.d == 4);

            a.b += 1;
            try expect(a.a == 2);
            try expect(a.b == 3);
            try expect(a.c == 3);
            try expect(a.d == 4);

            a.c += 1;
            try expect(a.a == 2);
            try expect(a.b == 3);
            try expect(a.c == 4);
            try expect(a.d == 4);

            a.d += 1;
            try expect(a.a == 2);
            try expect(a.b == 3);
            try expect(a.c == 4);
            try expect(a.d == 5);

            var b: B = .{
                .a = 1,
                .b = 2,
            };

            comptime assert(@TypeOf(&b.a) == *align(4:0:4) u16);
            comptime assert(@TypeOf(&b.b) == *align(4:16:4) u16);

            try expect(b.a == 1);
            try expect(b.b == 2);

            b.a += 1;
            try expect(b.a == 2);
            try expect(b.b == 2);

            b.b += 1;
            try expect(b.a == 2);
            try expect(b.b == 3);
        }
    };

    try S.doTheTest();
    try comptime S.doTheTest();
}

test "nested packed struct field pointers" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // ubsan unaligned pointer access
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO

    const S2 = packed struct {
        base: u8,
        p0: packed struct {
            a: u4,
            b: u4,
            c: u8,
        },
        bit: u1,
        p1: packed struct {
            a: u7,
            b: u8,
        },

        var s: @This() = .{ .base = 1, .p0 = .{ .a = 2, .b = 3, .c = 4 }, .bit = 0, .p1 = .{ .a = 5, .b = 6 } };
    };

    const ptr_base = &S2.s.base;
    const ptr_p0_a = &S2.s.p0.a;
    const ptr_p0_b = &S2.s.p0.b;
    const ptr_p0_c = &S2.s.p0.c;
    const ptr_p1_a = &S2.s.p1.a;
    const ptr_p1_b = &S2.s.p1.b;
    try expectEqual(1, ptr_base.*);
    try expectEqual(2, ptr_p0_a.*);
    try expectEqual(3, ptr_p0_b.*);
    try expectEqual(4, ptr_p0_c.*);
    try expectEqual(5, ptr_p1_a.*);
    try expectEqual(6, ptr_p1_b.*);
}

test "@intFromPtr on a packed struct field" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    const S = struct {
        const P = packed struct {
            x: u8,
            y: u8,
            z: u32,
        };

        var p0: P = P{
            .x = 1,
            .y = 2,
            .z = 0,
        };
    };
    try expect(@intFromPtr(&S.p0.z) - @intFromPtr(&S.p0.x) == 0);
}

test "@intFromPtr on a packed struct field unaligned and nested" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    const S1 = packed struct {
        a: u4,
        b: u4,
        c: u8,
    };
    const S2 = packed struct {
        base: u8,
        p0: S1,
        bit0: u1,
        p1: packed struct {
            a: u8,
        },
        p2: packed struct {
            a: u7,
            b: u8,
        },
        p3: S1,

        var s: @This() = .{
            .base = 1,
            .p0 = .{ .a = 2, .b = 3, .c = 4 },
            .bit0 = 0,
            .p1 = .{ .a = 5 },
            .p2 = .{ .a = 6, .b = 7 },
            .p3 = .{ .a = 8, .b = 9, .c = 10 },
        };
    };

    {
        const a = @alignOf(S2);
        comptime assert(@TypeOf(&S2.s.base) == *align(a:0:8) u8);
        comptime assert(@TypeOf(&S2.s.p0.a) == *align(a:8:8) u4);
        comptime assert(@TypeOf(&S2.s.p0.b) == *align(a:12:8) u4);
        comptime assert(@TypeOf(&S2.s.p0.c) == *align(a:16:8) u8);
        comptime assert(@TypeOf(&S2.s.bit0) == *align(a:24:8) u1);
        comptime assert(@TypeOf(&S2.s.p1.a) == *align(a:25:8) u8);
        comptime assert(@TypeOf(&S2.s.p2.a) == *align(a:33:8) u7);
        comptime assert(@TypeOf(&S2.s.p2.b) == *align(a:40:8) u8);
        comptime assert(@TypeOf(&S2.s.p3.a) == *align(a:48:8) u4);
        comptime assert(@TypeOf(&S2.s.p3.b) == *align(a:52:8) u4);
        comptime assert(@TypeOf(&S2.s.p3.c) == *align(a:56:8) u8);
    }

    try expect(@intFromPtr(&S2.s.base) - @intFromPtr(&S2.s) == 0);
    try expect(@intFromPtr(&S2.s.p0.a) - @intFromPtr(&S2.s) == 0);
    try expect(@intFromPtr(&S2.s.p0.b) - @intFromPtr(&S2.s) == 0);
    try expect(@intFromPtr(&S2.s.p0.c) - @intFromPtr(&S2.s) == 0);
    try expect(@intFromPtr(&S2.s.bit0) - @intFromPtr(&S2.s) == 0);
    try expect(@intFromPtr(&S2.s.p1.a) - @intFromPtr(&S2.s) == 0);
    try expect(@intFromPtr(&S2.s.p2.a) - @intFromPtr(&S2.s) == 0);
    try expect(@intFromPtr(&S2.s.p2.b) - @intFromPtr(&S2.s) == 0);
    try expect(@intFromPtr(&S2.s.p3.a) - @intFromPtr(&S2.s) == 0);
    try expect(@intFromPtr(&S2.s.p3.b) - @intFromPtr(&S2.s) == 0);
    try expect(@intFromPtr(&S2.s.p3.c) - @intFromPtr(&S2.s) == 0);

    const S3 = packed struct {
        pad: u8,
        v: u2,
        s: packed struct {
            v: u3,
            s: packed struct {
                v: u2,
                s: packed struct {
                    bit0: u1,
                    byte: u8,
                    bit1: u1,
                },
            },
        },
        var v0: @This() = .{ .pad = 0, .v = 1, .s = .{ .v = 2, .s = .{ .v = 3, .s = .{ .bit0 = 0, .byte = 4, .bit1 = 1 } } } };
    };

    comptime assert(@TypeOf(&S3.v0.v) == *align(4:8:4) u2);
    comptime assert(@TypeOf(&S3.v0.s.v) == *align(4:10:4) u3);
    comptime assert(@TypeOf(&S3.v0.s.s.v) == *align(4:13:4) u2);
    comptime assert(@TypeOf(&S3.v0.s.s.s.bit0) == *align(4:15:4) u1);
    comptime assert(@TypeOf(&S3.v0.s.s.s.byte) == *align(4:16:4) u8);
    comptime assert(@TypeOf(&S3.v0.s.s.s.bit1) == *align(4:24:4) u1);
    try expect(@intFromPtr(&S3.v0.v) - @intFromPtr(&S3.v0) == 0);
    try expect(@intFromPtr(&S3.v0.s) - @intFromPtr(&S3.v0) == 0);
    try expect(@intFromPtr(&S3.v0.s.v) - @intFromPtr(&S3.v0) == 0);
    try expect(@intFromPtr(&S3.v0.s.s) - @intFromPtr(&S3.v0) == 0);
    try expect(@intFromPtr(&S3.v0.s.s.v) - @intFromPtr(&S3.v0) == 0);
    try expect(@intFromPtr(&S3.v0.s.s.s) - @intFromPtr(&S3.v0) == 0);
    try expect(@intFromPtr(&S3.v0.s.s.s.bit0) - @intFromPtr(&S3.v0) == 0);
    try expect(@intFromPtr(&S3.v0.s.s.s.byte) - @intFromPtr(&S3.v0) == 0);
    try expect(@intFromPtr(&S3.v0.s.s.s.bit1) - @intFromPtr(&S3.v0) == 0);
}

test "packed struct fields modification" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    // Originally reported at https://github.com/ziglang/zig/issues/16615
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    const Small = packed struct {
        val: u8 = 0,
        lo: u4 = 0,
        hi: u4 = 0,

        var p: @This() = undefined;
    };
    Small.p = .{
        .val = 0x12,
        .lo = 3,
        .hi = 4,
    };
    try expect(@as(u16, @bitCast(Small.p)) == 0x4312);

    Small.p.val -= Small.p.lo;
    Small.p.val += Small.p.hi;
    Small.p.hi -= Small.p.lo;
    try expect(@as(u16, @bitCast(Small.p)) == 0x1313);
}

test "nested packed struct field access test" {
    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    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;

    const Vec2 = packed struct {
        x: f32,
        y: f32,
    };

    const Vec3 = packed struct {
        x: f32,
        y: f32,
        z: f32,
    };

    const NestedVec2 = packed struct {
        nested: Vec2,
    };

    const NestedVec3 = packed struct {
        nested: Vec3,
    };

    const vec2 = Vec2{
        .x = 1.0,
        .y = 2.0,
    };

    try std.testing.expectEqual(vec2.x, 1.0);
    try std.testing.expectEqual(vec2.y, 2.0);

    var vec2_o: Vec2 = undefined;
    const vec2_o_ptr: *Vec2 = &vec2_o;
    vec2_o_ptr.* = vec2;

    try std.testing.expectEqual(vec2_o.x, 1.0);
    try std.testing.expectEqual(vec2_o.y, 2.0);

    const nested_vec2 = NestedVec2{
        .nested = Vec2{
            .x = 1.0,
            .y = 2.0,
        },
    };

    try std.testing.expectEqual(nested_vec2.nested.x, 1.0);
    try std.testing.expectEqual(nested_vec2.nested.y, 2.0);

    var nested_o: NestedVec2 = undefined;
    const nested_o_ptr: *NestedVec2 = &nested_o;
    nested_o_ptr.* = nested_vec2;

    try std.testing.expectEqual(nested_o.nested.x, 1.0);
    try std.testing.expectEqual(nested_o.nested.y, 2.0);

    const vec3 = Vec3{
        .x = 1.0,
        .y = 2.0,
        .z = 3.0,
    };

    try std.testing.expectEqual(vec3.x, 1.0);
    try std.testing.expectEqual(vec3.y, 2.0);
    try std.testing.expectEqual(vec3.z, 3.0);

    var vec3_o: Vec3 = undefined;
    const vec3_o_ptr: *Vec3 = &vec3_o;
    vec3_o_ptr.* = vec3;

    try std.testing.expectEqual(vec3_o.x, 1.0);
    try std.testing.expectEqual(vec3_o.y, 2.0);
    try std.testing.expectEqual(vec3_o.z, 3.0);

    const nested_vec3 = NestedVec3{
        .nested = Vec3{
            .x = 1.0,
            .y = 2.0,
            .z = 3.0,
        },
    };

    try std.testing.expectEqual(nested_vec3.nested.x, 1.0);
    try std.testing.expectEqual(nested_vec3.nested.y, 2.0);
    try std.testing.expectEqual(nested_vec3.nested.z, 3.0);

    var nested_vec3_o: NestedVec3 = undefined;
    const nested_vec3_o_ptr: *NestedVec3 = &nested_vec3_o;
    nested_vec3_o_ptr.* = nested_vec3;

    try std.testing.expectEqual(nested_vec3_o.nested.x, 1.0);
    try std.testing.expectEqual(nested_vec3_o.nested.y, 2.0);
    try std.testing.expectEqual(nested_vec3_o.nested.z, 3.0);

    const hld = packed struct {
        c: u64,
        d: u32,
    };

    const mld = packed struct {
        h: u64,
        i: u64,
    };

    const a = packed struct {
        b: hld,
        g: mld,
    };

    var arg = a{ .b = hld{ .c = 1, .d = 2 }, .g = mld{ .h = 6, .i = 8 } };
    _ = &arg;
    try std.testing.expect(arg.b.c == 1);
    try std.testing.expect(arg.b.d == 2);
    try std.testing.expect(arg.g.h == 6);
    try std.testing.expect(arg.g.i == 8);
}

test "nested packed struct at non-zero offset" {
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;

    const Pair = packed struct(u24) {
        a: u16 = 0,
        b: u8 = 0,
    };
    const A = packed struct {
        p1: Pair,
        p2: Pair,
    };

    var k: u8 = 123;
    _ = &k;
    var v: A = .{
        .p1 = .{ .a = k + 1, .b = k },
        .p2 = .{ .a = k + 1, .b = k },
    };

    try expect(v.p1.a == k + 1 and v.p1.b == k);
    try expect(v.p2.a == k + 1 and v.p2.b == k);

    v.p2.a -= v.p1.a;
    v.p2.b -= v.p1.b;
    try expect(v.p2.a == 0 and v.p2.b == 0);
    try expect(v.p1.a == k + 1 and v.p1.b == k);
}

test "nested packed struct at non-zero offset 2" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    const S = struct {
        const Pair = packed struct(u40) {
            a: u32 = 0,
            b: u8 = 0,
        };
        const A = packed struct {
            p1: Pair,
            p2: Pair,
            c: C,
        };
        const C = packed struct {
            p1: Pair,
            pad1: u5,
            p2: Pair,
            pad2: u3,
            last: i16,
        };

        fn doTheTest() !void {
            var k: u8 = 123;
            _ = &k;
            var v: A = .{
                .p1 = .{ .a = k + 1, .b = k },
                .p2 = .{ .a = k + 1, .b = k },
                .c = .{
                    .pad1 = 11,
                    .pad2 = 2,
                    .p1 = .{ .a = k + 1, .b = k },
                    .p2 = .{ .a = k + 1, .b = k },
                    .last = -12345,
                },
            };

            try expect(v.p1.a == k + 1 and v.p1.b == k);
            try expect(v.p2.a == k + 1 and v.p2.b == k);
            try expect(v.c.p2.a == k + 1 and v.c.p2.b == k);
            try expect(v.c.p2.a == k + 1 and v.c.p2.b == k);
            try expect(v.c.last == -12345);
            try expect(v.c.pad1 == 11 and v.c.pad2 == 2);

            v.p2.a -= v.p1.a;
            v.p2.b -= v.p1.b;
            v.c.p2.a -= v.c.p1.a;
            v.c.p2.b -= v.c.p1.b;
            v.c.last -|= 32000;
            try expect(v.p2.a == 0 and v.p2.b == 0);
            try expect(v.p1.a == k + 1 and v.p1.b == k);
            try expect(v.c.p2.a == 0 and v.c.p2.b == 0);
            try expect(v.c.p1.a == k + 1 and v.c.p1.b == k);
            try expect(v.c.last == -32768);
            try expect(v.c.pad1 == 11 and v.c.pad2 == 2);
        }
    };

    try S.doTheTest();
    try comptime S.doTheTest();
}

test "runtime init of unnamed packed struct type" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    var z: u8 = 123;
    _ = &z;
    try (packed struct {
        x: u8,
        pub fn m(s: @This()) !void {
            try expect(s.x == 123);
        }
    }{ .x = z }).m();
}

test "packed struct passed to callconv(.c) function" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    const S = struct {
        const Packed = packed struct(u64) {
            a: u16,
            b: bool = true,
            c: bool = true,
            d: u46 = 0,
        };

        fn foo(p: Packed, a1: u64, a2: u64, a3: u64, a4: u64, a5: u64) callconv(.c) bool {
            return p.a == 12345 and p.b == true and p.c == true and p.d == 0 and a1 == 5 and a2 == 4 and a3 == 3 and a4 == 2 and a5 == 1;
        }
    };
    const result = S.foo(S.Packed{
        .a = 12345,
        .b = true,
        .c = true,
    }, 5, 4, 3, 2, 1);
    try expect(result);
}

test "overaligned pointer to packed struct" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;

    const S = packed struct { a: u32, b: u32 };
    var foo: S align(4) = .{ .a = 123, .b = 456 };
    const ptr: *align(4) S = &foo;
    const ptr_to_a: *align(4:0:8) u32 = &ptr.a;
    try expect(ptr_to_a.* == 123);
}

test "packed struct initialized in bitcast" {
    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
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    const T = packed struct { val: u8 };
    var val: u8 = 123;
    _ = &val;
    const t = @as(u8, @bitCast(T{ .val = val }));
    try expect(t == val);
}

test "pointer to container level packed struct field" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO

    const S = packed struct(u32) {
        test_bit: bool,
        someother_data: u12,
        other_test_bit: bool,
        someother_more_different_data: u12,
        other_bits: packed struct(u6) {
            enable_1: bool,
            enable_2: bool,
            enable_3: bool,
            enable_4: bool,
            enable_5: bool,
            enable_6: bool,
        },
        var arr: [2]u32 = @splat(0);
    };
    @as(*S, @ptrCast(&S.arr[0])).other_bits.enable_3 = true;
    try expect(S.arr[0] == 0x10000000);
}

test "store undefined to packed result location" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    var x: u4 = 0;
    _ = &x;
    const s = packed struct { x: u4, y: u4 }{ .x = x, .y = if (x > 0) x else undefined };
    try expectEqual(x, s.x);
}

// Originally reported at https://github.com/ziglang/zig/issues/9914
test "bitcast back and forth" {
    const S = packed struct { one: u6, two: u1 };
    const s = S{ .one = 0b110101, .two = 0b1 };
    const u: u7 = @bitCast(s);
    const s2: S = @bitCast(u);
    try expect(s.one == s2.one);
    try expect(s.two == s2.two);
}

// Originally reported at https://github.com/ziglang/zig/issues/14200
test "field access of packed struct smaller than its abi size inside struct initialized with rls" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO

    const S = struct {
        ps: packed struct { x: i2, y: i2 },

        fn init(cond: bool) @This() {
            return .{ .ps = .{ .x = 0, .y = if (cond) 1 else 0 } };
        }
    };

    const s = S.init(true);
    // note: this bug is triggered by the == operator, expectEqual will hide it
    try expect(@as(i2, 0) == s.ps.x);
    try expect(@as(i2, 1) == s.ps.y);
}

// Originally reported at https://github.com/ziglang/zig/issues/14632
test "modify nested packed struct aligned field" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    const Options = packed struct {
        foo: bool = false,
        bar: bool = false,
        pretty_print: packed struct {
            enabled: bool = false,
            num_spaces: u4 = 4,
            space_char: enum(u1) { space, tab } = .space,
            indent: u8 = 0,
        } = .{},
        baz: bool = false,
    };

    var opts = Options{};
    opts.pretty_print.indent += 1;
    try std.testing.expectEqual(0b00000000100100000, @as(u17, @bitCast(opts)));
    try std.testing.expect(!opts.foo);
    try std.testing.expect(!opts.bar);
    try std.testing.expect(!opts.pretty_print.enabled);
    try std.testing.expectEqual(4, opts.pretty_print.num_spaces);
    try std.testing.expectEqual(1, opts.pretty_print.indent);
    try std.testing.expect(!opts.baz);
}

// Originally reported at https://github.com/ziglang/zig/issues/9674
test "assigning packed struct inside another packed struct" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    const S = struct {
        const Inner = packed struct {
            bits: u3,
            more_bits: u6,
        };

        const Outer = packed struct {
            padding: u5,
            inner: Inner,
        };
        fn t(inner: Inner) void {
            r.inner = inner;
        }

        var mem: Outer = undefined;
        var r: *volatile Outer = &mem;
    };

    const val = S.Inner{ .bits = 1, .more_bits = 11 };
    S.mem.padding = 0;
    S.t(val);

    try expectEqual(val, S.mem.inner);
    try expect(S.mem.padding == 0);
}

test "packed struct acts as a namespace" {
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO

    const Bar = packed struct {
        const Baz = enum {
            fizz,
            buzz,
        };
    };
    var foo = Bar.Baz.fizz;
    _ = &foo;
    try expect(foo == .fizz);
}

test "assignment to non-byte-aligned field in packed struct" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO

    const Frame = packed struct {
        num: u20,
    };

    const Entry = packed struct {
        other: u12,
        frame: Frame,
    };

    const frame = Frame{ .num = 0x7FDE };
    var entry = Entry{ .other = 0, .frame = .{ .num = 0xFFFFF } };
    entry.frame = frame;
    try expect(entry.frame.num == 0x7FDE);
}

test "packed struct field pointer aligned properly" {
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO

    const Foo = packed struct {
        a: i32,
        b: u8,

        var buffer: [256]u8 = undefined;
    };

    var f1: *align(16) Foo = @alignCast(@as(*align(1) Foo, @ptrCast(&Foo.buffer[0])));
    try expect(@typeInfo(@TypeOf(f1)).pointer.attrs.@"align" == 16);
    try expect(@intFromPtr(f1) == @intFromPtr(&f1.a));
    try expect(@typeInfo(@TypeOf(&f1.a)).pointer.attrs.@"align" == 16);
}

test "load flag from packed struct in union" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;

    const A = packed struct {
        a: bool,
        b: bool,
        c: bool,
        d: bool,

        e: bool,
        f: bool,
        g: bool,
        h: bool,
    };

    const X = union {
        x: A,
        y: u64,

        pub fn a(_: i32, _: i32, _: i32, _: i32, _: i32, _: bool, flag_b: bool) !void {
            const flag_b_byte: u8 = @intFromBool(flag_b);
            try std.testing.expect(flag_b_byte == 1);
        }
        pub fn b(x: *@This()) !void {
            try a(0, 1, 2, 3, 4, x.x.a, x.x.b);
        }
    };
    var flags = A{
        .a = false,
        .b = true,
        .c = false,
        .d = false,

        .e = false,
        .f = true,
        .g = false,
        .h = false,
    };
    _ = &flags;
    var x = X{
        .x = flags,
    };
    try X.b(&x);
    comptime if (@sizeOf(A) != 1) unreachable;
}

test "bitcasting a packed struct at comptime and using the result" {
    comptime {
        const Struct = packed struct {
            x: packed union {
                a: u63,
                b: packed struct(u63) {
                    a: i32,
                    b: u31 = 0,
                },
            },
            y: u1,

            pub fn bitcast(fd: u64) @This() {
                return @bitCast(fd);
            }

            pub fn cannotReach(_: @This()) i32 {
                return 0;
            }
        };

        _ = Struct.bitcast(@as(u64, 0)).cannotReach();
    }
}

test "2-byte packed struct argument in C calling convention" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    const S = packed struct(u16) {
        x: u15 = 0,
        y: u1 = 0,

        fn foo(s: @This()) callconv(.c) i32 {
            return s.x;
        }
        fn bar(s: @This()) !void {
            try expect(foo(s) == 1);
        }
    };
    {
        var s: S = .{};
        s.x += 1;
        try S.bar(s);
    }
    comptime {
        var s: S = .{};
        s.x += 1;
        try S.bar(s);
    }
}

test "packed struct equality" {
    const Foo = packed struct {
        a: u4,
        b: u4,
    };

    const S = struct {
        fn doTest(x: Foo, y: Foo) !void {
            try expect(x == y);
            try expect(!(x != y));
        }
    };

    const x: Foo = .{ .a = 1, .b = 2 };
    const y: Foo = .{ .b = 2, .a = 1 };

    try S.doTest(x, y);
    comptime try S.doTest(x, y);
}

test "packed struct equality ignores padding bits" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    const S = packed struct { b: bool };
    var s: S = undefined;
    s.b = true;
    try std.testing.expect(s != S{ .b = false });
    try std.testing.expect(s == S{ .b = true });
}

test "packed struct with signed field" {
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;

    var s: packed struct {
        a: i2,
        b: u6,
    } = .{ .a = -1, .b = 42 };
    s = s;
    try expect(s.a == -1);
    try expect(s.b == 42);
}

test "assign packed struct initialized with RLS to packed struct literal field" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;

    const Inner = packed struct { x: u17 };
    const Outer = packed struct { inner: Inner, x: u15 };

    var x: u15 = undefined;
    x = 23385;
    var inner: Inner = undefined;
    inner = .{ .x = x };
    const outer = Outer{ .x = x, .inner = inner };
    try expect(outer.inner.x == x);
    try expect(outer.x == x);
}

test "packed struct store of comparison result" {
    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO

    const S1 = packed struct {
        val1: u3,
        val2: u3,
    };
    const S2 = packed struct {
        a: bool,
        b: bool,
    };

    var A: S1 = .{ .val1 = 1, .val2 = 1 };
    A.val2 += 1;
    try expectEqual(1, A.val1);
    try expectEqual(2, A.val2);
    try expect((A.val2 & 1) != 1);
    const result1: S2 = .{ .a = (A.val2 & 1) != 1, .b = (A.val1 & 1) != 1 };
    try expect(result1.a);
    try expect(!result1.b);

    try expect((A.val2 == 3) == false);
    try expect((A.val2 == 2) == true);
    const result2: S2 = .{ .a = !(A.val2 == 3), .b = (A.val1 == 2) };
    try expect(result2.a);
    try expect(!result2.b);
}

test "initialize packed struct field to undefined at comptime" {
    const S = packed struct(u8) { x: u8 };
    const val: S = .{ .x = undefined };
    _ = val;
}
