mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
This is a bit harder than it seems at first glance. Actually resolving the type is the easy part: the interesting thing is actually getting the capture value. We split this into three cases: * If all payload types are the same (as is required in status quo), we can just do what we already do: get the first field value. * If all payloads are in-memory coercible to the resolved type, we still fetch the first field, but we also emit a `bitcast` to convert to the resolved type. * Otherwise, we need to handle each case separately. We emit a nested `switch_br` which, for each possible case, gets the corresponding union field, and coerces it to the resolved type. As an optimization, the inner switch's 'else' prong is used for any peer which is in-memory coercible to the target type, and the bitcast approach described above is used. Pointer captures have the additional constraint that all payload types must be in-memory coercible to the resolved type. Resolves: #2812
788 lines
21 KiB
Zig
788 lines
21 KiB
Zig
const builtin = @import("builtin");
|
|
const std = @import("std");
|
|
const assert = std.debug.assert;
|
|
const expect = std.testing.expect;
|
|
const expectError = std.testing.expectError;
|
|
const expectEqual = std.testing.expectEqual;
|
|
|
|
test "switch with numbers" {
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
|
|
try testSwitchWithNumbers(13);
|
|
}
|
|
|
|
fn testSwitchWithNumbers(x: u32) !void {
|
|
const result = switch (x) {
|
|
1, 2, 3, 4...8 => false,
|
|
13 => true,
|
|
else => false,
|
|
};
|
|
try expect(result);
|
|
}
|
|
|
|
test "switch with all ranges" {
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
|
|
try expect(testSwitchWithAllRanges(50, 3) == 1);
|
|
try expect(testSwitchWithAllRanges(101, 0) == 2);
|
|
try expect(testSwitchWithAllRanges(300, 5) == 3);
|
|
try expect(testSwitchWithAllRanges(301, 6) == 6);
|
|
}
|
|
|
|
fn testSwitchWithAllRanges(x: u32, y: u32) u32 {
|
|
return switch (x) {
|
|
0...100 => 1,
|
|
101...200 => 2,
|
|
201...300 => 3,
|
|
else => y,
|
|
};
|
|
}
|
|
|
|
test "implicit comptime switch" {
|
|
const x = 3 + 4;
|
|
const result = switch (x) {
|
|
3 => 10,
|
|
4 => 11,
|
|
5, 6 => 12,
|
|
7, 8 => 13,
|
|
else => 14,
|
|
};
|
|
|
|
comptime {
|
|
try expect(result + 1 == 14);
|
|
}
|
|
}
|
|
|
|
test "switch on enum" {
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
|
|
const fruit = Fruit.Orange;
|
|
nonConstSwitchOnEnum(fruit);
|
|
}
|
|
const Fruit = enum {
|
|
Apple,
|
|
Orange,
|
|
Banana,
|
|
};
|
|
fn nonConstSwitchOnEnum(fruit: Fruit) void {
|
|
switch (fruit) {
|
|
Fruit.Apple => unreachable,
|
|
Fruit.Orange => {},
|
|
Fruit.Banana => unreachable,
|
|
}
|
|
}
|
|
|
|
test "switch statement" {
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
|
|
try nonConstSwitch(SwitchStatementFoo.C);
|
|
}
|
|
fn nonConstSwitch(foo: SwitchStatementFoo) !void {
|
|
const val = switch (foo) {
|
|
SwitchStatementFoo.A => @as(i32, 1),
|
|
SwitchStatementFoo.B => 2,
|
|
SwitchStatementFoo.C => 3,
|
|
SwitchStatementFoo.D => 4,
|
|
};
|
|
try expect(val == 3);
|
|
}
|
|
const SwitchStatementFoo = enum { A, B, C, D };
|
|
|
|
test "switch with multiple expressions" {
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
|
|
const x = switch (returnsFive()) {
|
|
1, 2, 3 => 1,
|
|
4, 5, 6 => 2,
|
|
else => @as(i32, 3),
|
|
};
|
|
try expect(x == 2);
|
|
}
|
|
fn returnsFive() i32 {
|
|
return 5;
|
|
}
|
|
|
|
test "switch on type" {
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
|
|
try expect(trueIfBoolFalseOtherwise(bool));
|
|
try expect(!trueIfBoolFalseOtherwise(i32));
|
|
}
|
|
|
|
fn trueIfBoolFalseOtherwise(comptime T: type) bool {
|
|
return switch (T) {
|
|
bool => true,
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
test "switching on booleans" {
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
try testSwitchOnBools();
|
|
comptime try testSwitchOnBools();
|
|
}
|
|
|
|
fn testSwitchOnBools() !void {
|
|
try expect(testSwitchOnBoolsTrueAndFalse(true) == false);
|
|
try expect(testSwitchOnBoolsTrueAndFalse(false) == true);
|
|
|
|
try expect(testSwitchOnBoolsTrueWithElse(true) == false);
|
|
try expect(testSwitchOnBoolsTrueWithElse(false) == true);
|
|
|
|
try expect(testSwitchOnBoolsFalseWithElse(true) == false);
|
|
try expect(testSwitchOnBoolsFalseWithElse(false) == true);
|
|
}
|
|
|
|
fn testSwitchOnBoolsTrueAndFalse(x: bool) bool {
|
|
return switch (x) {
|
|
true => false,
|
|
false => true,
|
|
};
|
|
}
|
|
|
|
fn testSwitchOnBoolsTrueWithElse(x: bool) bool {
|
|
return switch (x) {
|
|
true => false,
|
|
else => true,
|
|
};
|
|
}
|
|
|
|
fn testSwitchOnBoolsFalseWithElse(x: bool) bool {
|
|
return switch (x) {
|
|
false => true,
|
|
else => false,
|
|
};
|
|
}
|
|
|
|
test "u0" {
|
|
var val: u0 = 0;
|
|
switch (val) {
|
|
0 => try expect(val == 0),
|
|
}
|
|
}
|
|
|
|
test "undefined.u0" {
|
|
var val: u0 = undefined;
|
|
switch (val) {
|
|
0 => try expect(val == 0),
|
|
}
|
|
}
|
|
|
|
test "switch with disjoint range" {
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
|
|
var q: u8 = 0;
|
|
switch (q) {
|
|
0...125 => {},
|
|
127...255 => {},
|
|
126...126 => {},
|
|
}
|
|
}
|
|
|
|
test "switch variable for range and multiple prongs" {
|
|
const S = struct {
|
|
fn doTheTest() !void {
|
|
var u: u8 = 16;
|
|
try doTheSwitch(u);
|
|
comptime try doTheSwitch(u);
|
|
var v: u8 = 42;
|
|
try doTheSwitch(v);
|
|
comptime try doTheSwitch(v);
|
|
}
|
|
fn doTheSwitch(q: u8) !void {
|
|
switch (q) {
|
|
0...40 => |x| try expect(x == 16),
|
|
41, 42, 43 => |x| try expect(x == 42),
|
|
else => try expect(false),
|
|
}
|
|
}
|
|
};
|
|
_ = S;
|
|
}
|
|
|
|
var state: u32 = 0;
|
|
fn poll() void {
|
|
switch (state) {
|
|
0 => {
|
|
state = 1;
|
|
},
|
|
else => {
|
|
state += 1;
|
|
},
|
|
}
|
|
}
|
|
|
|
test "switch on global mutable var isn't constant-folded" {
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
|
|
while (state < 2) {
|
|
poll();
|
|
}
|
|
}
|
|
|
|
const SwitchProngWithVarEnum = union(enum) {
|
|
One: i32,
|
|
Two: f32,
|
|
Meh: void,
|
|
};
|
|
|
|
test "switch prong with variable" {
|
|
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;
|
|
|
|
try switchProngWithVarFn(SwitchProngWithVarEnum{ .One = 13 });
|
|
try switchProngWithVarFn(SwitchProngWithVarEnum{ .Two = 13.0 });
|
|
try switchProngWithVarFn(SwitchProngWithVarEnum{ .Meh = {} });
|
|
}
|
|
fn switchProngWithVarFn(a: SwitchProngWithVarEnum) !void {
|
|
switch (a) {
|
|
SwitchProngWithVarEnum.One => |x| {
|
|
try expect(x == 13);
|
|
},
|
|
SwitchProngWithVarEnum.Two => |x| {
|
|
try expect(x == 13.0);
|
|
},
|
|
SwitchProngWithVarEnum.Meh => |x| {
|
|
const v: void = x;
|
|
_ = v;
|
|
},
|
|
}
|
|
}
|
|
|
|
test "switch on enum using pointer capture" {
|
|
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;
|
|
|
|
try testSwitchEnumPtrCapture();
|
|
comptime try testSwitchEnumPtrCapture();
|
|
}
|
|
|
|
fn testSwitchEnumPtrCapture() !void {
|
|
var value = SwitchProngWithVarEnum{ .One = 1234 };
|
|
switch (value) {
|
|
SwitchProngWithVarEnum.One => |*x| x.* += 1,
|
|
else => unreachable,
|
|
}
|
|
switch (value) {
|
|
SwitchProngWithVarEnum.One => |x| try expect(x == 1235),
|
|
else => unreachable,
|
|
}
|
|
}
|
|
|
|
test "switch handles all cases of number" {
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
|
|
try testSwitchHandleAllCases();
|
|
comptime try testSwitchHandleAllCases();
|
|
}
|
|
|
|
fn testSwitchHandleAllCases() !void {
|
|
try expect(testSwitchHandleAllCasesExhaustive(0) == 3);
|
|
try expect(testSwitchHandleAllCasesExhaustive(1) == 2);
|
|
try expect(testSwitchHandleAllCasesExhaustive(2) == 1);
|
|
try expect(testSwitchHandleAllCasesExhaustive(3) == 0);
|
|
|
|
try expect(testSwitchHandleAllCasesRange(100) == 0);
|
|
try expect(testSwitchHandleAllCasesRange(200) == 1);
|
|
try expect(testSwitchHandleAllCasesRange(201) == 2);
|
|
try expect(testSwitchHandleAllCasesRange(202) == 4);
|
|
try expect(testSwitchHandleAllCasesRange(230) == 3);
|
|
}
|
|
|
|
fn testSwitchHandleAllCasesExhaustive(x: u2) u2 {
|
|
return switch (x) {
|
|
0 => @as(u2, 3),
|
|
1 => 2,
|
|
2 => 1,
|
|
3 => 0,
|
|
};
|
|
}
|
|
|
|
fn testSwitchHandleAllCasesRange(x: u8) u8 {
|
|
return switch (x) {
|
|
0...100 => @as(u8, 0),
|
|
101...200 => 1,
|
|
201, 203 => 2,
|
|
202 => 4,
|
|
204...255 => 3,
|
|
};
|
|
}
|
|
|
|
test "switch on union with some prongs capturing" {
|
|
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 X = union(enum) {
|
|
a,
|
|
b: i32,
|
|
};
|
|
|
|
var x: X = X{ .b = 10 };
|
|
var y: i32 = switch (x) {
|
|
.a => unreachable,
|
|
.b => |b| b + 1,
|
|
};
|
|
try expect(y == 11);
|
|
}
|
|
|
|
const Number = union(enum) {
|
|
One: u64,
|
|
Two: u8,
|
|
Three: f32,
|
|
};
|
|
|
|
const number = Number{ .Three = 1.23 };
|
|
|
|
fn returnsFalse() bool {
|
|
switch (number) {
|
|
Number.One => |x| return x > 1234,
|
|
Number.Two => |x| return x == 'a',
|
|
Number.Three => |x| return x > 12.34,
|
|
}
|
|
}
|
|
test "switch on const enum with var" {
|
|
try expect(!returnsFalse());
|
|
}
|
|
|
|
test "anon enum literal used in switch on union enum" {
|
|
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 Foo = union(enum) {
|
|
a: i32,
|
|
};
|
|
|
|
var foo = Foo{ .a = 1234 };
|
|
switch (foo) {
|
|
.a => |x| {
|
|
try expect(x == 1234);
|
|
},
|
|
}
|
|
}
|
|
|
|
test "switch all prongs unreachable" {
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
|
|
try testAllProngsUnreachable();
|
|
comptime try testAllProngsUnreachable();
|
|
}
|
|
|
|
fn testAllProngsUnreachable() !void {
|
|
try expect(switchWithUnreachable(1) == 2);
|
|
try expect(switchWithUnreachable(2) == 10);
|
|
}
|
|
|
|
fn switchWithUnreachable(x: i32) i32 {
|
|
while (true) {
|
|
switch (x) {
|
|
1 => return 2,
|
|
2 => break,
|
|
else => continue,
|
|
}
|
|
}
|
|
return 10;
|
|
}
|
|
|
|
test "capture value of switch with all unreachable prongs" {
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const x = return_a_number() catch |err| switch (err) {
|
|
else => unreachable,
|
|
};
|
|
try expect(x == 1);
|
|
}
|
|
|
|
fn return_a_number() anyerror!i32 {
|
|
return 1;
|
|
}
|
|
|
|
test "switch on integer with else capturing expr" {
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const S = struct {
|
|
fn doTheTest() !void {
|
|
var x: i32 = 5;
|
|
switch (x + 10) {
|
|
14 => @panic("fail"),
|
|
16 => @panic("fail"),
|
|
else => |e| try expect(e == 15),
|
|
}
|
|
}
|
|
};
|
|
try S.doTheTest();
|
|
comptime try S.doTheTest();
|
|
}
|
|
|
|
test "else prong of switch on error set excludes other cases" {
|
|
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 {
|
|
fn doTheTest() !void {
|
|
try expectError(error.C, bar());
|
|
}
|
|
const E = error{
|
|
A,
|
|
B,
|
|
} || E2;
|
|
|
|
const E2 = error{
|
|
C,
|
|
D,
|
|
};
|
|
|
|
fn foo() E!void {
|
|
return error.C;
|
|
}
|
|
|
|
fn bar() E2!void {
|
|
foo() catch |err| switch (err) {
|
|
error.A, error.B => {},
|
|
else => |e| return e,
|
|
};
|
|
}
|
|
};
|
|
try S.doTheTest();
|
|
comptime try S.doTheTest();
|
|
}
|
|
|
|
test "switch prongs with error set cases make a new error set type for capture 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 S = struct {
|
|
fn doTheTest() !void {
|
|
try expectError(error.B, bar());
|
|
}
|
|
const E = E1 || E2;
|
|
|
|
const E1 = error{
|
|
A,
|
|
B,
|
|
};
|
|
|
|
const E2 = error{
|
|
C,
|
|
D,
|
|
};
|
|
|
|
fn foo() E!void {
|
|
return error.B;
|
|
}
|
|
|
|
fn bar() E1!void {
|
|
foo() catch |err| switch (err) {
|
|
error.A, error.B => |e| return e,
|
|
else => {},
|
|
};
|
|
}
|
|
};
|
|
try S.doTheTest();
|
|
comptime try S.doTheTest();
|
|
}
|
|
|
|
test "return result loc and then switch with range implicit casted to error union" {
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const S = struct {
|
|
fn doTheTest() !void {
|
|
try expect((func(0xb) catch unreachable) == 0xb);
|
|
}
|
|
fn func(d: u8) anyerror!u8 {
|
|
return switch (d) {
|
|
0xa...0xf => d,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
};
|
|
try S.doTheTest();
|
|
comptime try S.doTheTest();
|
|
}
|
|
|
|
test "switch with null and T peer types and inferred result location type" {
|
|
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 {
|
|
fn doTheTest(c: u8) !void {
|
|
if (switch (c) {
|
|
0 => true,
|
|
else => null,
|
|
}) |v| {
|
|
_ = v;
|
|
@panic("fail");
|
|
}
|
|
}
|
|
};
|
|
try S.doTheTest(1);
|
|
comptime try S.doTheTest(1);
|
|
}
|
|
|
|
test "switch prongs with cases with identical payload types" {
|
|
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 Union = union(enum) {
|
|
A: usize,
|
|
B: isize,
|
|
C: usize,
|
|
};
|
|
const S = struct {
|
|
fn doTheTest() !void {
|
|
try doTheSwitch1(Union{ .A = 8 });
|
|
try doTheSwitch2(Union{ .B = -8 });
|
|
}
|
|
fn doTheSwitch1(u: Union) !void {
|
|
switch (u) {
|
|
.A, .C => |e| {
|
|
try expect(@TypeOf(e) == usize);
|
|
try expect(e == 8);
|
|
},
|
|
.B => |e| {
|
|
_ = e;
|
|
@panic("fail");
|
|
},
|
|
}
|
|
}
|
|
fn doTheSwitch2(u: Union) !void {
|
|
switch (u) {
|
|
.A, .C => |e| {
|
|
_ = e;
|
|
@panic("fail");
|
|
},
|
|
.B => |e| {
|
|
try expect(@TypeOf(e) == isize);
|
|
try expect(e == -8);
|
|
},
|
|
}
|
|
}
|
|
};
|
|
try S.doTheTest();
|
|
comptime try S.doTheTest();
|
|
}
|
|
|
|
test "switch on pointer type" {
|
|
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 X = struct {
|
|
field: u32,
|
|
};
|
|
|
|
const P1 = @intToPtr(*X, 0x400);
|
|
const P2 = @intToPtr(*X, 0x800);
|
|
const P3 = @intToPtr(*X, 0xC00);
|
|
|
|
fn doTheTest(arg: *X) i32 {
|
|
switch (arg) {
|
|
P1 => return 1,
|
|
P2 => return 2,
|
|
else => return 3,
|
|
}
|
|
}
|
|
};
|
|
|
|
try expect(1 == S.doTheTest(S.P1));
|
|
try expect(2 == S.doTheTest(S.P2));
|
|
try expect(3 == S.doTheTest(S.P3));
|
|
comptime try expect(1 == S.doTheTest(S.P1));
|
|
comptime try expect(2 == S.doTheTest(S.P2));
|
|
comptime try expect(3 == S.doTheTest(S.P3));
|
|
}
|
|
|
|
test "switch on error set with single else" {
|
|
const S = struct {
|
|
fn doTheTest() !void {
|
|
var some: error{Foo} = error.Foo;
|
|
try expect(switch (some) {
|
|
else => blk: {
|
|
break :blk true;
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
try S.doTheTest();
|
|
comptime try S.doTheTest();
|
|
}
|
|
|
|
test "switch capture copies its payload" {
|
|
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 {
|
|
fn doTheTest() !void {
|
|
var tmp: union(enum) {
|
|
A: u8,
|
|
B: u32,
|
|
} = .{ .A = 42 };
|
|
switch (tmp) {
|
|
.A => |value| {
|
|
// Modify the original union
|
|
tmp = .{ .B = 0x10101010 };
|
|
try expectEqual(@as(u8, 42), value);
|
|
},
|
|
else => unreachable,
|
|
}
|
|
}
|
|
};
|
|
try S.doTheTest();
|
|
comptime try S.doTheTest();
|
|
}
|
|
|
|
test "capture of integer forwards the switch condition directly" {
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
|
|
const S = struct {
|
|
fn foo(x: u8) !void {
|
|
switch (x) {
|
|
40...45 => |capture| {
|
|
try expect(capture == 42);
|
|
},
|
|
else => |capture| {
|
|
try expect(capture == 100);
|
|
},
|
|
}
|
|
}
|
|
};
|
|
try S.foo(42);
|
|
try S.foo(100);
|
|
comptime try S.foo(42);
|
|
comptime try S.foo(100);
|
|
}
|
|
|
|
test "enum value without tag name used as switch item" {
|
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
|
|
|
const E = enum(u32) {
|
|
a = 1,
|
|
b = 2,
|
|
_,
|
|
};
|
|
var e: E = @intToEnum(E, 0);
|
|
switch (e) {
|
|
@intToEnum(E, 0) => {},
|
|
.a => return error.TestFailed,
|
|
.b => return error.TestFailed,
|
|
_ => return error.TestFailed,
|
|
}
|
|
}
|
|
|
|
test "switch item sizeof" {
|
|
const S = struct {
|
|
fn doTheTest() !void {
|
|
var a: usize = 0;
|
|
switch (a) {
|
|
@sizeOf(struct {}) => {},
|
|
else => return error.TestFailed,
|
|
}
|
|
}
|
|
};
|
|
try S.doTheTest();
|
|
comptime try S.doTheTest();
|
|
}
|
|
|
|
test "comptime inline switch" {
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const U = union(enum) { a: type, b: type };
|
|
const value = comptime blk: {
|
|
var u: U = .{ .a = u32 };
|
|
break :blk switch (u) {
|
|
inline .a, .b => |v| v,
|
|
};
|
|
};
|
|
|
|
try expectEqual(u32, value);
|
|
}
|
|
|
|
test "switch capture peer type resolution" {
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const U = union(enum) {
|
|
a: u32,
|
|
b: u64,
|
|
fn innerVal(u: @This()) u64 {
|
|
switch (u) {
|
|
.a, .b => |x| return x,
|
|
}
|
|
}
|
|
};
|
|
|
|
try expectEqual(@as(u64, 100), U.innerVal(.{ .a = 100 }));
|
|
try expectEqual(@as(u64, 200), U.innerVal(.{ .b = 200 }));
|
|
}
|
|
|
|
test "switch capture peer type resolution for in-memory coercible payloads" {
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const T1 = c_int;
|
|
const T2 = @Type(@typeInfo(T1));
|
|
|
|
comptime assert(T1 != T2);
|
|
|
|
const U = union(enum) {
|
|
a: T1,
|
|
b: T2,
|
|
fn innerVal(u: @This()) c_int {
|
|
switch (u) {
|
|
.a, .b => |x| return x,
|
|
}
|
|
}
|
|
};
|
|
|
|
try expectEqual(@as(c_int, 100), U.innerVal(.{ .a = 100 }));
|
|
try expectEqual(@as(c_int, 200), U.innerVal(.{ .b = 200 }));
|
|
}
|
|
|
|
test "switch pointer capture peer type resolution" {
|
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
|
|
|
const T1 = c_int;
|
|
const T2 = @Type(@typeInfo(T1));
|
|
|
|
comptime assert(T1 != T2);
|
|
|
|
const U = union(enum) {
|
|
a: T1,
|
|
b: T2,
|
|
fn innerVal(u: *@This()) *c_int {
|
|
switch (u.*) {
|
|
.a, .b => |*ptr| return ptr,
|
|
}
|
|
}
|
|
};
|
|
|
|
var ua: U = .{ .a = 100 };
|
|
var ub: U = .{ .b = 200 };
|
|
|
|
ua.innerVal().* = 111;
|
|
ub.innerVal().* = 222;
|
|
|
|
try expectEqual(U{ .a = 111 }, ua);
|
|
try expectEqual(U{ .b = 222 }, ub);
|
|
}
|