diff options
| -rw-r--r-- | src/Module.zig | 19 | ||||
| -rw-r--r-- | src/Sema.zig | 135 | ||||
| -rw-r--r-- | src/type.zig | 6 | ||||
| -rw-r--r-- | test/behavior/switch.zig | 13 | ||||
| -rw-r--r-- | test/behavior/switch_stage1.zig | 13 | ||||
| -rw-r--r-- | test/behavior/union.zig | 205 | ||||
| -rw-r--r-- | test/behavior/union_stage1.zig | 211 |
7 files changed, 356 insertions, 246 deletions
diff --git a/src/Module.zig b/src/Module.zig index a1f804c282..ef01aa2c54 100644 --- a/src/Module.zig +++ b/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, diff --git a/src/Sema.zig b/src/Sema.zig index eca73a88e2..b65e88360f 100644 --- a/src/Sema.zig +++ b/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. diff --git a/src/type.zig b/src/type.zig index feea1fe2c1..5bcf310fc4 100644 --- a/src/type.zig +++ b/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, diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index 16bb890c9e..caec804ec2 100644 --- a/test/behavior/switch.zig +++ b/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); + }, + } +} diff --git a/test/behavior/switch_stage1.zig b/test/behavior/switch_stage1.zig index 1b85d767d5..267ac321b3 100644 --- a/test/behavior/switch_stage1.zig +++ b/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 { diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 05bd8070a2..325586d35c 100644 --- a/test/behavior/union.zig +++ b/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 }; +} diff --git a/test/behavior/union_stage1.zig b/test/behavior/union_stage1.zig index f5de087ce8..2eefa46705 100644 --- a/test/behavior/union_stage1.zig +++ b/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 }; -} |
