| 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); |
| 3 | const assert = std.debug.assert; |
| 4 | const expect = std.testing.expect; |
| 5 | const expectEqual = std.testing.expectEqual; |
| 6 | |
| 7 | test "flags in packed union" { |
| 8 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 9 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 10 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; |
| 11 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 12 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO |
| 13 | |
| 14 | try testFlagsInPackedUnion(); |
| 15 | try comptime testFlagsInPackedUnion(); |
| 16 | } |
| 17 | |
| 18 | fn testFlagsInPackedUnion() !void { |
| 19 | const FlagBits = packed struct(u8) { |
| 20 | enable_1: bool = false, |
| 21 | enable_2: bool = false, |
| 22 | enable_3: bool = false, |
| 23 | enable_4: bool = false, |
| 24 | other_flags: packed union { |
| 25 | flags: packed struct(u4) { |
| 26 | enable_1: bool = true, |
| 27 | enable_2: bool = false, |
| 28 | enable_3: bool = false, |
| 29 | enable_4: bool = false, |
| 30 | }, |
| 31 | bits: u4, |
| 32 | } = .{ .flags = .{} }, |
| 33 | }; |
| 34 | var test_bits: FlagBits = .{}; |
| 35 | |
| 36 | try expectEqual(false, test_bits.enable_1); |
| 37 | try expectEqual(true, test_bits.other_flags.flags.enable_1); |
| 38 | |
| 39 | test_bits.enable_1 = true; |
| 40 | |
| 41 | try expectEqual(true, test_bits.enable_1); |
| 42 | try expectEqual(true, test_bits.other_flags.flags.enable_1); |
| 43 | |
| 44 | test_bits.other_flags.flags.enable_1 = false; |
| 45 | |
| 46 | try expectEqual(true, test_bits.enable_1); |
| 47 | try expectEqual(false, test_bits.other_flags.flags.enable_1); |
| 48 | } |
| 49 | |
| 50 | test "flags in packed union at offset" { |
| 51 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 52 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 53 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; |
| 54 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 55 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO |
| 56 | |
| 57 | try testFlagsInPackedUnionAtOffset(); |
| 58 | try comptime testFlagsInPackedUnionAtOffset(); |
| 59 | } |
| 60 | |
| 61 | fn testFlagsInPackedUnionAtOffset() !void { |
| 62 | const FlagBits = packed union { |
| 63 | base_flags: packed struct(u12) { |
| 64 | a: packed union { |
| 65 | flags: packed struct(u4) { |
| 66 | enable_1: bool = true, |
| 67 | enable_2: bool = false, |
| 68 | enable_3: bool = false, |
| 69 | enable_4: bool = false, |
| 70 | }, |
| 71 | bits: u4, |
| 72 | }, |
| 73 | pad: u8 = 0, |
| 74 | }, |
| 75 | adv_flags: packed struct(u12) { |
| 76 | pad: u8 = 0, |
| 77 | adv: packed union { |
| 78 | flags: packed struct(u4) { |
| 79 | enable_1: bool = true, |
| 80 | enable_2: bool = false, |
| 81 | enable_3: bool = false, |
| 82 | enable_4: bool = false, |
| 83 | }, |
| 84 | bits: u4, |
| 85 | }, |
| 86 | }, |
| 87 | }; |
| 88 | var test_bits: FlagBits = .{ .adv_flags = .{ .adv = .{ .flags = .{} } } }; |
| 89 | |
| 90 | try expectEqual(@as(u8, 0), test_bits.adv_flags.pad); |
| 91 | try expectEqual(true, test_bits.adv_flags.adv.flags.enable_1); |
| 92 | try expectEqual(false, test_bits.adv_flags.adv.flags.enable_2); |
| 93 | |
| 94 | test_bits.adv_flags.adv.flags.enable_1 = false; |
| 95 | test_bits.adv_flags.adv.flags.enable_2 = true; |
| 96 | try expectEqual(@as(u8, 0), test_bits.adv_flags.pad); |
| 97 | try expectEqual(false, test_bits.adv_flags.adv.flags.enable_1); |
| 98 | try expectEqual(true, test_bits.adv_flags.adv.flags.enable_2); |
| 99 | |
| 100 | test_bits.adv_flags.adv.bits = 12; |
| 101 | try expectEqual(@as(u8, 0), test_bits.adv_flags.pad); |
| 102 | try expectEqual(false, test_bits.adv_flags.adv.flags.enable_1); |
| 103 | try expectEqual(false, test_bits.adv_flags.adv.flags.enable_2); |
| 104 | } |
| 105 | |
| 106 | // Originally reported at https://github.com/ziglang/zig/issues/16581 |
| 107 | test "packed union in packed struct" { |
| 108 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 109 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 110 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 111 | |
| 112 | try testPackedUnionInPackedStruct(); |
| 113 | try comptime testPackedUnionInPackedStruct(); |
| 114 | } |
| 115 | |
| 116 | fn testPackedUnionInPackedStruct() !void { |
| 117 | const ReadRequest = packed struct { key: i32 }; |
| 118 | const RequestType = enum(u1) { |
| 119 | read, |
| 120 | insert, |
| 121 | }; |
| 122 | const RequestUnion = packed union { |
| 123 | read: ReadRequest, |
| 124 | }; |
| 125 | |
| 126 | const Request = packed struct { |
| 127 | active_type: RequestType, |
| 128 | request: RequestUnion, |
| 129 | const Self = @This(); |
| 130 | |
| 131 | fn init(read: ReadRequest) Self { |
| 132 | return .{ |
| 133 | .active_type = .read, |
| 134 | .request = RequestUnion{ .read = read }, |
| 135 | }; |
| 136 | } |
| 137 | }; |
| 138 | |
| 139 | try std.testing.expectEqual(RequestType.read, Request.init(.{ .key = 3 }).active_type); |
| 140 | } |
| 141 | |
| 142 | test "packed union initialized with a runtime value" { |
| 143 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 144 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 145 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 146 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 147 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 148 | |
| 149 | const Fields = packed struct { |
| 150 | timestamp: u50, |
| 151 | random_bits: u13, |
| 152 | }; |
| 153 | const ID = packed union { |
| 154 | value: u63, |
| 155 | fields: Fields, |
| 156 | |
| 157 | fn getValue() i64 { |
| 158 | return 1341; |
| 159 | } |
| 160 | }; |
| 161 | |
| 162 | const timestamp: i64 = ID.getValue(); |
| 163 | const id = ID{ .fields = Fields{ |
| 164 | .timestamp = @as(u50, @intCast(timestamp)), |
| 165 | .random_bits = 420, |
| 166 | } }; |
| 167 | try std.testing.expect((ID{ .value = id.value }).fields.timestamp == timestamp); |
| 168 | } |
| 169 | |
| 170 | test "assigning to non-active field at comptime" { |
| 171 | comptime { |
| 172 | const FlagBits = packed union { |
| 173 | flags: packed struct {}, |
| 174 | bits: packed struct {}, |
| 175 | }; |
| 176 | |
| 177 | var test_bits: FlagBits = .{ .flags = .{} }; |
| 178 | test_bits.bits = .{}; |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | test "packed union with explicit backing integer" { |
| 183 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 184 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; |
| 185 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 186 | |
| 187 | const U = packed union(i32) { |
| 188 | raw: i32, |
| 189 | unsigned_halves: packed struct { low: u16, high: u16 }, |
| 190 | |
| 191 | fn check(val: @This()) !void { |
| 192 | try expect(@as(i32, @bitCast(val)) == -2); |
| 193 | try expect(@as(u32, @bitCast(val)) == 0xFFFFFFFE); |
| 194 | try expect(val.raw == -2); |
| 195 | try expect(val.unsigned_halves.low == 0xFFFE); |
| 196 | try expect(val.unsigned_halves.high == 0xFFFF); |
| 197 | } |
| 198 | }; |
| 199 | try U.check(.{ .raw = -2 }); |
| 200 | try comptime U.check(.{ .raw = -2 }); |
| 201 | } |
| 202 | |
| 203 | test "packed union equality" { |
| 204 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 205 | |
| 206 | const Foo = packed union { |
| 207 | a: u4, |
| 208 | b: i4, |
| 209 | }; |
| 210 | |
| 211 | const S = struct { |
| 212 | fn doTest(x: Foo, y: Foo) !void { |
| 213 | try expect(x == y); |
| 214 | try expect(!(x != y)); |
| 215 | } |
| 216 | }; |
| 217 | |
| 218 | const x: Foo = .{ .a = 3 }; |
| 219 | const y: Foo = .{ .b = 3 }; |
| 220 | |
| 221 | try S.doTest(x, y); |
| 222 | comptime try S.doTest(x, y); |
| 223 | } |
| 224 | |
| 225 | test "initialize packed union field to undefined at comptime" { |
| 226 | const U = packed union(u8) { x: u8 }; |
| 227 | const val: U = .{ .x = undefined }; |
| 228 | _ = val; |
| 229 | } |
| 230 | |
| 231 | test "convert from/to backing int" { |
| 232 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 233 | |
| 234 | const U = packed union(u10) { |
| 235 | a: i10, |
| 236 | b: enum(u10) { x, y, z }, |
| 237 | fn doTheTest(u: @This()) !void { |
| 238 | const backing_int = @backingInt(u); |
| 239 | const reconstructed: @This() = @fromBackingInt(backing_int); |
| 240 | try expect(reconstructed == u); |
| 241 | } |
| 242 | }; |
| 243 | try U.doTheTest(.{ .a = 123 }); |
| 244 | try comptime U.doTheTest(.{ .a = 123 }); |
| 245 | } |
| 246 | |
| 247 | test "equality with wide backing integer" { |
| 248 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35982 |
| 249 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| 250 | |
| 251 | const U = packed union(i200) { |
| 252 | x: u200, |
| 253 | fn doTheTest(s: @This(), int: i200) !void { |
| 254 | try expect(s == @as(@This(), @bitCast(int))); |
| 255 | } |
| 256 | }; |
| 257 | try U.doTheTest(.{ .x = (1 << 200) - 1 }, -1); |
| 258 | try comptime U.doTheTest(.{ .x = (1 << 200) - 1 }, -1); |
| 259 | } |