Commit fdd11f6cee

Andrew Kelley <andrew@ziglang.org>
2021-10-16 21:41:03
Sema: coercion from error sets to `anyerror`
1 parent 4d6d697
src/Sema.zig
@@ -4225,7 +4225,7 @@ fn zirErrorToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
     const src = inst_data.src();
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const op = sema.resolveInst(inst_data.operand);
-    const op_coerced = try sema.coerce(block, Type.initTag(.anyerror), op, operand_src);
+    const op_coerced = try sema.coerce(block, Type.anyerror, op, operand_src);
     const result_ty = Type.initTag(.u16);
 
     if (try sema.resolveMaybeUndefVal(block, src, op_coerced)) |val| {
@@ -4263,7 +4263,7 @@ fn zirIntToError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
             .base = .{ .tag = .@"error" },
             .data = .{ .name = sema.mod.error_name_list.items[@intCast(usize, int)] },
         };
-        return sema.addConstant(Type.initTag(.anyerror), Value.initPayload(&payload.base));
+        return sema.addConstant(Type.anyerror, Value.initPayload(&payload.base));
     }
     try sema.requireRuntimeBlock(block, src);
     if (block.wantSafety()) {
@@ -4271,7 +4271,7 @@ fn zirIntToError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
         // const is_gt_max = @panic("TODO get max errors in compilation");
         // try sema.addSafetyCheck(block, is_gt_max, .invalid_error_code);
     }
-    return block.addTyOp(.bitcast, Type.initTag(.anyerror), op);
+    return block.addTyOp(.bitcast, Type.anyerror, op);
 }
 
 fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -11605,6 +11605,15 @@ fn coerce(
             // T to E!T or E to E!T
             return sema.wrapErrorUnion(block, dest_ty, inst, inst_src);
         },
+        .ErrorSet => {
+            // Coercion to `anyerror`.
+            // TODO If the dest type tag is not `anyerror` it still could
+            // resolve to anyerror. `dest_ty` needs to have inferred error set resolution
+            // happen before this check.
+            if (dest_ty.tag() == .anyerror and inst_ty.zigTypeTag() == .ErrorSet) {
+                return sema.coerceErrSetToAnyError(block, inst, inst_src);
+            }
+        },
         .Union => switch (inst_ty.zigTypeTag()) {
             .Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src),
             else => {},
@@ -12236,6 +12245,20 @@ fn coerceVectorToArray(
     return block.addTyOp(.bitcast, array_ty, vector);
 }
 
