diff --git a/doc/langref.html.in b/doc/langref.html.in index b963eea92d622506b4fc485b7c15d01508497257..2c98fae1fc5a38b0a33d9b2796ac2c49eff8873c 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6657,7 +6657,7 @@ test "coercion from homogenous tuple to array" {
{#syntax#}@errSetCast(value: anytype) anytype{#endsyntax#}
+ {#header_open|@errorCast#}
+ {#syntax#}@errorCast(value: anytype) anytype{#endsyntax#}
- Converts an error value from one error set to another error set. The return type is the + Converts an error set or error union value from one error set to another error set. The return type is the inferred result type. Attempting to convert an error which is not in the destination error set results in safety-protected {#link|Undefined Behavior#}.
@@ -10257,7 +10257,7 @@ const Set2 = error{ C, }; comptime { - _ = @as(Set2, @errSetCast(Set1.B)); + _ = @as(Set2, @errorCast(Set1.B)); } {#code_end#}At runtime:
@@ -10276,7 +10276,7 @@ pub fn main() void { foo(Set1.B); } fn foo(set1: Set1) void { - const x: Set2 = @errSetCast(set1); + const x: Set2 = @errorCast(set1); std.debug.print("value: {}\n", .{x}); } {#code_end#} diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 6ddb92f55dd5d5d4255cdfa5d2a7e245af68aee6..6a5fabc41f4d937ff5611e231d43dc31577ce7ce 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -446,7 +446,7 @@ pub const ChildProcess = struct { // has a value greater than 0 if ((fd[0].revents & std.os.POLL.IN) != 0) { const err_int = try readIntFd(err_pipe[0]); - return @as(SpawnError, @errSetCast(@errorFromInt(err_int))); + return @as(SpawnError, @errorCast(@errorFromInt(err_int))); } } else { // Write maxInt(ErrInt) to the write end of the err_pipe. This is after @@ -459,7 +459,7 @@ pub const ChildProcess = struct { // Here we potentially return the fork child's error from the parent // pid. if (err_int != maxInt(ErrInt)) { - return @as(SpawnError, @errSetCast(@errorFromInt(err_int))); + return @as(SpawnError, @errorCast(@errorFromInt(err_int))); } } } diff --git a/lib/std/os.zig b/lib/std/os.zig index 565546c281ae0da157aedc0f801f2787e89a3036..ceeaa610d6bbd5ffed1b6373a53a14fdad354e84 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -5419,7 +5419,7 @@ pub fn dl_iterate_phdr( } }.callbackC, @as(?*anyopaque, @ptrFromInt(@intFromPtr(&context))))) { 0 => return, - else => |err| return @as(Error, @errSetCast(@errorFromInt(@as(u16, @intCast(err))))), // TODO don't hardcode u16 + else => |err| return @as(Error, @errorCast(@errorFromInt(@as(u16, @intCast(err))))), // TODO don't hardcode u16 } } diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 709d9f77069ed51b61b54ef534052c21bebfc0dd..bb4d6a9e8eebae80b3b8ca1a0a965d03d06c0b51 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -1444,7 +1444,7 @@ fn renderBuiltinCall( const slice = tree.tokenSlice(builtin_token); const rewrite_two_param_cast = params.len == 2 and for ([_][]const u8{ "@bitCast", - "@errSetCast", + "@errorCast", "@floatCast", "@intCast", "@ptrCast", @@ -1505,6 +1505,8 @@ fn renderBuiltinCall( try ais.writer().writeAll("@intFromPtr"); } else if (mem.eql(u8, slice, "@fabs")) { try ais.writer().writeAll("@abs"); + } else if (mem.eql(u8, slice, "@errSetCast")) { + try ais.writer().writeAll("@errorCast"); } else { try renderToken(ais, tree, builtin_token, .none); // @name } diff --git a/src/AstGen.zig b/src/AstGen.zig index 12e33bd803cbb948ce5e27eac59a868856803411..346177e5acd6f94b363617b4a455e61d4d01f7b2 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -8454,11 +8454,11 @@ fn builtinCall( }); return rvalue(gz, ri, result, node); }, - .err_set_cast => { + .error_cast => { try emitDbgNode(gz, node); - const result = try gz.addExtendedPayload(.err_set_cast, Zir.Inst.BinNode{ - .lhs = try ri.rl.resultTypeForCast(gz, node, "@errSetCast"), + const result = try gz.addExtendedPayload(.error_cast, Zir.Inst.BinNode{ + .lhs = try ri.rl.resultTypeForCast(gz, node, "@errorCast"), .rhs = try expr(gz, scope, .{ .rl = .none }, params[0]), .node = gz.nodeIndexToRelative(node), }); diff --git a/src/AstRlAnnotate.zig b/src/AstRlAnnotate.zig index f9d6804328b6c6db06578e2cacc547d5f649b72c..c751adea9d6c142f8cab50426566ffba39c150b9 100644 --- a/src/AstRlAnnotate.zig +++ b/src/AstRlAnnotate.zig @@ -945,7 +945,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast. .float_cast, .int_cast, .truncate, - .err_set_cast, + .error_cast, .ptr_cast, .align_cast, .addrspace_cast, diff --git a/src/BuiltinFn.zig b/src/BuiltinFn.zig index 0056854e779e87d2bc9fe07eab2313d35fd7d166..3296114ef9d31c1e4576435851ec7928fcf402b9 100644 --- a/src/BuiltinFn.zig +++ b/src/BuiltinFn.zig @@ -43,7 +43,7 @@ pub const Tag = enum { error_name, error_return_trace, int_from_error, - err_set_cast, + error_cast, @"export", @"extern", fence, @@ -455,9 +455,9 @@ pub const list = list: { }, }, .{ - "@errSetCast", + "@errorCast", .{ - .tag = .err_set_cast, + .tag = .error_cast, .eval_to_error = .always, .param_count = 1, }, diff --git a/src/Sema.zig b/src/Sema.zig index 963f7f54896b2f2c1b82689dc8baab35ea8a9ef7..88dd5f5b09c11fb84c5b56ed3920fc54dc9665dc 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1252,7 +1252,7 @@ fn analyzeBodyInner( .wasm_memory_size => try sema.zirWasmMemorySize( block, extended), .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended), .prefetch => try sema.zirPrefetch( block, extended), - .err_set_cast => try sema.zirErrSetCast( block, extended), + .error_cast => try sema.zirErrorCast( block, extended), .await_nosuspend => try sema.zirAwaitNosuspend( block, extended), .select => try sema.zirSelect( block, extended), .int_from_error => try sema.zirIntFromError( block, extended), @@ -21747,17 +21747,31 @@ fn ptrFromIntVal( }; } -fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { +fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { const mod = sema.mod; const ip = &mod.intern_pool; const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; const src = LazySrcLoc.nodeOffset(extra.node); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; - const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@errSetCast"); + const base_dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_opt, "@errorCast"); const operand = try sema.resolveInst(extra.rhs); - const operand_ty = sema.typeOf(operand); - try sema.checkErrorSetType(block, src, dest_ty); - try sema.checkErrorSetType(block, operand_src, operand_ty); + const base_operand_ty = sema.typeOf(operand); + const dest_tag = base_dest_ty.zigTypeTag(mod); + const operand_tag = base_operand_ty.zigTypeTag(mod); + if (dest_tag != operand_tag) { + return sema.fail(block, src, "expected source and destination types to match, found '{s}' and '{s}'", .{ + @tagName(operand_tag), @tagName(dest_tag), + }); + } else if (dest_tag != .ErrorSet and dest_tag != .ErrorUnion) { + return sema.fail(block, src, "expected error set or error union type, found '{s}'", .{@tagName(dest_tag)}); + } + const dest_ty, const operand_ty = if (dest_tag == .ErrorUnion) .{ + base_dest_ty.errorUnionSet(mod), + base_operand_ty.errorUnionSet(mod), + } else .{ + base_dest_ty, + base_operand_ty, + }; // operand must be defined since it can be an invalid error value const maybe_operand_val = try sema.resolveDefinedValue(block, operand_src, operand); @@ -21804,8 +21818,15 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat } if (maybe_operand_val) |val| { - if (!dest_ty.isAnyError(mod)) { - const error_name = mod.intern_pool.indexToKey(val.toIntern()).err.name; + if (!dest_ty.isAnyError(mod)) check: { + const operand_val = mod.intern_pool.indexToKey(val.toIntern()); + var error_name: InternPool.NullTerminatedString = undefined; + if (dest_tag == .ErrorUnion) { + if (operand_val.error_union.val != .err_name) break :check; + error_name = operand_val.error_union.val.err_name; + } else { + error_name = operand_val.err.name; + } if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), error_name)) { const msg = msg: { const msg = try sema.errMsg( @@ -21822,16 +21843,29 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat } } - return Air.internedToRef((try mod.getCoerced(val, dest_ty)).toIntern()); + return Air.internedToRef((try mod.getCoerced(val, base_dest_ty)).toIntern()); } try sema.requireRuntimeBlock(block, src, operand_src); if (block.wantSafety() and !dest_ty.isAnyError(mod) and sema.mod.backendSupportsFeature(.error_set_has_value)) { - const err_int_inst = try block.addBitCast(Type.err_int, operand); - const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst); - try sema.addSafetyCheck(block, src, ok, .invalid_error_code); + if (dest_tag == .ErrorUnion) { + const err_code = try sema.analyzeErrUnionCode(block, operand_src, operand); + const err_int = try block.addBitCast(Type.err_int, err_code); + const zero_u16 = Air.internedToRef(try mod.intern(.{ + .int = .{ .ty = .u16_type, .storage = .{ .u64 = 0 } }, + })); + + const has_value = try block.addTyOp(.error_set_has_value, dest_ty, err_code); + const is_zero = try block.addBinOp(.cmp_eq, err_int, zero_u16); + const ok = try block.addBinOp(.bit_or, has_value, is_zero); + try sema.addSafetyCheck(block, src, ok, .invalid_error_code); + } else { + const err_int_inst = try block.addBitCast(Type.err_int, operand); + const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst); + try sema.addSafetyCheck(block, src, ok, .invalid_error_code); + } } - return block.addBitCast(dest_ty, operand); + return block.addBitCast(base_dest_ty, operand); } fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { @@ -22916,14 +22950,6 @@ fn checkIntOrVectorAllowComptime( } } -fn checkErrorSetType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { - const mod = sema.mod; - switch (ty.zigTypeTag(mod)) { - .ErrorSet => return, - else => return sema.fail(block, src, "expected error set type, found '{}'", .{ty.fmt(mod)}), - } -} - const SimdBinOp = struct { len: ?usize, /// Coerced to `result_ty`. diff --git a/src/Zir.zig b/src/Zir.zig index 62c48ecbb6209783493d3a3485188a0c984dee3e..973177c1b009001f38feda920809f4b09aee8cc4 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -1997,9 +1997,9 @@ pub const Inst = struct { /// Implements `@setCold`. /// `operand` is payload index to `UnNode`. set_cold, - /// Implements the `@errSetCast` builtin. + /// Implements the `@errorCast` builtin. /// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand. - err_set_cast, + error_cast, /// `operand` is payload index to `UnNode`. await_nosuspend, /// Implements `@breakpoint`. diff --git a/src/print_zir.zig b/src/print_zir.zig index 5ced6cafe7b3ff5e787d5494319ae84971fbabc3..d295af28304e93e7ee4b12db95bb109ab361d6b9 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -594,7 +594,7 @@ const Writer = struct { .builtin_extern, .c_define, - .err_set_cast, + .error_cast, .wasm_memory_grow, .prefetch, .c_va_arg, diff --git a/stage1/zig1.wasm b/stage1/zig1.wasm index 2f6d9abc4ea240507b05a957a0cf1839a9c08b0b..c81de3122bb124bdde1a101f278268d87e581b80 100644 Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 5a25714d147bcfeff6cb822c20dbf9b137f11e0e..2c3ba3b8c769f0bc7d2f626f1f007beda39ece47 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -228,13 +228,29 @@ const Set1 = error{ A, B }; const Set2 = error{ A, C }; fn testExplicitErrorSetCast(set1: Set1) !void { - var x = @as(Set2, @errSetCast(set1)); + var x = @as(Set2, @errorCast(set1)); try expect(@TypeOf(x) == Set2); - var y = @as(Set1, @errSetCast(x)); + var y = @as(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); + } + + fn retErrUnion() anyerror!i32 { + return 1234; + } + }; + + 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 diff --git a/test/cases/compile_errors/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig b/test/cases/compile_errors/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig index cfb01c3ddca254d264d1997f5d6ed24262247ad1..fedfaf2d07067cf12a6218bc7c076d01481394bf 100644 --- a/test/cases/compile_errors/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig +++ b/test/cases/compile_errors/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig @@ -2,7 +2,7 @@ const Set1 = error{ A, B }; const Set2 = error{ A, C }; comptime { var x = Set1.B; - var y: Set2 = @errSetCast(x); + var y: Set2 = @errorCast(x); _ = y; } diff --git a/test/cases/compile_errors/int_to_err_non_global_invalid_number.zig b/test/cases/compile_errors/int_to_err_non_global_invalid_number.zig index 32f4657ed549383d09eac68b35f15959a75a5a78..23c3917b445a4fff38b4bba04900106e1d6560f7 100644 --- a/test/cases/compile_errors/int_to_err_non_global_invalid_number.zig +++ b/test/cases/compile_errors/int_to_err_non_global_invalid_number.zig @@ -8,7 +8,7 @@ const Set2 = error{ }; comptime { var x = @intFromError(Set1.B); - var y: Set2 = @errSetCast(@errorFromInt(x)); + var y: Set2 = @errorCast(@errorFromInt(x)); _ = y; } diff --git a/test/cases/safety/@errSetCast error not present in destination.zig b/test/cases/safety/@errSetCast error not present in destination.zig deleted file mode 100644 index 84aeb7610e9cafd5dfa651cdc059cedbb16172d3..0000000000000000000000000000000000000000 --- a/test/cases/safety/@errSetCast error not present in destination.zig +++ /dev/null @@ -1,21 +0,0 @@ -const std = @import("std"); - -pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { - _ = stack_trace; - if (std.mem.eql(u8, message, "invalid error code")) { - std.process.exit(0); - } - std.process.exit(1); -} -const Set1 = error{ A, B }; -const Set2 = error{ A, C }; -pub fn main() !void { - foo(Set1.B) catch {}; - return error.TestFailed; -} -fn foo(set1: Set1) Set2 { - return @errSetCast(set1); -} -// run -// backend=llvm -// target=native diff --git a/test/cases/safety/@errorCast error not present in destination.zig b/test/cases/safety/@errorCast error not present in destination.zig new file mode 100644 index 0000000000000000000000000000000000000000..ff86e1f783828cd38a63d359290f6f6250a000e6 --- /dev/null +++ b/test/cases/safety/@errorCast error not present in destination.zig @@ -0,0 +1,21 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "invalid error code")) { + std.process.exit(0); + } + std.process.exit(1); +} +const Set1 = error{ A, B }; +const Set2 = error{ A, C }; +pub fn main() !void { + foo(Set1.B) catch {}; + return error.TestFailed; +} +fn foo(set1: Set1) Set2 { + return @errorCast(set1); +} +// run +// backend=llvm +// target=native