const builtin = @import("builtin"); const std = @import("std"); const expect = std.testing.expect; const expectEqual = std.testing.expectEqual; const mem = std.mem; /// A more basic implementation of std.testing.expectError which /// does not require formatter/printing support fn expectError(expected_err: anyerror, observed_err_union: anytype) !void { if (observed_err_union) |_| { return error.TestExpectedError; } else |err| if (err == expected_err) { return; // Success } return error.TestExpectedError; } test "error values" { const a = @intFromError(error.err1); const b = @intFromError(error.err2); try expect(a != b); } test "redefinition of error values allowed" { shouldBeNotEqual(error.AnError, error.SecondError); } fn shouldBeNotEqual(a: anyerror, b: anyerror) void { if (a == b) unreachable; } test "error binary operator" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const a = errBinaryOperatorG(true) catch 3; const b = errBinaryOperatorG(false) catch 3; try expect(a == 3); try expect(b == 10); } fn errBinaryOperatorG(x: bool) anyerror!isize { return if (x) error.ItBroke else @as(isize, 10); } test "empty error union" { const x = error{} || error{}; _ = x; } 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" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO try expect((baz() catch unreachable) == 15); } test "unwrap simple value from error" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const i = unwrapSimpleValueFromErrorDo() catch unreachable; try expect(i == 13); } fn unwrapSimpleValueFromErrorDo() anyerror!isize { return 13; } test "error return in assignment" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO doErrReturnInAssignment() catch unreachable; } fn doErrReturnInAssignment() anyerror!void { var x: i32 = undefined; x = try makeANonErr(); } fn makeANonErr() anyerror!i32 { return 1; } test "syntax: optional operator in front of error union operator" { comptime { try expect(?(anyerror!i32) == ?(anyerror!i32)); } } test "widen cast integer payload of error union function call" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { fn errorable() !u64 { return @as(u64, try number()); } fn number() anyerror!u32 { return 1234; } }; try expect((try S.errorable()) == 1234); } test "debug info for optional error set" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const SomeError = error{ Hello, Hello2 }; var a_local_variable: ?SomeError = null; _ = &a_local_variable; } test "implicit cast to optional to error union to return result loc" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO 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 "fn returning empty error set can be passed as fn returning any error" { entry(); comptime entry(); } test "fn returning empty error set can be passed as fn returning any error - pointer" { if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; entryPtr(); comptime entryPtr(); } fn entry() void { foo2(bar2); } fn entryPtr() void { var ptr = &bar2; _ = &ptr; fooPtr(ptr); } fn foo2(comptime f: fn () anyerror!void) void { const x = f(); x catch { @panic("fail"); }; } fn fooPtr(f: *const fn () anyerror!void) void { const x = f(); x catch { @panic("fail"); }; } fn bar2() (error{}!void) {} test "error union type " { try testErrorUnionType(); try comptime 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(); try comptime 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" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO try testExplicitErrorSetCast(Set1.A); try comptime testExplicitErrorSetCast(Set1.A); } const Set1 = error{ A, B }; const Set2 = error{ A, C }; fn testExplicitErrorSetCast(set1: Set1) !void { const x: Set2 = @errorCast(set1); try expect(@TypeOf(x) == Set2); const y: Set1 = @errorCast(x); try expect(@TypeOf(y) == Set1); try expect(y == error.A); } test "@errorCast on error unions" { const S = struct { fn doTheTest() !void { { const casted: error{Bad}!i32 = @errorCast(retErrUnion()); try expect((try casted) == 1234); } { const casted: error{Bad}!i32 = @errorCast(retInferredErrUnion()); try expect((try casted) == 5678); } } fn retErrUnion() anyerror!i32 { return 1234; } fn retInferredErrUnion() !i32 { return 5678; } }; try S.doTheTest(); try comptime S.doTheTest(); } test "comptime test error for empty error set" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO try testComptimeTestErrorEmptySet(1234); try comptime testComptimeTestErrorEmptySet(1234); } const EmptyErrorSet = error{}; fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void { if (x) |v| try expect(v == 1234) else |err| { _ = err; @compileError("bad"); } } test "comptime err to int of error set with only 1 possible value" { testErrToIntWithOnePossibleValue(error.A, @intFromError(error.A)); comptime testErrToIntWithOnePossibleValue(error.A, @intFromError(error.A)); } fn testErrToIntWithOnePossibleValue( x: error{A}, comptime value: u32, ) void { if (@intFromError(x) != value) { @compileError("bad"); } } test "inferred empty error set comptime catch" { const S = struct { fn foo() !void {} }; S.foo() catch @compileError("fail"); } test "error inference with an empty set" { const S = struct { const Struct = struct { pub fn func() (error{})!usize { return 0; } }; fn AnotherStruct(comptime SubStruct: type) type { return struct { fn anotherFunc() !void { try expect(0 == (try SubStruct.func())); } }; } }; const GeneratedStruct = S.AnotherStruct(S.Struct); try GeneratedStruct.anotherFunc(); } test "error union peer type resolution" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO 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: Zero sized error set returned with value payload crash" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO _ = try foo3(0); _ = try comptime foo3(0); } const Error = error{}; fn foo3(b: usize) Error!usize { return b; } test "error: Infer error set from literals" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; _ = 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" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { const Foo = struct { a: i32, }; fn errorable() !i32 { const x: Foo = (try getFoo()) orelse return error.Other; return x.a; } fn errorable2() !i32 { const x: Foo = (try getFoo2()) orelse return error.Other; return x.a; } fn errorable3() !i32 { const 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 "return function call to error set from error union function" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { fn errorable() anyerror!i32 { return fail(); } fn fail() anyerror { return error.Failure; } }; try expectError(error.Failure, S.errorable()); try comptime expectError(error.Failure, S.errorable()); } test "optional error set is the same size as error set" { 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 try comptime expect(@sizeOf(?anyerror) == @sizeOf(anyerror)); try comptime expect(@alignOf(?anyerror) == @alignOf(anyerror)); const S = struct { fn returnsOptErrSet() ?anyerror { return null; } }; try expect(S.returnsOptErrSet() == null); try comptime expect(S.returnsOptErrSet() == null); } test "nested catch" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO 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(); try comptime S.entry(); } test "function pointer with return type that is error union with payload which is pointer of parent struct" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; const S = struct { const Foo = struct { fun: *const 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" { 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 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(); try comptime S.doTheTest(); } test "error payload type is correctly resolved" { 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 MyIntWrapper = struct { const Self = @This(); x: i32, pub fn create() anyerror!Self { return Self{ .x = 42 }; } }; try expect(std.meta.eql(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 "@errorName" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; try expect(mem.eql(u8, @errorName(error.AnError), "AnError")); try expect(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); try expect(mem.eql(u8, @errorName(gimmeItBroke()), "ItBroke")); } fn gimmeItBroke() anyerror { return error.ItBroke; } test "@errorName sentinel length matches slice length" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; const name = testBuiltinErrorName(error.FooBar); const length: usize = 6; try expect(length == std.mem.indexOfSentinel(u8, 0, name.ptr)); try expect(length == name.len); } pub fn testBuiltinErrorName(err: anyerror) [:0]const u8 { return @errorName(err); } test "error set equality" { const a = error{One}; const b = error{One}; try expect(a == a); try expect(a == b); try expect(a == error{One}); // should treat as a set const c = error{ One, Two }; const d = error{ Two, One }; try expect(c == d); } test "inferred error set equality" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { fn foo() !void { return @This().bar(); } fn bar() !void { return error.Bad; } fn baz() !void { return quux(); } fn quux() anyerror!void {} }; const FooError = @typeInfo(@typeInfo(@TypeOf(S.foo)).Fn.return_type.?).ErrorUnion.error_set; const BarError = @typeInfo(@typeInfo(@TypeOf(S.bar)).Fn.return_type.?).ErrorUnion.error_set; const BazError = @typeInfo(@typeInfo(@TypeOf(S.baz)).Fn.return_type.?).ErrorUnion.error_set; try expect(BarError != error{Bad}); try expect(FooError != anyerror); try expect(BarError != anyerror); try expect(BazError != anyerror); try expect(FooError != BarError); try expect(FooError != BazError); try expect(BarError != BazError); try expect(FooError == FooError); try expect(BarError == BarError); try expect(BazError == BazError); } test "peer type resolution of two different error unions" { const a: error{B}!void = {}; const b: error{A}!void = {}; var cond = true; _ = &cond; const err = if (cond) a else b; try err; } test "coerce error set to the current inferred error set" { const S = struct { fn foo() !void { var a = false; _ = &a; if (a) { const b: error{A}!void = error.A; return b; } const b = error.A; return b; } }; S.foo() catch {}; } test "error union payload is properly aligned" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; const S = struct { a: u128, b: u128, c: u128, fn foo() error{}!@This() { return @This(){ .a = 1, .b = 2, .c = 3 }; } }; const blk = S.foo() catch unreachable; if (blk.a != 1) unreachable; } test "ret_ptr doesn't cause own inferred error set to be resolved" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { fn foo() !void {} fn doTheTest() !void { errdefer @compileError("bad"); return try @This().foo(); } }; try S.doTheTest(); } test "simple else prong allowed even when all errors handled" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { fn foo() !u8 { return error.Foo; } }; var value = S.foo() catch |err| switch (err) { error.Foo => @as(u8, 255), else => |e| return e, }; try expect(value == 255); value = S.foo() catch |err| switch (err) { error.Foo => 255, else => unreachable, }; try expect(value == 255); value = S.foo() catch |err| switch (err) { error.Foo => 255, else => return, }; try expect(value == 255); } test "pointer to error union payload" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; var err_union: anyerror!u8 = 15; const payload_ptr = &(err_union catch unreachable); try expect(payload_ptr.* == 15); } const NoReturn = struct { var a: u32 = undefined; fn someData() bool { a -= 1; return a == 0; } fn loop() !noreturn { while (true) { if (someData()) return error.GenericFailure; } } fn testTry() anyerror { try loop(); } fn testCatch() anyerror { loop() catch return error.OtherFailure; @compileError("bad"); } }; test "error union of noreturn used with if" { 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; NoReturn.a = 64; if (NoReturn.loop()) { @compileError("bad"); } else |err| { try expect(err == error.GenericFailure); } } test "error union of noreturn used with try" { 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; NoReturn.a = 64; const err = NoReturn.testTry(); try expect(err == error.GenericFailure); } test "error union of noreturn used with catch" { 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; NoReturn.a = 64; const err = NoReturn.testCatch(); try expect(err == error.OtherFailure); } test "alignment of wrapping an error union payload" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; const S = struct { const I = extern struct { x: i128 }; fn foo() anyerror!I { var i: I = .{ .x = 1234 }; _ = &i; return i; } }; try expect((S.foo() catch unreachable).x == 1234); } test "compare error union and error set" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO var a: anyerror = error.Foo; var b: anyerror!u32 = error.Bar; _ = &a; try expect(a != b); try expect(b != a); b = error.Foo; try expect(a == b); try expect(b == a); b = 2; try expect(a != b); try expect(b != a); } fn non_errorable() void { // Make sure catch works even in a function that does not call any errorable functions. // // This test is needed because stage 2's fix for #1923 means that catch blocks interact // with the error return trace index. var x: error{Foo}!void = {}; _ = &x; return x catch {}; } test "catch within a function that calls no errorable functions" { non_errorable(); } test "error from comptime string" { 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 name = "Weird error name!"; const S = struct { fn foo() !void { return @field(anyerror, name); } }; if (S.foo()) unreachable else |err| { try expect(mem.eql(u8, name, @errorName(err))); } } test "field access of anyerror results in smaller error set" { const E1 = @TypeOf(error.Foo); try expect(@TypeOf(E1.Foo) == E1); const E2 = error{ A, B, C }; try expect(@TypeOf(E2.A) == E2); try expect(@TypeOf(@field(anyerror, "NotFound")) == error{NotFound}); } test "optional error union return type" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const S = struct { fn foo() ?anyerror!u32 { var x: u32 = 1234; _ = &x; return @as(anyerror!u32, x); } }; try expect(1234 == try S.foo().?); } test "optional error set return type" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO const E = error{ A, B }; const S = struct { fn foo(return_null: bool) ?E { return if (return_null) null else E.A; } }; try expect(null == S.foo(true)); try expect(E.A == S.foo(false).?); } test "returning an error union containing a type with no runtime bits" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; const ZeroByteType = struct { foo: void, pub fn init() !@This() { return .{ .foo = {} }; } }; var zero_byte: ZeroByteType = undefined; (&zero_byte).* = try ZeroByteType.init(); } test "try used in recursive function with inferred error set" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO const Value = union(enum) { values: []const @This(), b, fn x(value: @This()) !void { switch (value.values[0]) { .values => return try x(value.values[0]), .b => return error.a, } } }; const a = Value{ .values = &[1]Value{ .{ .values = &[1]Value{.{ .b = {} }}, }, }, }; try expectError(error.a, Value.x(a)); }