| author | |
| committer | |
| log | 8e1421f19ef3daea9b8a414bd5783189bc292676 |
| tree | cf642506120e82fed3de9c4a32a2232492d49b94 |
| parent | d8bfbbbf25984cfdc4d50a92569523a0a151d9e6 |
| parent | 0b1ba6eb521a15b38b08f9923dd84329487d5755 |
| signature |
Sema: implement `@errSetCast` for error unions16 files changed, 110 insertions(+), 66 deletions(-)
doc/langref.html.in+6-6| ... | ... | @@ -6657,7 +6657,7 @@ test "coercion from homogenous tuple to array" { |
| 6657 | 6657 | <li>{#link|@alignCast#} - make a pointer have more alignment</li> |
| 6658 | 6658 | <li>{#link|@enumFromInt#} - obtain an enum value based on its integer tag value</li> |
| 6659 | 6659 | <li>{#link|@errorFromInt#} - obtain an error code based on its integer value</li> |
| 6660 | <li>{#link|@errSetCast#} - convert to a smaller error set</li> | |
| 6660 | <li>{#link|@errorCast#} - convert to a smaller error set</li> | |
| 6661 | 6661 | <li>{#link|@floatCast#} - convert a larger float to a smaller float</li> |
| 6662 | 6662 | <li>{#link|@floatFromInt#} - convert an integer to a float value</li> |
| 6663 | 6663 | <li>{#link|@intCast#} - convert between integer types</li> |
| ... | ... | @@ -8410,10 +8410,10 @@ test "main" { |
| 8410 | 8410 | </p> |
| 8411 | 8411 | {#header_close#} |
| 8412 | 8412 | |
| 8413 | {#header_open|@errSetCast#} | |
| 8414 | <pre>{#syntax#}@errSetCast(value: anytype) anytype{#endsyntax#}</pre> | |
| 8413 | {#header_open|@errorCast#} | |
| 8414 | <pre>{#syntax#}@errorCast(value: anytype) anytype{#endsyntax#}</pre> | |
| 8415 | 8415 | <p> |
| 8416 | Converts an error value from one error set to another error set. The return type is the | |
| 8416 | Converts an error set or error union value from one error set to another error set. The return type is the | |
| 8417 | 8417 | 			inferred result type. Attempting to convert an error which is not in the destination error |
| 8418 | 8418 | 			set results in safety-protected {#link|Undefined Behavior#}. |
| 8419 | 8419 | </p> |
| ... | ... | @@ -10257,7 +10257,7 @@ const Set2 = error{ |
| 10257 | 10257 | C, |
| 10258 | 10258 | }; |
| 10259 | 10259 | comptime { |
| 10260 | _ = @as(Set2, @errSetCast(Set1.B)); | |
| 10260 | _ = @as(Set2, @errorCast(Set1.B)); | |
| 10261 | 10261 | } |
| 10262 | 10262 | {#code_end#} |
| 10263 | 10263 | <p>At runtime:</p> |
| ... | ... | @@ -10276,7 +10276,7 @@ pub fn main() void { |
| 10276 | 10276 | foo(Set1.B); |
| 10277 | 10277 | } |
| 10278 | 10278 | fn foo(set1: Set1) void { |
| 10279 | const x: Set2 = @errSetCast(set1); | |
| 10279 | const x: Set2 = @errorCast(set1); | |
| 10280 | 10280 | std.debug.print("value: {}\n", .{x}); |
| 10281 | 10281 | } |
| 10282 | 10282 | {#code_end#} |
lib/std/child_process.zig+2-2| ... | ... | @@ -446,7 +446,7 @@ pub const ChildProcess = struct { |
| 446 | 446 | // has a value greater than 0 |
| 447 | 447 | if ((fd[0].revents & std.os.POLL.IN) != 0) { |
| 448 | 448 | const err_int = try readIntFd(err_pipe[0]); |
| 449 | return @as(SpawnError, @errSetCast(@errorFromInt(err_int))); | |
| 449 | return @as(SpawnError, @errorCast(@errorFromInt(err_int))); | |
| 450 | 450 | } |
| 451 | 451 | } else { |
| 452 | 452 | // Write maxInt(ErrInt) to the write end of the err_pipe. This is after |
| ... | ... | @@ -459,7 +459,7 @@ pub const ChildProcess = struct { |
| 459 | 459 | // Here we potentially return the fork child's error from the parent |
| 460 | 460 | // pid. |
| 461 | 461 | if (err_int != maxInt(ErrInt)) { |
| 462 | return @as(SpawnError, @errSetCast(@errorFromInt(err_int))); | |
| 462 | return @as(SpawnError, @errorCast(@errorFromInt(err_int))); | |
| 463 | 463 | } |
| 464 | 464 | } |
| 465 | 465 | } |
lib/std/os.zig+1-1| ... | ... | @@ -5419,7 +5419,7 @@ pub fn dl_iterate_phdr( |
| 5419 | 5419 | } |
| 5420 | 5420 | }.callbackC, @as(?*anyopaque, @ptrFromInt(@intFromPtr(&context))))) { |
| 5421 | 5421 | 0 => return, |
| 5422 | else => |err| return @as(Error, @errSetCast(@errorFromInt(@as(u16, @intCast(err))))), // TODO don't hardcode u16 | |
| 5422 | else => |err| return @as(Error, @errorCast(@errorFromInt(@as(u16, @intCast(err))))), // TODO don't hardcode u16 | |
| 5423 | 5423 | } |
| 5424 | 5424 | } |
| 5425 | 5425 |
lib/std/zig/render.zig+3-1| ... | ... | @@ -1444,7 +1444,7 @@ fn renderBuiltinCall( |
| 1444 | 1444 | const slice = tree.tokenSlice(builtin_token); |
| 1445 | 1445 | const rewrite_two_param_cast = params.len == 2 and for ([_][]const u8{ |
| 1446 | 1446 | "@bitCast", |
| 1447 | "@errSetCast", | |
| 1447 | "@errorCast", | |
| 1448 | 1448 | "@floatCast", |
| 1449 | 1449 | "@intCast", |
| 1450 | 1450 | "@ptrCast", |
| ... | ... | @@ -1505,6 +1505,8 @@ fn renderBuiltinCall( |
| 1505 | 1505 | try ais.writer().writeAll("@intFromPtr"); |
| 1506 | 1506 | } else if (mem.eql(u8, slice, "@fabs")) { |
| 1507 | 1507 | try ais.writer().writeAll("@abs"); |
| 1508 | } else if (mem.eql(u8, slice, "@errSetCast")) { | |
| 1509 | try ais.writer().writeAll("@errorCast"); | |
| 1508 | 1510 | } else { |
| 1509 | 1511 | try renderToken(ais, tree, builtin_token, .none); // @name |
| 1510 | 1512 | } |
src/AstGen.zig+3-3| ... | ... | @@ -8454,11 +8454,11 @@ fn builtinCall( |
| 8454 | 8454 | }); |
| 8455 | 8455 | return rvalue(gz, ri, result, node); |
| 8456 | 8456 | }, |
| 8457 | .err_set_cast => { | |
| 8457 | .error_cast => { | |
| 8458 | 8458 | try emitDbgNode(gz, node); |
| 8459 | 8459 | |
| 8460 | const result = try gz.addExtendedPayload(.err_set_cast, Zir.Inst.BinNode{ | |
| 8461 | .lhs = try ri.rl.resultTypeForCast(gz, node, "@errSetCast"), | |
| 8460 | const result = try gz.addExtendedPayload(.error_cast, Zir.Inst.BinNode{ | |
| 8461 | .lhs = try ri.rl.resultTypeForCast(gz, node, "@errorCast"), | |
| 8462 | 8462 | .rhs = try expr(gz, scope, .{ .rl = .none }, params[0]), |
| 8463 | 8463 | .node = gz.nodeIndexToRelative(node), |
| 8464 | 8464 | }); |
src/AstRlAnnotate.zig+1-1| ... | ... | @@ -945,7 +945,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast. |
| 945 | 945 | .float_cast, |
| 946 | 946 | .int_cast, |
| 947 | 947 | .truncate, |
| 948 | .err_set_cast, | |
| 948 | .error_cast, | |
| 949 | 949 | .ptr_cast, |
| 950 | 950 | .align_cast, |
| 951 | 951 | .addrspace_cast, |
src/BuiltinFn.zig+3-3| ... | ... | @@ -43,7 +43,7 @@ pub const Tag = enum { |
| 43 | 43 | error_name, |
| 44 | 44 | error_return_trace, |
| 45 | 45 | int_from_error, |
| 46 | err_set_cast, | |
| 46 | error_cast, | |
| 47 | 47 | @"export", |
| 48 | 48 | @"extern", |
| 49 | 49 | fence, |
| ... | ... | @@ -455,9 +455,9 @@ pub const list = list: { |
| 455 | 455 | }, |
| 456 | 456 | }, |
| 457 | 457 | .{ |
| 458 | "@errSetCast", | |
| 458 | "@errorCast", | |
| 459 | 459 | .{ |
| 460 | .tag = .err_set_cast, | |
| 460 | .tag = .error_cast, | |
| 461 | 461 | .eval_to_error = .always, |
| 462 | 462 | .param_count = 1, |
| 463 | 463 | }, |
src/Sema.zig+47-21| ... | ... | @@ -1252,7 +1252,7 @@ fn analyzeBodyInner( |
| 1252 | 1252 | .wasm_memory_size => try sema.zirWasmMemorySize( block, extended), |
| 1253 | 1253 | .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended), |
| 1254 | 1254 | .prefetch => try sema.zirPrefetch( block, extended), |
| 1255 | .err_set_cast => try sema.zirErrSetCast( block, extended), | |
| 1255 | .error_cast => try sema.zirErrorCast( block, extended), | |
| 1256 | 1256 | .await_nosuspend => try sema.zirAwaitNosuspend( block, extended), |
| 1257 | 1257 | .select => try sema.zirSelect( block, extended), |
| 1258 | 1258 | .int_from_error => try sema.zirIntFromError( block, extended), |
| ... | ... | @@ -21747,17 +21747,31 @@ fn ptrFromIntVal( |
| 21747 | 21747 | }; |
| 21748 | 21748 | } |
| 21749 | 21749 | |
| 21750 | fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { | |
| 21750 | fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { | |
| 21751 | 21751 | const mod = sema.mod; |
| 21752 | 21752 | const ip = &mod.intern_pool; |
| 21753 | 21753 | const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; |
| 21754 | 21754 | const src = LazySrcLoc.nodeOffset(extra.node); |
| 21755 | 21755 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node }; |
| 21756 | const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@errSetCast"); | |
| 21756 | const base_dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_opt, "@errorCast"); | |
| 21757 | 21757 | const operand = try sema.resolveInst(extra.rhs); |
| 21758 | const operand_ty = sema.typeOf(operand); | |
| 21759 | try sema.checkErrorSetType(block, src, dest_ty); | |
| 21760 | try sema.checkErrorSetType(block, operand_src, operand_ty); | |
| 21758 | const base_operand_ty = sema.typeOf(operand); | |
| 21759 | const dest_tag = base_dest_ty.zigTypeTag(mod); | |
| 21760 | const operand_tag = base_operand_ty.zigTypeTag(mod); | |
| 21761 | if (dest_tag != operand_tag) { | |
| 21762 | return sema.fail(block, src, "expected source and destination types to match, found '{s}' and '{s}'", .{ | |
| 21763 | @tagName(operand_tag), @tagName(dest_tag), | |
| 21764 | }); | |
| 21765 | } else if (dest_tag != .ErrorSet and dest_tag != .ErrorUnion) { | |
| 21766 | return sema.fail(block, src, "expected error set or error union type, found '{s}'", .{@tagName(dest_tag)}); | |
| 21767 | } | |
| 21768 | const dest_ty, const operand_ty = if (dest_tag == .ErrorUnion) .{ | |
| 21769 | base_dest_ty.errorUnionSet(mod), | |
| 21770 | base_operand_ty.errorUnionSet(mod), | |
| 21771 | } else .{ | |
| 21772 | base_dest_ty, | |
| 21773 | base_operand_ty, | |
| 21774 | }; | |
| 21761 | 21775 | |
| 21762 | 21776 | // operand must be defined since it can be an invalid error value |
| 21763 | 21777 | 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 |
| 21804 | 21818 | } |
| 21805 | 21819 | |
| 21806 | 21820 | if (maybe_operand_val) |val| { |
| 21807 | if (!dest_ty.isAnyError(mod)) { | |
| 21808 | const error_name = mod.intern_pool.indexToKey(val.toIntern()).err.name; | |
| 21821 | if (!dest_ty.isAnyError(mod)) check: { | |
| 21822 | const operand_val = mod.intern_pool.indexToKey(val.toIntern()); | |
| 21823 | var error_name: InternPool.NullTerminatedString = undefined; | |
| 21824 | if (dest_tag == .ErrorUnion) { | |
| 21825 | if (operand_val.error_union.val != .err_name) break :check; | |
| 21826 | error_name = operand_val.error_union.val.err_name; | |
| 21827 | } else { | |
| 21828 | error_name = operand_val.err.name; | |
| 21829 | } | |
| 21809 | 21830 | if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), error_name)) { |
| 21810 | 21831 | const msg = msg: { |
| 21811 | 21832 | const msg = try sema.errMsg( |
| ... | ... | @@ -21822,16 +21843,29 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat |
| 21822 | 21843 | } |
| 21823 | 21844 | } |
| 21824 | 21845 | |
| 21825 | return Air.internedToRef((try mod.getCoerced(val, dest_ty)).toIntern()); | |
| 21846 | return Air.internedToRef((try mod.getCoerced(val, base_dest_ty)).toIntern()); | |
| 21826 | 21847 | } |
| 21827 | 21848 | |
| 21828 | 21849 | try sema.requireRuntimeBlock(block, src, operand_src); |
| 21829 | 21850 | if (block.wantSafety() and !dest_ty.isAnyError(mod) and sema.mod.backendSupportsFeature(.error_set_has_value)) { |
| 21830 | const err_int_inst = try block.addBitCast(Type.err_int, operand); | |
| 21831 | const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst); | |
| 21832 | try sema.addSafetyCheck(block, src, ok, .invalid_error_code); | |
| 21851 | if (dest_tag == .ErrorUnion) { | |
| 21852 | const err_code = try sema.analyzeErrUnionCode(block, operand_src, operand); | |
| 21853 | const err_int = try block.addBitCast(Type.err_int, err_code); | |
| 21854 | const zero_u16 = Air.internedToRef(try mod.intern(.{ | |
| 21855 | .int = .{ .ty = .u16_type, .storage = .{ .u64 = 0 } }, | |
| 21856 | })); | |
| 21857 | ||
| 21858 | const has_value = try block.addTyOp(.error_set_has_value, dest_ty, err_code); | |
| 21859 | const is_zero = try block.addBinOp(.cmp_eq, err_int, zero_u16); | |
| 21860 | const ok = try block.addBinOp(.bit_or, has_value, is_zero); | |
| 21861 | try sema.addSafetyCheck(block, src, ok, .invalid_error_code); | |
| 21862 | } else { | |
| 21863 | const err_int_inst = try block.addBitCast(Type.err_int, operand); | |
| 21864 | const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst); | |
| 21865 | try sema.addSafetyCheck(block, src, ok, .invalid_error_code); | |
| 21866 | } | |
| 21833 | 21867 | } |
| 21834 | return block.addBitCast(dest_ty, operand); | |
| 21868 | return block.addBitCast(base_dest_ty, operand); | |
| 21835 | 21869 | } |
| 21836 | 21870 | |
| 21837 | 21871 | fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -22916,14 +22950,6 @@ fn checkIntOrVectorAllowComptime( |
| 22916 | 22950 | } |
| 22917 | 22951 | } |
| 22918 | 22952 | |
| 22919 | fn checkErrorSetType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { | |
| 22920 | const mod = sema.mod; | |
| 22921 | switch (ty.zigTypeTag(mod)) { | |
| 22922 | .ErrorSet => return, | |
| 22923 | else => return sema.fail(block, src, "expected error set type, found '{}'", .{ty.fmt(mod)}), | |
| 22924 | } | |
| 22925 | } | |
| 22926 | ||
| 22927 | 22953 | const SimdBinOp = struct { |
| 22928 | 22954 | len: ?usize, |
| 22929 | 22955 | /// Coerced to `result_ty`. |
src/Zir.zig+2-2| ... | ... | @@ -1997,9 +1997,9 @@ pub const Inst = struct { |
| 1997 | 1997 | /// Implements `@setCold`. |
| 1998 | 1998 | /// `operand` is payload index to `UnNode`. |
| 1999 | 1999 | set_cold, |
| 2000 | /// Implements the `@errSetCast` builtin. | |
| 2000 | /// Implements the `@errorCast` builtin. | |
| 2001 | 2001 | /// `operand` is payload index to `BinNode`. `lhs` is dest type, `rhs` is operand. |
| 2002 | err_set_cast, | |
| 2002 | error_cast, | |
| 2003 | 2003 | /// `operand` is payload index to `UnNode`. |
| 2004 | 2004 | await_nosuspend, |
| 2005 | 2005 | /// Implements `@breakpoint`. |
src/print_zir.zig+1-1| ... | ... | @@ -594,7 +594,7 @@ const Writer = struct { |
| 594 | 594 | |
| 595 | 595 | .builtin_extern, |
| 596 | 596 | .c_define, |
| 597 | .err_set_cast, | |
| 597 | .error_cast, | |
| 598 | 598 | .wasm_memory_grow, |
| 599 | 599 | .prefetch, |
| 600 | 600 | .c_va_arg, |
stage1/zig1.wasm| Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ |
test/behavior/error.zig+18-2| ... | ... | @@ -228,13 +228,29 @@ const Set1 = error{ A, B }; |
| 228 | 228 | const Set2 = error{ A, C }; |
| 229 | 229 | |
| 230 | 230 | fn testExplicitErrorSetCast(set1: Set1) !void { |
| 231 | var x = @as(Set2, @errSetCast(set1)); | |
| 231 | var x = @as(Set2, @errorCast(set1)); | |
| 232 | 232 | try expect(@TypeOf(x) == Set2); |
| 233 | var y = @as(Set1, @errSetCast(x)); | |
| 233 | var y = @as(Set1, @errorCast(x)); | |
| 234 | 234 | try expect(@TypeOf(y) == Set1); |
| 235 | 235 | try expect(y == error.A); |
| 236 | 236 | } |
| 237 | 237 | |
| 238 | test "@errorCast on error unions" { | |
| 239 | const S = struct { | |
| 240 | fn doTheTest() !void { | |
| 241 | const casted: error{Bad}!i32 = @errorCast(retErrUnion()); | |
| 242 | try expect((try casted) == 1234); | |
| 243 | } | |
| 244 | ||
| 245 | fn retErrUnion() anyerror!i32 { | |
| 246 | return 1234; | |
| 247 | } | |
| 248 | }; | |
| 249 | ||
| 250 | try S.doTheTest(); | |
| 251 | try comptime S.doTheTest(); | |
| 252 | } | |
| 253 | ||
| 238 | 254 | test "comptime test error for empty error set" { |
| 239 | 255 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 240 | 256 |
test/cases/compile_errors/explicit_error_set_cast_known_at_comptime_violates_error_sets.zig+1-1| ... | ... | @@ -2,7 +2,7 @@ const Set1 = error{ A, B }; |
| 2 | 2 | const Set2 = error{ A, C }; |
| 3 | 3 | comptime { |
| 4 | 4 | var x = Set1.B; |
| 5 | var y: Set2 = @errSetCast(x); | |
| 5 | var y: Set2 = @errorCast(x); | |
| 6 | 6 | _ = y; |
| 7 | 7 | } |
| 8 | 8 |
test/cases/compile_errors/int_to_err_non_global_invalid_number.zig+1-1| ... | ... | @@ -8,7 +8,7 @@ const Set2 = error{ |
| 8 | 8 | }; |
| 9 | 9 | comptime { |
| 10 | 10 | var x = @intFromError(Set1.B); |
| 11 | var y: Set2 = @errSetCast(@errorFromInt(x)); | |
| 11 | var y: Set2 = @errorCast(@errorFromInt(x)); | |
| 12 | 12 | _ = y; |
| 13 | 13 | } |
| 14 | 14 |
test/cases/safety/@errSetCast error not present in destination.zig	 deleted-21| ... | ... | @@ -1,21 +0,0 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { | |
| 4 | _ = stack_trace; | |
| 5 | if (std.mem.eql(u8, message, "invalid error code")) { | |
| 6 | std.process.exit(0); | |
| 7 | } | |
| 8 | std.process.exit(1); | |
| 9 | } | |
| 10 | const Set1 = error{ A, B }; | |
| 11 | const Set2 = error{ A, C }; | |
| 12 | pub fn main() !void { | |
| 13 | foo(Set1.B) catch {}; | |
| 14 | return error.TestFailed; | |
| 15 | } | |
| 16 | fn foo(set1: Set1) Set2 { | |
| 17 | return @errSetCast(set1); | |
| 18 | } | |
| 19 | // run | |
| 20 | // backend=llvm | |
| 21 | // target=native |
test/cases/safety/@errorCast error not present in destination.zig	 created+21| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { | |
| 4 | _ = stack_trace; | |
| 5 | if (std.mem.eql(u8, message, "invalid error code")) { | |
| 6 | std.process.exit(0); | |
| 7 | } | |
| 8 | std.process.exit(1); | |
| 9 | } | |
| 10 | const Set1 = error{ A, B }; | |
| 11 | const Set2 = error{ A, C }; | |
| 12 | pub fn main() !void { | |
| 13 | foo(Set1.B) catch {}; | |
| 14 | return error.TestFailed; | |
| 15 | } | |
| 16 | fn foo(set1: Set1) Set2 { | |
| 17 | return @errorCast(set1); | |
| 18 | } | |
| 19 | // run | |
| 20 | // backend=llvm | |
| 21 | // target=native |