mirror of
https://github.com/ziglang/zig.git
synced 2026-01-04 12:33:19 +00:00
Sema: coercion from error sets to anyerror
This commit is contained in:
parent
4d6d6977b0
commit
fdd11f6cee
29
src/Sema.zig
29
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,
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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, {}));
|
||||
}
|
||||
|
||||
428
test/behavior/error_stage1.zig
Normal file
428
test/behavior/error_stage1.zig
Normal file
@ -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, {}));
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user