1const builtin = @import("builtin");
2const std = @import("std");
3const endian = builtin.cpu.arch.endian();
4const expect = std.testing.expect;
5const assert = std.debug.assert;
6const expectEqual = std.testing.expectEqual;
7const Tag = std.meta.Tag;
8
9const FooWithFloats = union {
10 float: f64,
11 int: i32,
12};
13
14test "basic unions with floats" {
15 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
16 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
17 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
18 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
19
20 var foo = FooWithFloats{ .int = 1 };
21 try expect(foo.int == 1);
22 foo = FooWithFloats{ .float = 12.34 };
23 try expect(foo.float == 12.34);
24}
25
26fn setFloat(foo: *FooWithFloats, x: f64) void {
27 foo.* = FooWithFloats{ .float = x };
28}
29
30test "init union with runtime value - floats" {
31 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
32 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
33 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
34
35 var foo: FooWithFloats = undefined;
36
37 setFloat(&foo, 12.34);
38 try expect(foo.float == 12.34);
39}
40
41test "basic unions" {
42 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
43 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
44 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
45
46 var foo = Foo{ .int = 1 };
47 try expect(foo.int == 1);
48 foo = Foo{ .str = .{ .slice = "Hello!" } };
49 try expect(std.mem.eql(u8, foo.str.slice, "Hello!"));
50}
51
52const Foo = union {
53 int: i32,
54 str: struct {
55 slice: []const u8,
56 },
57};
58
59test "init union with runtime value" {
60 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
61 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
62 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
63 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
64
65 var foo: Foo = undefined;
66
67 setInt(&foo, 42);
68 try expect(foo.int == 42);
69
70 setStr(&foo, "Hello!");
71 try expect(std.mem.eql(u8, foo.str.slice, "Hello!"));
72}
73
74fn setInt(foo: *Foo, x: i32) void {
75 foo.* = Foo{ .int = x };
76}
77
78fn setStr(foo: *Foo, slice: []const u8) void {
79 foo.* = Foo{ .str = .{ .slice = slice } };
80}
81
82test "comptime union field access" {
83 comptime {
84 var foo = FooWithFloats{ .int = 0 };
85 try expect(foo.int == 0);
86
87 foo = FooWithFloats{ .float = 12.34 };
88 try expect(foo.float == 12.34);
89 }
90}
91
92const FooExtern = extern union {
93 int: i32,
94 str: extern struct {
95 slice: [*:0]const u8,
96 },
97};
98
99test "basic extern unions" {
100 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
101 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
102
103 var foo = FooExtern{ .int = 1 };
104 try expect(foo.int == 1);
105 foo.str.slice = "Well";
106 try expect(foo.str.slice[0] == 'W');
107 try expect(foo.str.slice[1] == 'e');
108 try expect(foo.str.slice[2] == 'l');
109 try expect(foo.str.slice[3] == 'l');
110 try expect(foo.str.slice[4] == 0);
111}
112
113const ExternPtrOrInt = extern union {
114 ptr: *u8,
115 int: u64,
116};
117test "extern union size" {
118 comptime assert(@sizeOf(ExternPtrOrInt) == 8);
119}
120
121test "0-sized extern union definition" {
122 const U = extern union {
123 a: void,
124 const f = 1;
125 };
126
127 try expect(U.f == 1);
128}
129
130const Value = union(enum) {
131 Int: u64,
132 Array: [9]u8,
133};
134
135const Agg = struct {
136 val1: Value,
137 val2: Value,
138};
139
140const v1 = Value{ .Int = 1234 };
141const v2 = Value{ .Array = @splat(3) };
142
143const err = @as(anyerror!Agg, Agg{
144 .val1 = v1,
145 .val2 = v2,
146});
147
148const array = [_]Value{ v1, v2, v1, v2 };
149
150test "unions embedded in aggregate types" {
151 if (builtin.zig_backend == .stage2_c and builtin.target.abi == .msvc) return error.SkipZigTest;
152 switch (array[1]) {
153 Value.Array => |arr| try expect(arr[4] == 3),
154 else => unreachable,
155 }
156 switch ((err catch unreachable).val1) {
157 Value.Int => |x| try expect(x == 1234),
158 else => unreachable,
159 }
160}
161
162test "constant tagged union with payload" {
163 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
164 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
165
166 var empty = TaggedUnionWithPayload{ .Empty = {} };
167 var full = TaggedUnionWithPayload{ .Full = 13 };
168 _ = .{ &empty, &full };
169 shouldBeEmpty(empty);
170 shouldBeNotEmpty(full);
171}
172
173fn shouldBeEmpty(x: TaggedUnionWithPayload) void {
174 switch (x) {
175 TaggedUnionWithPayload.Empty => {},
176 else => unreachable,
177 }
178}
179
180fn shouldBeNotEmpty(x: TaggedUnionWithPayload) void {
181 switch (x) {
182 TaggedUnionWithPayload.Empty => unreachable,
183 else => {},
184 }
185}
186
187const TaggedUnionWithPayload = union(enum) {
188 Empty: void,
189 Full: i32,
190};
191
192test "union alignment" {
193 comptime {
194 try expect(@alignOf(AlignTestTaggedUnion) >= @alignOf([9]u8));
195 try expect(@alignOf(AlignTestTaggedUnion) >= @alignOf(u64));
196 }
197}
198
199const AlignTestTaggedUnion = union(enum) {
200 A: [9]u8,
201 B: u64,
202};
203
204const Letter = enum { A, B, C };
205const Payload = union(Letter) {
206 A: i32,
207 B: f64,
208 C: bool,
209};
210
211test "union with specified enum tag" {
212 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
213 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
214 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
215 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
216
217 try doTest();
218 try comptime doTest();
219}
220
221fn doTest() error{TestUnexpectedResult}!void {
222 try expect((try bar(Payload{ .A = 1234 })) == -10);
223}
224
225fn bar(value: Payload) error{TestUnexpectedResult}!i32 {
226 try expect(@as(Letter, value) == Letter.A);
227 return switch (value) {
228 Payload.A => |x| return x - 1244,
229 Payload.B => |x| if (x == 12.34) @as(i32, 20) else 21,
230 Payload.C => |x| if (x) @as(i32, 30) else 31,
231 };
232}
233
234fn testComparison() !void {
235 var x = Payload{ .A = 42 };
236 _ = &x;
237 try expect(x == .A);
238 try expect(x != .B);
239 try expect(x != .C);
240 try expect((x == .B) == false);
241 try expect((x == .C) == false);
242 try expect((x != .A) == false);
243}
244
245test "comparison between union and enum literal" {
246 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
247 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
248 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
249
250 try testComparison();
251 try comptime testComparison();
252}
253
254const TheTag = enum { A, B, C };
255const TheUnion = union(TheTag) {
256 A: i32,
257 B: i32,
258 C: i32,
259};
260test "cast union to tag type of union" {
261 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
262 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
263
264 try testCastUnionToTag();
265 try comptime testCastUnionToTag();
266}
267
268fn testCastUnionToTag() !void {
269 var u = TheUnion{ .B = 1234 };
270 _ = &u;
271 try expect(@as(TheTag, u) == TheTag.B);
272}
273
274test "union field access gives the enum values" {
275 try expect(TheUnion.A == TheTag.A);
276 try expect(TheUnion.B == TheTag.B);
277 try expect(TheUnion.C == TheTag.C);
278}
279
280test "cast tag type of union to union" {
281 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
282 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
283
284 var x: Value2 = Letter2.B;
285 _ = &x;
286 try expect(@as(Letter2, x) == Letter2.B);
287}
288const Letter2 = enum { A, B, C };
289const Value2 = union(Letter2) {
290 A: i32,
291 B,
292 C,
293};
294
295test "implicit cast union to its tag type" {
296 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
297 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
298
299 var x: Value2 = Letter2.B;
300 _ = &x;
301 try expect(x == Letter2.B);
302 try giveMeLetterB(x);
303}
304fn giveMeLetterB(x: Letter2) !void {
305 try expect(x == Value2.B);
306}
307
308// TODO it looks like this test intended to test packed unions, but this is not a packed
309// union. go through git history and find out what happened.
310pub const PackThis = union(enum) {
311 Invalid: bool,
312 StringLiteral: u2,
313};
314
315test "constant packed union" {
316 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
317 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
318 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
319
320 try testConstPackedUnion(&[_]PackThis{PackThis{ .StringLiteral = 1 }});
321}
322
323fn testConstPackedUnion(expected_tokens: []const PackThis) !void {
324 try expect(expected_tokens[0].StringLiteral == 1);
325}
326
327const MultipleChoice = union(enum(u32)) {
328 A = 20,
329 B = 40,
330 C = 60,
331 D = 1000,
332};
333test "simple union(enum(u32))" {
334 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
335 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
336
337 var x = MultipleChoice.C;
338 _ = &x;
339 try expect(x == MultipleChoice.C);
340 try expect(@backingInt(@as(Tag(MultipleChoice), x)) == 60);
341}
342
343test "packed union size" {
344 const U = packed union {
345 signed: isize,
346 unsigned: usize,
347 };
348 comptime assert(@sizeOf(U) == @sizeOf(usize));
349}
350
351const ZeroBits = union {
352 OnlyField: void,
353};
354test "union with only 1 field which is void should be zero bits" {
355 comptime assert(@sizeOf(ZeroBits) == 0);
356}
357
358test "assigning to union with zero size field" {
359 const U = union {
360 a: u32,
361 b: void,
362 c: f32,
363 };
364
365 const u: U = .{ .b = {} };
366 _ = u;
367
368 const UE = union(enum) {
369 a: f32,
370 b: u32,
371 c: u0,
372 };
373
374 const ue: UE = .{ .c = 0 };
375 _ = ue;
376}
377
378test "tagged union initialization with runtime void" {
379 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
380 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
381
382 try expect(testTaggedUnionInit({}));
383}
384
385const TaggedUnionWithAVoid = union(enum) {
386 A,
387 B: i32,
388};
389
390fn testTaggedUnionInit(x: anytype) bool {
391 const y = TaggedUnionWithAVoid{ .A = x };
392 return @as(Tag(TaggedUnionWithAVoid), y) == TaggedUnionWithAVoid.A;
393}
394
395pub const UnionEnumNoPayloads = union(enum) { A, B };
396
397test "tagged union with no payloads" {
398 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
399 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
400
401 const a = UnionEnumNoPayloads{ .B = {} };
402 switch (a) {
403 Tag(UnionEnumNoPayloads).A => @panic("wrong"),
404 Tag(UnionEnumNoPayloads).B => {},
405 }
406}
407
408test "union with only 1 field casted to its enum type" {
409 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
410 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
411
412 const Literal = union(enum) {
413 Number: f64,
414 Bool: bool,
415 };
416
417 const Expr = union(enum) {
418 Literal: Literal,
419 };
420
421 var e = Expr{ .Literal = Literal{ .Bool = true } };
422 _ = &e;
423 const ExprTag = Tag(Expr);
424 comptime assert(Tag(ExprTag) == u0);
425 var t = @as(ExprTag, e);
426 _ = &t;
427 try expect(t == Expr.Literal);
428}
429
430test "union with one member defaults to u0 tag type" {
431 const U0 = union(enum) {
432 X: u32,
433 };
434 comptime assert(Tag(Tag(U0)) == u0);
435}
436
437const Foo1 = union(enum) {
438 f: struct {
439 x: usize,
440 },
441};
442var glbl: Foo1 = undefined;
443
444test "global union with single field is correctly initialized" {
445 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
446 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
447
448 glbl = Foo1{
449 .f = @typeInfo(Foo1).@"union".field_types[0]{ .x = 123 },
450 };
451 try expect(glbl.f.x == 123);
452}
453
454pub const FooUnion = union(enum) {
455 U0: usize,
456 U1: u8,
457};
458
459var glbl_array: [2]FooUnion = undefined;
460
461test "initialize global array of union" {
462 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
463 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
464 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
465
466 glbl_array[1] = FooUnion{ .U1 = 2 };
467 glbl_array[0] = FooUnion{ .U0 = 1 };
468 try expect(glbl_array[0].U0 == 1);
469 try expect(glbl_array[1].U1 == 2);
470}
471
472test "update the tag value for zero-sized unions" {
473 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
474 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
475
476 const S = union(enum) {
477 U0: void,
478 U1: void,
479 };
480 var x = S{ .U0 = {} };
481 try expect(x == .U0);
482 x = S{ .U1 = {} };
483 try expect(x == .U1);
484}
485
486test "union initializer generates padding only if needed" {
487 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
488 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
489
490 const U = union(enum) {
491 A: u24,
492 };
493
494 var v = U{ .A = 532 };
495 _ = &v;
496 try expect(v.A == 532);
497}
498
499test "runtime tag name with single field" {
500 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
501 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
502
503 const U = union(enum) {
504 A: i32,
505 };
506
507 var v = U{ .A = 42 };
508 _ = &v;
509 try expect(std.mem.eql(u8, @tagName(v), "A"));
510}
511
512test "method call on an empty union" {
513 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
514 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
515
516 const S = struct {
517 const MyUnion = union(MyUnionTag) {
518 pub const MyUnionTag = enum { X1, X2 };
519 X1: [0]u8,
520 X2: [0]u8,
521
522 pub fn useIt(self: *@This()) bool {
523 _ = self;
524 return true;
525 }
526 };
527
528 fn doTheTest() !void {
529 var u = MyUnion{ .X1 = [0]u8{} };
530 try expect(u.useIt());
531 }
532 };
533 try S.doTheTest();
534 try comptime S.doTheTest();
535}
536
537const Point = struct {
538 x: u64,
539 y: u64,
540};
541const TaggedFoo = union(enum) {
542 One: i32,
543 Two: Point,
544 Three: void,
545};
546const FooNoVoid = union(enum) {
547 One: i32,
548 Two: Point,
549};
550const Baz = enum { A, B, C, D };
551
552test "tagged union type" {
553 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
554 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
555
556 const foo1 = TaggedFoo{ .One = 13 };
557 const foo2 = TaggedFoo{
558 .Two = Point{
559 .x = 1234,
560 .y = 5678,
561 },
562 };
563 try expect(foo1.One == 13);
564 try expect(foo2.Two.x == 1234 and foo2.Two.y == 5678);
565 const baz = Baz.B;
566
567 try expect(baz == Baz.B);
568 try expect(@typeInfo(TaggedFoo).@"union".field_names.len == 3);
569 try expect(@typeInfo(Baz).@"enum".field_names.len == 4);
570 try expect(@sizeOf(TaggedFoo) == @sizeOf(FooNoVoid));
571 try expect(@sizeOf(Baz) == 1);
572}
573
574test "tagged union as return value" {
575 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
576 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
577 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
578 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
579 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
580
581 switch (returnAnInt(13)) {
582 TaggedFoo.One => |value| try expect(value == 13),
583 else => unreachable,
584 }
585}
586
587fn returnAnInt(x: i32) TaggedFoo {
588 return TaggedFoo{ .One = x };
589}
590
591test "tagged union with all void fields but a meaningful tag" {
592 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
593 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
594
595 const S = struct {
596 const B = union(enum) {
597 c: C,
598 None,
599 };
600
601 const A = struct {
602 b: B,
603 };
604
605 const C = struct {};
606
607 fn doTheTest() !void {
608 var a: A = A{ .b = B{ .c = C{} } };
609 try expect(@as(Tag(B), a.b) == Tag(B).c);
610 a = A{ .b = B.None };
611 try expect(@as(Tag(B), a.b) == Tag(B).None);
612 }
613 };
614 try S.doTheTest();
615 try comptime S.doTheTest();
616}
617
618test "union(enum(u32)) with specified and unspecified tag values" {
619 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
620 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
621 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
622 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
623
624 comptime assert(Tag(Tag(MultipleChoice2)) == u32);
625 try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 });
626 try comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 });
627}
628
629const MultipleChoice2 = union(enum(u32)) {
630 Unspecified1: i32,
631 A: f32 = 20,
632 Unspecified2: void,
633 B: bool = 40,
634 Unspecified3: i32,
635 C: i8 = 60,
636 Unspecified4: void,
637 D: void = 1000,
638 Unspecified5: i32,
639};
640
641fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void {
642 try expect(@backingInt(@as(Tag(MultipleChoice2), x)) == 60);
643 try expect(1123 == switch (x) {
644 MultipleChoice2.A => 1,
645 MultipleChoice2.B => 2,
646 MultipleChoice2.C => |v| @as(i32, 1000) + v,
647 MultipleChoice2.D => 4,
648 MultipleChoice2.Unspecified1 => 5,
649 MultipleChoice2.Unspecified2 => 6,
650 MultipleChoice2.Unspecified3 => 7,
651 MultipleChoice2.Unspecified4 => 8,
652 MultipleChoice2.Unspecified5 => 9,
653 });
654}
655
656test "switch on union with only 1 field" {
657 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
658 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
659
660 var r: PartialInst = undefined;
661 r = PartialInst.Compiled;
662 switch (r) {
663 PartialInst.Compiled => {
664 var z: PartialInstWithPayload = undefined;
665 z = PartialInstWithPayload{ .Compiled = 1234 };
666 switch (z) {
667 PartialInstWithPayload.Compiled => |x| {
668 try expect(x == 1234);
669 return;
670 },
671 }
672 },
673 }
674 unreachable;
675}
676
677const PartialInst = union(enum) {
678 Compiled,
679};
680
681const PartialInstWithPayload = union(enum) {
682 Compiled: i32,
683};
684
685test "union with only 1 field casted to its enum type which has enum value specified" {
686 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
687 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
688
689 const Literal = union(enum) {
690 number: f64,
691 bool: bool,
692 };
693
694 const ExprTag = enum(u32) { literal = 33 };
695 const Expr = union(ExprTag) { literal: Literal };
696
697 comptime assert(Tag(ExprTag) == u32);
698
699 var e: Expr = undefined;
700 e = .{ .literal = .{ .bool = true } };
701
702 const t: ExprTag = e;
703 comptime assert(t == Expr.literal);
704 comptime assert(@backingInt(t) == 33);
705 try expect(t == Expr.literal);
706 try expect(@backingInt(t) == 33);
707}
708
709test "@intFromEnum works on unions" {
710 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
711 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
712 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
713
714 const Bar = union(enum) {
715 A: bool,
716 B: u8,
717 C,
718 };
719
720 const a = Bar{ .A = true };
721 var b = Bar{ .B = undefined };
722 var c = Bar.C;
723 _ = .{ &b, &c };
724 try expect(@backingInt(a) == 0);
725 try expect(@backingInt(b) == 1);
726 try expect(@backingInt(c) == 2);
727}
728
729test "comptime union field value equality" {
730 const a0 = Setter(Attribute{ .A = false });
731 const a1 = Setter(Attribute{ .A = true });
732 const a2 = Setter(Attribute{ .A = false });
733
734 const b0 = Setter(Attribute{ .B = 5 });
735 const b1 = Setter(Attribute{ .B = 9 });
736 const b2 = Setter(Attribute{ .B = 5 });
737
738 try expect(a0 == a0);
739 try expect(a1 == a1);
740 try expect(a0 == a2);
741
742 try expect(b0 == b0);
743 try expect(b1 == b1);
744 try expect(b0 == b2);
745
746 try expect(a0 != b0);
747 try expect(a0 != a1);
748 try expect(b0 != b1);
749}
750
751const Attribute = union(enum) {
752 A: bool,
753 B: u8,
754};
755
756fn setAttribute(attr: Attribute) void {
757 _ = attr;
758}
759
760fn Setter(comptime attr: Attribute) type {
761 return struct {
762 fn set() void {
763 setAttribute(attr);
764 }
765 };
766}
767
768test "return union init with void payload" {
769 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
770 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
771 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
772
773 const S = struct {
774 fn entry() !void {
775 try expect(func().state == State.one);
776 }
777 const Outer = union(enum) {
778 state: State,
779 };
780 const State = union(enum) {
781 one: void,
782 two: u32,
783 };
784 fn func() Outer {
785 return Outer{ .state = State{ .one = {} } };
786 }
787 };
788 try S.entry();
789 try comptime S.entry();
790}
791
792test "@unionInit stored to a const" {
793 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
794 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
795 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
796 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
797
798 const S = struct {
799 const U = union(enum) {
800 boolean: bool,
801 byte: u8,
802 };
803 fn doTheTest() !void {
804 {
805 var t = true;
806 _ = &t;
807 const u = @unionInit(U, "boolean", t);
808 try expect(u.boolean);
809 }
810 {
811 var byte: u8 = 69;
812 _ = &byte;
813 const u = @unionInit(U, "byte", byte);
814 try expect(u.byte == 69);
815 }
816 }
817 };
818
819 try comptime S.doTheTest();
820 try S.doTheTest();
821}
822
823test "@unionInit can modify a union type" {
824 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
825 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
826 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
827
828 const UnionInitEnum = union(enum) {
829 Boolean: bool,
830 Byte: u8,
831 };
832
833 var value: UnionInitEnum = undefined;
834
835 value = @unionInit(UnionInitEnum, "Boolean", true);
836 try expect(value.Boolean == true);
837 value.Boolean = false;
838 try expect(value.Boolean == false);
839
840 value = @unionInit(UnionInitEnum, "Byte", 2);
841 try expect(value.Byte == 2);
842 value.Byte = 3;
843 try expect(value.Byte == 3);
844}
845
846test "@unionInit can modify a pointer value" {
847 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
848 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
849 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
850
851 const UnionInitEnum = union(enum) {
852 Boolean: bool,
853 Byte: u8,
854 };
855
856 var value: UnionInitEnum = undefined;
857 const value_ptr = &value;
858
859 value_ptr.* = @unionInit(UnionInitEnum, "Boolean", true);
860 try expect(value.Boolean == true);
861
862 value_ptr.* = @unionInit(UnionInitEnum, "Byte", 2);
863 try expect(value.Byte == 2);
864}
865
866test "union no tag with struct member" {
867 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
868 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
869 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
870
871 const Struct = struct {};
872 const Union = union {
873 s: Struct,
874 pub fn foo(self: *@This()) void {
875 _ = self;
876 }
877 };
878 var u = Union{ .s = Struct{} };
879 u.foo();
880}
881
882test "extern union doesn't trigger field check at comptime" {
883 const U = extern union {
884 x: u32,
885 y: u8,
886 };
887
888 const x = U{ .x = 0x55AAAA55 };
889 comptime assert(x.y == 0x55);
890}
891
892test "anonymous union literal syntax" {
893 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
894 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
895 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
896 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
897 const S = struct {
898 const Number = union {
899 int: i32,
900 float: f64,
901 };
902
903 fn doTheTest() !void {
904 var i: Number = .{ .int = 42 };
905 _ = &i;
906 const f = makeNumber();
907 try expect(i.int == 42);
908 try expect(f.float == 12.34);
909 }
910
911 fn makeNumber() Number {
912 return .{ .float = 12.34 };
913 }
914 };
915 try S.doTheTest();
916 try comptime S.doTheTest();
917}
918
919test "function call result coerces from tagged union to the tag" {
920 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
921 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
922 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
923
924 const S = struct {
925 const Arch = union(enum) {
926 One,
927 Two: usize,
928 };
929
930 const ArchTag = Tag(Arch);
931
932 fn doTheTest() !void {
933 var x: ArchTag = getArch1();
934 _ = &x;
935 try expect(x == .One);
936
937 var y: ArchTag = getArch2();
938 _ = &y;
939 try expect(y == .Two);
940 }
941
942 pub fn getArch1() Arch {
943 return .One;
944 }
945
946 pub fn getArch2() Arch {
947 return .{ .Two = 99 };
948 }
949 };
950 try S.doTheTest();
951 try comptime S.doTheTest();
952}
953
954test "switching on non exhaustive union" {
955 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
956 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
957 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
958
959 const S = struct {
960 const E = enum(u8) {
961 a,
962 b,
963 _,
964 };
965 const U = union(E) {
966 a: i32,
967 b: u32,
968 };
969 fn doTheTest() !void {
970 var a = U{ .a = 2 };
971 _ = &a;
972 switch (a) {
973 .a => |val| try expect(val == 2),
974 .b => return error.Fail,
975 }
976 }
977 };
978 try S.doTheTest();
979 try comptime S.doTheTest();
980}
981
982test "containers with single-field enums" {
983 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
984 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
985
986 const S = struct {
987 const A = union(enum) { f1 };
988 const B = union(enum) { f1: void };
989 const C = struct { a: A };
990 const D = struct { a: B };
991
992 fn doTheTest() !void {
993 var array1 = [1]A{A{ .f1 = {} }};
994 var array2 = [1]B{B{ .f1 = {} }};
995 _ = .{ &array1, &array2 };
996 try expect(array1[0] == .f1);
997 try expect(array2[0] == .f1);
998
999 var struct1 = C{ .a = A{ .f1 = {} } };
1000 var struct2 = D{ .a = B{ .f1 = {} } };
1001 _ = .{ &struct1, &struct2 };
1002 try expect(struct1.a == .f1);
1003 try expect(struct2.a == .f1);
1004 }
1005 };
1006
1007 try S.doTheTest();
1008 try comptime S.doTheTest();
1009}
1010
1011test "@unionInit on union with u8 tag but no fields" {
1012 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1013 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1014 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1015
1016 const S = struct {
1017 const Type = enum(u8) { no_op = 105 };
1018
1019 const Data = union(Type) {
1020 no_op: void,
1021
1022 pub fn decode(buf: []const u8) Data {
1023 _ = buf;
1024 return @unionInit(Data, "no_op", {});
1025 }
1026 };
1027
1028 fn doTheTest() !void {
1029 var data: Data = .{ .no_op = {} };
1030 _ = &data;
1031 var o = Data.decode(&[_]u8{});
1032 _ = &o;
1033 try expectEqual(Type.no_op, o);
1034 }
1035 };
1036
1037 try S.doTheTest();
1038 try comptime S.doTheTest();
1039}
1040
1041test "union enum type gets a separate scope" {
1042 const S = struct {
1043 const U = union(enum) {
1044 a: u8,
1045 const foo = 1;
1046 };
1047
1048 fn doTheTest() !void {
1049 try expect(!@hasDecl(Tag(U), "foo"));
1050 }
1051 };
1052
1053 try S.doTheTest();
1054}
1055
1056test "global variable struct contains union initialized to non-most-aligned field" {
1057 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1058 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1059 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1060
1061 const T = struct {
1062 const U = union(enum) {
1063 a: i32,
1064 b: f64,
1065 };
1066
1067 const S = struct {
1068 u: U,
1069 };
1070
1071 var s: S = .{
1072 .u = .{
1073 .a = 3,
1074 },
1075 };
1076 };
1077
1078 T.s.u.a += 1;
1079 try expect(T.s.u.a == 4);
1080}
1081
1082test "union with no result loc initiated with a runtime value" {
1083 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1084 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1085 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1086
1087 const U = union {
1088 a: u32,
1089 b: u32,
1090 fn foo(u: @This()) void {
1091 _ = u;
1092 }
1093 };
1094 var a: u32 = 1;
1095 _ = &a;
1096 U.foo(U{ .a = a });
1097}
1098
1099test "union with a large struct field" {
1100 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1101 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1102 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1103
1104 const S = struct {
1105 a: [8]usize,
1106 };
1107
1108 const U = union {
1109 s: S,
1110 b: u32,
1111 fn foo(_: @This()) void {}
1112 };
1113 var s: S = undefined;
1114 _ = &s;
1115 U.foo(U{ .s = s });
1116}
1117
1118test "comptime equality of extern unions with same tag" {
1119 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1120
1121 const S = struct {
1122 const U = extern union {
1123 a: i32,
1124 b: f32,
1125 };
1126 fn foo(comptime x: U) i32 {
1127 return x.a;
1128 }
1129 };
1130 const a = S.U{ .a = 1234 };
1131 const b = S.U{ .a = 1234 };
1132 try expect(S.foo(a) == S.foo(b));
1133}
1134
1135test "union tag is set when initiated as a temporary value at runtime" {
1136 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1137 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1138 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1139
1140 const U = union(enum) {
1141 a,
1142 b: u32,
1143 c,
1144
1145 fn doTheTest(u: @This()) !void {
1146 try expect(u == .b);
1147 }
1148 };
1149 var b: u32 = 1;
1150 _ = &b;
1151 try (U{ .b = b }).doTheTest();
1152}
1153
1154test "extern union most-aligned field is smaller" {
1155 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1156 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1157 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1158 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1159
1160 const U = extern union {
1161 in6: extern struct {
1162 family: u16,
1163 port: u16,
1164 flowinfo: u32,
1165 addr: [20]u8,
1166 },
1167 un: [110]u8,
1168 };
1169 var a: ?U = .{ .un = @splat(0) };
1170 _ = &a;
1171 try expect(a != null);
1172}
1173
1174test "return an extern union from C calling convention" {
1175 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1176 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1177 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1178 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1179
1180 const namespace = struct {
1181 const S = extern struct {
1182 x: c_int,
1183 };
1184 const U = extern union {
1185 l: c_long,
1186 d: f64,
1187 s: S,
1188 };
1189
1190 fn bar(arg_u: U) callconv(.c) U {
1191 var u = arg_u;
1192 _ = &u;
1193 return u;
1194 }
1195 };
1196
1197 var u: namespace.U = namespace.U{
1198 .l = @as(c_long, 42),
1199 };
1200 u = namespace.bar(namespace.U{
1201 .d = 4.0,
1202 });
1203 try expect(u.d == 4.0);
1204}
1205
1206test "noreturn field in union" {
1207 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1208 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1209 const U = union(enum) {
1210 a: u32,
1211 b: noreturn,
1212 c: noreturn,
1213 };
1214 var a = U{ .a = 1 };
1215 var count: u32 = 0;
1216 if (a == .b) @compileError("bad");
1217 switch (a) {
1218 .a => count += 1,
1219 .b => |val| {
1220 _ = val;
1221 @compileError("bad");
1222 },
1223 .c => @compileError("bad"),
1224 }
1225 switch (a) {
1226 .a => count += 1,
1227 .b, .c => @compileError("bad"),
1228 }
1229 switch (a) {
1230 .a, .b, .c => {
1231 count += 1;
1232 try expect(a == .a);
1233 },
1234 }
1235 switch (a) {
1236 .a => count += 1,
1237 else => @compileError("bad"),
1238 }
1239 switch (a) {
1240 else => {
1241 count += 1;
1242 try expect(a == .a);
1243 },
1244 }
1245 switch (a) {
1246 .a => count += 1,
1247 .b, .c => |*val| {
1248 _ = val;
1249 @compileError("bad");
1250 },
1251 }
1252 try expect(count == 6);
1253}
1254
1255test "@unionInit uses tag value instead of field index" {
1256 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1257 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1258 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1259 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1260 const E = enum(u8) {
1261 b = 255,
1262 a = 3,
1263 };
1264 const U = union(E) {
1265 b: isize,
1266 a: usize,
1267 };
1268 var i: isize = -1;
1269 _ = &i;
1270 var u = @unionInit(U, "b", i);
1271 {
1272 var a = u.b;
1273 _ = &a;
1274 try expect(a == i);
1275 }
1276 {
1277 var a = &u.b;
1278 _ = &a;
1279 try expect(a.* == i);
1280 }
1281 try expect(@backingInt(u) == 255);
1282}
1283
1284test "union field ptr - zero sized payload" {
1285 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1286 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1287 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1288
1289 const U = union {
1290 foo: void,
1291 bar: void,
1292 fn qux(_: *void) void {}
1293 };
1294 var u: U = .{ .foo = {} };
1295 U.qux(&u.foo);
1296}
1297
1298test "union field ptr - zero sized field" {
1299 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1300 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1301 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1302
1303 const U = union {
1304 foo: void,
1305 bar: u32,
1306 fn qux(_: *void) void {}
1307 };
1308 var u: U = .{ .foo = {} };
1309 U.qux(&u.foo);
1310}
1311
1312test "packed union in packed struct" {
1313 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1314 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1315 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1316 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1317
1318 const S = packed struct {
1319 nested: packed union {
1320 val: u32,
1321 foo: u32,
1322 },
1323 bar: u16,
1324
1325 fn unpack(self: @This()) usize {
1326 return self.nested.foo;
1327 }
1328 };
1329 const a: S = .{ .nested = .{ .foo = 123 }, .bar = 5 };
1330 try expect(a.unpack() == 123);
1331}
1332
1333test "Namespace-like union" {
1334 const DepType = enum {
1335 git,
1336 http,
1337 const DepType = @This();
1338 const Version = union(DepType) {
1339 git: Git,
1340 http: void,
1341 const Git = enum {
1342 branch,
1343 tag,
1344 commit,
1345 fn frozen(self: Git) bool {
1346 return self == .tag;
1347 }
1348 };
1349 };
1350 };
1351 var a: DepType.Version.Git = .tag;
1352 try expect(a.frozen());
1353}
1354
1355test "union int tag type is properly managed" {
1356 const Bar = union(enum(u2)) {
1357 x: bool,
1358 y: u8,
1359 z: u8,
1360 };
1361 try expect(@sizeOf(Bar) + 1 == 3);
1362}
1363
1364test "no dependency loop when function pointer in union returns the union" {
1365 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1366 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1367 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1368
1369 const U = union(enum) {
1370 const U = @This();
1371 a: u8,
1372 b: *const fn (x: U) void,
1373 c: *const fn (x: U) U,
1374 d: *const fn (x: u8) U,
1375 e: *const fn (x: *U) void,
1376 f: *const fn (x: *U) U,
1377 fn foo(x: u8) U {
1378 return .{ .a = x };
1379 }
1380 };
1381 var b: U = .{ .d = U.foo };
1382 try expect(b.d(2).a == 2);
1383}
1384
1385test "union reassignment can use previous value" {
1386 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1387 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1388
1389 const U = union {
1390 a: u32,
1391 b: u32,
1392 };
1393 var a = U{ .a = 32 };
1394 a = U{ .b = a.a };
1395 try expect(a.b == 32);
1396}
1397
1398test "reinterpreting enum value inside packed union" {
1399 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1400 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1401
1402 const U = packed union {
1403 tag: enum(u8) { a, b },
1404 val: u8,
1405
1406 fn doTest() !void {
1407 var u: @This() = .{ .tag = .a };
1408 u.val += 1;
1409 try expect(u.tag == .b);
1410 }
1411 };
1412 try U.doTest();
1413 try comptime U.doTest();
1414}
1415
1416test "access the tag of a global tagged union" {
1417 const U = union(enum) {
1418 a,
1419 b: u8,
1420 var u: @This() = .a;
1421 };
1422 try expect(U.u == .a);
1423}
1424
1425test "coerce enum literal to union in result loc" {
1426 const U = union(enum) {
1427 a,
1428 b: u8,
1429
1430 fn doTest(c: bool) !void {
1431 const u = if (c) .a else @This(){ .b = 0 };
1432 try expect(u == .a);
1433 }
1434 };
1435 try U.doTest(true);
1436 try comptime U.doTest(true);
1437}
1438
1439test "defined-layout union field pointer has correct alignment" {
1440 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1441 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1442 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1443
1444 const S = struct {
1445 fn doTheTest(comptime U: type) !void {
1446 var a: U = .{ .x = 123 };
1447 var b: U align(1) = .{ .x = 456 };
1448 var c: U align(64) = .{ .x = 789 };
1449
1450 const ap = &a.x;
1451 const bp = &b.x;
1452 const cp = &c.x;
1453
1454 comptime assert(@TypeOf(ap) == *u32);
1455 comptime assert(@TypeOf(bp) == *align(1) u32);
1456 comptime assert(@TypeOf(cp) == *align(64) u32);
1457
1458 try expectEqual(@as(u32, 123), ap.*);
1459 try expectEqual(@as(u32, 456), bp.*);
1460 try expectEqual(@as(u32, 789), cp.*);
1461 }
1462 };
1463
1464 const U1 = extern union { x: u32 };
1465 const U2 = packed union { x: u32 };
1466
1467 try S.doTheTest(U1);
1468 try S.doTheTest(U2);
1469 try comptime S.doTheTest(U1);
1470 try comptime S.doTheTest(U2);
1471}
1472
1473test "undefined-layout union field pointer has correct alignment" {
1474 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1475 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1476 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1477
1478 const S = struct {
1479 fn doTheTest(comptime U: type) !void {
1480 var a: U = .{ .x = 123 };
1481 var b: U align(1) = .{ .x = 456 };
1482 var c: U align(64) = .{ .x = 789 };
1483
1484 const ap = &a.x;
1485 const bp = &b.x;
1486 const cp = &c.x;
1487
1488 comptime assert(@TypeOf(ap) == *u32);
1489 comptime assert(@TypeOf(bp) == *align(1) u32);
1490 comptime assert(@TypeOf(cp) == *u32); // undefined layout so does not inherit larger aligns
1491
1492 try expectEqual(@as(u32, 123), ap.*);
1493 try expectEqual(@as(u32, 456), bp.*);
1494 try expectEqual(@as(u32, 789), cp.*);
1495 }
1496 };
1497
1498 const U1 = union { x: u32 };
1499 const U2 = union(enum) { x: u32 };
1500
1501 try S.doTheTest(U1);
1502 try S.doTheTest(U2);
1503 try comptime S.doTheTest(U1);
1504 try comptime S.doTheTest(U2);
1505}
1506
1507test "packed union field pointer has correct alignment" {
1508 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1509 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1510 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1511 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
1512
1513 const U = packed union { x: u20 };
1514 const S = packed struct(u24) { a: u2, u: U, b: u2 };
1515
1516 var a: S = undefined;
1517 var b: S align(1) = undefined;
1518 var c: S align(64) = undefined;
1519
1520 const ap = &a.u.x;
1521 const bp = &b.u.x;
1522 const cp = &c.u.x;
1523
1524 const host_size = switch (builtin.zig_backend) {
1525 else => comptime std.math.divCeil(comptime_int, @bitSizeOf(S), 8) catch unreachable,
1526 .stage2_x86_64, .stage2_c => @sizeOf(S),
1527 };
1528 comptime assert(@TypeOf(ap) == *align(4:2:host_size) u20);
1529 comptime assert(@TypeOf(bp) == *align(1:2:host_size) u20);
1530 comptime assert(@TypeOf(cp) == *align(64:2:host_size) u20);
1531
1532 a.u = .{ .x = 123 };
1533 b.u = .{ .x = 456 };
1534 c.u = .{ .x = 789 };
1535
1536 try expectEqual(@as(u20, 123), ap.*);
1537 try expectEqual(@as(u20, 456), bp.*);
1538 try expectEqual(@as(u20, 789), cp.*);
1539}
1540
1541test "union with 128 bit integer" {
1542 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1543
1544 const ValueTag = enum { int, other };
1545
1546 const Value3 = union(ValueTag) {
1547 int: i128,
1548 other: bool,
1549 };
1550 var values: [2]Value3 = undefined;
1551 values[0] = .{ .int = 3 };
1552 values[1] = .{ .int = 4 };
1553
1554 var ok: usize = 0;
1555
1556 for (values) |val| {
1557 switch (val) {
1558 .int => ok += 1,
1559 else => return error.TestFailed,
1560 }
1561 }
1562}
1563
1564test "memset extern union" {
1565 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1566
1567 const U = extern union {
1568 foo: u8,
1569 bar: u32,
1570 };
1571
1572 const S = struct {
1573 fn doTheTest() !void {
1574 var u: U = undefined;
1575 @memset(std.mem.asBytes(&u), 0);
1576 try expectEqual(@as(u8, 0), u.foo);
1577 try expectEqual(@as(u32, 0), u.bar);
1578 }
1579 };
1580
1581 try comptime S.doTheTest();
1582 try S.doTheTest();
1583}
1584
1585test "memset packed union" {
1586 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1587
1588 const U = packed union {
1589 a: u32,
1590 b: u32,
1591 };
1592
1593 const S = struct {
1594 fn doTheTest() !void {
1595 var u: U = undefined;
1596 @memset(@as([]u8, @ptrCast(&u)), 42);
1597 try expectEqual(@as(u32, 0x2a2a2a2a), u.a);
1598 try expectEqual(@as(u32, 0x2a2a2a2a), u.b);
1599 }
1600 };
1601
1602 try comptime S.doTheTest();
1603
1604 try S.doTheTest();
1605}
1606
1607fn littleToNativeEndian(comptime T: type, v: T) T {
1608 return if (endian == .little) v else @byteSwap(v);
1609}
1610
1611test "reinterpret extern union" {
1612 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .hexagon) return error.SkipZigTest;
1613 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isRiscv32() and builtin.link_libc) return error.SkipZigTest;
1614 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch.isWasm()) return error.SkipZigTest;
1615
1616 if (true) {
1617 // https://github.com/ziglang/zig/issues/19389
1618 return error.SkipZigTest;
1619 }
1620
1621 const U = extern union {
1622 foo: u8,
1623 baz: u32 align(8),
1624 bar: u32,
1625 };
1626
1627 const S = struct {
1628 fn doTheTest() !void {
1629 {
1630 // Undefined initialization
1631 const u = blk: {
1632 var u: U = undefined;
1633 @memset(std.mem.asBytes(&u), 0);
1634 u.bar = 0xbbbbbbbb;
1635 u.foo = 0x2a;
1636 break :blk u;
1637 };
1638
1639 try expectEqual(@as(u8, 0x2a), u.foo);
1640 try expectEqual(littleToNativeEndian(u32, 0xbbbbbb2a), u.bar);
1641 try expectEqual(littleToNativeEndian(u32, 0xbbbbbb2a), u.baz);
1642 }
1643
1644 {
1645 // Union initialization
1646 var u: U = .{
1647 .foo = 0x2a,
1648 };
1649
1650 {
1651 const expected, const mask = switch (endian) {
1652 .little => .{ 0x2a, 0xff },
1653 .big => .{ 0x2a000000, 0xff000000 },
1654 };
1655
1656 try expectEqual(@as(u8, 0x2a), u.foo);
1657 try expectEqual(@as(u32, expected), u.bar & mask);
1658 try expectEqual(@as(u32, expected), u.baz & mask);
1659 }
1660
1661 // Writing to a larger field
1662 u.baz = 0xbbbbbbbb;
1663 try expectEqual(@as(u8, 0xbb), u.foo);
1664 try expectEqual(@as(u32, 0xbbbbbbbb), u.bar);
1665 try expectEqual(@as(u32, 0xbbbbbbbb), u.baz);
1666
1667 // Writing to the same field
1668 u.baz = 0xcccccccc;
1669 try expectEqual(@as(u8, 0xcc), u.foo);
1670 try expectEqual(@as(u32, 0xcccccccc), u.bar);
1671 try expectEqual(@as(u32, 0xcccccccc), u.baz);
1672
1673 // Writing to a smaller field
1674 u.foo = 0xdd;
1675 try expectEqual(@as(u8, 0xdd), u.foo);
1676 try expectEqual(littleToNativeEndian(u32, 0xccccccdd), u.bar);
1677 try expectEqual(littleToNativeEndian(u32, 0xccccccdd), u.baz);
1678 }
1679 }
1680 };
1681
1682 try comptime S.doTheTest();
1683 try S.doTheTest();
1684}
1685
1686test "reinterpret packed union" {
1687 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1688 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1689
1690 const U = packed union {
1691 foo: packed struct(u64) {
1692 a: u8,
1693 b: u56,
1694 },
1695 bar: packed struct(u64) {
1696 a: u29,
1697 b: u35,
1698 },
1699 baz: u64,
1700 qux: packed struct(u64) {
1701 a: u12,
1702 b: u52,
1703 },
1704 };
1705
1706 const S = struct {
1707 fn doTheTest() !void {
1708 {
1709 const u = blk: {
1710 var u: U = undefined;
1711 @memset(std.mem.asBytes(&u), 0);
1712 u.baz = 0xbbbbbbbb;
1713 u.qux.a = 0xe2a;
1714 break :blk u;
1715 };
1716
1717 try expectEqual(@as(u8, 0x2a), u.foo.a);
1718 try expectEqual(@as(u12, 0xe2a), u.qux.a);
1719
1720 // https://github.com/ziglang/zig/issues/17360
1721 if (@inComptime()) {
1722 try expectEqual(@as(u29, 0x1bbbbe2a), u.bar.a);
1723 try expectEqual(@as(u64, 0xbbbbbe2a), u.baz);
1724 }
1725 }
1726
1727 {
1728 // Union initialization
1729 var u: U = .{ .baz = 0 }; // ensure all bits are defined
1730 u.qux.a = 0xe2a;
1731 try expectEqual(@as(u8, 0x2a), u.foo.a);
1732 try expectEqual(@as(u12, 0xe2a), u.qux.a);
1733 try expectEqual(@as(u29, 0xe2a), u.bar.a & 0xfff);
1734 try expectEqual(@as(u64, 0xe2a), u.baz & 0xfff);
1735
1736 // Writing to a larger field
1737 u.baz = 0xbbbbbbbb;
1738 try expectEqual(@as(u8, 0xbb), u.foo.a);
1739 try expectEqual(@as(u12, 0xbbb), u.qux.a);
1740 try expectEqual(@as(u29, 0x1bbbbbbb), u.bar.a);
1741 try expectEqual(@as(u64, 0xbbbbbbbb), u.baz);
1742
1743 // Writing to the same field
1744 u.baz = 0xcccccccc;
1745 try expectEqual(@as(u8, 0xcc), u.foo.a);
1746 try expectEqual(@as(u12, 0xccc), u.qux.a);
1747 try expectEqual(@as(u29, 0x0ccccccc), u.bar.a);
1748 try expectEqual(@as(u64, 0xcccccccc), u.baz);
1749
1750 // Writing to a smaller field
1751 u.foo.a = 0xdd;
1752 try expectEqual(@as(u8, 0xdd), u.foo.a);
1753 try expectEqual(@as(u12, 0xcdd), u.qux.a);
1754 try expectEqual(@as(u29, 0x0cccccdd), u.bar.a);
1755 try expectEqual(@as(u64, 0xccccccdd), u.baz);
1756 }
1757 }
1758 };
1759
1760 try comptime S.doTheTest();
1761 try S.doTheTest();
1762}
1763
1764test "reinterpret packed union inside packed struct" {
1765 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1766 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1767 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
1768
1769 const U = packed union {
1770 a: u7,
1771 b: packed struct(u7) {
1772 a: u1,
1773 b: u6,
1774 },
1775 };
1776
1777 const V = packed struct {
1778 lo: U,
1779 hi: U,
1780 };
1781
1782 const S = struct {
1783 fn doTheTest() !void {
1784 var v: V = undefined;
1785 @memset(@as([]u8, @ptrCast(&v)), 0x55);
1786 try expect(@as(u7, 0x55) == v.lo.a);
1787 try expect(@as(u1, 1) == v.lo.b.a);
1788 try expect(@as(u7, 0x2a) == v.hi.a);
1789 try expect(@as(u1, 0) == v.hi.b.a);
1790
1791 v.lo.b.a = 0;
1792 try expect(@as(u7, 0x54) == v.lo.a);
1793 try expect(@as(u1, 0) == v.lo.b.a);
1794 v.hi.b.a = 1;
1795 try expect(@as(u7, 0x2b) == v.hi.a);
1796 try expect(@as(u1, 1) == v.hi.b.a);
1797 }
1798 };
1799
1800 try comptime S.doTheTest();
1801 try S.doTheTest();
1802}
1803
1804test "inner struct initializer uses union layout" {
1805 const namespace = struct {
1806 const U = union {
1807 a: struct {
1808 x: u32 = @alignOf(U) + 1,
1809 },
1810 b: struct {
1811 y: u16 = @sizeOf(U) + 2,
1812 },
1813 };
1814 };
1815
1816 {
1817 const u: namespace.U = .{ .a = .{} };
1818 try expectEqual(4, @alignOf(namespace.U));
1819 try expectEqual(@as(usize, 5), u.a.x);
1820 }
1821
1822 {
1823 const u: namespace.U = .{ .b = .{} };
1824 try expectEqual(@as(usize, @sizeOf(namespace.U) + 2), u.b.y);
1825 }
1826}
1827
1828test "inner struct initializer uses packed union layout" {
1829 const namespace = struct {
1830 const U = packed union {
1831 a: packed struct {
1832 x: u32 = @alignOf(U) + 1,
1833 },
1834 b: packed struct(u32) {
1835 y: u16 = @sizeOf(U) + 2,
1836 padding: u16 = 0,
1837 },
1838 };
1839 };
1840
1841 {
1842 const u: namespace.U = .{ .a = .{} };
1843 try expectEqual(4, @alignOf(namespace.U));
1844 try expectEqual(@as(usize, 5), u.a.x);
1845 }
1846
1847 {
1848 const u: namespace.U = .{ .b = .{} };
1849 try expectEqual(@as(usize, @sizeOf(namespace.U) + 2), u.b.y);
1850 }
1851}
1852
1853test "extern union initialized via reintepreted struct field initializer" {
1854 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1855
1856 const bytes = [_]u8{ 0xaa, 0xbb, 0xcc, 0xdd };
1857
1858 const U = extern union {
1859 a: u32,
1860 b: u8,
1861 };
1862
1863 const S = extern struct {
1864 u: U = @as(*align(1) const U, @ptrCast(&bytes)).*,
1865 };
1866
1867 const s: S = .{};
1868 try expect(s.u.a == littleToNativeEndian(u32, 0xddccbbaa));
1869 try expect(s.u.b == 0xaa);
1870}
1871
1872test "packed union initialized via reintepreted struct field initializer" {
1873 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1874 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1875
1876 const bytes = [_]u8{ 0xaa, 0xbb, 0xcc, 0xdd };
1877
1878 const U = packed union {
1879 a: u32,
1880 b: packed struct(u32) {
1881 a: u8,
1882 b: u24,
1883 },
1884 };
1885
1886 const S = packed struct {
1887 u: U = @as(*align(1) const U, @ptrCast(&bytes)).*,
1888 };
1889
1890 var s: S = .{};
1891 _ = &s;
1892 try expect(s.u.a == littleToNativeEndian(u32, 0xddccbbaa));
1893 try expect(s.u.b.a == if (endian == .little) 0xaa else 0xdd);
1894}
1895
1896test "store of comptime reinterpreted memory to extern union" {
1897 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1898
1899 const bytes = [_]u8{ 0xaa, 0xbb, 0xcc, 0xdd };
1900
1901 const U = extern union {
1902 a: u32,
1903 b: u8,
1904 };
1905
1906 const reinterpreted = comptime b: {
1907 var u: U = undefined;
1908 u = @as(*align(1) const U, @ptrCast(&bytes)).*;
1909 break :b u;
1910 };
1911
1912 var u: U = reinterpreted;
1913 _ = &u;
1914 try expect(u.a == littleToNativeEndian(u32, 0xddccbbaa));
1915 try expect(u.b == 0xaa);
1916}
1917
1918test "store of comptime reinterpreted memory to packed union" {
1919 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1920 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1921
1922 const bytes = [_]u8{ 0xaa, 0xbb, 0xcc, 0xdd };
1923
1924 const U = packed union {
1925 a: u32,
1926 b: packed struct(u32) {
1927 a: u8,
1928 b: u24,
1929 },
1930 };
1931
1932 const reinterpreted = comptime b: {
1933 var u: U = undefined;
1934 u = @as(*align(1) const U, @ptrCast(&bytes)).*;
1935 break :b u;
1936 };
1937
1938 var u: U = reinterpreted;
1939 _ = &u;
1940 try expect(u.a == littleToNativeEndian(u32, 0xddccbbaa));
1941 try expect(u.b.a == if (endian == .little) 0xaa else 0xdd);
1942}
1943
1944test "union field is a pointer to an aligned version of itself" {
1945 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1946 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1947
1948 const E = union {
1949 next: *align(1) @This(),
1950 };
1951 var e: E = undefined;
1952 e = .{ .next = &e };
1953
1954 try expect(&e == e.next);
1955}
1956
1957test "pass register-sized field as non-register-sized union" {
1958 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1959 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1960 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1961
1962 const S = struct {
1963 fn taggedUnion(u: union(enum) { x: usize, y: [2]usize }) !void {
1964 try expectEqual(@as(usize, 42), u.x);
1965 }
1966
1967 fn untaggedUnion(u: union { x: usize, y: [2]usize }) !void {
1968 try expectEqual(@as(usize, 42), u.x);
1969 }
1970
1971 fn externUnion(u: extern union { x: usize, y: [2]usize }) !void {
1972 try expectEqual(@as(usize, 42), u.x);
1973 }
1974 };
1975
1976 var x: usize = 42;
1977 _ = &x;
1978 try S.taggedUnion(.{ .x = x });
1979 try S.untaggedUnion(.{ .x = x });
1980 try S.externUnion(.{ .x = x });
1981}
1982
1983test "circular dependency through pointer field of a union" {
1984 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1985
1986 const S = struct {
1987 const UnionInner = extern struct {
1988 outer: UnionOuter = std.mem.zeroes(UnionOuter),
1989 };
1990
1991 const UnionMiddle = extern union {
1992 outer: ?*UnionOuter,
1993 inner: ?*UnionInner,
1994 };
1995
1996 const UnionOuter = extern struct {
1997 u: UnionMiddle = std.mem.zeroes(UnionMiddle),
1998 };
1999 };
2000 var outer: S.UnionOuter = .{};
2001 _ = &outer;
2002 try expect(outer.u.outer == null);
2003 try expect(outer.u.inner == null);
2004}
2005
2006test "pass nested union with rls" {
2007 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
2008 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
2009
2010 const Union = union(enum) {
2011 a: u32,
2012 b: union(enum) {
2013 c: u7,
2014 d: u3,
2015 },
2016
2017 fn getC(u: @This()) u7 {
2018 return u.b.c;
2019 }
2020 };
2021
2022 var c: u7 = 32;
2023 _ = &c;
2024 try expectEqual(@as(u7, 32), Union.getC(.{ .b = .{ .c = c } }));
2025}
2026
2027test "runtime union init, most-aligned field != largest" {
2028 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2029 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2030 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
2031 if (builtin.zig_backend == .stage2_c and builtin.target.abi == .msvc) return error.SkipZigTest;
2032
2033 const U = union(enum) {
2034 x: u128,
2035 y: [17]u8,
2036
2037 fn foo(val: @This()) !void {
2038 try expect(val.x == 1);
2039 }
2040 };
2041 var x: u8 = 1;
2042 _ = &x;
2043 try U.foo(.{ .x = x });
2044
2045 const val: U = @unionInit(U, "x", x);
2046 try expect(val.x == 1);
2047
2048 const val2: U = .{ .x = x };
2049 try expect(val2.x == 1);
2050}
2051
2052test "copied union field doesn't alias source" {
2053 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2054 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2055 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
2056
2057 const U = union(enum) {
2058 array: [10]u32,
2059 other: u32,
2060 };
2061
2062 var x = U{ .array = undefined };
2063
2064 x.array[1] = 0;
2065 const a = x.array;
2066 x.array[1] = 15;
2067
2068 try expect(a[1] == 0);
2069}
2070
2071test "create union(enum) from other union(enum)" {
2072 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
2073 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2074 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
2075 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
2076
2077 const string = "hello world";
2078 const TempRef = struct {
2079 index: usize,
2080 is_weak: bool,
2081 };
2082 const BuiltinEnum = struct {
2083 name: []const u8,
2084 };
2085 const ParamType = union(enum) {
2086 boolean,
2087 buffer,
2088 one_of: BuiltinEnum,
2089 };
2090 const EnumLiteral = struct {
2091 label: []const u8,
2092 };
2093
2094 const ExpressionResult = union(enum) {
2095 temp_buffer: TempRef,
2096 literal_boolean: bool,
2097 literal_enum_value: []const u8,
2098
2099 fn commitCalleeParam(result: @This(), callee_param_type: ParamType) @This() {
2100 switch (callee_param_type) {
2101 .boolean => return result,
2102 .buffer => return .{
2103 .temp_buffer = .{ .index = 0, .is_weak = false },
2104 },
2105 .one_of => return result,
2106 }
2107 }
2108 };
2109 const Expression = union(enum) {
2110 literal_boolean: bool,
2111 literal_enum_value: EnumLiteral,
2112
2113 fn genExpression(expr: @This()) !ExpressionResult {
2114 switch (expr) {
2115 .literal_boolean => |value| return .{
2116 .literal_boolean = value,
2117 },
2118 .literal_enum_value => |v| {
2119 try std.testing.expectEqualStrings(string, v.label);
2120 const result: ExpressionResult = .{
2121 .literal_enum_value = v.label,
2122 };
2123 switch (result) {
2124 .literal_enum_value => |w| {
2125 try std.testing.expectEqualStrings(string, w);
2126 },
2127 else => {},
2128 }
2129 return result;
2130 },
2131 }
2132 }
2133 };
2134 const CallArg = struct {
2135 value: Expression,
2136 };
2137
2138 var param: ParamType = .{
2139 .one_of = .{ .name = "name" },
2140 };
2141 _ = &param;
2142 var arg: CallArg = .{
2143 .value = .{
2144 .literal_enum_value = .{
2145 .label = string,
2146 },
2147 },
2148 };
2149 _ = &arg;
2150
2151 const result = try arg.value.genExpression();
2152 switch (result) {
2153 .literal_enum_value => |w| {
2154 try std.testing.expectEqualStrings(string, w);
2155 },
2156 else => {},
2157 }
2158
2159 const derp = result.commitCalleeParam(param);
2160 switch (derp) {
2161 .literal_enum_value => |w| {
2162 try std.testing.expectEqualStrings(string, w);
2163 },
2164 else => {},
2165 }
2166}
2167
2168test "matching captures causes union equivalence" {
2169 const S = struct {
2170 fn SignedUnsigned(comptime I: type) type {
2171 const bits = @typeInfo(I).int.bits;
2172 return union {
2173 u: @Int(.unsigned, bits),
2174 i: @Int(.signed, bits),
2175 };
2176 }
2177 };
2178
2179 comptime assert(S.SignedUnsigned(u8) == S.SignedUnsigned(i8));
2180 comptime assert(S.SignedUnsigned(u16) == S.SignedUnsigned(i16));
2181 comptime assert(S.SignedUnsigned(u8) != S.SignedUnsigned(u16));
2182
2183 const a: S.SignedUnsigned(u8) = .{ .u = 10 };
2184 const b: S.SignedUnsigned(i8) = .{ .u = 10 };
2185 comptime assert(@TypeOf(a) == @TypeOf(b));
2186 try expect(a.u == b.u);
2187}
2188
2189test "signed enum tag with negative value" {
2190 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2191 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
2192
2193 const Enum = enum(i8) {
2194 a = -1,
2195 };
2196
2197 const Union = union(Enum) {
2198 a: i32,
2199 };
2200
2201 var i: i32 = 0;
2202 i = i;
2203 const e = Union{ .a = i };
2204
2205 try expect(e.a == i);
2206}
2207
2208test "union @FieldType" {
2209 const U = union {
2210 a: u32,
2211 b: f64,
2212 c: *@This(),
2213 };
2214
2215 comptime assert(@FieldType(U, "a") == u32);
2216 comptime assert(@FieldType(U, "b") == f64);
2217 comptime assert(@FieldType(U, "c") == *U);
2218}
2219
2220test "tagged union @FieldType" {
2221 const U = union(enum) {
2222 a: u32,
2223 b: f64,
2224 c: *@This(),
2225 };
2226
2227 comptime assert(@FieldType(U, "a") == u32);
2228 comptime assert(@FieldType(U, "b") == f64);
2229 comptime assert(@FieldType(U, "c") == *U);
2230}
2231
2232test "extern union @FieldType" {
2233 const U = extern union {
2234 a: u32,
2235 b: f64,
2236 c: *@This(),
2237 };
2238
2239 comptime assert(@FieldType(U, "a") == u32);
2240 comptime assert(@FieldType(U, "b") == f64);
2241 comptime assert(@FieldType(U, "c") == *U);
2242}
2243
2244test "assign global tagged union" {
2245 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
2246
2247 const U = union(enum) {
2248 a: u16,
2249 b: u32,
2250
2251 var global: @This() = undefined;
2252 };
2253
2254 U.global = .{ .a = 123 };
2255 try expect(U.global == .a);
2256 try expect(U.global != .b);
2257 try expect(U.global.a == 123);
2258
2259 U.global = .{ .b = 123456 };
2260 try expect(U.global != .a);
2261 try expect(U.global == .b);
2262 try expect(U.global.b == 123456);
2263}
2264
2265test "set mutable union by switching on same union" {
2266 const U = union(enum) {
2267 foo,
2268 bar: usize,
2269 };
2270
2271 var val: U = .foo;
2272 val = switch (val) {
2273 .foo => .{ .bar = 2 },
2274 .bar => .foo,
2275 };
2276
2277 try expect(val == .bar);
2278 try expect(val.bar == 2);
2279}
2280
2281test "initialize empty field of union inside comptime-known struct constant" {
2282 const Inner = union { none: void, some: u8 };
2283 const Wrapper = struct { inner: Inner };
2284
2285 const val: Wrapper = .{ .inner = .{ .none = {} } };
2286 comptime assert(val.inner.none == {});
2287}
2288
2289test "union with function body field" {
2290 const U = union {
2291 f: fn () void,
2292 fn foo() void {}
2293 fn bar() void {}
2294 };
2295 const x: U = .{ .f = U.foo };
2296 try std.testing.expect(x.f == U.foo);
2297 x.f();
2298
2299 comptime var y: U = .{ .f = U.bar };
2300 try std.testing.expect(y.f == U.bar);
2301 y.f();
2302 y.f = U.foo;
2303 try std.testing.expect(y.f == U.foo);
2304 y.f();
2305}