test: add tests for switch_block_err_union

This commit is contained in:
dweiller 2023-11-30 19:12:47 +11:00
parent fc6dc797ce
commit 69ab687156
8 changed files with 935 additions and 0 deletions

View File

@ -0,0 +1,750 @@
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 on error union catch capture" {
const S = struct {
const Error = error{ A, B, C };
fn doTheTest() !void {
try testScalar();
try testMulti();
try testElse();
try testCapture();
try testInline();
try testEmptyErrSet();
}
fn testScalar() !void {
{
var a: Error!u64 = 3;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.A => 0,
error.B => 1,
error.C => 2,
};
try expectEqual(@as(u64, 3), b);
}
{
var a: Error!u64 = 3;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.A => 0,
error.B => @intFromError(err) + 4,
error.C => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 3), b);
}
{
var a: Error!u64 = error.A;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.A => 0,
error.B => @intFromError(err) + 4,
error.C => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 0), b);
}
}
fn testMulti() !void {
{
var a: Error!u64 = 3;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.A, error.B => 0,
error.C => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 3), b);
}
{
var a: Error!u64 = 3;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.A => 0,
error.B, error.C => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 3), b);
}
{
var a: Error!u64 = error.A;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.A, error.B => 0,
error.C => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 0), b);
}
{
var a: Error!u64 = error.A;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.A => 0,
error.B, error.C => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 0), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.A => 0,
error.B, error.C => @intFromError(err) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
}
fn testElse() !void {
{
var a: Error!u64 = 3;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.A => 0,
else => 1,
};
try expectEqual(@as(u64, 3), b);
}
{
var a: Error!u64 = 3;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.A => 0,
else => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 3), b);
}
{
var a: Error!u64 = error.A;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.A => 1,
else => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 1), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.A => 0,
else => 1,
};
try expectEqual(@as(u64, 1), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.A => 0,
else => @intFromError(err) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
}
fn testCapture() !void {
{
var a: Error!u64 = error.A;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.A => |e| @intFromError(e) + 4,
else => 0,
};
try expectEqual(@as(u64, @intFromError(error.A) + 4), b);
}
{
var a: Error!u64 = error.A;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.A => 0,
else => |e| @intFromError(e) + 4,
};
try expectEqual(@as(u64, 0), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.A => 0,
else => |e| @intFromError(e) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.A => |e| @intFromError(e) + 4,
else => |e| @intFromError(e) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.A => 0,
error.B, error.C => |e| @intFromError(e) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
}
fn testInline() !void {
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.A => 0,
inline else => @intFromError(err) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.A => |e| @intFromError(e) + 4,
inline else => @intFromError(err) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = a catch |err| switch (err) {
inline else => |e| @intFromError(e) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.A => 0,
inline error.B, error.C => |e| @intFromError(e) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
}
fn testEmptyErrSet() !void {
{
var a: error{}!u64 = 0;
_ = &a;
const b: u64 = a catch |err| switch (err) {
else => |e| return e,
};
try expectEqual(@as(u64, 0), b);
}
{
var a: error{}!u64 = 0;
_ = &a;
const b: u64 = a catch |err| switch (err) {
error.UnknownError => return error.Fail,
else => |e| return e,
};
try expectEqual(@as(u64, 0), b);
}
}
};
try comptime S.doTheTest();
try S.doTheTest();
}
test "switch on error union if else capture" {
const S = struct {
const Error = error{ A, B, C };
fn doTheTest() !void {
try testScalar();
try testScalarPtr();
try testMulti();
try testMultiPtr();
try testElse();
try testElsePtr();
try testCapture();
try testCapturePtr();
try testInline();
try testInlinePtr();
try testEmptyErrSet();
try testEmptyErrSetPtr();
}
fn testScalar() !void {
{
var a: Error!u64 = 3;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.A => 0,
error.B => 1,
error.C => 2,
};
try expectEqual(@as(u64, 3), b);
}
{
var a: Error!u64 = 3;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.A => 0,
error.B => @intFromError(err) + 4,
error.C => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 3), b);
}
{
var a: Error!u64 = error.A;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.A => 0,
error.B => @intFromError(err) + 4,
error.C => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 0), b);
}
}
fn testScalarPtr() !void {
{
var a: Error!u64 = 3;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.A => 0,
error.B => 1,
error.C => 2,
};
try expectEqual(@as(u64, 3), b);
}
{
var a: Error!u64 = 3;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.A => 0,
error.B => @intFromError(err) + 4,
error.C => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 3), b);
}
{
var a: Error!u64 = error.A;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.A => 0,
error.B => @intFromError(err) + 4,
error.C => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 0), b);
}
}
fn testMulti() !void {
{
var a: Error!u64 = 3;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.A, error.B => 0,
error.C => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 3), b);
}
{
var a: Error!u64 = 3;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.A => 0,
error.B, error.C => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 3), b);
}
{
var a: Error!u64 = error.A;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.A, error.B => 0,
error.C => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 0), b);
}
{
var a: Error!u64 = error.A;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.A => 0,
error.B, error.C => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 0), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.A => 0,
error.B, error.C => @intFromError(err) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
}
fn testMultiPtr() !void {
{
var a: Error!u64 = 3;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.A, error.B => 0,
error.C => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 3), b);
}
{
var a: Error!u64 = 3;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.A => 0,
error.B, error.C => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 3), b);
}
{
var a: Error!u64 = error.A;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.A, error.B => 0,
error.C => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 0), b);
}
{
var a: Error!u64 = error.A;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.A => 0,
error.B, error.C => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 0), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.A => 0,
error.B, error.C => @intFromError(err) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
}
fn testElse() !void {
{
var a: Error!u64 = 3;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.A => 0,
else => 1,
};
try expectEqual(@as(u64, 3), b);
}
{
var a: Error!u64 = 3;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.A => 0,
else => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 3), b);
}
{
var a: Error!u64 = error.A;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.A => 1,
else => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 1), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.A => 0,
else => 1,
};
try expectEqual(@as(u64, 1), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.A => 0,
else => @intFromError(err) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
}
fn testElsePtr() !void {
{
var a: Error!u64 = 3;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.A => 0,
else => 1,
};
try expectEqual(@as(u64, 3), b);
}
{
var a: Error!u64 = 3;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.A => 0,
else => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 3), b);
}
{
var a: Error!u64 = error.A;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.A => 1,
else => @intFromError(err) + 4,
};
try expectEqual(@as(u64, 1), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.A => 0,
else => 1,
};
try expectEqual(@as(u64, 1), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.A => 0,
else => @intFromError(err) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
}
fn testCapture() !void {
{
var a: Error!u64 = error.A;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.A => |e| @intFromError(e) + 4,
else => 0,
};
try expectEqual(@as(u64, @intFromError(error.A) + 4), b);
}
{
var a: Error!u64 = error.A;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.A => 0,
else => |e| @intFromError(e) + 4,
};
try expectEqual(@as(u64, 0), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.A => 0,
else => |e| @intFromError(e) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.A => |e| @intFromError(e) + 4,
else => |e| @intFromError(e) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.A => 0,
error.B, error.C => |e| @intFromError(e) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
}
fn testCapturePtr() !void {
{
var a: Error!u64 = error.A;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.A => |e| @intFromError(e) + 4,
else => 0,
};
try expectEqual(@as(u64, @intFromError(error.A) + 4), b);
}
{
var a: Error!u64 = error.A;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.A => 0,
else => |e| @intFromError(e) + 4,
};
try expectEqual(@as(u64, 0), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.A => 0,
else => |e| @intFromError(e) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.A => |e| @intFromError(e) + 4,
else => |e| @intFromError(e) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.A => 0,
error.B, error.C => |e| @intFromError(e) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
}
fn testInline() !void {
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.A => 0,
inline else => @intFromError(err) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.A => |e| @intFromError(e) + 4,
inline else => @intFromError(err) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
inline else => |e| @intFromError(e) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.A => 0,
inline error.B, error.C => |e| @intFromError(e) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
}
fn testInlinePtr() !void {
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.A => 0,
inline else => @intFromError(err) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.A => |e| @intFromError(e) + 4,
inline else => @intFromError(err) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
inline else => |e| @intFromError(e) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
{
var a: Error!u64 = error.B;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.A => 0,
inline error.B, error.C => |e| @intFromError(e) + 4,
};
try expectEqual(@as(u64, @intFromError(error.B) + 4), b);
}
}
fn testEmptyErrSet() !void {
{
var a: error{}!u64 = 0;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
else => |e| return e,
};
try expectEqual(@as(u64, 0), b);
}
{
var a: error{}!u64 = 0;
_ = &a;
const b: u64 = if (a) |x| x else |err| switch (err) {
error.UnknownError => return error.Fail,
else => |e| return e,
};
try expectEqual(@as(u64, 0), b);
}
}
fn testEmptyErrSetPtr() !void {
{
var a: error{}!u64 = 0;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
else => |e| return e,
};
try expectEqual(@as(u64, 0), b);
}
{
var a: error{}!u64 = 0;
_ = &a;
const b: u64 = if (a) |*x| x.* else |err| switch (err) {
error.UnknownError => return error.Fail,
else => |e| return e,
};
try expectEqual(@as(u64, 0), b);
}
}
};
try comptime S.doTheTest();
try S.doTheTest();
}

