mirror of
https://github.com/ziglang/zig.git
synced 2024-11-27 15:42:49 +00:00
ada0010471
There are a couple concepts here worth understanding: Key.UnionType - This type is available *before* resolving the union's fields. The enum tag type, number of fields, and field names, field types, and field alignments are not available with this. InternPool.UnionType - This one can be obtained from the above type with `InternPool.loadUnionType` which asserts that the union's enum tag type has been resolved. This one has all the information available. Additionally: * ZIR: Turn an unused bit into `any_aligned_fields` flag to help semantic analysis know whether a union has explicit alignment on any fields (usually not). * Sema: delete `resolveTypeRequiresComptime` which had the same type signature and near-duplicate logic to `typeRequiresComptime`. - Make opaque types not report comptime-only (this was inconsistent between the two implementations of this function). * Implement accepted proposal #12556 which is a breaking change.
1691 lines
49 KiB
Zig
1691 lines
49 KiB
Zig
const builtin = @import("builtin");
|
|
const std = @import("std");
|
|
const expect = std.testing.expect;
|
|
const assert = std.debug.assert;
|
|
const expectEqual = std.testing.expectEqual;
|
|
const Tag = std.meta.Tag;
|
|
|
|
const FooWithFloats = union {
|
|
float: f64,
|
|
int: i32,
|
|
};
|
|
|
|
test "basic unions with floats" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
var foo = FooWithFloats{ .int = 1 };
|
|
try expect(foo.int == 1);
|
|
foo = FooWithFloats{ .float = 12.34 };
|
|
try expect(foo.float == 12.34);
|
|
}
|
|
|
|
fn setFloat(foo: *FooWithFloats, x: f64) void {
|
|
foo.* = FooWithFloats{ .float = x };
|
|
}
|
|
|
|
test "init union with runtime value - floats" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
var foo: FooWithFloats = undefined;
|
|
|
|
setFloat(&foo, 12.34);
|
|
try expect(foo.float == 12.34);
|
|
}
|
|
|
|
test "basic unions" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
var foo = Foo{ .int = 1 };
|
|
try expect(foo.int == 1);
|
|
foo = Foo{ .str = .{ .slice = "Hello!" } };
|
|
try expect(std.mem.eql(u8, foo.str.slice, "Hello!"));
|
|
}
|
|
|
|
const Foo = union {
|
|
int: i32,
|
|
str: struct {
|
|
slice: []const u8,
|
|
},
|
|
};
|
|
|
|
test "init union with runtime value" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
var foo: Foo = undefined;
|
|
|
|
setInt(&foo, 42);
|
|
try expect(foo.int == 42);
|
|
|
|
setStr(&foo, "Hello!");
|
|
try expect(std.mem.eql(u8, foo.str.slice, "Hello!"));
|
|
}
|
|
|
|
fn setInt(foo: *Foo, x: i32) void {
|
|
foo.* = Foo{ .int = x };
|
|
}
|
|
|
|
fn setStr(foo: *Foo, slice: []const u8) void {
|
|
foo.* = Foo{ .str = .{ .slice = slice } };
|
|
}
|
|
|
|
test "comptime union field access" {
|
|
comptime {
|
|
var foo = FooWithFloats{ .int = 0 };
|
|
try expect(foo.int == 0);
|
|
|
|
foo = FooWithFloats{ .float = 12.34 };
|
|
try expect(foo.float == 12.34);
|
|
}
|
|
}
|
|
|
|
const FooExtern = extern union {
|
|
int: i32,
|
|
str: extern struct {
|
|
slice: [*:0]const u8,
|
|
},
|
|
};
|
|
|
|
test "basic extern unions" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
var foo = FooExtern{ .int = 1 };
|
|
try expect(foo.int == 1);
|
|
foo.str.slice = "Well";
|
|
try expect(std.mem.eql(u8, std.mem.sliceTo(foo.str.slice, 0), "Well"));
|
|
}
|
|
|
|
const ExternPtrOrInt = extern union {
|
|
ptr: *u8,
|
|
int: u64,
|
|
};
|
|
test "extern union size" {
|
|
try comptime expect(@sizeOf(ExternPtrOrInt) == 8);
|
|
}
|
|
|
|
test "0-sized extern union definition" {
|
|
const U = extern union {
|
|
a: void,
|
|
const f = 1;
|
|
};
|
|
|
|
try expect(U.f == 1);
|
|
}
|
|
|
|
const Value = union(enum) {
|
|
Int: u64,
|
|
Array: [9]u8,
|
|
};
|
|
|
|
const Agg = struct {
|
|
val1: Value,
|
|
val2: Value,
|
|
};
|
|
|
|
const v1 = Value{ .Int = 1234 };
|
|
const v2 = Value{ .Array = [_]u8{3} ** 9 };
|
|
|
|
const err = @as(anyerror!Agg, Agg{
|
|
.val1 = v1,
|
|
.val2 = v2,
|
|
});
|
|
|
|
const array = [_]Value{ v1, v2, v1, v2 };
|
|
|
|
test "unions embedded in aggregate types" {
|
|
switch (array[1]) {
|
|
Value.Array => |arr| try expect(arr[4] == 3),
|
|
else => unreachable,
|
|
}
|
|
switch ((err catch unreachable).val1) {
|
|
Value.Int => |x| try expect(x == 1234),
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
test "access a member of tagged union with conflicting enum tag name" {
|
|
const Bar = union(enum) {
|
|
A: A,
|
|
B: B,
|
|
|
|
const A = u8;
|
|
const B = void;
|
|
};
|
|
|
|
try comptime expect(Bar.A == u8);
|
|
}
|
|
|
|
test "constant tagged union with payload" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
var empty = TaggedUnionWithPayload{ .Empty = {} };
|
|
var full = TaggedUnionWithPayload{ .Full = 13 };
|
|
shouldBeEmpty(empty);
|
|
shouldBeNotEmpty(full);
|
|
}
|
|
|
|
fn shouldBeEmpty(x: TaggedUnionWithPayload) void {
|
|
switch (x) {
|
|
TaggedUnionWithPayload.Empty => {},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
fn shouldBeNotEmpty(x: TaggedUnionWithPayload) void {
|
|
switch (x) {
|
|
TaggedUnionWithPayload.Empty => unreachable,
|
|
else => {},
|
|
}
|
|
}
|
|
|
|
const TaggedUnionWithPayload = union(enum) {
|
|
Empty: void,
|
|
Full: i32,
|
|
};
|
|
|
|
test "union alignment" {
|
|
comptime {
|
|
try expect(@alignOf(AlignTestTaggedUnion) >= @alignOf([9]u8));
|
|
try expect(@alignOf(AlignTestTaggedUnion) >= @alignOf(u64));
|
|
}
|
|
}
|
|
|
|
const AlignTestTaggedUnion = union(enum) {
|
|
A: [9]u8,
|
|
B: u64,
|
|
};
|
|
|
|
const Letter = enum { A, B, C };
|
|
const Payload = union(Letter) {
|
|
A: i32,
|
|
B: f64,
|
|
C: bool,
|
|
};
|
|
|
|
test "union with specified enum tag" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
try doTest();
|
|
try comptime doTest();
|
|
}
|
|
|
|
test "packed union generates correctly aligned type" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const U = packed union {
|
|
f1: *const fn () error{TestUnexpectedResult}!void,
|
|
f2: u32,
|
|
};
|
|
var foo = [_]U{
|
|
U{ .f1 = doTest },
|
|
U{ .f2 = 0 },
|
|
};
|
|
try foo[0].f1();
|
|
}
|
|
|
|
fn doTest() error{TestUnexpectedResult}!void {
|
|
try expect((try bar(Payload{ .A = 1234 })) == -10);
|
|
}
|
|
|
|
fn bar(value: Payload) error{TestUnexpectedResult}!i32 {
|
|
try expect(@as(Letter, value) == Letter.A);
|
|
return switch (value) {
|
|
Payload.A => |x| return x - 1244,
|
|
Payload.B => |x| if (x == 12.34) @as(i32, 20) else 21,
|
|
Payload.C => |x| if (x) @as(i32, 30) else 31,
|
|
};
|
|
}
|
|
|
|
fn testComparison() !void {
|
|
var x = Payload{ .A = 42 };
|
|
try expect(x == .A);
|
|
try expect(x != .B);
|
|
try expect(x != .C);
|
|
try expect((x == .B) == false);
|
|
try expect((x == .C) == false);
|
|
try expect((x != .A) == false);
|
|
}
|
|
|
|
test "comparison between union and enum literal" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
|
|
try testComparison();
|
|
try comptime testComparison();
|
|
}
|
|
|
|
const TheTag = enum { A, B, C };
|
|
const TheUnion = union(TheTag) {
|
|
A: i32,
|
|
B: i32,
|
|
C: i32,
|
|
};
|
|
test "cast union to tag type of union" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
|
|
try testCastUnionToTag();
|
|
try comptime testCastUnionToTag();
|
|
}
|
|
|
|
fn testCastUnionToTag() !void {
|
|
var u = TheUnion{ .B = 1234 };
|
|
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" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
|
|
var x: Value2 = Letter2.B;
|
|
try expect(@as(Letter2, x) == Letter2.B);
|
|
}
|
|
const Letter2 = enum { A, B, C };
|
|
const Value2 = union(Letter2) {
|
|
A: i32,
|
|
B,
|
|
C,
|
|
};
|
|
|
|
test "implicit cast union to its tag type" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
|
|
var x: Value2 = Letter2.B;
|
|
try expect(x == Letter2.B);
|
|
try giveMeLetterB(x);
|
|
}
|
|
fn giveMeLetterB(x: Letter2) !void {
|
|
try expect(x == Value2.B);
|
|
}
|
|
|
|
// TODO it looks like this test intended to test packed unions, but this is not a packed
|
|
// union. go through git history and find out what happened.
|
|
pub const PackThis = union(enum) {
|
|
Invalid: bool,
|
|
StringLiteral: u2,
|
|
};
|
|
|
|
test "constant packed union" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
try testConstPackedUnion(&[_]PackThis{PackThis{ .StringLiteral = 1 }});
|
|
}
|
|
|
|
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))" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
|
|
var x = MultipleChoice.C;
|
|
try expect(x == MultipleChoice.C);
|
|
try expect(@intFromEnum(@as(Tag(MultipleChoice), x)) == 60);
|
|
}
|
|
|
|
const PackedPtrOrInt = packed union {
|
|
ptr: *u8,
|
|
int: u64,
|
|
};
|
|
test "packed union size" {
|
|
try comptime expect(@sizeOf(PackedPtrOrInt) == 8);
|
|
}
|
|
|
|
const ZeroBits = union {
|
|
OnlyField: void,
|
|
};
|
|
test "union with only 1 field which is void should be zero bits" {
|
|
try comptime expect(@sizeOf(ZeroBits) == 0);
|
|
}
|
|
|
|
test "tagged union initialization with runtime void" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
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" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
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" {
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
|
|
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);
|
|
try comptime 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,
|
|
};
|
|
try comptime 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" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
glbl = Foo1{
|
|
.f = @typeInfo(Foo1).Union.fields[0].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" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
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" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
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" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const U = union(enum) {
|
|
A: u24,
|
|
};
|
|
|
|
var v = U{ .A = 532 };
|
|
try expect(v.A == 532);
|
|
}
|
|
|
|
test "runtime tag name with single field" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
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" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
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();
|
|
try comptime 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" {
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
|
|
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" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
switch (returnAnInt(13)) {
|
|
TaggedFoo.One => |value| try expect(value == 13),
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
fn returnAnInt(x: i32) TaggedFoo {
|
|
return TaggedFoo{ .One = x };
|
|
}
|
|
|
|
test "tagged union with all void fields but a meaningful tag" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const S = struct {
|
|
const B = union(enum) {
|
|
c: C,
|
|
None,
|
|
};
|
|
|
|
const A = struct {
|
|
b: B,
|
|
};
|
|
|
|
const C = struct {};
|
|
|
|
fn doTheTest() !void {
|
|
var a: A = A{ .b = B{ .c = C{} } };
|
|
try expect(@as(Tag(B), a.b) == Tag(B).c);
|
|
a = A{ .b = B.None };
|
|
try expect(@as(Tag(B), a.b) == Tag(B).None);
|
|
}
|
|
};
|
|
try S.doTheTest();
|
|
try comptime S.doTheTest();
|
|
}
|
|
|
|
test "union(enum(u32)) with specified and unspecified tag values" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
try comptime expect(Tag(Tag(MultipleChoice2)) == u32);
|
|
try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 });
|
|
try comptime testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2{ .C = 123 });
|
|
}
|
|
|
|
const MultipleChoice2 = union(enum(u32)) {
|
|
Unspecified1: i32,
|
|
A: f32 = 20,
|
|
Unspecified2: void,
|
|
B: bool = 40,
|
|
Unspecified3: i32,
|
|
C: i8 = 60,
|
|
Unspecified4: void,
|
|
D: void = 1000,
|
|
Unspecified5: i32,
|
|
};
|
|
|
|
fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void {
|
|
try expect(@intFromEnum(@as(Tag(MultipleChoice2), x)) == 60);
|
|
try expect(1123 == switch (x) {
|
|
MultipleChoice2.A => 1,
|
|
MultipleChoice2.B => 2,
|
|
MultipleChoice2.C => |v| @as(i32, 1000) + v,
|
|
MultipleChoice2.D => 4,
|
|
MultipleChoice2.Unspecified1 => 5,
|
|
MultipleChoice2.Unspecified2 => 6,
|
|
MultipleChoice2.Unspecified3 => 7,
|
|
MultipleChoice2.Unspecified4 => 8,
|
|
MultipleChoice2.Unspecified5 => 9,
|
|
});
|
|
}
|
|
|
|
test "switch on union with only 1 field" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
var r: PartialInst = undefined;
|
|
r = PartialInst.Compiled;
|
|
switch (r) {
|
|
PartialInst.Compiled => {
|
|
var z: PartialInstWithPayload = undefined;
|
|
z = PartialInstWithPayload{ .Compiled = 1234 };
|
|
switch (z) {
|
|
PartialInstWithPayload.Compiled => |x| {
|
|
try expect(x == 1234);
|
|
return;
|
|
},
|
|
}
|
|
},
|
|
}
|
|
unreachable;
|
|
}
|
|
|
|
const PartialInst = union(enum) {
|
|
Compiled,
|
|
};
|
|
|
|
const PartialInstWithPayload = union(enum) {
|
|
Compiled: i32,
|
|
};
|
|
|
|
test "union with only 1 field casted to its enum type which has enum value specified" {
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const Literal = union(enum) {
|
|
Number: f64,
|
|
Bool: bool,
|
|
};
|
|
|
|
const ExprTag = enum(comptime_int) {
|
|
Literal = 33,
|
|
};
|
|
|
|
const Expr = union(ExprTag) {
|
|
Literal: Literal,
|
|
};
|
|
|
|
var e = Expr{ .Literal = Literal{ .Bool = true } };
|
|
try comptime expect(Tag(ExprTag) == comptime_int);
|
|
comptime var t = @as(ExprTag, e);
|
|
try expect(t == Expr.Literal);
|
|
try expect(@intFromEnum(t) == 33);
|
|
try comptime expect(@intFromEnum(t) == 33);
|
|
}
|
|
|
|
test "@intFromEnum works on unions" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
|
|
const Bar = union(enum) {
|
|
A: bool,
|
|
B: u8,
|
|
C,
|
|
};
|
|
|
|
const a = Bar{ .A = true };
|
|
var b = Bar{ .B = undefined };
|
|
var c = Bar.C;
|
|
try expect(@intFromEnum(a) == 0);
|
|
try expect(@intFromEnum(b) == 1);
|
|
try expect(@intFromEnum(c) == 2);
|
|
}
|
|
|
|
test "comptime union field value equality" {
|
|
const a0 = Setter(Attribute{ .A = false });
|
|
const a1 = Setter(Attribute{ .A = true });
|
|
const a2 = Setter(Attribute{ .A = false });
|
|
|
|
const b0 = Setter(Attribute{ .B = 5 });
|
|
const b1 = Setter(Attribute{ .B = 9 });
|
|
const b2 = Setter(Attribute{ .B = 5 });
|
|
|
|
try expect(a0 == a0);
|
|
try expect(a1 == a1);
|
|
try expect(a0 == a2);
|
|
|
|
try expect(b0 == b0);
|
|
try expect(b1 == b1);
|
|
try expect(b0 == b2);
|
|
|
|
try expect(a0 != b0);
|
|
try expect(a0 != a1);
|
|
try expect(b0 != b1);
|
|
}
|
|
|
|
const Attribute = union(enum) {
|
|
A: bool,
|
|
B: u8,
|
|
};
|
|
|
|
fn setAttribute(attr: Attribute) void {
|
|
_ = attr;
|
|
}
|
|
|
|
fn Setter(comptime attr: Attribute) type {
|
|
return struct {
|
|
fn set() void {
|
|
setAttribute(attr);
|
|
}
|
|
};
|
|
}
|
|
|
|
test "return union init with void payload" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const S = struct {
|
|
fn entry() !void {
|
|
try expect(func().state == State.one);
|
|
}
|
|
const Outer = union(enum) {
|
|
state: State,
|
|
};
|
|
const State = union(enum) {
|
|
one: void,
|
|
two: u32,
|
|
};
|
|
fn func() Outer {
|
|
return Outer{ .state = State{ .one = {} } };
|
|
}
|
|
};
|
|
try S.entry();
|
|
try comptime S.entry();
|
|
}
|
|
|
|
test "@unionInit stored to a const" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const S = struct {
|
|
const U = union(enum) {
|
|
boolean: bool,
|
|
byte: u8,
|
|
};
|
|
fn doTheTest() !void {
|
|
{
|
|
var t = true;
|
|
const u = @unionInit(U, "boolean", t);
|
|
try expect(u.boolean);
|
|
}
|
|
{
|
|
var byte: u8 = 69;
|
|
const u = @unionInit(U, "byte", byte);
|
|
try expect(u.byte == 69);
|
|
}
|
|
}
|
|
};
|
|
|
|
try comptime S.doTheTest();
|
|
try S.doTheTest();
|
|
}
|
|
|
|
test "@unionInit can modify a union type" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const UnionInitEnum = union(enum) {
|
|
Boolean: bool,
|
|
Byte: u8,
|
|
};
|
|
|
|
var value: UnionInitEnum = undefined;
|
|
|
|
value = @unionInit(UnionInitEnum, "Boolean", true);
|
|
try expect(value.Boolean == true);
|
|
value.Boolean = false;
|
|
try expect(value.Boolean == false);
|
|
|
|
value = @unionInit(UnionInitEnum, "Byte", 2);
|
|
try expect(value.Byte == 2);
|
|
value.Byte = 3;
|
|
try expect(value.Byte == 3);
|
|
}
|
|
|
|
test "@unionInit can modify a pointer value" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const UnionInitEnum = union(enum) {
|
|
Boolean: bool,
|
|
Byte: u8,
|
|
};
|
|
|
|
var value: UnionInitEnum = undefined;
|
|
var value_ptr = &value;
|
|
|
|
value_ptr.* = @unionInit(UnionInitEnum, "Boolean", true);
|
|
try expect(value.Boolean == true);
|
|
|
|
value_ptr.* = @unionInit(UnionInitEnum, "Byte", 2);
|
|
try expect(value.Byte == 2);
|
|
}
|
|
|
|
test "union no tag with struct member" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const Struct = struct {};
|
|
const Union = union {
|
|
s: Struct,
|
|
pub fn foo(self: *@This()) void {
|
|
_ = self;
|
|
}
|
|
};
|
|
var u = Union{ .s = Struct{} };
|
|
u.foo();
|
|
}
|
|
|
|
test "union with comptime_int tag" {
|
|
const Union = union(enum(comptime_int)) {
|
|
X: u32,
|
|
Y: u16,
|
|
Z: u8,
|
|
};
|
|
try comptime expect(Tag(Tag(Union)) == comptime_int);
|
|
}
|
|
|
|
test "extern union doesn't trigger field check at comptime" {
|
|
const U = extern union {
|
|
x: u32,
|
|
y: u8,
|
|
};
|
|
|
|
const x = U{ .x = 0x55AAAA55 };
|
|
try comptime expect(x.y == 0x55);
|
|
}
|
|
|
|
test "anonymous union literal syntax" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const S = struct {
|
|
const Number = union {
|
|
int: i32,
|
|
float: f64,
|
|
};
|
|
|
|
fn doTheTest() !void {
|
|
var i: Number = .{ .int = 42 };
|
|
var f = makeNumber();
|
|
try expect(i.int == 42);
|
|
try expect(f.float == 12.34);
|
|
}
|
|
|
|
fn makeNumber() Number {
|
|
return .{ .float = 12.34 };
|
|
}
|
|
};
|
|
try S.doTheTest();
|
|
try comptime S.doTheTest();
|
|
}
|
|
|
|
test "function call result coerces from tagged union to the tag" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
|
|
const S = struct {
|
|
const Arch = union(enum) {
|
|
One,
|
|
Two: usize,
|
|
};
|
|
|
|
const ArchTag = Tag(Arch);
|
|
|
|
fn doTheTest() !void {
|
|
var x: ArchTag = getArch1();
|
|
try expect(x == .One);
|
|
|
|
var y: ArchTag = getArch2();
|
|
try expect(y == .Two);
|
|
}
|
|
|
|
pub fn getArch1() Arch {
|
|
return .One;
|
|
}
|
|
|
|
pub fn getArch2() Arch {
|
|
return .{ .Two = 99 };
|
|
}
|
|
};
|
|
try S.doTheTest();
|
|
try comptime S.doTheTest();
|
|
}
|
|
|
|
test "cast from anonymous struct to union" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const S = struct {
|
|
const U = union(enum) {
|
|
A: u32,
|
|
B: []const u8,
|
|
C: void,
|
|
};
|
|
fn doTheTest() !void {
|
|
var y: u32 = 42;
|
|
const t0 = .{ .A = 123 };
|
|
const t1 = .{ .B = "foo" };
|
|
const t2 = .{ .C = {} };
|
|
const t3 = .{ .A = y };
|
|
const x0: U = t0;
|
|
var x1: U = t1;
|
|
const x2: U = t2;
|
|
var x3: U = t3;
|
|
try expect(x0.A == 123);
|
|
try expect(std.mem.eql(u8, x1.B, "foo"));
|
|
try expect(x2 == .C);
|
|
try expect(x3.A == y);
|
|
}
|
|
};
|
|
try S.doTheTest();
|
|
try comptime S.doTheTest();
|
|
}
|
|
|
|
test "cast from pointer to anonymous struct to pointer to union" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const S = struct {
|
|
const U = union(enum) {
|
|
A: u32,
|
|
B: []const u8,
|
|
C: void,
|
|
};
|
|
fn doTheTest() !void {
|
|
var y: u32 = 42;
|
|
const t0 = &.{ .A = 123 };
|
|
const t1 = &.{ .B = "foo" };
|
|
const t2 = &.{ .C = {} };
|
|
const t3 = &.{ .A = y };
|
|
const x0: *const U = t0;
|
|
var x1: *const U = t1;
|
|
const x2: *const U = t2;
|
|
var x3: *const U = t3;
|
|
try expect(x0.A == 123);
|
|
try expect(std.mem.eql(u8, x1.B, "foo"));
|
|
try expect(x2.* == .C);
|
|
try expect(x3.A == y);
|
|
}
|
|
};
|
|
try S.doTheTest();
|
|
try comptime S.doTheTest();
|
|
}
|
|
|
|
test "switching on non exhaustive union" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const S = struct {
|
|
const E = enum(u8) {
|
|
a,
|
|
b,
|
|
_,
|
|
};
|
|
const U = union(E) {
|
|
a: i32,
|
|
b: u32,
|
|
};
|
|
fn doTheTest() !void {
|
|
var a = U{ .a = 2 };
|
|
switch (a) {
|
|
.a => |val| try expect(val == 2),
|
|
.b => return error.Fail,
|
|
}
|
|
}
|
|
};
|
|
try S.doTheTest();
|
|
try comptime S.doTheTest();
|
|
}
|
|
|
|
test "containers with single-field enums" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const S = struct {
|
|
const A = union(enum) { f1 };
|
|
const B = union(enum) { f1: void };
|
|
const C = struct { a: A };
|
|
const D = struct { a: B };
|
|
|
|
fn doTheTest() !void {
|
|
var array1 = [1]A{A{ .f1 = {} }};
|
|
var array2 = [1]B{B{ .f1 = {} }};
|
|
try expect(array1[0] == .f1);
|
|
try expect(array2[0] == .f1);
|
|
|
|
var struct1 = C{ .a = A{ .f1 = {} } };
|
|
var struct2 = D{ .a = B{ .f1 = {} } };
|
|
try expect(struct1.a == .f1);
|
|
try expect(struct2.a == .f1);
|
|
}
|
|
};
|
|
|
|
try S.doTheTest();
|
|
try comptime S.doTheTest();
|
|
}
|
|
|
|
test "@unionInit on union with tag but no fields" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const S = struct {
|
|
const Type = enum(u8) { no_op = 105 };
|
|
|
|
const Data = union(Type) {
|
|
no_op: void,
|
|
|
|
pub fn decode(buf: []const u8) Data {
|
|
_ = buf;
|
|
return @unionInit(Data, "no_op", {});
|
|
}
|
|
};
|
|
|
|
comptime {
|
|
assert(@sizeOf(Data) == 1);
|
|
}
|
|
|
|
fn doTheTest() !void {
|
|
var data: Data = .{ .no_op = {} };
|
|
_ = data;
|
|
var o = Data.decode(&[_]u8{});
|
|
try expectEqual(Type.no_op, o);
|
|
}
|
|
};
|
|
|
|
try S.doTheTest();
|
|
try comptime S.doTheTest();
|
|
}
|
|
|
|
test "union enum type gets a separate scope" {
|
|
const S = struct {
|
|
const U = union(enum) {
|
|
a: u8,
|
|
const foo = 1;
|
|
};
|
|
|
|
fn doTheTest() !void {
|
|
try expect(!@hasDecl(Tag(U), "foo"));
|
|
}
|
|
};
|
|
|
|
try S.doTheTest();
|
|
}
|
|
|
|
test "global variable struct contains union initialized to non-most-aligned field" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const T = struct {
|
|
const U = union(enum) {
|
|
a: i32,
|
|
b: f64,
|
|
};
|
|
|
|
const S = struct {
|
|
u: U,
|
|
};
|
|
|
|
var s: S = .{
|
|
.u = .{
|
|
.a = 3,
|
|
},
|
|
};
|
|
};
|
|
|
|
T.s.u.a += 1;
|
|
try expect(T.s.u.a == 4);
|
|
}
|
|
|
|
test "union with no result loc initiated with a runtime value" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const U = union {
|
|
a: u32,
|
|
b: u32,
|
|
fn foo(u: @This()) void {
|
|
_ = u;
|
|
}
|
|
};
|
|
var a: u32 = 1;
|
|
U.foo(U{ .a = a });
|
|
}
|
|
|
|
test "union with a large struct field" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const S = struct {
|
|
a: [8]usize,
|
|
};
|
|
|
|
const U = union {
|
|
s: S,
|
|
b: u32,
|
|
fn foo(_: @This()) void {}
|
|
};
|
|
var s: S = undefined;
|
|
U.foo(U{ .s = s });
|
|
}
|
|
|
|
test "comptime equality of extern unions with same tag" {
|
|
const S = struct {
|
|
const U = extern union {
|
|
a: i32,
|
|
b: f32,
|
|
};
|
|
fn foo(comptime x: U) i32 {
|
|
return x.a;
|
|
}
|
|
};
|
|
const a = S.U{ .a = 1234 };
|
|
const b = S.U{ .a = 1234 };
|
|
try expect(S.foo(a) == S.foo(b));
|
|
}
|
|
|
|
test "union tag is set when initiated as a temporary value at runtime" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const U = union(enum) {
|
|
a,
|
|
b: u32,
|
|
c,
|
|
|
|
fn doTheTest(u: @This()) !void {
|
|
try expect(u == .b);
|
|
}
|
|
};
|
|
var b: u32 = 1;
|
|
try (U{ .b = b }).doTheTest();
|
|
}
|
|
|
|
test "extern union most-aligned field is smaller" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const U = extern union {
|
|
in6: extern struct {
|
|
family: u16,
|
|
port: u16,
|
|
flowinfo: u32,
|
|
addr: [20]u8,
|
|
},
|
|
un: [110]u8,
|
|
};
|
|
var a: ?U = .{ .un = [_]u8{0} ** 110 };
|
|
try expect(a != null);
|
|
}
|
|
|
|
test "return an extern union from C calling convention" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const namespace = struct {
|
|
const S = extern struct {
|
|
x: c_int,
|
|
};
|
|
const U = extern union {
|
|
l: c_long,
|
|
d: f64,
|
|
s: S,
|
|
};
|
|
|
|
fn bar(arg_u: U) callconv(.C) U {
|
|
var u = arg_u;
|
|
return u;
|
|
}
|
|
};
|
|
|
|
var u: namespace.U = namespace.U{
|
|
.l = @as(c_long, 42),
|
|
};
|
|
u = namespace.bar(namespace.U{
|
|
.d = 4.0,
|
|
});
|
|
try expect(u.d == 4.0);
|
|
}
|
|
|
|
test "noreturn field in union" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const U = union(enum) {
|
|
a: u32,
|
|
b: noreturn,
|
|
c: noreturn,
|
|
};
|
|
var a = U{ .a = 1 };
|
|
var count: u32 = 0;
|
|
if (a == .b) @compileError("bad");
|
|
switch (a) {
|
|
.a => count += 1,
|
|
.b => |val| {
|
|
_ = val;
|
|
@compileError("bad");
|
|
},
|
|
.c => @compileError("bad"),
|
|
}
|
|
switch (a) {
|
|
.a => count += 1,
|
|
.b, .c => @compileError("bad"),
|
|
}
|
|
switch (a) {
|
|
.a, .b, .c => {
|
|
count += 1;
|
|
try expect(a == .a);
|
|
},
|
|
}
|
|
switch (a) {
|
|
.a => count += 1,
|
|
else => @compileError("bad"),
|
|
}
|
|
switch (a) {
|
|
else => {
|
|
count += 1;
|
|
try expect(a == .a);
|
|
},
|
|
}
|
|
switch (a) {
|
|
.a => count += 1,
|
|
.b, .c => |*val| {
|
|
_ = val;
|
|
@compileError("bad");
|
|
},
|
|
}
|
|
try expect(count == 6);
|
|
}
|
|
|
|
test "@unionInit uses tag value instead of field index" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const E = enum(u8) {
|
|
b = 255,
|
|
a = 3,
|
|
};
|
|
const U = union(E) {
|
|
b: isize,
|
|
a: usize,
|
|
};
|
|
var i: isize = -1;
|
|
var u = @unionInit(U, "b", i);
|
|
{
|
|
var a = u.b;
|
|
try expect(a == i);
|
|
}
|
|
{
|
|
var a = &u.b;
|
|
try expect(a.* == i);
|
|
}
|
|
try expect(@intFromEnum(u) == 255);
|
|
}
|
|
|
|
test "union field ptr - zero sized payload" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const U = union {
|
|
foo: void,
|
|
bar: void,
|
|
fn bar(_: *void) void {}
|
|
};
|
|
var u: U = .{ .foo = {} };
|
|
U.bar(&u.foo);
|
|
}
|
|
|
|
test "union field ptr - zero sized field" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const U = union {
|
|
foo: void,
|
|
bar: u32,
|
|
fn bar(_: *void) void {}
|
|
};
|
|
var u: U = .{ .foo = {} };
|
|
U.bar(&u.foo);
|
|
}
|
|
|
|
test "packed union in packed struct" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const S = packed struct {
|
|
nested: packed union {
|
|
val: usize,
|
|
foo: u32,
|
|
},
|
|
bar: u32,
|
|
|
|
fn unpack(self: @This()) usize {
|
|
return self.nested.foo;
|
|
}
|
|
};
|
|
const a: S = .{ .nested = .{ .foo = 123 }, .bar = 5 };
|
|
try expect(a.unpack() == 123);
|
|
}
|
|
|
|
test "Namespace-like union" {
|
|
const DepType = enum {
|
|
git,
|
|
http,
|
|
const DepType = @This();
|
|
const Version = union(DepType) {
|
|
git: Git,
|
|
http: void,
|
|
const Git = enum {
|
|
branch,
|
|
tag,
|
|
commit,
|
|
fn frozen(self: Git) bool {
|
|
return self == .tag;
|
|
}
|
|
};
|
|
};
|
|
};
|
|
var a: DepType.Version.Git = .tag;
|
|
try expect(a.frozen());
|
|
}
|
|
|
|
test "union int tag type is properly managed" {
|
|
const Bar = union(enum(u2)) {
|
|
x: bool,
|
|
y: u8,
|
|
z: u8,
|
|
};
|
|
try expect(@sizeOf(Bar) + 1 == 3);
|
|
}
|
|
|
|
test "no dependency loop when function pointer in union returns the union" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const U = union(enum) {
|
|
const U = @This();
|
|
a: u8,
|
|
b: *const fn (x: U) void,
|
|
c: *const fn (x: U) U,
|
|
d: *const fn (x: u8) U,
|
|
e: *const fn (x: *U) void,
|
|
f: *const fn (x: *U) U,
|
|
fn foo(x: u8) U {
|
|
return .{ .a = x };
|
|
}
|
|
};
|
|
var b: U = .{ .d = U.foo };
|
|
try expect(b.d(2).a == 2);
|
|
}
|
|
|
|
test "union reassignment can use previous value" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const U = union {
|
|
a: u32,
|
|
b: u32,
|
|
};
|
|
var a = U{ .a = 32 };
|
|
a = U{ .b = a.a };
|
|
try expect(a.b == 32);
|
|
}
|
|
|
|
test "packed union with zero-bit field" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const S = packed struct {
|
|
nested: packed union {
|
|
zero: void,
|
|
sized: u32,
|
|
},
|
|
bar: u32,
|
|
|
|
fn doTest(self: @This()) !void {
|
|
try expect(self.bar == 42);
|
|
}
|
|
};
|
|
try S.doTest(.{ .nested = .{ .zero = {} }, .bar = 42 });
|
|
}
|
|
|
|
test "reinterpreting enum value inside packed union" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const U = packed union {
|
|
tag: enum { a, b },
|
|
val: u8,
|
|
|
|
fn doTest() !void {
|
|
var u: @This() = .{ .tag = .a };
|
|
u.val += 1;
|
|
try expect(u.tag == .b);
|
|
}
|
|
};
|
|
try U.doTest();
|
|
try comptime U.doTest();
|
|
}
|
|
|
|
test "access the tag of a global tagged union" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const U = union(enum) {
|
|
a,
|
|
b: u8,
|
|
var u: @This() = .a;
|
|
};
|
|
try expect(U.u == .a);
|
|
}
|
|
|
|
test "coerce enum literal to union in result loc" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const U = union(enum) {
|
|
a,
|
|
b: u8,
|
|
|
|
fn doTest(c: bool) !void {
|
|
var u = if (c) .a else @This(){ .b = 0 };
|
|
try expect(u == .a);
|
|
}
|
|
};
|
|
try U.doTest(true);
|
|
try comptime U.doTest(true);
|
|
}
|
|
|
|
test "defined-layout union field pointer has correct alignment" {
|
|
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
|
|
|
|
const S = struct {
|
|
fn doTheTest(comptime U: type) !void {
|
|
var a: U = .{ .x = 123 };
|
|
var b: U align(1) = .{ .x = 456 };
|
|
var c: U align(64) = .{ .x = 789 };
|
|
|
|
const ap = &a.x;
|
|
const bp = &b.x;
|
|
const cp = &c.x;
|
|
|
|
comptime assert(@TypeOf(ap) == *u32);
|
|
comptime assert(@TypeOf(bp) == *align(1) u32);
|
|
comptime assert(@TypeOf(cp) == *align(64) u32);
|
|
|
|
try expectEqual(@as(u32, 123), ap.*);
|
|
try expectEqual(@as(u32, 456), bp.*);
|
|
try expectEqual(@as(u32, 789), cp.*);
|
|
}
|
|
};
|
|
|
|
const U1 = extern union { x: u32 };
|
|
const U2 = packed union { x: u32 };
|
|
|
|
try S.doTheTest(U1);
|
|
try S.doTheTest(U2);
|
|
try comptime S.doTheTest(U1);
|
|
try comptime S.doTheTest(U2);
|
|
}
|
|
|
|
test "undefined-layout union field pointer has correct alignment" {
|
|
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
|
|
|
|
const S = struct {
|
|
fn doTheTest(comptime U: type) !void {
|
|
var a: U = .{ .x = 123 };
|
|
var b: U align(1) = .{ .x = 456 };
|
|
var c: U align(64) = .{ .x = 789 };
|
|
|
|
const ap = &a.x;
|
|
const bp = &b.x;
|
|
const cp = &c.x;
|
|
|
|
comptime assert(@TypeOf(ap) == *u32);
|
|
comptime assert(@TypeOf(bp) == *align(1) u32);
|
|
comptime assert(@TypeOf(cp) == *u32); // undefined layout so does not inherit larger aligns
|
|
|
|
try expectEqual(@as(u32, 123), ap.*);
|
|
try expectEqual(@as(u32, 456), bp.*);
|
|
try expectEqual(@as(u32, 789), cp.*);
|
|
}
|
|
};
|
|
|
|
const U1 = union { x: u32 };
|
|
const U2 = union(enum) { x: u32 };
|
|
|
|
try S.doTheTest(U1);
|
|
try S.doTheTest(U2);
|
|
try comptime S.doTheTest(U1);
|
|
try comptime S.doTheTest(U2);
|
|
}
|
|
|
|
test "packed union field pointer has correct alignment" {
|
|
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
|
|
|
|
const U = packed union { x: u20 };
|
|
const S = packed struct(u24) { a: u2, u: U, b: u2 };
|
|
|
|
var a: S = undefined;
|
|
var b: S align(1) = undefined;
|
|
var c: S align(64) = undefined;
|
|
|
|
const ap = &a.u.x;
|
|
const bp = &b.u.x;
|
|
const cp = &c.u.x;
|
|
|
|
comptime assert(@TypeOf(ap) == *align(4:2:3) u20);
|
|
comptime assert(@TypeOf(bp) == *align(1:2:3) u20);
|
|
comptime assert(@TypeOf(cp) == *align(64:2:3) u20);
|
|
|
|
a.u = .{ .x = 123 };
|
|
b.u = .{ .x = 456 };
|
|
c.u = .{ .x = 789 };
|
|
|
|
try expectEqual(@as(u20, 123), ap.*);
|
|
try expectEqual(@as(u20, 456), bp.*);
|
|
try expectEqual(@as(u20, 789), cp.*);
|
|
}
|
|
|
|
test "union with 128 bit integer" {
|
|
const ValueTag = enum { int, other };
|
|
|
|
const Value3 = union(ValueTag) {
|
|
int: i128,
|
|
other: bool,
|
|
};
|
|
var values: [2]Value3 = undefined;
|
|
values[0] = .{ .int = 3 };
|
|
values[1] = .{ .int = 4 };
|
|
|
|
var ok: usize = 0;
|
|
|
|
for (values) |val| {
|
|
switch (val) {
|
|
.int => ok += 1,
|
|
else => return error.TestFailed,
|
|
}
|
|
}
|
|
}
|