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