View File

@ -0,0 +1,33 @@
fn f(n: Error!i32) i32 {
if (n) |x|
_ = x
else |e| switch (e) {
error.Foo => 1,
error.Bar => 2,
error.Baz => 3,
error.Foo => 2,
}
}
fn g(n: Error!i32) i32 {
n catch |e| switch (e) {
error.Foo => 1,
error.Bar => 2,
error.Baz => 3,
error.Foo => 2,
};
}
const Error = error{ Foo, Bar, Baz };
export fn entry() usize {
return @sizeOf(@TypeOf(&f)) + @sizeOf(@TypeOf(&g));
}
// error
// backend=stage2
// target=native
//
// :8:9: error: duplicate switch value
// :5:9: note: previous value here
// :16:9: error: duplicate switch value
// :13:9: note: previous value here

View File

@ -0,0 +1,35 @@
fn f(n: Error!i32) i32 {
if (n) |x|
_ = x
else |e| switch (e) {
error.Foo => 1,
error.Bar => 2,
error.Baz => 3,
error.Foo => 2,
else => 10,
}
}
fn g(n: Error!i32) i32 {
n catch |e| switch (e) {
error.Foo => 1,
error.Bar => 2,
error.Baz => 3,
error.Foo => 2,
else => 10,
};
}
const Error = error{ Foo, Bar, Baz };
export fn entry() usize {
return @sizeOf(@TypeOf(&f)) + @sizeOf(@TypeOf(&g));
}
// error
// backend=stage2
// target=native
//
// :8:9: error: duplicate switch value
// :5:9: note: previous value here
// :17:9: error: duplicate switch value
// :14:9: note: previous value here

