| author | |
| committer | |
| log | fdd11f6cee7ef14253cef53729e09d9415b4be7e |
| tree | 6de8143acc2874da3464ddb931370f21c71ce3ee |
| parent | 4d6d6977b05571e6d164dbf5f26c7a48ee3541a3 |
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! |
| 4225 | 4225 | const src = inst_data.src(); |
| 4226 | 4226 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 4227 | 4227 | 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); | |
| 4229 | 4229 | const result_ty = Type.initTag(.u16); |
| 4230 | 4230 | |
| 4231 | 4231 | if (try sema.resolveMaybeUndefVal(block, src, op_coerced)) |val| { |
| ... | ... | @@ -4263,7 +4263,7 @@ fn zirIntToError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! |
| 4263 | 4263 | .base = .{ .tag = .@"error" }, |
| 4264 | 4264 | .data = .{ .name = sema.mod.error_name_list.items[@intCast(usize, int)] }, |
| 4265 | 4265 | }; |
| 4266 | return sema.addConstant(Type.initTag(.anyerror), Value.initPayload(&payload.base)); | |
| 4266 | return sema.addConstant(Type.anyerror, Value.initPayload(&payload.base)); | |
| 4267 | 4267 | } |
| 4268 | 4268 | try sema.requireRuntimeBlock(block, src); |
| 4269 | 4269 | if (block.wantSafety()) { |
| ... | ... | @@ -4271,7 +4271,7 @@ fn zirIntToError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! |
| 4271 | 4271 | // const is_gt_max = @panic("TODO get max errors in compilation"); |
| 4272 | 4272 | // try sema.addSafetyCheck(block, is_gt_max, .invalid_error_code); |
| 4273 | 4273 | } |
| 4274 | return block.addTyOp(.bitcast, Type.initTag(.anyerror), op); | |
| 4274 | return block.addTyOp(.bitcast, Type.anyerror, op); | |
| 4275 | 4275 | } |
| 4276 | 4276 | |
| 4277 | 4277 | fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -11605,6 +11605,15 @@ fn coerce( |
| 11605 | 11605 | // T to E!T or E to E!T |
| 11606 | 11606 | return sema.wrapErrorUnion(block, dest_ty, inst, inst_src); |
| 11607 | 11607 | }, |
| 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 | }, | |
| 11608 | 11617 | .Union => switch (inst_ty.zigTypeTag()) { |
| 11609 | 11618 | .Enum, .EnumLiteral => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src), |
| 11610 | 11619 | else => {}, |
| ... | ... | @@ -12236,6 +12245,20 @@ fn coerceVectorToArray( |
| 12236 | 12245 | return block.addTyOp(.bitcast, array_ty, vector); |
| 12237 | 12246 | } |
| 12238 | 12247 | |
| 12248 | fn 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 | ||
| 12239 | 12262 | fn analyzeDeclVal( |
| 12240 | 12263 | sema: *Sema, |
| 12241 | 12264 | block: *Block, |
src/type.zig+1| ... | ... | @@ -3962,6 +3962,7 @@ pub const Type = extern union { |
| 3962 | 3962 | pub const @"comptime_int" = initTag(.comptime_int); |
| 3963 | 3963 | pub const @"void" = initTag(.void); |
| 3964 | 3964 | pub const @"type" = initTag(.type); |
| 3965 | pub const @"anyerror" = initTag(.anyerror); | |
| 3965 | 3966 | |
| 3966 | 3967 | pub fn ptr(arena: *Allocator, d: Payload.Pointer.Data) !Type { |
| 3967 | 3968 | assert(d.host_size == 0 or d.bit_offset < d.host_size * 8); |
test/behavior.zig+2-1| ... | ... | @@ -30,6 +30,7 @@ test { |
| 30 | 30 | _ = @import("behavior/cast.zig"); |
| 31 | 31 | _ = @import("behavior/defer.zig"); |
| 32 | 32 | _ = @import("behavior/enum.zig"); |
| 33 | _ = @import("behavior/error.zig"); | |
| 33 | 34 | _ = @import("behavior/eval.zig"); |
| 34 | 35 | _ = @import("behavior/for.zig"); |
| 35 | 36 | _ = @import("behavior/generics.zig"); |
| ... | ... | @@ -116,7 +117,7 @@ test { |
| 116 | 117 | _ = @import("behavior/const_slice_child.zig"); |
| 117 | 118 | _ = @import("behavior/defer_stage1.zig"); |
| 118 | 119 | _ = @import("behavior/enum_stage1.zig"); |
| 119 | _ = @import("behavior/error.zig"); | |
| 120 | _ = @import("behavior/error_stage1.zig"); | |
| 120 | 121 | _ = @import("behavior/eval_stage1.zig"); |
| 121 | 122 | _ = @import("behavior/field_parent_ptr.zig"); |
| 122 | 123 | _ = @import("behavior/floatop.zig"); |
test/behavior/error.zig-429| ... | ... | @@ -4,33 +4,6 @@ const expectError = std.testing.expectError; |
| 4 | 4 | const expectEqual = std.testing.expectEqual; |
| 5 | 5 | const mem = std.mem; |
| 6 | 6 | |
| 7 | pub fn foo() anyerror!i32 { | |
| 8 | const x = try bar(); | |
| 9 | return x + 1; | |
| 10 | } | |
| 11 | ||
| 12 | pub fn bar() anyerror!i32 { | |
| 13 | return 13; | |
| 14 | } | |
| 15 | ||
| 16 | pub fn baz() anyerror!i32 { | |
| 17 | const y = foo() catch 1234; | |
| 18 | return y + 1; | |
| 19 | } | |
| 20 | ||
| 21 | test "error wrapping" { | |
| 22 | try expect((baz() catch unreachable) == 15); | |
| 23 | } | |
| 24 | ||
| 25 | fn gimmeItBroke() []const u8 { | |
| 26 | return @errorName(error.ItBroke); | |
| 27 | } | |
| 28 | ||
| 29 | test "@errorName" { | |
| 30 | try expect(mem.eql(u8, @errorName(error.AnError), "AnError")); | |
| 31 | try expect(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); | |
| 32 | } | |
| 33 | ||
| 34 | 7 | test "error values" { |
| 35 | 8 | const a = @errorToInt(error.err1); |
| 36 | 9 | const b = @errorToInt(error.err2); |
| ... | ... | @@ -54,409 +27,7 @@ fn errBinaryOperatorG(x: bool) anyerror!isize { |
| 54 | 27 | return if (x) error.ItBroke else @as(isize, 10); |
| 55 | 28 | } |
| 56 | 29 | |
| 57 | test "unwrap simple value from error" { | |
| 58 | const i = unwrapSimpleValueFromErrorDo() catch unreachable; | |
| 59 | try expect(i == 13); | |
| 60 | } | |
| 61 | fn unwrapSimpleValueFromErrorDo() anyerror!isize { | |
| 62 | return 13; | |
| 63 | } | |
| 64 | ||
| 65 | test "error return in assignment" { | |
| 66 | doErrReturnInAssignment() catch unreachable; | |
| 67 | } | |
| 68 | ||
| 69 | fn doErrReturnInAssignment() anyerror!void { | |
| 70 | var x: i32 = undefined; | |
| 71 | x = try makeANonErr(); | |
| 72 | } | |
| 73 | ||
| 74 | fn makeANonErr() anyerror!i32 { | |
| 75 | return 1; | |
| 76 | } | |
| 77 | ||
| 78 | test "error union type " { | |
| 79 | try testErrorUnionType(); | |
| 80 | comptime try testErrorUnionType(); | |
| 81 | } | |
| 82 | ||
| 83 | fn 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 | ||
| 91 | test "error set type" { | |
| 92 | try testErrorSetType(); | |
| 93 | comptime try testErrorSetType(); | |
| 94 | } | |
| 95 | ||
| 96 | const MyErrSet = error{ | |
| 97 | OutOfMemory, | |
| 98 | FileNotFound, | |
| 99 | }; | |
| 100 | ||
| 101 | fn 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 | ||
| 114 | test "explicit error set cast" { | |
| 115 | try testExplicitErrorSetCast(Set1.A); | |
| 116 | comptime try testExplicitErrorSetCast(Set1.A); | |
| 117 | } | |
| 118 | ||
| 119 | const Set1 = error{ | |
| 120 | A, | |
| 121 | B, | |
| 122 | }; | |
| 123 | const Set2 = error{ | |
| 124 | A, | |
| 125 | C, | |
| 126 | }; | |
| 127 | ||
| 128 | fn testExplicitErrorSetCast(set1: Set1) !void { | |
| 129 | var x = @errSetCast(Set2, set1); | |
| 130 | var y = @errSetCast(Set1, x); | |
| 131 | try expect(y == error.A); | |
| 132 | } | |
| 133 | ||
| 134 | test "comptime test error for empty error set" { | |
| 135 | try testComptimeTestErrorEmptySet(1234); | |
| 136 | comptime try testComptimeTestErrorEmptySet(1234); | |
| 137 | } | |
| 138 | ||
| 139 | const EmptyErrorSet = error{}; | |
| 140 | ||
| 141 | fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void { | |
| 142 | if (x) |v| try expect(v == 1234) else |err| { | |
| 143 | _ = err; | |
| 144 | @compileError("bad"); | |
| 145 | } | |
| 146 | } | |
| 147 | ||
| 148 | test "syntax: optional operator in front of error union operator" { | |
| 149 | comptime { | |
| 150 | try expect(?(anyerror!i32) == ?(anyerror!i32)); | |
| 151 | } | |
| 152 | } | |
| 153 | ||
| 154 | test "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 | } | |
| 158 | fn testErrToIntWithOnePossibleValue( | |
| 159 | x: error{A}, | |
| 160 | comptime value: u32, | |
| 161 | ) void { | |
| 162 | if (@errorToInt(x) != value) { | |
| 163 | @compileError("bad"); | |
| 164 | } | |
| 165 | } | |
| 166 | ||
| 167 | 30 | test "empty error union" { |
| 168 | 31 | const x = error{} || error{}; |
| 169 | 32 | _ = x; |
| 170 | 33 | } |
| 171 | ||
| 172 | test "error union peer type resolution" { | |
| 173 | try testErrorUnionPeerTypeResolution(1); | |
| 174 | } | |
| 175 | ||
| 176 | fn 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 | ||
| 189 | fn bar_1() anyerror { | |
| 190 | return error.A; | |
| 191 | } | |
| 192 | ||
| 193 | fn baz_1() !i32 { | |
| 194 | return error.B; | |
| 195 | } | |
| 196 | ||
| 197 | fn quux_1() !i32 { | |
| 198 | return error.C; | |
| 199 | } | |
| 200 | ||
| 201 | test "error: fn returning empty error set can be passed as fn returning any error" { | |
| 202 | entry(); | |
| 203 | comptime entry(); | |
| 204 | } | |
| 205 | ||
| 206 | fn entry() void { | |
| 207 | foo2(bar2); | |
| 208 | } | |
| 209 | ||
| 210 | fn foo2(f: fn () anyerror!void) void { | |
| 211 | const x = f(); | |
| 212 | x catch {}; | |
| 213 | } | |
| 214 | ||
| 215 | fn bar2() (error{}!void) {} | |
| 216 | ||
| 217 | test "error: Zero sized error set returned with value payload crash" { | |
| 218 | _ = foo3(0) catch {}; | |
| 219 | _ = comptime foo3(0) catch {}; | |
| 220 | } | |
| 221 | ||
| 222 | const Error = error{}; | |
| 223 | fn foo3(b: usize) Error!usize { | |
| 224 | return b; | |
| 225 | } | |
| 226 | ||
| 227 | test "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 | ||
| 236 | fn handleErrors(err: anytype) noreturn { | |
| 237 | switch (err) { | |
| 238 | error.T => {}, | |
| 239 | } | |
| 240 | ||
| 241 | unreachable; | |
| 242 | } | |
| 243 | ||
| 244 | fn nullLiteral(str: []const u8) !?i64 { | |
| 245 | if (str[0] == 'n') return null; | |
| 246 | ||
| 247 | return error.T; | |
| 248 | } | |
| 249 | ||
| 250 | fn floatLiteral(str: []const u8) !?f64 { | |
| 251 | if (str[0] == 'n') return 1.0; | |
| 252 | ||
| 253 | return error.T; | |
| 254 | } | |
| 255 | ||
| 256 | fn intLiteral(str: []const u8) !?i64 { | |
| 257 | if (str[0] == 'n') return 1; | |
| 258 | ||
| 259 | return error.T; | |
| 260 | } | |
| 261 | ||
| 262 | test "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 | ||
| 305 | test "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 | ||
| 319 | test "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 | ||
| 333 | test "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 | ||
| 344 | test "debug info for optional error set" { | |
| 345 | const SomeError = error{Hello}; | |
| 346 | var a_local_variable: ?SomeError = null; | |
| 347 | _ = a_local_variable; | |
| 348 | } | |
| 349 | ||
| 350 | test "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 | ||
| 372 | test "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 | ||
| 391 | test "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 | ||
| 412 | test "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 | ||
| 439 | test "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 | ||
| 453 | test "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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | const expectError = std.testing.expectError; | |
| 4 | const expectEqual = std.testing.expectEqual; | |
| 5 | const mem = std.mem; | |
| 6 | ||
| 7 | pub fn foo() anyerror!i32 { | |
| 8 | const x = try bar(); | |
| 9 | return x + 1; | |
| 10 | } | |
| 11 | ||
| 12 | pub fn bar() anyerror!i32 { | |
| 13 | return 13; | |
| 14 | } | |
| 15 | ||
| 16 | pub fn baz() anyerror!i32 { | |
| 17 | const y = foo() catch 1234; | |
| 18 | return y + 1; | |
| 19 | } | |
| 20 | ||
| 21 | test "error wrapping" { | |
| 22 | try expect((baz() catch unreachable) == 15); | |
| 23 | } | |
| 24 | ||
| 25 | fn gimmeItBroke() []const u8 { | |
| 26 | return @errorName(error.ItBroke); | |
| 27 | } | |
| 28 | ||
| 29 | test "@errorName" { | |
| 30 | try expect(mem.eql(u8, @errorName(error.AnError), "AnError")); | |
| 31 | try expect(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); | |
| 32 | } | |
| 33 | ||
| 34 | test "unwrap simple value from error" { | |
| 35 | const i = unwrapSimpleValueFromErrorDo() catch unreachable; | |
| 36 | try expect(i == 13); | |
| 37 | } | |
| 38 | fn unwrapSimpleValueFromErrorDo() anyerror!isize { | |
| 39 | return 13; | |
| 40 | } | |
| 41 | ||
| 42 | test "error return in assignment" { | |
| 43 | doErrReturnInAssignment() catch unreachable; | |
| 44 | } | |
| 45 | ||
| 46 | fn doErrReturnInAssignment() anyerror!void { | |
| 47 | var x: i32 = undefined; | |
| 48 | x = try makeANonErr(); | |
| 49 | } | |
| 50 | ||
| 51 | fn makeANonErr() anyerror!i32 { | |
| 52 | return 1; | |
| 53 | } | |
| 54 | ||
| 55 | test "error union type " { | |
| 56 | try testErrorUnionType(); | |
| 57 | comptime try testErrorUnionType(); | |
| 58 | } | |
| 59 | ||
| 60 | fn 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 | ||
| 68 | test "error set type" { | |
| 69 | try testErrorSetType(); | |
| 70 | comptime try testErrorSetType(); | |
| 71 | } | |
| 72 | ||
| 73 | const MyErrSet = error{ | |
| 74 | OutOfMemory, | |
| 75 | FileNotFound, | |
| 76 | }; | |
| 77 | ||
| 78 | fn 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 | ||
| 91 | test "explicit error set cast" { | |
| 92 | try testExplicitErrorSetCast(Set1.A); | |
| 93 | comptime try testExplicitErrorSetCast(Set1.A); | |
| 94 | } | |
| 95 | ||
| 96 | const Set1 = error{ A, B }; | |
| 97 | const Set2 = error{ A, C }; | |
| 98 | ||
| 99 | fn testExplicitErrorSetCast(set1: Set1) !void { | |
| 100 | var x = @errSetCast(Set2, set1); | |
| 101 | var y = @errSetCast(Set1, x); | |
| 102 | try expect(y == error.A); | |
| 103 | } | |
| 104 | ||
| 105 | test "comptime test error for empty error set" { | |
| 106 | try testComptimeTestErrorEmptySet(1234); | |
| 107 | comptime try testComptimeTestErrorEmptySet(1234); | |
| 108 | } | |
| 109 | ||
| 110 | const EmptyErrorSet = error{}; | |
| 111 | ||
| 112 | fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void { | |
| 113 | if (x) |v| try expect(v == 1234) else |err| { | |
| 114 | _ = err; | |
| 115 | @compileError("bad"); | |
| 116 | } | |
| 117 | } | |
| 118 | ||
| 119 | test "syntax: optional operator in front of error union operator" { | |
| 120 | comptime { | |
| 121 | try expect(?(anyerror!i32) == ?(anyerror!i32)); | |
| 122 | } | |
| 123 | } | |
| 124 | ||
| 125 | test "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 | } | |
| 129 | fn testErrToIntWithOnePossibleValue( | |
| 130 | x: error{A}, | |
| 131 | comptime value: u32, | |
| 132 | ) void { | |
| 133 | if (@errorToInt(x) != value) { | |
| 134 | @compileError("bad"); | |
| 135 | } | |
| 136 | } | |
| 137 | ||
| 138 | test "error union peer type resolution" { | |
| 139 | try testErrorUnionPeerTypeResolution(1); | |
| 140 | } | |
| 141 | ||
| 142 | fn 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 | ||
| 155 | fn bar_1() anyerror { | |
| 156 | return error.A; | |
| 157 | } | |
| 158 | ||
| 159 | fn baz_1() !i32 { | |
| 160 | return error.B; | |
| 161 | } | |
| 162 | ||
| 163 | fn quux_1() !i32 { | |
| 164 | return error.C; | |
| 165 | } | |
| 166 | ||
| 167 | test "error: fn returning empty error set can be passed as fn returning any error" { | |
| 168 | entry(); | |
| 169 | comptime entry(); | |
| 170 | } | |
| 171 | ||
| 172 | fn entry() void { | |
| 173 | foo2(bar2); | |
| 174 | } | |
| 175 | ||
| 176 | fn foo2(f: fn () anyerror!void) void { | |
| 177 | const x = f(); | |
| 178 | x catch {}; | |
| 179 | } | |
| 180 | ||
| 181 | fn bar2() (error{}!void) {} | |
| 182 | ||
| 183 | test "error: Zero sized error set returned with value payload crash" { | |
| 184 | _ = foo3(0) catch {}; | |
| 185 | _ = comptime foo3(0) catch {}; | |
| 186 | } | |
| 187 | ||
| 188 | const Error = error{}; | |
| 189 | fn foo3(b: usize) Error!usize { | |
| 190 | return b; | |
| 191 | } | |
| 192 | ||
| 193 | test "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 | ||
| 202 | fn handleErrors(err: anytype) noreturn { | |
| 203 | switch (err) { | |
| 204 | error.T => {}, | |
| 205 | } | |
| 206 | ||
| 207 | unreachable; | |
| 208 | } | |
| 209 | ||
| 210 | fn nullLiteral(str: []const u8) !?i64 { | |
| 211 | if (str[0] == 'n') return null; | |
| 212 | ||
| 213 | return error.T; | |
| 214 | } | |
| 215 | ||
| 216 | fn floatLiteral(str: []const u8) !?f64 { | |
| 217 | if (str[0] == 'n') return 1.0; | |
| 218 | ||
| 219 | return error.T; | |
| 220 | } | |
| 221 | ||
| 222 | fn intLiteral(str: []const u8) !?i64 { | |
| 223 | if (str[0] == 'n') return 1; | |
| 224 | ||
| 225 | return error.T; | |
| 226 | } | |
| 227 | ||
| 228 | test "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 | ||
| 271 | test "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 | ||
| 285 | test "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 | ||
| 299 | test "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 | ||
| 310 | test "debug info for optional error set" { | |
| 311 | const SomeError = error{Hello}; | |
| 312 | var a_local_variable: ?SomeError = null; | |
| 313 | _ = a_local_variable; | |
| 314 | } | |
| 315 | ||
| 316 | test "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 | ||
| 338 | test "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 | ||
| 357 | test "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 | ||
| 378 | test "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 | ||
| 405 | test "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 | ||
| 419 | test "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 | } |