blob 7c5c6532 (10426B) - Raw
1 const expect = @import("std").testing.expect; 2 3 const Value = union(enum) { 4 Int: u64, 5 Array: [9]u8, 6 }; 7 8 const Agg = struct { 9 val1: Value, 10 val2: Value, 11 }; 12 13 const v1 = Value{ .Int = 1234 }; 14 const v2 = Value{ .Array = [_]u8{3} ** 9 }; 15 16 const err = (anyerror!Agg)(Agg{ 17 .val1 = v1, 18 .val2 = v2, 19 }); 20 21 const array = [_]Value{ 22 v1, 23 v2, 24 v1, 25 v2, 26 }; 27 28 test "unions embedded in aggregate types" { 29 switch (array[1]) { 30 Value.Array => |arr| expect(arr[4] == 3), 31 else => unreachable, 32 } 33 switch ((err catch unreachable).val1) { 34 Value.Int => |x| expect(x == 1234), 35 else => unreachable, 36 } 37 } 38 39 const Foo = union { 40 float: f64, 41 int: i32, 42 }; 43 44 test "basic unions" { 45 var foo = Foo{ .int = 1 }; 46 expect(foo.int == 1); 47 foo = Foo{ .float = 12.34 }; 48 expect(foo.float == 12.34); 49 } 50 51 test "comptime union field access" { 52 comptime { 53 var foo = Foo{ .int = 0 }; 54 expect(foo.int == 0); 55 56 foo = Foo{ .float = 42.42 }; 57 expect(foo.float == 42.42); 58 } 59 } 60 61 test "init union with runtime value" { 62 var foo: Foo = undefined; 63 64 setFloat(&foo, 12.34); 65 expect(foo.float == 12.34); 66 67 setInt(&foo, 42); 68 expect(foo.int == 42); 69 } 70 71 fn setFloat(foo: *Foo, x: f64) void { 72 foo.* = Foo{ .float = x }; 73 } 74 75 fn setInt(foo: *Foo, x: i32) void { 76 foo.* = Foo{ .int = x }; 77 } 78 79 const FooExtern = extern union { 80 float: f64, 81 int: i32, 82 }; 83 84 test "basic extern unions" { 85 var foo = FooExtern{ .int = 1 }; 86 expect(foo.int == 1); 87 foo.float = 12.34; 88 expect(foo.float == 12.34); 89 } 90 91 const Letter = enum { 92 A, 93 B, 94 C, 95 }; 96 const Payload = union(Letter) { 97 A: i32, 98 B: f64, 99 C: bool, 100 }; 101 102 test "union with specified enum tag" { 103 doTest(); 104 comptime doTest(); 105 } 106 107 fn doTest() void { 108 expect(bar(Payload{ .A = 1234 }) == -10); 109 } 110 111 fn bar(value: Payload) i32 { 112 expect(Letter(value) == Letter.A); 113 return switch (value) { 114 Payload.A => |x| return x - 1244, 115 Payload.B => |x| if (x == 12.34) i32(20) else 21, 116 Payload.C => |x| if (x) i32(30) else 31, 117 }; 118 } 119 120 const MultipleChoice = union(enum(u32)) { 121 A = 20, 122 B = 40, 123 C = 60, 124 D = 1000, 125 }; 126 test "simple union(enum(u32))" { 127 var x = MultipleChoice.C; 128 expect(x == MultipleChoice.C); 129 expect(@enumToInt(@TagType(MultipleChoice)(x)) == 60); 130 } 131 132 const MultipleChoice2 = union(enum(u32)) { 133 Unspecified1: i32, 134 A: f32 = 20, 135 Unspecified2: void, 136 B: bool = 40, 137 Unspecified3: i32, 138 C: i8 = 60, 139 Unspecified4: void, 140 D: void = 1000, 141 Unspecified5: i32, 142 }; 143 144 test "union(enum(u32)) with specified and unspecified tag values" { 145 comptime expect(@TagType(@TagType(MultipleChoice2)) == u32); 146 testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); 147 comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 }); 148 } 149 150 fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) void { 151 expect(@enumToInt(@TagType(MultipleChoice2)(x)) == 60); 152 expect(1123 == switch (x) { 153 MultipleChoice2.A => 1, 154 MultipleChoice2.B => 2, 155 MultipleChoice2.C => |v| i32(1000) + v, 156 MultipleChoice2.D => 4, 157 MultipleChoice2.Unspecified1 => 5, 158 MultipleChoice2.Unspecified2 => 6, 159 MultipleChoice2.Unspecified3 => 7, 160 MultipleChoice2.Unspecified4 => 8, 161 MultipleChoice2.Unspecified5 => 9, 162 }); 163 } 164 165 const ExternPtrOrInt = extern union { 166 ptr: *u8, 167 int: u64, 168 }; 169 test "extern union size" { 170 comptime expect(@sizeOf(ExternPtrOrInt) == 8); 171 } 172 173 const PackedPtrOrInt = packed union { 174 ptr: *u8, 175 int: u64, 176 }; 177 test "extern union size" { 178 comptime expect(@sizeOf(PackedPtrOrInt) == 8); 179 } 180 181 const ZeroBits = union { 182 OnlyField: void, 183 }; 184 test "union with only 1 field which is void should be zero bits" { 185 comptime expect(@sizeOf(ZeroBits) == 0); 186 } 187 188 const TheTag = enum { 189 A, 190 B, 191 C, 192 }; 193 const TheUnion = union(TheTag) { 194 A: i32, 195 B: i32, 196 C: i32, 197 }; 198 test "union field access gives the enum values" { 199 expect(TheUnion.A == TheTag.A); 200 expect(TheUnion.B == TheTag.B); 201 expect(TheUnion.C == TheTag.C); 202 } 203 204 test "cast union to tag type of union" { 205 testCastUnionToTagType(TheUnion{ .B = 1234 }); 206 comptime testCastUnionToTagType(TheUnion{ .B = 1234 }); 207 } 208 209 fn testCastUnionToTagType(x: TheUnion) void { 210 expect(TheTag(x) == TheTag.B); 211 } 212 213 test "cast tag type of union to union" { 214 var x: Value2 = Letter2.B; 215 expect(Letter2(x) == Letter2.B); 216 } 217 const Letter2 = enum { 218 A, 219 B, 220 C, 221 }; 222 const Value2 = union(Letter2) { 223 A: i32, 224 B, 225 C, 226 }; 227 228 test "implicit cast union to its tag type" { 229 var x: Value2 = Letter2.B; 230 expect(x == Letter2.B); 231 giveMeLetterB(x); 232 } 233 fn giveMeLetterB(x: Letter2) void { 234 expect(x == Value2.B); 235 } 236 237 pub const PackThis = union(enum) { 238 Invalid: bool, 239 StringLiteral: u2, 240 }; 241 242 test "constant packed union" { 243 testConstPackedUnion([_]PackThis{PackThis{ .StringLiteral = 1 }}); 244 } 245 246 fn testConstPackedUnion(expected_tokens: []const PackThis) void { 247 expect(expected_tokens[0].StringLiteral == 1); 248 } 249 250 test "switch on union with only 1 field" { 251 var r: PartialInst = undefined; 252 r = PartialInst.Compiled; 253 switch (r) { 254 PartialInst.Compiled => { 255 var z: PartialInstWithPayload = undefined; 256 z = PartialInstWithPayload{ .Compiled = 1234 }; 257 switch (z) { 258 PartialInstWithPayload.Compiled => |x| { 259 expect(x == 1234); 260 return; 261 }, 262 } 263 }, 264 } 265 unreachable; 266 } 267 268 const PartialInst = union(enum) { 269 Compiled, 270 }; 271 272 const PartialInstWithPayload = union(enum) { 273 Compiled: i32, 274 }; 275 276 test "access a member of tagged union with conflicting enum tag name" { 277 const Bar = union(enum) { 278 A: A, 279 B: B, 280 281 const A = u8; 282 const B = void; 283 }; 284 285 comptime expect(Bar.A == u8); 286 } 287 288 test "tagged union initialization with runtime void" { 289 expect(testTaggedUnionInit({})); 290 } 291 292 const TaggedUnionWithAVoid = union(enum) { 293 A, 294 B: i32, 295 }; 296 297 fn testTaggedUnionInit(x: var) bool { 298 const y = TaggedUnionWithAVoid{ .A = x }; 299 return @TagType(TaggedUnionWithAVoid)(y) == TaggedUnionWithAVoid.A; 300 } 301 302 pub const UnionEnumNoPayloads = union(enum) { 303 A, 304 B, 305 }; 306 307 test "tagged union with no payloads" { 308 const a = UnionEnumNoPayloads{ .B = {} }; 309 switch (a) { 310 @TagType(UnionEnumNoPayloads).A => @panic("wrong"), 311 @TagType(UnionEnumNoPayloads).B => {}, 312 } 313 } 314 315 test "union with only 1 field casted to its enum type" { 316 const Literal = union(enum) { 317 Number: f64, 318 Bool: bool, 319 }; 320 321 const Expr = union(enum) { 322 Literal: Literal, 323 }; 324 325 var e = Expr{ .Literal = Literal{ .Bool = true } }; 326 const Tag = @TagType(Expr); 327 comptime expect(@TagType(Tag) == comptime_int); 328 var t = Tag(e); 329 expect(t == Expr.Literal); 330 } 331 332 test "union with only 1 field casted to its enum type which has enum value specified" { 333 const Literal = union(enum) { 334 Number: f64, 335 Bool: bool, 336 }; 337 338 const Tag = enum { 339 Literal = 33, 340 }; 341 342 const Expr = union(Tag) { 343 Literal: Literal, 344 }; 345 346 var e = Expr{ .Literal = Literal{ .Bool = true } }; 347 comptime expect(@TagType(Tag) == comptime_int); 348 var t = Tag(e); 349 expect(t == Expr.Literal); 350 expect(@enumToInt(t) == 33); 351 comptime expect(@enumToInt(t) == 33); 352 } 353 354 test "@enumToInt works on unions" { 355 const Bar = union(enum) { 356 A: bool, 357 B: u8, 358 C, 359 }; 360 361 const a = Bar{ .A = true }; 362 var b = Bar{ .B = undefined }; 363 var c = Bar.C; 364 expect(@enumToInt(a) == 0); 365 expect(@enumToInt(b) == 1); 366 expect(@enumToInt(c) == 2); 367 } 368 369 const Attribute = union(enum) { 370 A: bool, 371 B: u8, 372 }; 373 374 fn setAttribute(attr: Attribute) void {} 375 376 fn Setter(attr: Attribute) type { 377 return struct { 378 fn set() void { 379 setAttribute(attr); 380 } 381 }; 382 } 383 384 test "comptime union field value equality" { 385 const a0 = Setter(Attribute{ .A = false }); 386 const a1 = Setter(Attribute{ .A = true }); 387 const a2 = Setter(Attribute{ .A = false }); 388 389 const b0 = Setter(Attribute{ .B = 5 }); 390 const b1 = Setter(Attribute{ .B = 9 }); 391 const b2 = Setter(Attribute{ .B = 5 }); 392 393 expect(a0 == a0); 394 expect(a1 == a1); 395 expect(a0 == a2); 396 397 expect(b0 == b0); 398 expect(b1 == b1); 399 expect(b0 == b2); 400 401 expect(a0 != b0); 402 expect(a0 != a1); 403 expect(b0 != b1); 404 } 405 406 test "return union init with void payload" { 407 const S = struct { 408 fn entry() void { 409 expect(func().state == State.one); 410 } 411 const Outer = union(enum) { 412 state: State, 413 }; 414 const State = union(enum) { 415 one: void, 416 two: u32, 417 }; 418 fn func() Outer { 419 return Outer{ .state = State{ .one = {} } }; 420 } 421 }; 422 S.entry(); 423 comptime S.entry(); 424 } 425 426 test "@unionInit can modify a union type" { 427 const UnionInitEnum = union(enum) { 428 Boolean: bool, 429 Byte: u8, 430 }; 431 432 var value: UnionInitEnum = undefined; 433 434 value = @unionInit(UnionInitEnum, "Boolean", true); 435 expect(value.Boolean == true); 436 value.Boolean = false; 437 expect(value.Boolean == false); 438 439 value = @unionInit(UnionInitEnum, "Byte", 2); 440 expect(value.Byte == 2); 441 value.Byte = 3; 442 expect(value.Byte == 3); 443 } 444 445 test "@unionInit can modify a pointer value" { 446 const UnionInitEnum = union(enum) { 447 Boolean: bool, 448 Byte: u8, 449 }; 450 451 var value: UnionInitEnum = undefined; 452 var value_ptr = &value; 453 454 value_ptr.* = @unionInit(UnionInitEnum, "Boolean", true); 455 expect(value.Boolean == true); 456 457 value_ptr.* = @unionInit(UnionInitEnum, "Byte", 2); 458 expect(value.Byte == 2); 459 } 460 461 test "union no tag with struct member" { 462 const Struct = struct {}; 463 const Union = union { 464 s: Struct, 465 pub fn foo(self: *@This()) void {} 466 }; 467 var u = Union{ .s = Struct{} }; 468 u.foo(); 469 } 470 471 fn testComparison() void { 472 var x = Payload{.A = 42}; 473 expect(x == .A); 474 expect(x != .B); 475 expect(x != .C); 476 expect((x == .B) == false); 477 expect((x == .C) == false); 478 expect((x != .A) == false); 479 } 480 481 test "comparison between union and enum literal" { 482 testComparison(); 483 comptime testComparison(); 484 }