| author | |
| committer | |
| log | 0b1dd845d9ac15ade5748dcb6ffa6b868fdfe0c0 |
| tree | bf69edb5019874be43c15492265ed8418fd71453 |
| parent | b626977f45f5175ae26e212b20adbd514649d6e5 |
See https://github.com/ziglang/zig/pull/6060#discussion_r4710329126 files changed, 64 insertions(+), 29 deletions(-)
src/AstGen.zig+3-1| ... | ... | @@ -2505,10 +2505,10 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2505 | 2505 | .dbg_block_end, |
| 2506 | 2506 | .ensure_result_used, |
| 2507 | 2507 | .ensure_result_non_error, |
| 2508 | .ensure_err_union_payload_void, | |
| 2508 | 2509 | .@"export", |
| 2509 | 2510 | .export_value, |
| 2510 | 2511 | .set_eval_branch_quota, |
| 2511 | .ensure_err_payload_void, | |
| 2512 | 2512 | .atomic_store, |
| 2513 | 2513 | .store, |
| 2514 | 2514 | .store_node, |
| ... | ... | @@ -5492,6 +5492,7 @@ fn ifExpr( |
| 5492 | 5492 | try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst); |
| 5493 | 5493 | break :s &payload_val_scope.base; |
| 5494 | 5494 | } else { |
| 5495 | _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node); | |
| 5495 | 5496 | break :s &then_scope.base; |
| 5496 | 5497 | } |
| 5497 | 5498 | } else if (if_full.payload_token) |payload_token| { |
| ... | ... | @@ -5829,6 +5830,7 @@ fn whileExpr( |
| 5829 | 5830 | dbg_var_inst = indexToRef(payload_inst); |
| 5830 | 5831 | break :s &payload_val_scope.base; |
| 5831 | 5832 | } else { |
| 5833 | _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node); | |
| 5832 | 5834 | break :s &then_scope.base; |
| 5833 | 5835 | } |
| 5834 | 5836 | } else if (while_full.payload_token) |payload_token| { |
src/Sema.zig+29-20| ... | ... | @@ -1034,8 +1034,8 @@ fn analyzeBodyInner( |
| 1034 | 1034 | i += 1; |
| 1035 | 1035 | continue; |
| 1036 | 1036 | }, |
| 1037 | .ensure_err_payload_void => { | |
| 1038 | try sema.zirEnsureErrPayloadVoid(block, inst); | |
| 1037 | .ensure_err_union_payload_void => { | |
| 1038 | try sema.zirEnsureErrUnionPayloadVoid(block, inst); | |
| 1039 | 1039 | i += 1; |
| 1040 | 1040 | continue; |
| 1041 | 1041 | }, |
| ... | ... | @@ -3100,6 +3100,33 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com |
| 3100 | 3100 | } |
| 3101 | 3101 | } |
| 3102 | 3102 | |
| 3103 | fn zirEnsureErrUnionPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { | |
| 3104 | const tracy = trace(@src()); | |
| 3105 | defer tracy.end(); | |
| 3106 | ||
| 3107 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | |
| 3108 | const src = inst_data.src(); | |
| 3109 | const operand = try sema.resolveInst(inst_data.operand); | |
| 3110 | const operand_ty = sema.typeOf(operand); | |
| 3111 | const err_union_ty = if (operand_ty.zigTypeTag() == .Pointer) | |
| 3112 | operand_ty.childType() | |
| 3113 | else | |
| 3114 | operand_ty; | |
| 3115 | // TODO this should be validated in a more generic instruction that is | |
| 3116 | // emitted for all ifs and whiles with an error union condition. | |
| 3117 | if (err_union_ty.zigTypeTag() != .ErrorUnion) return; | |
| 3118 | const payload_ty = err_union_ty.errorUnionPayload().zigTypeTag(); | |
| 3119 | if (payload_ty != .Void and payload_ty != .NoReturn) { | |
| 3120 | const msg = msg: { | |
| 3121 | const msg = try sema.errMsg(block, src, "error union payload is ignored", .{}); | |
| 3122 | errdefer msg.destroy(sema.gpa); | |
| 3123 | try sema.errNote(block, src, msg, "payload value can be explicitly ignored with '|_|'", .{}); | |
| 3124 | break :msg msg; | |
| 3125 | }; | |
| 3126 | return sema.failWithOwnedErrorMsg(msg); | |
| 3127 | } | |
| 3128 | } | |
| 3129 | ||
| 3103 | 3130 | fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 3104 | 3131 | const tracy = trace(@src()); |
| 3105 | 3132 | defer tracy.end(); |
| ... | ... | @@ -7681,24 +7708,6 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE |
| 7681 | 7708 | return block.addTyOp(.unwrap_errunion_err_ptr, result_ty, operand); |
| 7682 | 7709 | } |
| 7683 | 7710 | |
| 7684 | fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { | |
| 7685 | const tracy = trace(@src()); | |
| 7686 | defer tracy.end(); | |
| 7687 | ||
| 7688 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | |
| 7689 | const src = inst_data.src(); | |
| 7690 | const operand = try sema.resolveInst(inst_data.operand); | |
| 7691 | const operand_ty = sema.typeOf(operand); | |
| 7692 | if (operand_ty.zigTypeTag() != .ErrorUnion) { | |
| 7693 | return sema.fail(block, src, "expected error union type, found '{}'", .{ | |
| 7694 | operand_ty.fmt(sema.mod), | |
| 7695 | }); | |
| 7696 | } | |
| 7697 | if (operand_ty.errorUnionPayload().zigTypeTag() != .Void) { | |
| 7698 | return sema.fail(block, src, "expression value is ignored", .{}); | |
| 7699 | } | |
| 7700 | } | |
| 7701 | ||
| 7702 | 7711 | fn zirFunc( |
| 7703 | 7712 | sema: *Sema, |
| 7704 | 7713 | block: *Block, |
src/Zir.zig+5-6| ... | ... | @@ -402,6 +402,8 @@ pub const Inst = struct { |
| 402 | 402 | /// Emits a compile error if an error is ignored. |
| 403 | 403 | /// Uses the `un_node` field. |
| 404 | 404 | ensure_result_non_error, |
| 405 | /// Emits a compile error error union payload is not void. | |
| 406 | ensure_err_union_payload_void, | |
| 405 | 407 | /// Create a `E!T` type. |
| 406 | 408 | /// Uses the `pl_node` field with `Bin` payload. |
| 407 | 409 | error_union_type, |
| ... | ... | @@ -646,9 +648,6 @@ pub const Inst = struct { |
| 646 | 648 | /// Given a pointer to an error union value, returns the error code. No safety checks. |
| 647 | 649 | /// Uses the `un_node` field. |
| 648 | 650 | err_union_code_ptr, |
| 649 | /// Takes a *E!T and raises a compiler error if T != void | |
| 650 | /// Uses the `un_tok` field. | |
| 651 | ensure_err_payload_void, | |
| 652 | 651 | /// An enum literal. Uses the `str_tok` union field. |
| 653 | 652 | enum_literal, |
| 654 | 653 | /// A switch expression. Uses the `pl_node` union field. |
| ... | ... | @@ -1060,6 +1059,7 @@ pub const Inst = struct { |
| 1060 | 1059 | .elem_val_node, |
| 1061 | 1060 | .ensure_result_used, |
| 1062 | 1061 | .ensure_result_non_error, |
| 1062 | .ensure_err_union_payload_void, | |
| 1063 | 1063 | .@"export", |
| 1064 | 1064 | .export_value, |
| 1065 | 1065 | .field_ptr, |
| ... | ... | @@ -1113,7 +1113,6 @@ pub const Inst = struct { |
| 1113 | 1113 | .err_union_code_ptr, |
| 1114 | 1114 | .ptr_type, |
| 1115 | 1115 | .overflow_arithmetic_ptr, |
| 1116 | .ensure_err_payload_void, | |
| 1117 | 1116 | .enum_literal, |
| 1118 | 1117 | .merge_error_sets, |
| 1119 | 1118 | .error_union_type, |
| ... | ... | @@ -1282,7 +1281,7 @@ pub const Inst = struct { |
| 1282 | 1281 | .dbg_block_end, |
| 1283 | 1282 | .ensure_result_used, |
| 1284 | 1283 | .ensure_result_non_error, |
| 1285 | .ensure_err_payload_void, | |
| 1284 | .ensure_err_union_payload_void, | |
| 1286 | 1285 | .set_eval_branch_quota, |
| 1287 | 1286 | .atomic_store, |
| 1288 | 1287 | .store, |
| ... | ... | @@ -1615,6 +1614,7 @@ pub const Inst = struct { |
| 1615 | 1614 | .elem_val_node = .pl_node, |
| 1616 | 1615 | .ensure_result_used = .un_node, |
| 1617 | 1616 | .ensure_result_non_error = .un_node, |
| 1617 | .ensure_err_union_payload_void = .un_node, | |
| 1618 | 1618 | .error_union_type = .pl_node, |
| 1619 | 1619 | .error_value = .str_tok, |
| 1620 | 1620 | .@"export" = .pl_node, |
| ... | ... | @@ -1677,7 +1677,6 @@ pub const Inst = struct { |
| 1677 | 1677 | .err_union_payload_unsafe_ptr = .un_node, |
| 1678 | 1678 | .err_union_code = .un_node, |
| 1679 | 1679 | .err_union_code_ptr = .un_node, |
| 1680 | .ensure_err_payload_void = .un_tok, | |
| 1681 | 1680 | .enum_literal = .str_tok, |
| 1682 | 1681 | .switch_block = .pl_node, |
| 1683 | 1682 | .switch_cond = .un_node, |
src/print_zir.zig+1-1| ... | ... | @@ -162,6 +162,7 @@ const Writer = struct { |
| 162 | 162 | .load, |
| 163 | 163 | .ensure_result_used, |
| 164 | 164 | .ensure_result_non_error, |
| 165 | .ensure_err_union_payload_void, | |
| 165 | 166 | .ret_node, |
| 166 | 167 | .ret_load, |
| 167 | 168 | .resolve_inferred_alloc, |
| ... | ... | @@ -235,7 +236,6 @@ const Writer = struct { |
| 235 | 236 | |
| 236 | 237 | .ref, |
| 237 | 238 | .ret_tok, |
| 238 | .ensure_err_payload_void, | |
| 239 | 239 | .closure_capture, |
| 240 | 240 | .switch_capture_tag, |
| 241 | 241 | => try self.writeUnTok(stream, inst), |
test/behavior/error.zig+1-1| ... | ... | @@ -7,7 +7,7 @@ const mem = std.mem; |
| 7 | 7 | /// A more basic implementation of std.testing.expectError which |
| 8 | 8 | /// does not require formatter/printing support |
| 9 | 9 | fn expectError(expected_err: anyerror, observed_err_union: anytype) !void { |
| 10 | if (observed_err_union) { | |
| 10 | if (observed_err_union) |_| { | |
| 11 | 11 | return error.TestExpectedError; |
| 12 | 12 | } else |err| if (err == expected_err) { |
| 13 | 13 | return; // Success |
test/cases/compile_errors/non_void_error_union_payload_ignored.zig created+25| ... | ... | @@ -0,0 +1,25 @@ |
| 1 | pub export fn entry1() void { | |
| 2 | var x: anyerror!usize = 5; | |
| 3 | if (x) { | |
| 4 | // foo | |
| 5 | } else |_| { | |
| 6 | // bar | |
| 7 | } | |
| 8 | } | |
| 9 | pub export fn entry2() void { | |
| 10 | var x: anyerror!usize = 5; | |
| 11 | while (x) { | |
| 12 | // foo | |
| 13 | } else |_| { | |
| 14 | // bar | |
| 15 | } | |
| 16 | } | |
| 17 | ||
| 18 | // error | |
| 19 | // backend=stage2 | |
| 20 | // target=native | |
| 21 | // | |
| 22 | // :3:5: error: error union payload is ignored | |
| 23 | // :3:5: note: payload value can be explicitly ignored with '|_|' | |
| 24 | // :11:5: error: error union payload is ignored | |
| 25 | // :11:5: note: payload value can be explicitly ignored with '|_|' |