+fn coerceErrSetToAnyError(
+    sema: *Sema,
+    block: *Block,
+    err_set: Air.Inst.Ref,
+    err_set_src: LazySrcLoc,
+) !Air.Inst.Ref {
+    if (try sema.resolveDefinedValue(block, err_set_src, err_set)) |err_set_val| {
+        // Same representation works.
+        return sema.addConstant(Type.anyerror, err_set_val);
+    }
+    try sema.requireRuntimeBlock(block, err_set_src);
+    return block.addTyOp(.bitcast, Type.anyerror, err_set);
+}
+
 fn analyzeDeclVal(
     sema: *Sema,
     block: *Block,
src/type.zig
@@ -3962,6 +3962,7 @@ pub const Type = extern union {
     pub const @"comptime_int" = initTag(.comptime_int);
     pub const @"void" = initTag(.void);
     pub const @"type" = initTag(.type);
+    pub const @"anyerror" = initTag(.anyerror);
 
     pub fn ptr(arena: *Allocator, d: Payload.Pointer.Data) !Type {
         assert(d.host_size == 0 or d.bit_offset < d.host_size * 8);
test/behavior/error.zig
@@ -4,33 +4,6 @@ const expectError = std.testing.expectError;
 const expectEqual = std.testing.expectEqual;
 const mem = std.mem;
 
-pub fn foo() anyerror!i32 {
-    const x = try bar();
-    return x + 1;
-}
-
-pub fn bar() anyerror!i32 {
-    return 13;
-}
-
-pub fn baz() anyerror!i32 {
-    const y = foo() catch 1234;
-    return y + 1;
-}
-
-test "error wrapping" {
-    try expect((baz() catch unreachable) == 15);
-}
-
-fn gimmeItBroke() []const u8 {
-    return @errorName(error.ItBroke);
-}
-
-test "@errorName" {
-    try expect(mem.eql(u8, @errorName(error.AnError), "AnError"));
-    try expect(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName"));
-}
-
 test "error values" {
     const a = @errorToInt(error.err1);
     const b = @errorToInt(error.err2);
@@ -54,409 +27,7 @@ fn errBinaryOperatorG(x: bool) anyerror!isize {
     return if (x) error.ItBroke else @as(isize, 10);
 }
 
-test "unwrap simple value from error" {
-    const i = unwrapSimpleValueFromErrorDo() catch unreachable;
-    try expect(i == 13);
-}
-fn unwrapSimpleValueFromErrorDo() anyerror!isize {
-    return 13;
-}
-
-test "error return in assignment" {
-    doErrReturnInAssignment() catch unreachable;
-}
-
-fn doErrReturnInAssignment() anyerror!void {
-    var x: i32 = undefined;
-    x = try makeANonErr();
-}
-
-fn makeANonErr() anyerror!i32 {
-    return 1;
-}
-
-test "error union type " {
-    try testErrorUnionType();
-    comptime try testErrorUnionType();
-}
-
-fn testErrorUnionType() !void {
-    const x: anyerror!i32 = 1234;
-    if (x) |value| try expect(value == 1234) else |_| unreachable;
-    try expect(@typeInfo(@TypeOf(x)) == .ErrorUnion);
-    try expect(@typeInfo(@typeInfo(@TypeOf(x)).ErrorUnion.error_set) == .ErrorSet);
-    try expect(@typeInfo(@TypeOf(x)).ErrorUnion.error_set == anyerror);
-}
-
-test "error set type" {
-    try testErrorSetType();
-    comptime try testErrorSetType();
-}
-
-const MyErrSet = error{
-    OutOfMemory,
-    FileNotFound,
-};
-
-fn testErrorSetType() !void {
-    try expect(@typeInfo(MyErrSet).ErrorSet.?.len == 2);
-
-    const a: MyErrSet!i32 = 5678;
-    const b: MyErrSet!i32 = MyErrSet.OutOfMemory;
-    try expect(b catch error.OutOfMemory == error.OutOfMemory);
-
-    if (a) |value| try expect(value == 5678) else |err| switch (err) {
-        error.OutOfMemory => unreachable,
-        error.FileNotFound => unreachable,
-    }
-}
-
-test "explicit error set cast" {
-    try testExplicitErrorSetCast(Set1.A);
-    comptime try testExplicitErrorSetCast(Set1.A);
-}
-
-const Set1 = error{
-    A,
-    B,
-};
-const Set2 = error{
-    A,
-    C,
-};
-
-fn testExplicitErrorSetCast(set1: Set1) !void {
-    var x = @errSetCast(Set2, set1);
-    var y = @errSetCast(Set1, x);
-    try expect(y == error.A);
-}
-
-test "comptime test error for empty error set" {
-    try testComptimeTestErrorEmptySet(1234);
-    comptime try testComptimeTestErrorEmptySet(1234);
-}
-
-const EmptyErrorSet = error{};
-
-fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void {
-    if (x) |v| try expect(v == 1234) else |err| {
-        _ = err;
-        @compileError("bad");
-    }
-}
-
-test "syntax: optional operator in front of error union operator" {
-    comptime {
-        try expect(?(anyerror!i32) == ?(anyerror!i32));
-    }
-}
-
-test "comptime err to int of error set with only 1 possible value" {
-    testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
-    comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
-}
-fn testErrToIntWithOnePossibleValue(
-    x: error{A},
-    comptime value: u32,
-) void {
-    if (@errorToInt(x) != value) {
-        @compileError("bad");
-    }
-}
-
 test "empty error union" {
     const x = error{} || error{};
     _ = x;
 }
-
-test "error union peer type resolution" {
-    try testErrorUnionPeerTypeResolution(1);
-}
-
-fn testErrorUnionPeerTypeResolution(x: i32) !void {
-    const y = switch (x) {
-        1 => bar_1(),
-        2 => baz_1(),
-        else => quux_1(),
-    };
-    if (y) |_| {
-        @panic("expected error");
-    } else |e| {
-        try expect(e == error.A);
-    }
-}
-
-fn bar_1() anyerror {
-    return error.A;
-}
-
-fn baz_1() !i32 {
-    return error.B;
-}
-
-fn quux_1() !i32 {
-    return error.C;
-}
-
-test "error: fn returning empty error set can be passed as fn returning any error" {
-    entry();
-    comptime entry();
-}
-
-fn entry() void {
-    foo2(bar2);
-}
-
-fn foo2(f: fn () anyerror!void) void {
-    const x = f();
-    x catch {};
-}
-
-fn bar2() (error{}!void) {}
-
-test "error: Zero sized error set returned with value payload crash" {
-    _ = foo3(0) catch {};
-    _ = comptime foo3(0) catch {};
-}
-
-const Error = error{};
-fn foo3(b: usize) Error!usize {
-    return b;
-}
-
-test "error: Infer error set from literals" {
-    _ = nullLiteral("n") catch |err| handleErrors(err);
-    _ = floatLiteral("n") catch |err| handleErrors(err);
-    _ = intLiteral("n") catch |err| handleErrors(err);
-    _ = comptime nullLiteral("n") catch |err| handleErrors(err);
-    _ = comptime floatLiteral("n") catch |err| handleErrors(err);
-    _ = comptime intLiteral("n") catch |err| handleErrors(err);
-}
-
-fn handleErrors(err: anytype) noreturn {
-    switch (err) {
-        error.T => {},
-    }
-
-    unreachable;
-}
-
-fn nullLiteral(str: []const u8) !?i64 {
-    if (str[0] == 'n') return null;
-
-    return error.T;
-}
-
-fn floatLiteral(str: []const u8) !?f64 {
-    if (str[0] == 'n') return 1.0;
-
-    return error.T;
-}
-
-fn intLiteral(str: []const u8) !?i64 {
-    if (str[0] == 'n') return 1;
-
-    return error.T;
-}
-
-test "nested error union function call in optional unwrap" {
-    const S = struct {
-        const Foo = struct {
-            a: i32,
-        };
-
-        fn errorable() !i32 {
-            var x: Foo = (try getFoo()) orelse return error.Other;
-            return x.a;
-        }
-
-        fn errorable2() !i32 {
-            var x: Foo = (try getFoo2()) orelse return error.Other;
-            return x.a;
-        }
-
-        fn errorable3() !i32 {
-            var x: Foo = (try getFoo3()) orelse return error.Other;
-            return x.a;
-        }
-
-        fn getFoo() anyerror!?Foo {
-            return Foo{ .a = 1234 };
-        }
-
-        fn getFoo2() anyerror!?Foo {
-            return error.Failure;
-        }
-
-        fn getFoo3() anyerror!?Foo {
-            return null;
-        }
-    };
-    try expect((try S.errorable()) == 1234);
-    try expectError(error.Failure, S.errorable2());
-    try expectError(error.Other, S.errorable3());
-    comptime {
-        try expect((try S.errorable()) == 1234);
-        try expectError(error.Failure, S.errorable2());
-        try expectError(error.Other, S.errorable3());
-    }
-}
-
-test "widen cast integer payload of error union function call" {
-    const S = struct {
-        fn errorable() !u64 {
-            var x = @as(u64, try number());
-            return x;
-        }
-
-        fn number() anyerror!u32 {
-            return 1234;
-        }
-    };
-    try expect((try S.errorable()) == 1234);
-}
-
-test "return function call to error set from error union function" {
-    const S = struct {
-        fn errorable() anyerror!i32 {
-            return fail();
-        }
-
-        fn fail() anyerror {
-            return error.Failure;
-        }
-    };
-    try expectError(error.Failure, S.errorable());
-    comptime try expectError(error.Failure, S.errorable());
-}
-
-test "optional error set is the same size as error set" {
-    comptime try expect(@sizeOf(?anyerror) == @sizeOf(anyerror));
-    const S = struct {
-        fn returnsOptErrSet() ?anyerror {
-            return null;
-        }
-    };
-    try expect(S.returnsOptErrSet() == null);
-    comptime try expect(S.returnsOptErrSet() == null);
-}
-
-test "debug info for optional error set" {
-    const SomeError = error{Hello};
-    var a_local_variable: ?SomeError = null;
-    _ = a_local_variable;
-}
-
-test "nested catch" {
-    const S = struct {
-        fn entry() !void {
-            try expectError(error.Bad, func());
-        }
-        fn fail() anyerror!Foo {
-            return error.Wrong;
-        }
-        fn func() anyerror!Foo {
-            _ = fail() catch
-                fail() catch
-                return error.Bad;
-            unreachable;
-        }
-        const Foo = struct {
-            field: i32,
-        };
-    };
-    try S.entry();
-    comptime try S.entry();
-}
-
-test "implicit cast to optional to error union to return result loc" {
-    const S = struct {
-        fn entry() !void {
-            var x: Foo = undefined;
-            if (func(&x)) |opt| {
-                try expect(opt != null);
-            } else |_| @panic("expected non error");
-        }
-        fn func(f: *Foo) anyerror!?*Foo {
-            return f;
-        }
-        const Foo = struct {
-            field: i32,
-        };
-    };
-    try S.entry();
-    //comptime S.entry(); TODO
-}
-
-test "function pointer with return type that is error union with payload which is pointer of parent struct" {
-    const S = struct {
-        const Foo = struct {
-            fun: fn (a: i32) (anyerror!*Foo),
-        };
-
-        const Err = error{UnspecifiedErr};
-
-        fn bar(a: i32) anyerror!*Foo {
-            _ = a;
-            return Err.UnspecifiedErr;
-        }
-
-        fn doTheTest() !void {
-            var x = Foo{ .fun = @This().bar };
-            try expectError(error.UnspecifiedErr, x.fun(1));
-        }
-    };
-    try S.doTheTest();
-}
-
-test "return result loc as peer result loc in inferred error set function" {
-    const S = struct {
-        fn doTheTest() !void {
-            if (quux(2)) |x| {
-                try expect(x.Two);
-            } else |e| switch (e) {
-                error.Whatever => @panic("fail"),
-            }
-            try expectError(error.Whatever, quux(99));
-        }
-        const FormValue = union(enum) {
-            One: void,
-            Two: bool,
-        };
-
-        fn quux(id: u64) !FormValue {
-            return switch (id) {
-                2 => FormValue{ .Two = true },
-                1 => FormValue{ .One = {} },
-                else => return error.Whatever,
-            };
-        }
-    };
-    try S.doTheTest();
-    comptime try S.doTheTest();
-}
-
-test "error payload type is correctly resolved" {
-    const MyIntWrapper = struct {
-        const Self = @This();
-
-        x: i32,
-
-        pub fn create() anyerror!Self {
-            return Self{ .x = 42 };
-        }
-    };
-
-    try expectEqual(MyIntWrapper{ .x = 42 }, try MyIntWrapper.create());
-}
-
-test "error union comptime caching" {
-    const S = struct {
-        fn quux(comptime arg: anytype) void {
-            arg catch {};
-        }
-    };
-
-    S.quux(@as(anyerror!void, {}));
-    S.quux(@as(anyerror!void, {}));
-}
test/behavior/error_stage1.zig
@@ -0,0 +1,428 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const expectError = std.testing.expectError;
+const expectEqual = std.testing.expectEqual;
+const mem = std.mem;
+
+pub fn foo() anyerror!i32 {
+    const x = try bar();
+    return x + 1;
+}
+
+pub fn bar() anyerror!i32 {
+    return 13;
+}
+
+pub fn baz() anyerror!i32 {
+    const y = foo() catch 1234;
+    return y + 1;
+}
+
+test "error wrapping" {
+    try expect((baz() catch unreachable) == 15);
+}
+
+fn gimmeItBroke() []const u8 {
+    return @errorName(error.ItBroke);
+}
+
+test "@errorName" {
+    try expect(mem.eql(u8, @errorName(error.AnError), "AnError"));
+    try expect(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName"));
+}
+
+test "unwrap simple value from error" {
+    const i = unwrapSimpleValueFromErrorDo() catch unreachable;
+    try expect(i == 13);
+}
+fn unwrapSimpleValueFromErrorDo() anyerror!isize {
+    return 13;
+}
+
+test "error return in assignment" {
+    doErrReturnInAssignment() catch unreachable;
+}
+
+fn doErrReturnInAssignment() anyerror!void {
+    var x: i32 = undefined;
+    x = try makeANonErr();
+}
+
+fn makeANonErr() anyerror!i32 {
+    return 1;
+}
+
+test "error union type " {
+    try testErrorUnionType();
+    comptime try testErrorUnionType();
+}
+
+fn testErrorUnionType() !void {
+    const x: anyerror!i32 = 1234;
+    if (x) |value| try expect(value == 1234) else |_| unreachable;
+    try expect(@typeInfo(@TypeOf(x)) == .ErrorUnion);
+    try expect(@typeInfo(@typeInfo(@TypeOf(x)).ErrorUnion.error_set) == .ErrorSet);
+    try expect(@typeInfo(@TypeOf(x)).ErrorUnion.error_set == anyerror);
+}
+
+test "error set type" {
+    try testErrorSetType();
+    comptime try testErrorSetType();
+}
+
+const MyErrSet = error{
+    OutOfMemory,
+    FileNotFound,
+};
+
+fn testErrorSetType() !void {
+    try expect(@typeInfo(MyErrSet).ErrorSet.?.len == 2);
+
+    const a: MyErrSet!i32 = 5678;
+    const b: MyErrSet!i32 = MyErrSet.OutOfMemory;
+    try expect(b catch error.OutOfMemory == error.OutOfMemory);
+
+    if (a) |value| try expect(value == 5678) else |err| switch (err) {
+        error.OutOfMemory => unreachable,
+        error.FileNotFound => unreachable,
+    }
+}
+
+test "explicit error set cast" {
+    try testExplicitErrorSetCast(Set1.A);
+    comptime try testExplicitErrorSetCast(Set1.A);
+}
+
+const Set1 = error{ A, B };
+const Set2 = error{ A, C };
+
+fn testExplicitErrorSetCast(set1: Set1) !void {
+    var x = @errSetCast(Set2, set1);
+    var y = @errSetCast(Set1, x);
+    try expect(y == error.A);
+}
+
+test "comptime test error for empty error set" {
+    try testComptimeTestErrorEmptySet(1234);
+    comptime try testComptimeTestErrorEmptySet(1234);
+}
+
+const EmptyErrorSet = error{};
+
+fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void {
+    if (x) |v| try expect(v == 1234) else |err| {
+        _ = err;
+        @compileError("bad");
+    }
+}
+
+test "syntax: optional operator in front of error union operator" {
+    comptime {
+        try expect(?(anyerror!i32) == ?(anyerror!i32));
+    }
+}
+
+test "comptime err to int of error set with only 1 possible value" {
+    testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
+    comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
+}
+fn testErrToIntWithOnePossibleValue(
+    x: error{A},
+    comptime value: u32,
+) void {
+    if (@errorToInt(x) != value) {
+        @compileError("bad");
+    }
+}
+
+test "error union peer type resolution" {
+    try testErrorUnionPeerTypeResolution(1);
+}
+
+fn testErrorUnionPeerTypeResolution(x: i32) !void {
+    const y = switch (x) {
+        1 => bar_1(),
+        2 => baz_1(),
+        else => quux_1(),
+    };
+    if (y) |_| {
+        @panic("expected error");
+    } else |e| {
+        try expect(e == error.A);
+    }
+}
+
+fn bar_1() anyerror {
+    return error.A;
+}
+
+fn baz_1() !i32 {
+    return error.B;
+}
+
+fn quux_1() !i32 {
+    return error.C;
+}
+
+test "error: fn returning empty error set can be passed as fn returning any error" {
+    entry();
+    comptime entry();
+}
+
+fn entry() void {
+    foo2(bar2);
+}
+
+fn foo2(f: fn () anyerror!void) void {
+    const x = f();
+    x catch {};
+}
+
+fn bar2() (error{}!void) {}
+
+test "error: Zero sized error set returned with value payload crash" {
+    _ = foo3(0) catch {};
+    _ = comptime foo3(0) catch {};
+}
+
+const Error = error{};
+fn foo3(b: usize) Error!usize {
+    return b;
+}
+
+test "error: Infer error set from literals" {
+    _ = nullLiteral("n") catch |err| handleErrors(err);
+    _ = floatLiteral("n") catch |err| handleErrors(err);
+    _ = intLiteral("n") catch |err| handleErrors(err);
+    _ = comptime nullLiteral("n") catch |err| handleErrors(err);
+    _ = comptime floatLiteral("n") catch |err| handleErrors(err);
+    _ = comptime intLiteral("n") catch |err| handleErrors(err);
+}
+
+fn handleErrors(err: anytype) noreturn {
+    switch (err) {
+        error.T => {},
+    }
+
+    unreachable;
+}
+
+fn nullLiteral(str: []const u8) !?i64 {
+    if (str[0] == 'n') return null;
+
+    return error.T;
+}
+
+fn floatLiteral(str: []const u8) !?f64 {
+    if (str[0] == 'n') return 1.0;
+
+    return error.T;
+}
+
+fn intLiteral(str: []const u8) !?i64 {
+    if (str[0] == 'n') return 1;
+
+    return error.T;
+}
+
+test "nested error union function call in optional unwrap" {
+    const S = struct {
+        const Foo = struct {
+            a: i32,
+        };
+
+        fn errorable() !i32 {
+            var x: Foo = (try getFoo()) orelse return error.Other;
+            return x.a;
+        }
+
+        fn errorable2() !i32 {
+            var x: Foo = (try getFoo2()) orelse return error.Other;
+            return x.a;
+        }
+
+        fn errorable3() !i32 {
+            var x: Foo = (try getFoo3()) orelse return error.Other;
+            return x.a;
+        }
+
+        fn getFoo() anyerror!?Foo {
+            return Foo{ .a = 1234 };
+        }
+
+        fn getFoo2() anyerror!?Foo {
+            return error.Failure;
+        }
+
+        fn getFoo3() anyerror!?Foo {
+            return null;
+        }
+    };
+    try expect((try S.errorable()) == 1234);
+    try expectError(error.Failure, S.errorable2());
+    try expectError(error.Other, S.errorable3());
+    comptime {
+        try expect((try S.errorable()) == 1234);
+        try expectError(error.Failure, S.errorable2());
+        try expectError(error.Other, S.errorable3());
+    }
+}
+
+test "widen cast integer payload of error union function call" {
+    const S = struct {
+        fn errorable() !u64 {
+            var x = @as(u64, try number());
+            return x;
+        }
+
+        fn number() anyerror!u32 {
+            return 1234;
+        }
+    };
+    try expect((try S.errorable()) == 1234);
+}
+
+test "return function call to error set from error union function" {
+    const S = struct {
+        fn errorable() anyerror!i32 {
+            return fail();
+        }
+
+        fn fail() anyerror {
+            return error.Failure;
+        }
+    };
+    try expectError(error.Failure, S.errorable());
+    comptime try expectError(error.Failure, S.errorable());
+}
+
+test "optional error set is the same size as error set" {
+    comptime try expect(@sizeOf(?anyerror) == @sizeOf(anyerror));
+    const S = struct {
+        fn returnsOptErrSet() ?anyerror {
+            return null;
+        }
+    };
+    try expect(S.returnsOptErrSet() == null);
+    comptime try expect(S.returnsOptErrSet() == null);
+}
+
+test "debug info for optional error set" {
+    const SomeError = error{Hello};
+    var a_local_variable: ?SomeError = null;
+    _ = a_local_variable;
+}
+
+test "nested catch" {
+    const S = struct {
+        fn entry() !void {
+            try expectError(error.Bad, func());
+        }
+        fn fail() anyerror!Foo {
+            return error.Wrong;
+        }
+        fn func() anyerror!Foo {
+            _ = fail() catch
+                fail() catch
+                return error.Bad;
+            unreachable;
+        }
+        const Foo = struct {
+            field: i32,
+        };
+    };
+    try S.entry();
+    comptime try S.entry();
+}
+
+test "implicit cast to optional to error union to return result loc" {
+    const S = struct {
+        fn entry() !void {
+            var x: Foo = undefined;
+            if (func(&x)) |opt| {
+                try expect(opt != null);
+            } else |_| @panic("expected non error");
+        }
+        fn func(f: *Foo) anyerror!?*Foo {
+            return f;
+        }
+        const Foo = struct {
+            field: i32,
+        };
+    };
+    try S.entry();
+    //comptime S.entry(); TODO
+}
+
+test "function pointer with return type that is error union with payload which is pointer of parent struct" {
+    const S = struct {
+        const Foo = struct {
+            fun: fn (a: i32) (anyerror!*Foo),
+        };
+
+        const Err = error{UnspecifiedErr};
+
+        fn bar(a: i32) anyerror!*Foo {
+            _ = a;
+            return Err.UnspecifiedErr;
+        }
+
+        fn doTheTest() !void {
+            var x = Foo{ .fun = @This().bar };
+            try expectError(error.UnspecifiedErr, x.fun(1));
+        }
+    };
+    try S.doTheTest();
+}
+
+test "return result loc as peer result loc in inferred error set function" {
+    const S = struct {
+        fn doTheTest() !void {
+            if (quux(2)) |x| {
+                try expect(x.Two);
+            } else |e| switch (e) {
+                error.Whatever => @panic("fail"),
+            }
+            try expectError(error.Whatever, quux(99));
+        }
+        const FormValue = union(enum) {
+            One: void,
+            Two: bool,
+        };
+
+        fn quux(id: u64) !FormValue {
+            return switch (id) {
+                2 => FormValue{ .Two = true },
+                1 => FormValue{ .One = {} },
+                else => return error.Whatever,
+            };
+        }
+    };
+    try S.doTheTest();
+    comptime try S.doTheTest();
+}
+
+test "error payload type is correctly resolved" {
+    const MyIntWrapper = struct {
+        const Self = @This();
+
+        x: i32,
+
+        pub fn create() anyerror!Self {
+            return Self{ .x = 42 };
+        }
+    };
+
+    try expectEqual(MyIntWrapper{ .x = 42 }, try MyIntWrapper.create());
+}
+
+test "error union comptime caching" {
+    const S = struct {
+        fn quux(comptime arg: anytype) void {
+            arg catch {};
+        }
+    };
+
+    S.quux(@as(anyerror!void, {}));
+    S.quux(@as(anyerror!void, {}));
+}
test/behavior.zig
@@ -30,6 +30,7 @@ test {
     _ = @import("behavior/cast.zig");
     _ = @import("behavior/defer.zig");
     _ = @import("behavior/enum.zig");
+    _ = @import("behavior/error.zig");
     _ = @import("behavior/eval.zig");
     _ = @import("behavior/for.zig");
     _ = @import("behavior/generics.zig");
@@ -116,7 +117,7 @@ test {
         _ = @import("behavior/const_slice_child.zig");
         _ = @import("behavior/defer_stage1.zig");
         _ = @import("behavior/enum_stage1.zig");
-        _ = @import("behavior/error.zig");
+        _ = @import("behavior/error_stage1.zig");
         _ = @import("behavior/eval_stage1.zig");
         _ = @import("behavior/field_parent_ptr.zig");
         _ = @import("behavior/floatop.zig");