diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-12-28 23:10:48 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-12-28 23:22:09 -0700 |
| commit | efb7148a4574c608b21359fcbf2edf06afdb5e0c (patch) | |
| tree | 23e47a49f95e60ea315603407bd5793310978c62 /test/behavior | |
| parent | 91619cdf57f54accbdbb3ff616856eaf79b537a3 (diff) | |
| download | zig-efb7148a4574c608b21359fcbf2edf06afdb5e0c.tar.gz zig-efb7148a4574c608b21359fcbf2edf06afdb5e0c.zip | |
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.
Diffstat (limited to 'test/behavior')
| -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 |
4 files changed, 218 insertions, 224 deletions
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 }; -} |
