authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-10 00:24:52-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-10 00:27:02-07:00
logc10fdde5a64a46bc514500e97b8c87d19f86e431
tree0512ae6e4bd7082d6df656f53ee174b99fe7f49e
parent57357c43e3b56fd636cd08af591c50a08223b654

stage2: LLVM backend: make unnamed struct globals

LLVM union globals have to be lowered as unnamed structs if the non-most-aligned field is the active tag. In this case it bubbles up so that structs containing unions have the same restriction. This fix needs to be applied to optionals and other callsites of createNamedStruct. The bug fixed in this commit was revealed in searching for the cause of #10837.

4 files changed, 514 insertions(+), 433 deletions(-)

src/codegen/llvm.zig+34-11
...@@ -809,7 +809,16 @@ pub const DeclGen = struct {...@@ -809,7 +809,16 @@ pub const DeclGen = struct {
809 };809 };
810 }810 }
811811
812 fn llvmType(dg: *DeclGen, t: Type) Error!*const llvm.Type {812 fn isUnnamedType(dg: *DeclGen, ty: Type, val: *const llvm.Value) bool {
813 // Once `llvmType` succeeds, successive calls to it with the same Zig type
814 // are guaranteed to succeed. So if a call to `llvmType` fails here it means
815 // it is the first time lowering the type, which means the value can't possible
816 // have that type.
817 const llvm_ty = dg.llvmType(ty) catch return true;
818 return val.typeOf() != llvm_ty;
819 }
820
821 fn llvmType(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type {
813 const gpa = dg.gpa;822 const gpa = dg.gpa;
814 switch (t.zigTypeTag()) {823 switch (t.zigTypeTag()) {
815 .Void, .NoReturn => return dg.context.voidType(),824 .Void, .NoReturn => return dg.context.voidType(),
...@@ -1168,9 +1177,8 @@ pub const DeclGen = struct {...@@ -1168,9 +1177,8 @@ pub const DeclGen = struct {
11681177
1169 .BoundFn => @panic("TODO remove BoundFn from the language"),1178 .BoundFn => @panic("TODO remove BoundFn from the language"),
11701179
1171 .Frame,1180 .Frame => @panic("TODO implement llvmType for Frame types"),
1172 .AnyFrame,1181 .AnyFrame => @panic("TODO implement llvmType for AnyFrame types"),
1173 => return dg.todo("implement llvmType for type '{}'", .{t}),
1174 }1182 }
1175 }1183 }
11761184
...@@ -1299,7 +1307,8 @@ pub const DeclGen = struct {...@@ -1299,7 +1307,8 @@ pub const DeclGen = struct {
1299 llvm_u32.constInt(0, .False),1307 llvm_u32.constInt(0, .False),
1300 llvm_u32.constInt(field_ptr.field_index, .False),1308 llvm_u32.constInt(field_ptr.field_index, .False),
1301 };1309 };
1302 return parent_ptr.constInBoundsGEP(&indices, indices.len);1310 const uncasted = parent_ptr.constInBoundsGEP(&indices, indices.len);
1311 return uncasted.constBitCast(try dg.llvmType(tv.ty));
1303 },1312 },
1304 .elem_ptr => {1313 .elem_ptr => {
1305 const elem_ptr = tv.val.castTag(.elem_ptr).?.data;1314 const elem_ptr = tv.val.castTag(.elem_ptr).?.data;
...@@ -1463,6 +1472,7 @@ pub const DeclGen = struct {...@@ -1463,6 +1472,7 @@ pub const DeclGen = struct {
1463 var llvm_fields = try std.ArrayListUnmanaged(*const llvm.Value).initCapacity(gpa, llvm_field_count);1472 var llvm_fields = try std.ArrayListUnmanaged(*const llvm.Value).initCapacity(gpa, llvm_field_count);
1464 defer llvm_fields.deinit(gpa);1473 defer llvm_fields.deinit(gpa);
14651474
1475 var make_unnamed_struct = false;
1466 const struct_obj = tv.ty.castTag(.@"struct").?.data;1476 const struct_obj = tv.ty.castTag(.@"struct").?.data;
1467 if (struct_obj.layout == .Packed) {1477 if (struct_obj.layout == .Packed) {
1468 const target = dg.module.getTarget();1478 const target = dg.module.getTarget();
...@@ -1558,17 +1568,30 @@ pub const DeclGen = struct {...@@ -1558,17 +1568,30 @@ pub const DeclGen = struct {
1558 const field_ty = tv.ty.structFieldType(i);1568 const field_ty = tv.ty.structFieldType(i);
1559 if (!field_ty.hasRuntimeBits()) continue;1569 if (!field_ty.hasRuntimeBits()) continue;
15601570
1561 llvm_fields.appendAssumeCapacity(try dg.genTypedValue(.{1571 const field_llvm_val = try dg.genTypedValue(.{
1562 .ty = field_ty,1572 .ty = field_ty,
1563 .val = field_val,1573 .val = field_val,
1564 }));1574 });
1575
1576 make_unnamed_struct = make_unnamed_struct or
1577 dg.isUnnamedType(field_ty, field_llvm_val);
1578
1579 llvm_fields.appendAssumeCapacity(field_llvm_val);
1565 }1580 }
1566 }1581 }
15671582
1568 return llvm_struct_ty.constNamedStruct(1583 if (make_unnamed_struct) {
1569 llvm_fields.items.ptr,1584 return dg.context.constStruct(
1570 @intCast(c_uint, llvm_fields.items.len),1585 llvm_fields.items.ptr,
1571 );1586 @intCast(c_uint, llvm_fields.items.len),
1587 .False,
1588 );
1589 } else {
1590 return llvm_struct_ty.constNamedStruct(
1591 llvm_fields.items.ptr,
1592 @intCast(c_uint, llvm_fields.items.len),
1593 );
1594 }
1572 },1595 },
1573 .Union => {1596 .Union => {
1574 const llvm_union_ty = try dg.llvmType(tv.ty);1597 const llvm_union_ty = try dg.llvmType(tv.ty);
test/behavior.zig-1
...@@ -166,7 +166,6 @@ test {...@@ -166,7 +166,6 @@ test {
166 _ = @import("behavior/tuple.zig");166 _ = @import("behavior/tuple.zig");
167 _ = @import("behavior/type_stage1.zig");167 _ = @import("behavior/type_stage1.zig");
168 _ = @import("behavior/typename.zig");168 _ = @import("behavior/typename.zig");
169 _ = @import("behavior/union_stage1.zig");
170 _ = @import("behavior/union_with_members.zig");169 _ = @import("behavior/union_with_members.zig");
171 _ = @import("behavior/var_args.zig");170 _ = @import("behavior/var_args.zig");
172 _ = @import("behavior/vector.zig");171 _ = @import("behavior/vector.zig");
test/behavior/union.zig+480
...@@ -490,3 +490,483 @@ test "tagged union with all void fields but a meaningful tag" {...@@ -490,3 +490,483 @@ test "tagged union with all void fields but a meaningful tag" {
490 // TODO enable the test at comptime too490 // TODO enable the test at comptime too
491 //comptime try S.doTheTest();491 //comptime try S.doTheTest();
492}492}
493
494test "union(enum(u32)) with specified and unspecified tag values" {
495 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
496
497 comptime try expect(Tag(Tag(MultipleChoice2)) == u32);
498 try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 });
499 comptime try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 });
500}
501
502const MultipleChoice2 = union(enum(u32)) {
503 Unspecified1: i32,
504 A: f32 = 20,
505 Unspecified2: void,
506 B: bool = 40,
507 Unspecified3: i32,
508 C: i8 = 60,
509 Unspecified4: void,
510 D: void = 1000,
511 Unspecified5: i32,
512};
513
514fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void {
515 try expect(@enumToInt(@as(Tag(MultipleChoice2), x)) == 60);
516 try expect(1123 == switch (x) {
517 MultipleChoice2.A => 1,
518 MultipleChoice2.B => 2,
519 MultipleChoice2.C => |v| @as(i32, 1000) + v,
520 MultipleChoice2.D => 4,
521 MultipleChoice2.Unspecified1 => 5,
522 MultipleChoice2.Unspecified2 => 6,
523 MultipleChoice2.Unspecified3 => 7,
524 MultipleChoice2.Unspecified4 => 8,
525 MultipleChoice2.Unspecified5 => 9,
526 });
527}
528
529test "switch on union with only 1 field" {
530 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
531
532 var r: PartialInst = undefined;
533 r = PartialInst.Compiled;
534 switch (r) {
535 PartialInst.Compiled => {
536 var z: PartialInstWithPayload = undefined;
537 z = PartialInstWithPayload{ .Compiled = 1234 };
538 switch (z) {
539 PartialInstWithPayload.Compiled => |x| {
540 try expect(x == 1234);
541 return;
542 },
543 }
544 },
545 }
546 unreachable;
547}
548
549const PartialInst = union(enum) {
550 Compiled,
551};
552
553const PartialInstWithPayload = union(enum) {
554 Compiled: i32,
555};
556
557test "union with only 1 field casted to its enum type which has enum value specified" {
558 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
559
560 const Literal = union(enum) {
561 Number: f64,
562 Bool: bool,
563 };
564
565 const ExprTag = enum(comptime_int) {
566 Literal = 33,
567 };
568
569 const Expr = union(ExprTag) {
570 Literal: Literal,
571 };
572
573 var e = Expr{ .Literal = Literal{ .Bool = true } };
574 comptime try expect(Tag(ExprTag) == comptime_int);
575 var t = @as(ExprTag, e);
576 try expect(t == Expr.Literal);
577 try expect(@enumToInt(t) == 33);
578 comptime try expect(@enumToInt(t) == 33);
579}
580
581test "@enumToInt works on unions" {
582 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
583
584 const Bar = union(enum) {
585 A: bool,
586 B: u8,
587 C,
588 };
589
590 const a = Bar{ .A = true };
591 var b = Bar{ .B = undefined };
592 var c = Bar.C;
593 try expect(@enumToInt(a) == 0);
594 try expect(@enumToInt(b) == 1);
595 try expect(@enumToInt(c) == 2);
596}
597
598test "comptime union field value equality" {
599 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
600
601 const a0 = Setter(Attribute{ .A = false });
602 const a1 = Setter(Attribute{ .A = true });
603 const a2 = Setter(Attribute{ .A = false });
604
605 const b0 = Setter(Attribute{ .B = 5 });
606 const b1 = Setter(Attribute{ .B = 9 });
607 const b2 = Setter(Attribute{ .B = 5 });
608
609 try expect(a0 == a0);
610 try expect(a1 == a1);
611 try expect(a0 == a2);
612
613 try expect(b0 == b0);
614 try expect(b1 == b1);
615 try expect(b0 == b2);
616
617 try expect(a0 != b0);
618 try expect(a0 != a1);
619 try expect(b0 != b1);
620}
621
622const Attribute = union(enum) {
623 A: bool,
624 B: u8,
625};
626
627fn setAttribute(attr: Attribute) void {
628 _ = attr;
629}
630
631fn Setter(attr: Attribute) type {
632 return struct {
633 fn set() void {
634 setAttribute(attr);
635 }
636 };
637}
638
639test "return union init with void payload" {
640 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
641
642 const S = struct {
643 fn entry() !void {
644 try expect(func().state == State.one);
645 }
646 const Outer = union(enum) {
647 state: State,
648 };
649 const State = union(enum) {
650 one: void,
651 two: u32,
652 };
653 fn func() Outer {
654 return Outer{ .state = State{ .one = {} } };
655 }
656 };
657 try S.entry();
658 comptime try S.entry();
659}
660
661test "@unionInit can modify a union type" {
662 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
663
664 const UnionInitEnum = union(enum) {
665 Boolean: bool,
666 Byte: u8,
667 };
668
669 var value: UnionInitEnum = undefined;
670
671 value = @unionInit(UnionInitEnum, "Boolean", true);
672 try expect(value.Boolean == true);
673 value.Boolean = false;
674 try expect(value.Boolean == false);
675
676 value = @unionInit(UnionInitEnum, "Byte", 2);
677 try expect(value.Byte == 2);
678 value.Byte = 3;
679 try expect(value.Byte == 3);
680}
681
682test "@unionInit can modify a pointer value" {
683 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
684
685 const UnionInitEnum = union(enum) {
686 Boolean: bool,
687 Byte: u8,
688 };
689
690 var value: UnionInitEnum = undefined;
691 var value_ptr = &value;
692
693 value_ptr.* = @unionInit(UnionInitEnum, "Boolean", true);
694 try expect(value.Boolean == true);
695
696 value_ptr.* = @unionInit(UnionInitEnum, "Byte", 2);
697 try expect(value.Byte == 2);
698}
699
700test "union no tag with struct member" {
701 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
702
703 const Struct = struct {};
704 const Union = union {
705 s: Struct,
706 pub fn foo(self: *@This()) void {
707 _ = self;
708 }
709 };
710 var u = Union{ .s = Struct{} };
711 u.foo();
712}
713
714test "union with comptime_int tag" {
715 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
716
717 const Union = union(enum(comptime_int)) {
718 X: u32,
719 Y: u16,
720 Z: u8,
721 };
722 comptime try expect(Tag(Tag(Union)) == comptime_int);
723}
724
725test "extern union doesn't trigger field check at comptime" {
726 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
727
728 const U = extern union {
729 x: u32,
730 y: u8,
731 };
732
733 const x = U{ .x = 0x55AAAA55 };
734 comptime try expect(x.y == 0x55);
735}
736
737test "anonymous union literal syntax" {
738 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
739
740 const S = struct {
741 const Number = union {
742 int: i32,
743 float: f64,
744 };
745
746 fn doTheTest() !void {
747 var i: Number = .{ .int = 42 };
748 var f = makeNumber();
749 try expect(i.int == 42);
750 try expect(f.float == 12.34);
751 }
752
753 fn makeNumber() Number {
754 return .{ .float = 12.34 };
755 }
756 };
757 try S.doTheTest();
758 comptime try S.doTheTest();
759}
760
761test "function call result coerces from tagged union to the tag" {
762 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
763
764 const S = struct {
765 const Arch = union(enum) {
766 One,
767 Two: usize,
768 };
769
770 const ArchTag = Tag(Arch);
771
772 fn doTheTest() !void {
773 var x: ArchTag = getArch1();
774 try expect(x == .One);
775
776 var y: ArchTag = getArch2();
777 try expect(y == .Two);
778 }
779
780 pub fn getArch1() Arch {
781 return .One;
782 }
783
784 pub fn getArch2() Arch {
785 return .{ .Two = 99 };
786 }
787 };
788 try S.doTheTest();
789 comptime try S.doTheTest();
790}
791
792test "cast from anonymous struct to union" {
793 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
794
795 const S = struct {
796 const U = union(enum) {
797 A: u32,
798 B: []const u8,
799 C: void,
800 };
801 fn doTheTest() !void {
802 var y: u32 = 42;
803 const t0 = .{ .A = 123 };
804 const t1 = .{ .B = "foo" };
805 const t2 = .{ .C = {} };
806 const t3 = .{ .A = y };
807 const x0: U = t0;
808 var x1: U = t1;
809 const x2: U = t2;
810 var x3: U = t3;
811 try expect(x0.A == 123);
812 try expect(std.mem.eql(u8, x1.B, "foo"));
813 try expect(x2 == .C);
814 try expect(x3.A == y);
815 }
816 };
817 try S.doTheTest();
818 comptime try S.doTheTest();
819}
820
821test "cast from pointer to anonymous struct to pointer to union" {
822 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
823
824 const S = struct {
825 const U = union(enum) {
826 A: u32,
827 B: []const u8,
828 C: void,
829 };
830 fn doTheTest() !void {
831 var y: u32 = 42;
832 const t0 = &.{ .A = 123 };
833 const t1 = &.{ .B = "foo" };
834 const t2 = &.{ .C = {} };
835 const t3 = &.{ .A = y };
836 const x0: *const U = t0;
837 var x1: *const U = t1;
838 const x2: *const U = t2;
839 var x3: *const U = t3;
840 try expect(x0.A == 123);
841 try expect(std.mem.eql(u8, x1.B, "foo"));
842 try expect(x2.* == .C);
843 try expect(x3.A == y);
844 }
845 };
846 try S.doTheTest();
847 comptime try S.doTheTest();
848}
849
850test "switching on non exhaustive union" {
851 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
852
853 const S = struct {
854 const E = enum(u8) {
855 a,
856 b,
857 _,
858 };
859 const U = union(E) {
860 a: i32,
861 b: u32,
862 };
863 fn doTheTest() !void {
864 var a = U{ .a = 2 };
865 switch (a) {
866 .a => |val| try expect(val == 2),
867 .b => unreachable,
868 }
869 }
870 };
871 try S.doTheTest();
872 comptime try S.doTheTest();
873}
874
875test "containers with single-field enums" {
876 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
877
878 const S = struct {
879 const A = union(enum) { f1 };
880 const B = union(enum) { f1: void };
881 const C = struct { a: A };
882 const D = struct { a: B };
883
884 fn doTheTest() !void {
885 var array1 = [1]A{A{ .f1 = {} }};
886 var array2 = [1]B{B{ .f1 = {} }};
887 try expect(array1[0] == .f1);
888 try expect(array2[0] == .f1);
889
890 var struct1 = C{ .a = A{ .f1 = {} } };
891 var struct2 = D{ .a = B{ .f1 = {} } };
892 try expect(struct1.a == .f1);
893 try expect(struct2.a == .f1);
894 }
895 };
896
897 try S.doTheTest();
898 comptime try S.doTheTest();
899}
900
901test "@unionInit on union w/ tag but no fields" {
902 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
903
904 const S = struct {
905 const Type = enum(u8) { no_op = 105 };
906
907 const Data = union(Type) {
908 no_op: void,
909
910 pub fn decode(buf: []const u8) Data {
911 _ = buf;
912 return @unionInit(Data, "no_op", {});
913 }
914 };
915
916 comptime {
917 std.debug.assert(@sizeOf(Data) != 0);
918 }
919
920 fn doTheTest() !void {
921 var data: Data = .{ .no_op = .{} };
922 _ = data;
923 var o = Data.decode(&[_]u8{});
924 try expectEqual(Type.no_op, o);
925 }
926 };
927
928 try S.doTheTest();
929 comptime try S.doTheTest();
930}
931
932test "union enum type gets a separate scope" {
933 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
934
935 const S = struct {
936 const U = union(enum) {
937 a: u8,
938 const foo = 1;
939 };
940
941 fn doTheTest() !void {
942 try expect(!@hasDecl(Tag(U), "foo"));
943 }
944 };
945
946 try S.doTheTest();
947}
948
949test "global variable struct contains union initialized to non-most-aligned field" {
950 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
951 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
952
953 const T = struct {
954 const U = union(enum) {
955 a: i32,
956 b: f64,
957 };
958
959 const S = struct {
960 u: U,
961 };
962
963 var s: S = .{
964 .u = .{
965 .a = 3,
966 },
967 };
968 };
969
970 T.s.u.a += 1;
971 try expect(T.s.u.a == 4);
972}
test/behavior/union_stage1.zig deleted-421
...@@ -1,421 +0,0 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;
4const Tag = std.meta.Tag;
5
6const MultipleChoice2 = union(enum(u32)) {
7 Unspecified1: i32,
8 A: f32 = 20,
9 Unspecified2: void,
10 B: bool = 40,
11 Unspecified3: i32,
12 C: i8 = 60,
13 Unspecified4: void,
14 D: void = 1000,
15 Unspecified5: i32,
16};
17
18test "union(enum(u32)) with specified and unspecified tag values" {
19 comptime try expect(Tag(Tag(MultipleChoice2)) == u32);
20 try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 });
21 comptime try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 });
22}
23
24fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void {
25 try expect(@enumToInt(@as(Tag(MultipleChoice2), x)) == 60);
26 try expect(1123 == switch (x) {
27 MultipleChoice2.A => 1,
28 MultipleChoice2.B => 2,
29 MultipleChoice2.C => |v| @as(i32, 1000) + v,
30 MultipleChoice2.D => 4,
31 MultipleChoice2.Unspecified1 => 5,
32 MultipleChoice2.Unspecified2 => 6,
33 MultipleChoice2.Unspecified3 => 7,
34 MultipleChoice2.Unspecified4 => 8,
35 MultipleChoice2.Unspecified5 => 9,
36 });
37}
38
39test "switch on union with only 1 field" {
40 var r: PartialInst = undefined;
41 r = PartialInst.Compiled;
42 switch (r) {
43 PartialInst.Compiled => {
44 var z: PartialInstWithPayload = undefined;
45 z = PartialInstWithPayload{ .Compiled = 1234 };
46 switch (z) {
47 PartialInstWithPayload.Compiled => |x| {
48 try expect(x == 1234);
49 return;
50 },
51 }
52 },
53 }
54 unreachable;
55}
56
57const PartialInst = union(enum) {
58 Compiled,
59};
60
61const PartialInstWithPayload = union(enum) {
62 Compiled: i32,
63};
64
65test "union with only 1 field casted to its enum type which has enum value specified" {
66 const Literal = union(enum) {
67 Number: f64,
68 Bool: bool,
69 };
70
71 const ExprTag = enum(comptime_int) {
72 Literal = 33,
73 };
74
75 const Expr = union(ExprTag) {
76 Literal: Literal,
77 };
78
79 var e = Expr{ .Literal = Literal{ .Bool = true } };
80 comptime try expect(Tag(ExprTag) == comptime_int);
81 var t = @as(ExprTag, e);
82 try expect(t == Expr.Literal);
83 try expect(@enumToInt(t) == 33);
84 comptime try expect(@enumToInt(t) == 33);
85}
86
87test "@enumToInt works on unions" {
88 const Bar = union(enum) {
89 A: bool,
90 B: u8,
91 C,
92 };
93
94 const a = Bar{ .A = true };
95 var b = Bar{ .B = undefined };
96 var c = Bar.C;
97 try expect(@enumToInt(a) == 0);
98 try expect(@enumToInt(b) == 1);
99 try expect(@enumToInt(c) == 2);
100}
101
102const Attribute = union(enum) {
103 A: bool,
104 B: u8,
105};
106
107fn setAttribute(attr: Attribute) void {
108 _ = attr;
109}
110
111fn Setter(attr: Attribute) type {
112 return struct {
113 fn set() void {
114 setAttribute(attr);
115 }
116 };
117}
118
119test "comptime union field value equality" {
120 const a0 = Setter(Attribute{ .A = false });
121 const a1 = Setter(Attribute{ .A = true });
122 const a2 = Setter(Attribute{ .A = false });
123
124 const b0 = Setter(Attribute{ .B = 5 });
125 const b1 = Setter(Attribute{ .B = 9 });
126 const b2 = Setter(Attribute{ .B = 5 });
127
128 try expect(a0 == a0);
129 try expect(a1 == a1);
130 try expect(a0 == a2);
131
132 try expect(b0 == b0);
133 try expect(b1 == b1);
134 try expect(b0 == b2);
135
136 try expect(a0 != b0);
137 try expect(a0 != a1);
138 try expect(b0 != b1);
139}
140
141test "return union init with void payload" {
142 const S = struct {
143 fn entry() !void {
144 try expect(func().state == State.one);
145 }
146 const Outer = union(enum) {
147 state: State,
148 };
149 const State = union(enum) {
150 one: void,
151 two: u32,
152 };
153 fn func() Outer {
154 return Outer{ .state = State{ .one = {} } };
155 }
156 };
157 try S.entry();
158 comptime try S.entry();
159}
160
161test "@unionInit can modify a union type" {
162 const UnionInitEnum = union(enum) {
163 Boolean: bool,
164 Byte: u8,
165 };
166
167 var value: UnionInitEnum = undefined;
168
169 value = @unionInit(UnionInitEnum, "Boolean", true);
170 try expect(value.Boolean == true);
171 value.Boolean = false;
172 try expect(value.Boolean == false);
173
174 value = @unionInit(UnionInitEnum, "Byte", 2);
175 try expect(value.Byte == 2);
176 value.Byte = 3;
177 try expect(value.Byte == 3);
178}
179
180test "@unionInit can modify a pointer value" {
181 const UnionInitEnum = union(enum) {
182 Boolean: bool,
183 Byte: u8,
184 };
185
186 var value: UnionInitEnum = undefined;
187 var value_ptr = &value;
188
189 value_ptr.* = @unionInit(UnionInitEnum, "Boolean", true);
190 try expect(value.Boolean == true);
191
192 value_ptr.* = @unionInit(UnionInitEnum, "Byte", 2);
193 try expect(value.Byte == 2);
194}
195
196test "union no tag with struct member" {
197 const Struct = struct {};
198 const Union = union {
199 s: Struct,
200 pub fn foo(self: *@This()) void {
201 _ = self;
202 }
203 };
204 var u = Union{ .s = Struct{} };
205 u.foo();
206}
207
208test "union with comptime_int tag" {
209 const Union = union(enum(comptime_int)) {
210 X: u32,
211 Y: u16,
212 Z: u8,
213 };
214 comptime try expect(Tag(Tag(Union)) == comptime_int);
215}
216
217test "extern union doesn't trigger field check at comptime" {
218 const U = extern union {
219 x: u32,
220 y: u8,
221 };
222
223 const x = U{ .x = 0x55AAAA55 };
224 comptime try expect(x.y == 0x55);
225}
226
227test "anonymous union literal syntax" {
228 const S = struct {
229 const Number = union {
230 int: i32,
231 float: f64,
232 };
233
234 fn doTheTest() !void {
235 var i: Number = .{ .int = 42 };
236 var f = makeNumber();
237 try expect(i.int == 42);
238 try expect(f.float == 12.34);
239 }
240
241 fn makeNumber() Number {
242 return .{ .float = 12.34 };
243 }
244 };
245 try S.doTheTest();
246 comptime try S.doTheTest();
247}
248
249test "function call result coerces from tagged union to the tag" {
250 const S = struct {
251 const Arch = union(enum) {
252 One,
253 Two: usize,
254 };
255
256 const ArchTag = Tag(Arch);
257
258 fn doTheTest() !void {
259 var x: ArchTag = getArch1();
260 try expect(x == .One);
261
262 var y: ArchTag = getArch2();
263 try expect(y == .Two);
264 }
265
266 pub fn getArch1() Arch {
267 return .One;
268 }
269
270 pub fn getArch2() Arch {
271 return .{ .Two = 99 };
272 }
273 };
274 try S.doTheTest();
275 comptime try S.doTheTest();
276}
277
278test "cast from anonymous struct to union" {
279 const S = struct {
280 const U = union(enum) {
281 A: u32,
282 B: []const u8,
283 C: void,
284 };
285 fn doTheTest() !void {
286 var y: u32 = 42;
287 const t0 = .{ .A = 123 };
288 const t1 = .{ .B = "foo" };
289 const t2 = .{ .C = {} };
290 const t3 = .{ .A = y };
291 const x0: U = t0;
292 var x1: U = t1;
293 const x2: U = t2;
294 var x3: U = t3;
295 try expect(x0.A == 123);
296 try expect(std.mem.eql(u8, x1.B, "foo"));
297 try expect(x2 == .C);
298 try expect(x3.A == y);
299 }
300 };
301 try S.doTheTest();
302 comptime try S.doTheTest();
303}
304
305test "cast from pointer to anonymous struct to pointer to union" {
306 const S = struct {
307 const U = union(enum) {
308 A: u32,
309 B: []const u8,
310 C: void,
311 };
312 fn doTheTest() !void {
313 var y: u32 = 42;
314 const t0 = &.{ .A = 123 };
315 const t1 = &.{ .B = "foo" };
316 const t2 = &.{ .C = {} };
317 const t3 = &.{ .A = y };
318 const x0: *const U = t0;
319 var x1: *const U = t1;
320 const x2: *const U = t2;
321 var x3: *const U = t3;
322 try expect(x0.A == 123);
323 try expect(std.mem.eql(u8, x1.B, "foo"));
324 try expect(x2.* == .C);
325 try expect(x3.A == y);
326 }
327 };
328 try S.doTheTest();
329 comptime try S.doTheTest();
330}
331
332test "switching on non exhaustive union" {
333 const S = struct {
334 const E = enum(u8) {
335 a,
336 b,
337 _,
338 };
339 const U = union(E) {
340 a: i32,
341 b: u32,
342 };
343 fn doTheTest() !void {
344 var a = U{ .a = 2 };
345 switch (a) {
346 .a => |val| try expect(val == 2),
347 .b => unreachable,
348 }
349 }
350 };
351 try S.doTheTest();
352 comptime try S.doTheTest();
353}
354
355test "containers with single-field enums" {
356 const S = struct {
357 const A = union(enum) { f1 };
358 const B = union(enum) { f1: void };
359 const C = struct { a: A };
360 const D = struct { a: B };
361
362 fn doTheTest() !void {
363 var array1 = [1]A{A{ .f1 = {} }};
364 var array2 = [1]B{B{ .f1 = {} }};
365 try expect(array1[0] == .f1);
366 try expect(array2[0] == .f1);
367
368 var struct1 = C{ .a = A{ .f1 = {} } };
369 var struct2 = D{ .a = B{ .f1 = {} } };
370 try expect(struct1.a == .f1);
371 try expect(struct2.a == .f1);
372 }
373 };
374
375 try S.doTheTest();
376 comptime try S.doTheTest();
377}
378
379test "@unionInit on union w/ tag but no fields" {
380 const S = struct {
381 const Type = enum(u8) { no_op = 105 };
382
383 const Data = union(Type) {
384 no_op: void,
385
386 pub fn decode(buf: []const u8) Data {
387 _ = buf;
388 return @unionInit(Data, "no_op", {});
389 }
390 };
391
392 comptime {
393 std.debug.assert(@sizeOf(Data) != 0);
394 }
395
396 fn doTheTest() !void {
397 var data: Data = .{ .no_op = .{} };
398 _ = data;
399 var o = Data.decode(&[_]u8{});
400 try expectEqual(Type.no_op, o);
401 }
402 };
403
404 try S.doTheTest();
405 comptime try S.doTheTest();
406}
407
408test "union enum type gets a separate scope" {
409 const S = struct {
410 const U = union(enum) {
411 a: u8,
412 const foo = 1;
413 };
414
415 fn doTheTest() !void {
416 try expect(!@hasDecl(Tag(U), "foo"));
417 }
418 };
419
420 try S.doTheTest();
421}