View File

@ -0,0 +1,33 @@
const Error = error {
One,
Two,
Three,
Four,
};
fn f(n: Error!i32) i32 {
if (n) |x| x else |e| switch (e) {
error.One => 1,
error.Two => 2,
error.Three => 3,
}
}
fn h(n: Error!i32) i32 {
n catch |e| switch (e) {
error.One => 1,
error.Two => 2,
error.Three => 3,
};
}
export fn entry() usize {
return @sizeOf(@TypeOf(&f)) + @sizeOf(@TypeOf(&h));
}
// error
// backend=stage2
// target=native
//
// :8:27: error: switch must handle all possibilities
// :8:27: note: unhandled error value: 'error.Four'
// :15:17: error: switch must handle all possibilities
// :15:17: note: unhandled error value: 'error.Four'

View File

@ -5,8 +5,24 @@ fn f(x: u32) void {
else => true,
};
}
fn g(x: error{Foo, Bar, Baz}!u32) void {
const value: bool = if (x) |_| true else |e| switch (e) {
error.Foo => false,
else => true,
else => true,
};
}
fn h(x: error{Foo, Bar, Baz}!u32) void {
const value: u32 = x catch |e| switch (e) {
error.Foo => 1,
else => 2,
else => 3,
};
}
export fn entry() void {
f(1234);
g(1234);
h(1234);
}
// error
@ -15,3 +31,7 @@ export fn entry() void {
//
// :5:9: error: multiple else prongs in switch expression
// :4:9: note: previous else prong here
// :12:9: error: multiple else prongs in switch expression
// :11:9: note: previous else prong here
// :19:9: error: multiple else prongs in switch expression
// :18:9: note: previous else prong here

View File

@ -0,0 +1,32 @@
fn foo(x: u2) void {
const y: Error!u2 = x;
if (y) |_| {} else |e| switch (e) {
error.Foo => {},
error.Bar => {},
error.Baz => {},
else => {},
}
}
fn bar(x: u2) void {
const y: Error!u2 = x;
y catch |e| switch (e) {
error.Foo => {},
error.Bar => {},
error.Baz => {},
else => {},
};
}
const Error = error{ Foo, Bar, Baz };
export fn entry() usize {
return @sizeOf(@TypeOf(&foo)) + @sizeOf(@TypeOf(&bar));
}
// error
// backend=stage2
// target=native
//
// :7:14: error: unreachable else prong; all cases already handled
// :17:14: error: unreachable else prong; all cases already handled

View File

@ -0,0 +1,12 @@
export fn entry() void {
const x: error{}!u32 = 0;
if (x) |v| v else |_| switch (_) {
}
}
// error
// backend=stage2
// target=native
//
// :3:24: error: discard of error capture; omit it instead

View File

@ -0,0 +1,20 @@
const Error = error{M};
export fn entry() void {
const f: Error!void = void{};
if (f) {} else |e| switch (e) {}
}
export fn entry2() void {
const f: Error!void = void{};
f catch |e| switch (e) {};
}
// error
// backend=stage2
// target=native
//
// :5:24: error: switch must handle all possibilities
// :5:24: note: unhandled error value: 'error.M'
// :10:17: error: switch must handle all possibilities
// :10:17: note: unhandled error value: 'error.M'