Commit efb7148a45

Andrew Kelley <andrew@ziglang.org>
2021-12-29 07:10:48
Sema: more union fixes
* `Module.Union.getLayout`: fixes to support components of the union being 0 bits. * Implement `@typeInfo` for unions. * Add missing calls to `resolveTypeFields`. * Fix explicitly-provided union tag types passing a `Zir.Inst.Ref` where an `Air.Inst.Ref` was expected. We don't have any type safety for this; these typess are aliases. * Fix explicitly-provided `union(enum)` tag Values allocated to the wrong arena.
1 parent 91619cd
src/Module.zig
@@ -1104,9 +1104,9 @@ pub const Union = struct {
 
     pub fn getLayout(u: Union, target: Target, have_tag: bool) Layout {
         assert(u.status == .have_layout);
-        var most_aligned_field: usize = undefined;
+        var most_aligned_field: u32 = undefined;
         var most_aligned_field_size: u64 = undefined;
-        var biggest_field: usize = undefined;
+        var biggest_field: u32 = undefined;
         var payload_size: u64 = 0;
         var payload_align: u32 = 0;
         for (u.fields.values()) |field, i| {
@@ -1122,20 +1122,21 @@ pub const Union = struct {
             const field_size = field.ty.abiSize(target);
             if (field_size > payload_size) {
                 payload_size = field_size;
-                biggest_field = i;
+                biggest_field = @intCast(u32, i);
             }
             if (field_align > payload_align) {
                 payload_align = field_align;
-                most_aligned_field = i;
+                most_aligned_field = @intCast(u32, i);
                 most_aligned_field_size = field_size;
             }
         }
+        payload_align = @maximum(payload_align, 1);
         if (!have_tag) return .{
             .abi_size = std.mem.alignForwardGeneric(u64, payload_size, payload_align),
             .abi_align = payload_align,
-            .most_aligned_field = @intCast(u32, most_aligned_field),
+            .most_aligned_field = most_aligned_field,
             .most_aligned_field_size = most_aligned_field_size,
-            .biggest_field = @intCast(u32, biggest_field),
+            .biggest_field = biggest_field,
             .payload_size = payload_size,
             .payload_align = payload_align,
             .tag_align = 0,
@@ -1144,7 +1145,7 @@ pub const Union = struct {
         // Put the tag before or after the payload depending on which one's
         // alignment is greater.
         const tag_size = u.tag_ty.abiSize(target);
-        const tag_align = u.tag_ty.abiAlignment(target);
+        const tag_align = @maximum(1, u.tag_ty.abiAlignment(target));
         var size: u64 = 0;
         if (tag_align >= payload_align) {
             // {Tag, Payload}
@@ -1162,9 +1163,9 @@ pub const Union = struct {
         return .{
             .abi_size = size,
             .abi_align = @maximum(tag_align, payload_align),
-            .most_aligned_field = @intCast(u32, most_aligned_field),
+            .most_aligned_field = most_aligned_field,
             .most_aligned_field_size = most_aligned_field_size,
-            .biggest_field = @intCast(u32, biggest_field),
+            .biggest_field = biggest_field,
             .payload_size = payload_size,
             .payload_align = payload_align,
             .tag_align = tag_align,
src/Sema.zig
@@ -5799,8 +5799,12 @@ fn zirSwitchCond(
 ) CompileError!Air.Inst.Ref {
     const inst_data = sema.code.instructions.items(.data)[inst].un_node;
     const src = inst_data.src();
+    const operand_src = src; // TODO make this point at the switch operand
     const operand_ptr = sema.resolveInst(inst_data.operand);
-    const operand = if (is_ref) try sema.analyzeLoad(block, src, operand_ptr, src) else operand_ptr;
+    const operand = if (is_ref)
+        try sema.analyzeLoad(block, src, operand_ptr, operand_src)
+    else
+        operand_ptr;
     const operand_ty = sema.typeOf(operand);
 
     switch (operand_ty.zigTypeTag()) {
@@ -5817,18 +5821,19 @@ fn zirSwitchCond(
         .ErrorSet,
         .Enum,
         => {
-            if ((try sema.typeHasOnePossibleValue(block, src, operand_ty))) |opv| {
+            if ((try sema.typeHasOnePossibleValue(block, operand_src, operand_ty))) |opv| {
                 return sema.addConstant(operand_ty, opv);
             }
             return operand;
         },
 
         .Union => {
-            const enum_ty = operand_ty.unionTagType() orelse {
+            const union_ty = try sema.resolveTypeFields(block, operand_src, operand_ty);
+            const enum_ty = union_ty.unionTagType() orelse {
                 const msg = msg: {
                     const msg = try sema.errMsg(block, src, "switch on untagged union", .{});
                     errdefer msg.destroy(sema.gpa);
-                    try sema.addDeclaredHereNote(msg, operand_ty);
+                    try sema.addDeclaredHereNote(msg, union_ty);
                     break :msg msg;
                 };
                 return sema.failWithOwnedErrorMsg(msg);
@@ -9154,9 +9159,107 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 }),
             );
         },
+        .Union => {
+            // TODO: look into memoizing this result.
+
+            var fields_anon_decl = try block.startAnonDecl();
+            defer fields_anon_decl.deinit();
+
+            const union_field_ty = t: {
+                const union_field_ty_decl = (try sema.namespaceLookup(
+                    block,
+                    src,
+                    type_info_ty.getNamespace().?,
+                    "UnionField",
+                )).?;
+                try sema.mod.declareDeclDependency(sema.owner_decl, union_field_ty_decl);
+                try sema.ensureDeclAnalyzed(union_field_ty_decl);
+                var buffer: Value.ToTypeBuffer = undefined;
+                break :t try union_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena());
+            };
+
+            const union_ty = try sema.resolveTypeFields(block, src, ty);
+            const union_fields = union_ty.unionFields();
+            const union_field_vals = try fields_anon_decl.arena().alloc(Value, union_fields.count());
+
+            for (union_field_vals) |*field_val, i| {
+                const field = union_fields.values()[i];
+                const name = union_fields.keys()[i];
+                const name_val = v: {
+                    var anon_decl = try block.startAnonDecl();
+                    defer anon_decl.deinit();
+                    const bytes = try anon_decl.arena().dupeZ(u8, name);
+                    const new_decl = try anon_decl.finish(
+                        try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), bytes.len),
+                        try Value.Tag.bytes.create(anon_decl.arena(), bytes[0 .. bytes.len + 1]),
+                    );
+                    break :v try Value.Tag.decl_ref.create(fields_anon_decl.arena(), new_decl);
+                };
+
+                const union_field_fields = try fields_anon_decl.arena().create([3]Value);
+                union_field_fields.* = .{
+                    // name: []const u8,
+                    name_val,
+                    // field_type: type,
+                    try Value.Tag.ty.create(fields_anon_decl.arena(), field.ty),
+                    // alignment: comptime_int,
+                    try field.abi_align.copy(fields_anon_decl.arena()),
+                };
+                field_val.* = try Value.Tag.@"struct".create(fields_anon_decl.arena(), union_field_fields);
+            }
+
+            const fields_val = v: {
+                const new_decl = try fields_anon_decl.finish(
+                    try Type.Tag.array.create(fields_anon_decl.arena(), .{
+                        .len = union_field_vals.len,
+                        .elem_type = union_field_ty,
+                    }),
+                    try Value.Tag.array.create(
+                        fields_anon_decl.arena(),
+                        try fields_anon_decl.arena().dupe(Value, union_field_vals),
+                    ),
+                );
+                break :v try Value.Tag.decl_ref.create(sema.arena, new_decl);
+            };
+
+            if (ty.getNamespace()) |namespace| {
+                if (namespace.decls.count() != 0) {
+                    return sema.fail(block, src, "TODO: implement zirTypeInfo for Union which has declarations", .{});
+                }
+            }
+            const decls_val = Value.initTag(.empty_array);
+
+            const enum_tag_ty_val = if (union_ty.unionTagType()) |tag_ty| v: {
+                const ty_val = try Value.Tag.ty.create(sema.arena, tag_ty);
+                break :v try Value.Tag.opt_payload.create(sema.arena, ty_val);
+            } else Value.@"null";
+
+            const field_values = try sema.arena.create([4]Value);
+            field_values.* = .{
+                // layout: ContainerLayout,
+                try Value.Tag.enum_field_index.create(
+                    sema.arena,
+                    @enumToInt(std.builtin.TypeInfo.ContainerLayout.Auto),
+                ),
+
+                // tag_type: ?type,
+                enum_tag_ty_val,
+                // fields: []const UnionField,
+                fields_val,
+                // decls: []const Declaration,
+                decls_val,
+            };
+
+            return sema.addConstant(
+                type_info_ty,
+                try Value.Tag.@"union".create(sema.arena, .{
+                    .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Union)),
+                    .val = try Value.Tag.@"struct".create(sema.arena, field_values),
+                }),
+            );
+        },
         .Struct => return sema.fail(block, src, "TODO: implement zirTypeInfo for Struct", .{}),
         .ErrorSet => return sema.fail(block, src, "TODO: implement zirTypeInfo for ErrorSet", .{}),
-        .Union => return sema.fail(block, src, "TODO: implement zirTypeInfo for Union", .{}),
         .BoundFn => @panic("TODO remove this type from the language and compiler"),
         .Opaque => return sema.fail(block, src, "TODO: implement zirTypeInfo for Opaque", .{}),
         .Frame => return sema.fail(block, src, "TODO: implement zirTypeInfo for Frame", .{}),
@@ -11847,12 +11950,14 @@ fn fieldVal(
                     );
                 },
                 .Union => {
-                    if (child_type.getNamespace()) |namespace| {
+                    const union_ty = try sema.resolveTypeFields(block, src, child_type);
+
+                    if (union_ty.getNamespace()) |namespace| {
                         if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
                             return inst;
                         }
                     }
-                    if (child_type.unionTagType()) |enum_ty| {
+                    if (union_ty.unionTagType()) |enum_ty| {
                         if (enum_ty.enumFieldIndex(field_name)) |field_index_usize| {
                             const field_index = @intCast(u32, field_index_usize);
                             return sema.addConstant(
@@ -11861,7 +11966,7 @@ fn fieldVal(
                             );
                         }
                     }
-                    return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
+                    return sema.failWithBadMemberAccess(block, union_ty, field_name_src, field_name);
                 },
                 .Enum => {
                     if (child_type.getNamespace()) |namespace| {
@@ -15185,8 +15290,9 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
             // The provided type is an integer type and we must construct the enum tag type here.
             int_tag_ty = provided_ty;
             union_obj.tag_ty = try sema.generateUnionTagTypeNumbered(&block_scope, fields_len, provided_ty);
-            enum_field_names = &union_obj.tag_ty.castTag(.enum_numbered).?.data.fields;
-            enum_value_map = &union_obj.tag_ty.castTag(.enum_numbered).?.data.values;
+            const enum_obj = union_obj.tag_ty.castTag(.enum_numbered).?.data;
+            enum_field_names = &enum_obj.fields;
+            enum_value_map = &enum_obj.values;
         } else {
             // The provided type is the enum tag type.
             union_obj.tag_ty = try provided_ty.copy(decl_arena_allocator);
@@ -15239,14 +15345,19 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
         const tag_ref: Zir.Inst.Ref = if (has_tag) blk: {
             const tag_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
             extra_index += 1;
-            break :blk tag_ref;
+            break :blk sema.resolveInst(tag_ref);
         } else .none;
 
         if (enum_value_map) |map| {
             const tag_src = src; // TODO better source location
             const coerced = try sema.coerce(&block_scope, int_tag_ty, tag_ref, tag_src);
             const val = try sema.resolveConstValue(&block_scope, tag_src, coerced);
-            map.putAssumeCapacityContext(val, {}, .{ .ty = int_tag_ty });
+
+            // This puts the memory into the union arena, not the enum arena, but
+            // it is OK since they share the same lifetime.
+            const copied_val = try val.copy(decl_arena_allocator);
+
+            map.putAssumeCapacityContext(copied_val, {}, .{ .ty = int_tag_ty });
         }
 
         // This string needs to outlive the ZIR code.
src/type.zig
@@ -2868,7 +2868,11 @@ pub const Type = extern union {
     /// Otherwise, returns `null`.
     pub fn unionTagType(ty: Type) ?Type {
         return switch (ty.tag()) {
-            .union_tagged => ty.castTag(.union_tagged).?.data.tag_ty,
+            .union_tagged => {
+                const union_obj = ty.castTag(.union_tagged).?.data;
+                assert(union_obj.haveFieldTypes());
+                return union_obj.tag_ty;
+            },
 
             .atomic_order,
             .atomic_rmw_op,
test/behavior/switch.zig
@@ -313,3 +313,16 @@ fn returnsFalse() bool {
 test "switch on const enum with var" {
     try expect(!returnsFalse());
 }
+
+test "anon enum literal used in switch on union enum" {
+    const Foo = union(enum) {
+        a: i32,
+    };
+
+    var foo = Foo{ .a = 1234 };
+    switch (foo) {
+        .a => |x| {
+            try expect(x == 1234);
+        },
+    }
+}
test/behavior/switch_stage1.zig
@@ -35,19 +35,6 @@ test "capture value of switch with all unreachable prongs" {
     try expect(x == 1);
 }
 
-test "anon enum literal used in switch on union enum" {
-    const Foo = union(enum) {
-        a: i32,
-    };
-
-    var foo = Foo{ .a = 1234 };
-    switch (foo) {
-        .a => |x| {
-            try expect(x == 1234);
-        },
-    }
-}
-
 test "else prong of switch on error set excludes other cases" {
     const S = struct {
         fn doTheTest() !void {
test/behavior/union.zig
@@ -221,6 +221,12 @@ fn testCastUnionToTag() !void {
     try expect(@as(TheTag, u) == TheTag.B);
 }
 
+test "union field access gives the enum values" {
+    try expect(TheUnion.A == TheTag.A);
+    try expect(TheUnion.B == TheTag.B);
+    try expect(TheUnion.C == TheTag.C);
+}
+
 test "cast tag type of union to union" {
     var x: Value2 = Letter2.B;
     try expect(@as(Letter2, x) == Letter2.B);
@@ -255,3 +261,202 @@ test "constant packed union" {
 fn testConstPackedUnion(expected_tokens: []const PackThis) !void {
     try expect(expected_tokens[0].StringLiteral == 1);
 }
+
+const MultipleChoice = union(enum(u32)) {
+    A = 20,
+    B = 40,
+    C = 60,
+    D = 1000,
+};
+test "simple union(enum(u32))" {
+    var x = MultipleChoice.C;
+    try expect(x == MultipleChoice.C);
+    try expect(@enumToInt(@as(Tag(MultipleChoice), x)) == 60);
+}
+
+const PackedPtrOrInt = packed union {
+    ptr: *u8,
+    int: u64,
+};
+test "packed union size" {
+    comptime try expect(@sizeOf(PackedPtrOrInt) == 8);
+}
+
+const ZeroBits = union {
+    OnlyField: void,
+};
+test "union with only 1 field which is void should be zero bits" {
+    comptime try expect(@sizeOf(ZeroBits) == 0);
+}
+
+test "tagged union initialization with runtime void" {
+    try expect(testTaggedUnionInit({}));
+}
+
+const TaggedUnionWithAVoid = union(enum) {
+    A,
+    B: i32,
+};
+
+fn testTaggedUnionInit(x: anytype) bool {
+    const y = TaggedUnionWithAVoid{ .A = x };
+    return @as(Tag(TaggedUnionWithAVoid), y) == TaggedUnionWithAVoid.A;
+}
+
+pub const UnionEnumNoPayloads = union(enum) { A, B };
+
+test "tagged union with no payloads" {
+    const a = UnionEnumNoPayloads{ .B = {} };
+    switch (a) {
+        Tag(UnionEnumNoPayloads).A => @panic("wrong"),
+        Tag(UnionEnumNoPayloads).B => {},
+    }
+}
+
+test "union with only 1 field casted to its enum type" {
+    const Literal = union(enum) {
+        Number: f64,
+        Bool: bool,
+    };
+
+    const Expr = union(enum) {
+        Literal: Literal,
+    };
+
+    var e = Expr{ .Literal = Literal{ .Bool = true } };
+    const ExprTag = Tag(Expr);
+    comptime try expect(Tag(ExprTag) == u0);
+    var t = @as(ExprTag, e);
+    try expect(t == Expr.Literal);
+}
+
+test "union with one member defaults to u0 tag type" {
+    const U0 = union(enum) {
+        X: u32,
+    };
+    comptime try expect(Tag(Tag(U0)) == u0);
+}
+
+const Foo1 = union(enum) {
+    f: struct {
+        x: usize,
+    },
+};
+var glbl: Foo1 = undefined;
+
+test "global union with single field is correctly initialized" {
+    glbl = Foo1{
+        .f = @typeInfo(Foo1).Union.fields[0].field_type{ .x = 123 },
+    };
+    try expect(glbl.f.x == 123);
+}
+
+pub const FooUnion = union(enum) {
+    U0: usize,
+    U1: u8,
+};
+
+var glbl_array: [2]FooUnion = undefined;
+
+test "initialize global array of union" {
+    glbl_array[1] = FooUnion{ .U1 = 2 };
+    glbl_array[0] = FooUnion{ .U0 = 1 };
+    try expect(glbl_array[0].U0 == 1);
+    try expect(glbl_array[1].U1 == 2);
+}
+
+test "update the tag value for zero-sized unions" {
+    const S = union(enum) {
+        U0: void,
+        U1: void,
+    };
+    var x = S{ .U0 = {} };
+    try expect(x == .U0);
+    x = S{ .U1 = {} };
+    try expect(x == .U1);
+}
+
+test "union initializer generates padding only if needed" {
+    const U = union(enum) {
+        A: u24,
+    };
+
+    var v = U{ .A = 532 };
+    try expect(v.A == 532);
+}
+
+test "runtime tag name with single field" {
+    const U = union(enum) {
+        A: i32,
+    };
+
+    var v = U{ .A = 42 };
+    try expect(std.mem.eql(u8, @tagName(v), "A"));
+}
+
+test "method call on an empty union" {
+    const S = struct {
+        const MyUnion = union(MyUnionTag) {
+            pub const MyUnionTag = enum { X1, X2 };
+            X1: [0]u8,
+            X2: [0]u8,
+
+            pub fn useIt(self: *@This()) bool {
+                _ = self;
+                return true;
+            }
+        };
+
+        fn doTheTest() !void {
+            var u = MyUnion{ .X1 = [0]u8{} };
+            try expect(u.useIt());
+        }
+    };
+    try S.doTheTest();
+    comptime try S.doTheTest();
+}
+
+const Point = struct {
+    x: u64,
+    y: u64,
+};
+const TaggedFoo = union(enum) {
+    One: i32,
+    Two: Point,
+    Three: void,
+};
+const FooNoVoid = union(enum) {
+    One: i32,
+    Two: Point,
+};
+const Baz = enum { A, B, C, D };
+
+test "tagged union type" {
+    const foo1 = TaggedFoo{ .One = 13 };
+    const foo2 = TaggedFoo{
+        .Two = Point{
+            .x = 1234,
+            .y = 5678,
+        },
+    };
+    try expect(foo1.One == 13);
+    try expect(foo2.Two.x == 1234 and foo2.Two.y == 5678);
+    const baz = Baz.B;
+
+    try expect(baz == Baz.B);
+    try expect(@typeInfo(TaggedFoo).Union.fields.len == 3);
+    try expect(@typeInfo(Baz).Enum.fields.len == 4);
+    try expect(@sizeOf(TaggedFoo) == @sizeOf(FooNoVoid));
+    try expect(@sizeOf(Baz) == 1);
+}
+
+test "tagged union as return value" {
+    switch (returnAnInt(13)) {
+        TaggedFoo.One => |value| try expect(value == 13),
+        else => unreachable,
+    }
+}
+
+fn returnAnInt(x: i32) TaggedFoo {
+    return TaggedFoo{ .One = x };
+}
test/behavior/union_stage1.zig
@@ -3,18 +3,6 @@ const expect = std.testing.expect;
 const expectEqual = std.testing.expectEqual;
 const Tag = std.meta.Tag;
 
-const MultipleChoice = union(enum(u32)) {
-    A = 20,
-    B = 40,
-    C = 60,
-    D = 1000,
-};
-test "simple union(enum(u32))" {
-    var x = MultipleChoice.C;
-    try expect(x == MultipleChoice.C);
-    try expect(@enumToInt(@as(Tag(MultipleChoice), x)) == 60);
-}
-
 const MultipleChoice2 = union(enum(u32)) {
     Unspecified1: i32,
     A: f32 = 20,
@@ -48,33 +36,6 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void {
     });
 }
 
-const PackedPtrOrInt = packed union {
-    ptr: *u8,
-    int: u64,
-};
-test "packed union size" {
-    comptime try expect(@sizeOf(PackedPtrOrInt) == 8);
-}
-
-const ZeroBits = union {
-    OnlyField: void,
-};
-test "union with only 1 field which is void should be zero bits" {
-    comptime try expect(@sizeOf(ZeroBits) == 0);
-}
-
-const TheTag = enum { A, B, C };
-const TheUnion = union(TheTag) {
-    A: i32,
-    B: i32,
-    C: i32,
-};
-test "union field access gives the enum values" {
-    try expect(TheUnion.A == TheTag.A);
-    try expect(TheUnion.B == TheTag.B);
-    try expect(TheUnion.C == TheTag.C);
-}
-
 test "switch on union with only 1 field" {
     var r: PartialInst = undefined;
     r = PartialInst.Compiled;
@@ -101,47 +62,6 @@ const PartialInstWithPayload = union(enum) {
     Compiled: i32,
 };
 
-test "tagged union initialization with runtime void" {
-    try expect(testTaggedUnionInit({}));
-}
-
-const TaggedUnionWithAVoid = union(enum) {
-    A,
-    B: i32,
-};
-
-fn testTaggedUnionInit(x: anytype) bool {
-    const y = TaggedUnionWithAVoid{ .A = x };
-    return @as(Tag(TaggedUnionWithAVoid), y) == TaggedUnionWithAVoid.A;
-}
-
-pub const UnionEnumNoPayloads = union(enum) { A, B };
-
-test "tagged union with no payloads" {
-    const a = UnionEnumNoPayloads{ .B = {} };
-    switch (a) {
-        Tag(UnionEnumNoPayloads).A => @panic("wrong"),
-        Tag(UnionEnumNoPayloads).B => {},
-    }
-}
-
-test "union with only 1 field casted to its enum type" {
-    const Literal = union(enum) {
-        Number: f64,
-        Bool: bool,
-    };
-
-    const Expr = union(enum) {
-        Literal: Literal,
-    };
-
-    var e = Expr{ .Literal = Literal{ .Bool = true } };
-    const ExprTag = Tag(Expr);
-    comptime try expect(Tag(ExprTag) == u0);
-    var t = @as(ExprTag, e);
-    try expect(t == Expr.Literal);
-}
-
 test "union with only 1 field casted to its enum type which has enum value specified" {
     const Literal = union(enum) {
         Number: f64,
@@ -285,13 +205,6 @@ test "union no tag with struct member" {
     u.foo();
 }
 
-test "union with one member defaults to u0 tag type" {
-    const U0 = union(enum) {
-        X: u32,
-    };
-    comptime try expect(Tag(Tag(U0)) == u0);
-}
-
 test "union with comptime_int tag" {
     const Union = union(enum(comptime_int)) {
         X: u32,
@@ -311,34 +224,6 @@ test "extern union doesn't trigger field check at comptime" {
     comptime try expect(x.y == 0x55);
 }
 
-const Foo1 = union(enum) {
-    f: struct {
-        x: usize,
-    },
-};
-var glbl: Foo1 = undefined;
-
-test "global union with single field is correctly initialized" {
-    glbl = Foo1{
-        .f = @typeInfo(Foo1).Union.fields[0].field_type{ .x = 123 },
-    };
-    try expect(glbl.f.x == 123);
-}
-
-pub const FooUnion = union(enum) {
-    U0: usize,
-    U1: u8,
-};
-
-var glbl_array: [2]FooUnion = undefined;
-
-test "initialize global array of union" {
-    glbl_array[1] = FooUnion{ .U1 = 2 };
-    glbl_array[0] = FooUnion{ .U0 = 1 };
-    try expect(glbl_array[0].U0 == 1);
-    try expect(glbl_array[1].U1 == 2);
-}
-
 test "anonymous union literal syntax" {
     const S = struct {
         const Number = union {
@@ -361,17 +246,6 @@ test "anonymous union literal syntax" {
     comptime try S.doTheTest();
 }
 
-test "update the tag value for zero-sized unions" {
-    const S = union(enum) {
-        U0: void,
-        U1: void,
-    };
-    var x = S{ .U0 = {} };
-    try expect(x == .U0);
-    x = S{ .U1 = {} };
-    try expect(x == .U1);
-}
-
 test "function call result coerces from tagged union to the tag" {
     const S = struct {
         const Arch = union(enum) {
@@ -401,24 +275,6 @@ test "function call result coerces from tagged union to the tag" {
     comptime try S.doTheTest();
 }
 
-test "union initializer generates padding only if needed" {
-    const U = union(enum) {
-        A: u24,
-    };
-
-    var v = U{ .A = 532 };
-    try expect(v.A == 532);
-}
-
-test "runtime tag name with single field" {
-    const U = union(enum) {
-        A: i32,
-    };
-
-    var v = U{ .A = 42 };
-    try expect(std.mem.eql(u8, @tagName(v), "A"));
-}
-
 test "cast from anonymous struct to union" {
     const S = struct {
         const U = union(enum) {
@@ -473,28 +329,6 @@ test "cast from pointer to anonymous struct to pointer to union" {
     comptime try S.doTheTest();
 }
 
-test "method call on an empty union" {
-    const S = struct {
-        const MyUnion = union(MyUnionTag) {
-            pub const MyUnionTag = enum { X1, X2 };
-            X1: [0]u8,
-            X2: [0]u8,
-
-            pub fn useIt(self: *@This()) bool {
-                _ = self;
-                return true;
-            }
-        };
-
-        fn doTheTest() !void {
-            var u = MyUnion{ .X1 = [0]u8{} };
-            try expect(u.useIt());
-        }
-    };
-    try S.doTheTest();
-    comptime try S.doTheTest();
-}
-
 test "switching on non exhaustive union" {
     const S = struct {
         const E = enum(u8) {
@@ -590,48 +424,3 @@ test "anytype union field: issue #9233" {
     const Quux = union(enum) { bar: anytype };
     _ = Quux;
 }
-
-const Point = struct {
-    x: u64,
-    y: u64,
-};
-const TaggedFoo = union(enum) {
-    One: i32,
-    Two: Point,
-    Three: void,
-};
-const FooNoVoid = union(enum) {
-    One: i32,
-    Two: Point,
-};
-const Baz = enum { A, B, C, D };
-
-test "tagged union type" {
-    const foo1 = TaggedFoo{ .One = 13 };
-    const foo2 = TaggedFoo{
-        .Two = Point{
-            .x = 1234,
-            .y = 5678,
-        },
-    };
-    try expect(foo1.One == 13);
-    try expect(foo2.Two.x == 1234 and foo2.Two.y == 5678);
-    const baz = Baz.B;
-
-    try expect(baz == Baz.B);
-    try expect(@typeInfo(TaggedFoo).Union.fields.len == 3);
-    try expect(@typeInfo(Baz).Enum.fields.len == 4);
-    try expect(@sizeOf(TaggedFoo) == @sizeOf(FooNoVoid));
-    try expect(@sizeOf(Baz) == 1);
-}
-
-test "tagged union as return value" {
-    switch (returnAnInt(13)) {
-        TaggedFoo.One => |value| try expect(value == 13),
-        else => unreachable,
-    }
-}
-
-fn returnAnInt(x: i32) TaggedFoo {
-    return TaggedFoo{ .One = x };
-}