authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-16 12:41:03-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-16 12:41:03-07:00
logfdd11f6cee7ef14253cef53729e09d9415b4be7e
tree6de8143acc2874da3464ddb931370f21c71ce3ee
parent4d6d6977b05571e6d164dbf5f26c7a48ee3541a3

Sema: coercion from error sets to `anyerror`


5 files changed, 457 insertions(+), 433 deletions(-)

src/Sema.zig+26-3
......@@ -4225,7 +4225,7 @@ fn zirErrorToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
42254225 const src = inst_data.src();
42264226 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
42274227 const op = sema.resolveInst(inst_data.operand);
4228 const op_coerced = try sema.coerce(block, Type.initTag(.anyerror), op, operand_src);
4228 const op_coerced = try sema.coerce(block, Type.anyerror, op, operand_src);
42294229 const result_ty = Type.initTag(.u16);
42304230
42314231 if (try sema.resolveMaybeUndefVal(block, src, op_coerced)) |val| {
......@@ -4263,7 +4263,7 @@ fn zirIntToError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
42634263 .base = .{ .tag = .@"error" },
42644264 .data = .{ .name = sema.mod.error_name_list.items[@intCast(usize, int)] },
42654265 };
4266 return sema.addConstant(Type.initTag(.anyerror), Value.initPayload(&payload.base));
4266 return sema.addConstant(Type.anyerror, Value.initPayload(&payload.base));
42674267 }
42684268 try sema.requireRuntimeBlock(block, src);
42694269 if (block.wantSafety()) {
......@@ -4271,7 +4271,7 @@ fn zirIntToError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
42714271 // const is_gt_max = @panic("TODO get max errors in compilation");
42724272 // try sema.addSafetyCheck(block, is_gt_max, .invalid_error_code);
42734273 }
4274 return block.addTyOp(.bitcast, Type.initTag(.anyerror), op);
4274 return block.addTyOp(.bitcast, Type.anyerror, op);
42754275}
42764276
42774277fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -11605,6 +11605,15 @@ fn coerce(
1160511605 // T to E!T or E to E!T
1160611606 return sema.wrapErrorUnion(block, dest_ty, inst, inst_src);
1160711607 },
11608 .ErrorSet => {
11609 // Coercion to `anyerror`.
11610 // TODO If the dest type tag is not `anyerror` it still could
11611 // resolve to anyerror. `dest_ty` needs to have inferred error set resolution
11612 // happen before this check.
11613 if (dest_ty.tag() == .anyerror and inst_ty.zigTypeTag() == .ErrorSet) {
11614 return sema.coerceErrSetToAnyError(block, inst, inst_src);
11615 }
11616 },
1160811617 .Union => switch (inst_ty.zigTypeTag()) {
1160911618 .Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src),
1161011619 else => {},
......@@ -12236,6 +12245,20 @@ fn coerceVectorToArray(
1223612245 return block.addTyOp(.bitcast, array_ty, vector);
1223712246}
1223812247
12248fn coerceErrSetToAnyError(
12249 sema: *Sema,
12250 block: *Block,
12251 err_set: Air.Inst.Ref,
12252 err_set_src: LazySrcLoc,
12253) !Air.Inst.Ref {
12254 if (try sema.resolveDefinedValue(block, err_set_src, err_set)) |err_set_val| {
12255 // Same representation works.
12256 return sema.addConstant(Type.anyerror, err_set_val);
12257 }
12258 try sema.requireRuntimeBlock(block, err_set_src);
12259 return block.addTyOp(.bitcast, Type.anyerror, err_set);
12260}
12261
1223912262fn analyzeDeclVal(
1224012263 sema: *Sema,
1224112264 block: *Block,
src/type.zig+1
......@@ -3962,6 +3962,7 @@ pub const Type = extern union {
39623962 pub const @"comptime_int" = initTag(.comptime_int);
39633963 pub const @"void" = initTag(.void);
39643964 pub const @"type" = initTag(.type);
3965 pub const @"anyerror" = initTag(.anyerror);
39653966
39663967 pub fn ptr(arena: *Allocator, d: Payload.Pointer.Data) !Type {
39673968 assert(d.host_size == 0 or d.bit_offset < d.host_size * 8);
test/behavior.zig+2-1
......@@ -30,6 +30,7 @@ test {
3030 _ = @import("behavior/cast.zig");
3131 _ = @import("behavior/defer.zig");
3232 _ = @import("behavior/enum.zig");
33 _ = @import("behavior/error.zig");
3334 _ = @import("behavior/eval.zig");
3435 _ = @import("behavior/for.zig");
3536 _ = @import("behavior/generics.zig");
......@@ -116,7 +117,7 @@ test {
116117 _ = @import("behavior/const_slice_child.zig");
117118 _ = @import("behavior/defer_stage1.zig");
118119 _ = @import("behavior/enum_stage1.zig");
119 _ = @import("behavior/error.zig");
120 _ = @import("behavior/error_stage1.zig");
120121 _ = @import("behavior/eval_stage1.zig");
121122 _ = @import("behavior/field_parent_ptr.zig");
122123 _ = @import("behavior/floatop.zig");
test/behavior/error.zig-429
......@@ -4,33 +4,6 @@ const expectError = std.testing.expectError;
44const expectEqual = std.testing.expectEqual;
55const mem = std.mem;
66
7pub fn foo() anyerror!i32 {
8 const x = try bar();
9 return x + 1;
10}
11
12pub fn bar() anyerror!i32 {
13 return 13;
14}
15
16pub fn baz() anyerror!i32 {
17 const y = foo() catch 1234;
18 return y + 1;
19}
20
21test "error wrapping" {
22 try expect((baz() catch unreachable) == 15);
23}
24
25fn gimmeItBroke() []const u8 {
26 return @errorName(error.ItBroke);
27}
28
29test "@errorName" {
30 try expect(mem.eql(u8, @errorName(error.AnError), "AnError"));
31 try expect(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName"));
32}
33
347test "error values" {
358 const a = @errorToInt(error.err1);
369 const b = @errorToInt(error.err2);
......@@ -54,409 +27,7 @@ fn errBinaryOperatorG(x: bool) anyerror!isize {
5427 return if (x) error.ItBroke else @as(isize, 10);
5528}
5629
57test "unwrap simple value from error" {
58 const i = unwrapSimpleValueFromErrorDo() catch unreachable;
59 try expect(i == 13);
60}
61fn unwrapSimpleValueFromErrorDo() anyerror!isize {
62 return 13;
63}
64
65test "error return in assignment" {
66 doErrReturnInAssignment() catch unreachable;
67}
68
69fn doErrReturnInAssignment() anyerror!void {
70 var x: i32 = undefined;
71 x = try makeANonErr();
72}
73
74fn makeANonErr() anyerror!i32 {
75 return 1;
76}
77
78test "error union type " {
79 try testErrorUnionType();
80 comptime try testErrorUnionType();
81}
82
83fn testErrorUnionType() !void {
84 const x: anyerror!i32 = 1234;
85 if (x) |value| try expect(value == 1234) else |_| unreachable;
86 try expect(@typeInfo(@TypeOf(x)) == .ErrorUnion);
87 try expect(@typeInfo(@typeInfo(@TypeOf(x)).ErrorUnion.error_set) == .ErrorSet);
88 try expect(@typeInfo(@TypeOf(x)).ErrorUnion.error_set == anyerror);
89}
90
91test "error set type" {
92 try testErrorSetType();
93 comptime try testErrorSetType();
94}
95
96const MyErrSet = error{
97 OutOfMemory,
98 FileNotFound,
99};
100
101fn testErrorSetType() !void {
102 try expect(@typeInfo(MyErrSet).ErrorSet.?.len == 2);
103
104 const a: MyErrSet!i32 = 5678;
105 const b: MyErrSet!i32 = MyErrSet.OutOfMemory;
106 try expect(b catch error.OutOfMemory == error.OutOfMemory);
107
108 if (a) |value| try expect(value == 5678) else |err| switch (err) {
109 error.OutOfMemory => unreachable,
110 error.FileNotFound => unreachable,
111 }
112}
113
114test "explicit error set cast" {
115 try testExplicitErrorSetCast(Set1.A);
116 comptime try testExplicitErrorSetCast(Set1.A);
117}
118
119const Set1 = error{
120 A,
121 B,
122};
123const Set2 = error{
124 A,
125 C,
126};
127
128fn testExplicitErrorSetCast(set1: Set1) !void {
129 var x = @errSetCast(Set2, set1);
130 var y = @errSetCast(Set1, x);
131 try expect(y == error.A);
132}
133
134test "comptime test error for empty error set" {
135 try testComptimeTestErrorEmptySet(1234);
136 comptime try testComptimeTestErrorEmptySet(1234);
137}
138
139const EmptyErrorSet = error{};
140
141fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void {
142 if (x) |v| try expect(v == 1234) else |err| {
143 _ = err;
144 @compileError("bad");
145 }
146}
147
148test "syntax: optional operator in front of error union operator" {
149 comptime {
150 try expect(?(anyerror!i32) == ?(anyerror!i32));
151 }
152}
153
154test "comptime err to int of error set with only 1 possible value" {
155 testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
156 comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
157}
158fn testErrToIntWithOnePossibleValue(
159 x: error{A},
160 comptime value: u32,
161) void {
162 if (@errorToInt(x) != value) {
163 @compileError("bad");
164 }
165}
166
16730test "empty error union" {
16831 const x = error{} || error{};
16932 _ = x;
17033}
171
172test "error union peer type resolution" {
173 try testErrorUnionPeerTypeResolution(1);
174}
175
176fn testErrorUnionPeerTypeResolution(x: i32) !void {
177 const y = switch (x) {
178 1 => bar_1(),
179 2 => baz_1(),
180 else => quux_1(),
181 };
182 if (y) |_| {
183 @panic("expected error");
184 } else |e| {
185 try expect(e == error.A);
186 }
187}
188
189fn bar_1() anyerror {
190 return error.A;
191}
192
193fn baz_1() !i32 {
194 return error.B;
195}
196
197fn quux_1() !i32 {
198 return error.C;
199}
200
201test "error: fn returning empty error set can be passed as fn returning any error" {
202 entry();
203 comptime entry();
204}
205
206fn entry() void {
207 foo2(bar2);
208}
209
210fn foo2(f: fn () anyerror!void) void {
211 const x = f();
212 x catch {};
213}
214
215fn bar2() (error{}!void) {}
216
217test "error: Zero sized error set returned with value payload crash" {
218 _ = foo3(0) catch {};
219 _ = comptime foo3(0) catch {};
220}
221
222const Error = error{};
223fn foo3(b: usize) Error!usize {
224 return b;
225}
226
227test "error: Infer error set from literals" {
228 _ = nullLiteral("n") catch |err| handleErrors(err);
229 _ = floatLiteral("n") catch |err| handleErrors(err);
230 _ = intLiteral("n") catch |err| handleErrors(err);
231 _ = comptime nullLiteral("n") catch |err| handleErrors(err);
232 _ = comptime floatLiteral("n") catch |err| handleErrors(err);
233 _ = comptime intLiteral("n") catch |err| handleErrors(err);
234}
235
236fn handleErrors(err: anytype) noreturn {
237 switch (err) {
238 error.T => {},
239 }
240
241 unreachable;
242}
243
244fn nullLiteral(str: []const u8) !?i64 {
245 if (str[0] == 'n') return null;
246
247 return error.T;
248}
249
250fn floatLiteral(str: []const u8) !?f64 {
251 if (str[0] == 'n') return 1.0;
252
253 return error.T;
254}
255
256fn intLiteral(str: []const u8) !?i64 {
257 if (str[0] == 'n') return 1;
258
259 return error.T;
260}
261
262test "nested error union function call in optional unwrap" {
263 const S = struct {
264 const Foo = struct {
265 a: i32,
266 };
267
268 fn errorable() !i32 {
269 var x: Foo = (try getFoo()) orelse return error.Other;
270 return x.a;
271 }
272
273 fn errorable2() !i32 {
274 var x: Foo = (try getFoo2()) orelse return error.Other;
275 return x.a;
276 }
277
278 fn errorable3() !i32 {
279 var x: Foo = (try getFoo3()) orelse return error.Other;
280 return x.a;
281 }
282
283 fn getFoo() anyerror!?Foo {
284 return Foo{ .a = 1234 };
285 }
286
287 fn getFoo2() anyerror!?Foo {
288 return error.Failure;
289 }
290
291 fn getFoo3() anyerror!?Foo {
292 return null;
293 }
294 };
295 try expect((try S.errorable()) == 1234);
296 try expectError(error.Failure, S.errorable2());
297 try expectError(error.Other, S.errorable3());
298 comptime {
299 try expect((try S.errorable()) == 1234);
300 try expectError(error.Failure, S.errorable2());
301 try expectError(error.Other, S.errorable3());
302 }
303}
304
305test "widen cast integer payload of error union function call" {
306 const S = struct {
307 fn errorable() !u64 {
308 var x = @as(u64, try number());
309 return x;
310 }
311
312 fn number() anyerror!u32 {
313 return 1234;
314 }
315 };
316 try expect((try S.errorable()) == 1234);
317}
318
319test "return function call to error set from error union function" {
320 const S = struct {
321 fn errorable() anyerror!i32 {
322 return fail();
323 }
324
325 fn fail() anyerror {
326 return error.Failure;
327 }
328 };
329 try expectError(error.Failure, S.errorable());
330 comptime try expectError(error.Failure, S.errorable());
331}
332
333test "optional error set is the same size as error set" {
334 comptime try expect(@sizeOf(?anyerror) == @sizeOf(anyerror));
335 const S = struct {
336 fn returnsOptErrSet() ?anyerror {
337 return null;
338 }
339 };
340 try expect(S.returnsOptErrSet() == null);
341 comptime try expect(S.returnsOptErrSet() == null);
342}
343
344test "debug info for optional error set" {
345 const SomeError = error{Hello};
346 var a_local_variable: ?SomeError = null;
347 _ = a_local_variable;
348}
349
350test "nested catch" {
351 const S = struct {
352 fn entry() !void {
353 try expectError(error.Bad, func());
354 }
355 fn fail() anyerror!Foo {
356 return error.Wrong;
357 }
358 fn func() anyerror!Foo {
359 _ = fail() catch
360 fail() catch
361 return error.Bad;
362 unreachable;
363 }
364 const Foo = struct {
365 field: i32,
366 };
367 };
368 try S.entry();
369 comptime try S.entry();
370}
371
372test "implicit cast to optional to error union to return result loc" {
373 const S = struct {
374 fn entry() !void {
375 var x: Foo = undefined;
376 if (func(&x)) |opt| {
377 try expect(opt != null);
378 } else |_| @panic("expected non error");
379 }
380 fn func(f: *Foo) anyerror!?*Foo {
381 return f;
382 }
383 const Foo = struct {
384 field: i32,
385 };
386 };
387 try S.entry();
388 //comptime S.entry(); TODO
389}
390
391test "function pointer with return type that is error union with payload which is pointer of parent struct" {
392 const S = struct {
393 const Foo = struct {
394 fun: fn (a: i32) (anyerror!*Foo),
395 };
396
397 const Err = error{UnspecifiedErr};
398
399 fn bar(a: i32) anyerror!*Foo {
400 _ = a;
401 return Err.UnspecifiedErr;
402 }
403
404 fn doTheTest() !void {
405 var x = Foo{ .fun = @This().bar };
406 try expectError(error.UnspecifiedErr, x.fun(1));
407 }
408 };
409 try S.doTheTest();
410}
411
412test "return result loc as peer result loc in inferred error set function" {
413 const S = struct {
414 fn doTheTest() !void {
415 if (quux(2)) |x| {
416 try expect(x.Two);
417 } else |e| switch (e) {
418 error.Whatever => @panic("fail"),
419 }
420 try expectError(error.Whatever, quux(99));
421 }
422 const FormValue = union(enum) {
423 One: void,
424 Two: bool,
425 };
426
427 fn quux(id: u64) !FormValue {
428 return switch (id) {
429 2 => FormValue{ .Two = true },
430 1 => FormValue{ .One = {} },
431 else => return error.Whatever,
432 };
433 }
434 };
435 try S.doTheTest();
436 comptime try S.doTheTest();
437}
438
439test "error payload type is correctly resolved" {
440 const MyIntWrapper = struct {
441 const Self = @This();
442
443 x: i32,
444
445 pub fn create() anyerror!Self {
446 return Self{ .x = 42 };
447 }
448 };
449
450 try expectEqual(MyIntWrapper{ .x = 42 }, try MyIntWrapper.create());
451}
452
453test "error union comptime caching" {
454 const S = struct {
455 fn quux(comptime arg: anytype) void {
456 arg catch {};
457 }
458 };
459
460 S.quux(@as(anyerror!void, {}));
461 S.quux(@as(anyerror!void, {}));
462}
test/behavior/error_stage1.zig created+428
......@@ -0,0 +1,428 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const expectError = std.testing.expectError;
4const expectEqual = std.testing.expectEqual;
5const mem = std.mem;
6
7pub fn foo() anyerror!i32 {
8 const x = try bar();
9 return x + 1;
10}
11
12pub fn bar() anyerror!i32 {
13 return 13;
14}
15
16pub fn baz() anyerror!i32 {
17 const y = foo() catch 1234;
18 return y + 1;
19}
20
21test "error wrapping" {
22 try expect((baz() catch unreachable) == 15);
23}
24
25fn gimmeItBroke() []const u8 {
26 return @errorName(error.ItBroke);
27}
28
29test "@errorName" {
30 try expect(mem.eql(u8, @errorName(error.AnError), "AnError"));
31 try expect(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName"));
32}
33
34test "unwrap simple value from error" {
35 const i = unwrapSimpleValueFromErrorDo() catch unreachable;
36 try expect(i == 13);
37}
38fn unwrapSimpleValueFromErrorDo() anyerror!isize {
39 return 13;
40}
41
42test "error return in assignment" {
43 doErrReturnInAssignment() catch unreachable;
44}
45
46fn doErrReturnInAssignment() anyerror!void {
47 var x: i32 = undefined;
48 x = try makeANonErr();
49}
50
51fn makeANonErr() anyerror!i32 {
52 return 1;
53}
54
55test "error union type " {
56 try testErrorUnionType();
57 comptime try testErrorUnionType();
58}
59
60fn testErrorUnionType() !void {
61 const x: anyerror!i32 = 1234;
62 if (x) |value| try expect(value == 1234) else |_| unreachable;
63 try expect(@typeInfo(@TypeOf(x)) == .ErrorUnion);
64 try expect(@typeInfo(@typeInfo(@TypeOf(x)).ErrorUnion.error_set) == .ErrorSet);
65 try expect(@typeInfo(@TypeOf(x)).ErrorUnion.error_set == anyerror);
66}
67
68test "error set type" {
69 try testErrorSetType();
70 comptime try testErrorSetType();
71}
72
73const MyErrSet = error{
74 OutOfMemory,
75 FileNotFound,
76};
77
78fn testErrorSetType() !void {
79 try expect(@typeInfo(MyErrSet).ErrorSet.?.len == 2);
80
81 const a: MyErrSet!i32 = 5678;
82 const b: MyErrSet!i32 = MyErrSet.OutOfMemory;
83 try expect(b catch error.OutOfMemory == error.OutOfMemory);
84
85 if (a) |value| try expect(value == 5678) else |err| switch (err) {
86 error.OutOfMemory => unreachable,
87 error.FileNotFound => unreachable,
88 }
89}
90
91test "explicit error set cast" {
92 try testExplicitErrorSetCast(Set1.A);
93 comptime try testExplicitErrorSetCast(Set1.A);
94}
95
96const Set1 = error{ A, B };
97const Set2 = error{ A, C };
98
99fn testExplicitErrorSetCast(set1: Set1) !void {
100 var x = @errSetCast(Set2, set1);
101 var y = @errSetCast(Set1, x);
102 try expect(y == error.A);
103}
104
105test "comptime test error for empty error set" {
106 try testComptimeTestErrorEmptySet(1234);
107 comptime try testComptimeTestErrorEmptySet(1234);
108}
109
110const EmptyErrorSet = error{};
111
112fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void {
113 if (x) |v| try expect(v == 1234) else |err| {
114 _ = err;
115 @compileError("bad");
116 }
117}
118
119test "syntax: optional operator in front of error union operator" {
120 comptime {
121 try expect(?(anyerror!i32) == ?(anyerror!i32));
122 }
123}
124
125test "comptime err to int of error set with only 1 possible value" {
126 testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
127 comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
128}
129fn testErrToIntWithOnePossibleValue(
130 x: error{A},
131 comptime value: u32,
132) void {
133 if (@errorToInt(x) != value) {
134 @compileError("bad");
135 }
136}
137
138test "error union peer type resolution" {
139 try testErrorUnionPeerTypeResolution(1);
140}
141
142fn testErrorUnionPeerTypeResolution(x: i32) !void {
143 const y = switch (x) {
144 1 => bar_1(),
145 2 => baz_1(),
146 else => quux_1(),
147 };
148 if (y) |_| {
149 @panic("expected error");
150 } else |e| {
151 try expect(e == error.A);
152 }
153}
154
155fn bar_1() anyerror {
156 return error.A;
157}
158
159fn baz_1() !i32 {
160 return error.B;
161}
162
163fn quux_1() !i32 {
164 return error.C;
165}
166
167test "error: fn returning empty error set can be passed as fn returning any error" {
168 entry();
169 comptime entry();
170}
171
172fn entry() void {
173 foo2(bar2);
174}
175
176fn foo2(f: fn () anyerror!void) void {
177 const x = f();
178 x catch {};
179}
180
181fn bar2() (error{}!void) {}
182
183test "error: Zero sized error set returned with value payload crash" {
184 _ = foo3(0) catch {};
185 _ = comptime foo3(0) catch {};
186}
187
188const Error = error{};
189fn foo3(b: usize) Error!usize {
190 return b;
191}
192
193test "error: Infer error set from literals" {
194 _ = nullLiteral("n") catch |err| handleErrors(err);
195 _ = floatLiteral("n") catch |err| handleErrors(err);
196 _ = intLiteral("n") catch |err| handleErrors(err);
197 _ = comptime nullLiteral("n") catch |err| handleErrors(err);
198 _ = comptime floatLiteral("n") catch |err| handleErrors(err);
199 _ = comptime intLiteral("n") catch |err| handleErrors(err);
200}
201
202fn handleErrors(err: anytype) noreturn {
203 switch (err) {
204 error.T => {},
205 }
206
207 unreachable;
208}
209
210fn nullLiteral(str: []const u8) !?i64 {
211 if (str[0] == 'n') return null;
212
213 return error.T;
214}
215
216fn floatLiteral(str: []const u8) !?f64 {
217 if (str[0] == 'n') return 1.0;
218
219 return error.T;
220}
221
222fn intLiteral(str: []const u8) !?i64 {
223 if (str[0] == 'n') return 1;
224
225 return error.T;
226}
227
228test "nested error union function call in optional unwrap" {
229 const S = struct {
230 const Foo = struct {
231 a: i32,
232 };
233
234 fn errorable() !i32 {
235 var x: Foo = (try getFoo()) orelse return error.Other;
236 return x.a;
237 }
238
239 fn errorable2() !i32 {
240 var x: Foo = (try getFoo2()) orelse return error.Other;
241 return x.a;
242 }
243
244 fn errorable3() !i32 {
245 var x: Foo = (try getFoo3()) orelse return error.Other;
246 return x.a;
247 }
248
249 fn getFoo() anyerror!?Foo {
250 return Foo{ .a = 1234 };
251 }
252
253 fn getFoo2() anyerror!?Foo {
254 return error.Failure;
255 }
256
257 fn getFoo3() anyerror!?Foo {
258 return null;
259 }
260 };
261 try expect((try S.errorable()) == 1234);
262 try expectError(error.Failure, S.errorable2());
263 try expectError(error.Other, S.errorable3());
264 comptime {
265 try expect((try S.errorable()) == 1234);
266 try expectError(error.Failure, S.errorable2());
267 try expectError(error.Other, S.errorable3());
268 }
269}
270
271test "widen cast integer payload of error union function call" {
272 const S = struct {
273 fn errorable() !u64 {
274 var x = @as(u64, try number());
275 return x;
276 }
277
278 fn number() anyerror!u32 {
279 return 1234;
280 }
281 };
282 try expect((try S.errorable()) == 1234);
283}
284
285test "return function call to error set from error union function" {
286 const S = struct {
287 fn errorable() anyerror!i32 {
288 return fail();
289 }
290
291 fn fail() anyerror {
292 return error.Failure;
293 }
294 };
295 try expectError(error.Failure, S.errorable());
296 comptime try expectError(error.Failure, S.errorable());
297}
298
299test "optional error set is the same size as error set" {
300 comptime try expect(@sizeOf(?anyerror) == @sizeOf(anyerror));
301 const S = struct {
302 fn returnsOptErrSet() ?anyerror {
303 return null;
304 }
305 };
306 try expect(S.returnsOptErrSet() == null);
307 comptime try expect(S.returnsOptErrSet() == null);
308}
309
310test "debug info for optional error set" {
311 const SomeError = error{Hello};
312 var a_local_variable: ?SomeError = null;
313 _ = a_local_variable;
314}
315
316test "nested catch" {
317 const S = struct {
318 fn entry() !void {
319 try expectError(error.Bad, func());
320 }
321 fn fail() anyerror!Foo {
322 return error.Wrong;
323 }
324 fn func() anyerror!Foo {
325 _ = fail() catch
326 fail() catch
327 return error.Bad;
328 unreachable;
329 }
330 const Foo = struct {
331 field: i32,
332 };
333 };
334 try S.entry();
335 comptime try S.entry();
336}
337
338test "implicit cast to optional to error union to return result loc" {
339 const S = struct {
340 fn entry() !void {
341 var x: Foo = undefined;
342 if (func(&x)) |opt| {
343 try expect(opt != null);
344 } else |_| @panic("expected non error");
345 }
346 fn func(f: *Foo) anyerror!?*Foo {
347 return f;
348 }
349 const Foo = struct {
350 field: i32,
351 };
352 };
353 try S.entry();
354 //comptime S.entry(); TODO
355}
356
357test "function pointer with return type that is error union with payload which is pointer of parent struct" {
358 const S = struct {
359 const Foo = struct {
360 fun: fn (a: i32) (anyerror!*Foo),
361 };
362
363 const Err = error{UnspecifiedErr};
364
365 fn bar(a: i32) anyerror!*Foo {
366 _ = a;
367 return Err.UnspecifiedErr;
368 }
369
370 fn doTheTest() !void {
371 var x = Foo{ .fun = @This().bar };
372 try expectError(error.UnspecifiedErr, x.fun(1));
373 }
374 };
375 try S.doTheTest();
376}
377
378test "return result loc as peer result loc in inferred error set function" {
379 const S = struct {
380 fn doTheTest() !void {
381 if (quux(2)) |x| {
382 try expect(x.Two);
383 } else |e| switch (e) {
384 error.Whatever => @panic("fail"),
385 }
386 try expectError(error.Whatever, quux(99));
387 }
388 const FormValue = union(enum) {
389 One: void,
390 Two: bool,
391 };
392
393 fn quux(id: u64) !FormValue {
394 return switch (id) {
395 2 => FormValue{ .Two = true },
396 1 => FormValue{ .One = {} },
397 else => return error.Whatever,
398 };
399 }
400 };
401 try S.doTheTest();
402 comptime try S.doTheTest();
403}
404
405test "error payload type is correctly resolved" {
406 const MyIntWrapper = struct {
407 const Self = @This();
408
409 x: i32,
410
411 pub fn create() anyerror!Self {
412 return Self{ .x = 42 };
413 }
414 };
415
416 try expectEqual(MyIntWrapper{ .x = 42 }, try MyIntWrapper.create());
417}
418
419test "error union comptime caching" {
420 const S = struct {
421 fn quux(comptime arg: anytype) void {
422 arg catch {};
423 }
424 };
425
426 S.quux(@as(anyerror!void, {}));
427 S.quux(@as(anyerror!void, {}));
428}