| 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,10 +2505,10 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2505 | .dbg_block_end, | 2505 | .dbg_block_end, |
| 2506 | .ensure_result_used, | 2506 | .ensure_result_used, |
| 2507 | .ensure_result_non_error, | 2507 | .ensure_result_non_error, |
| 2508 | .ensure_err_union_payload_void, | ||
| 2508 | .@"export", | 2509 | .@"export", |
| 2509 | .export_value, | 2510 | .export_value, |
| 2510 | .set_eval_branch_quota, | 2511 | .set_eval_branch_quota, |
| 2511 | .ensure_err_payload_void, | ||
| 2512 | .atomic_store, | 2512 | .atomic_store, |
| 2513 | .store, | 2513 | .store, |
| 2514 | .store_node, | 2514 | .store_node, |
| ... | @@ -5492,6 +5492,7 @@ fn ifExpr( | ... | @@ -5492,6 +5492,7 @@ fn ifExpr( |
| 5492 | try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst); | 5492 | try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst); |
| 5493 | break :s &payload_val_scope.base; | 5493 | break :s &payload_val_scope.base; |
| 5494 | } else { | 5494 | } else { |
| 5495 | _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node); | ||
| 5495 | break :s &then_scope.base; | 5496 | break :s &then_scope.base; |
| 5496 | } | 5497 | } |
| 5497 | } else if (if_full.payload_token) |payload_token| { | 5498 | } else if (if_full.payload_token) |payload_token| { |
| ... | @@ -5829,6 +5830,7 @@ fn whileExpr( | ... | @@ -5829,6 +5830,7 @@ fn whileExpr( |
| 5829 | dbg_var_inst = indexToRef(payload_inst); | 5830 | dbg_var_inst = indexToRef(payload_inst); |
| 5830 | break :s &payload_val_scope.base; | 5831 | break :s &payload_val_scope.base; |
| 5831 | } else { | 5832 | } else { |
| 5833 | _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node); | ||
| 5832 | break :s &then_scope.base; | 5834 | break :s &then_scope.base; |
| 5833 | } | 5835 | } |
| 5834 | } else if (while_full.payload_token) |payload_token| { | 5836 | } else if (while_full.payload_token) |payload_token| { |
src/Sema.zig+29-20| ... | @@ -1034,8 +1034,8 @@ fn analyzeBodyInner( | ... | @@ -1034,8 +1034,8 @@ fn analyzeBodyInner( |
| 1034 | i += 1; | 1034 | i += 1; |
| 1035 | continue; | 1035 | continue; |
| 1036 | }, | 1036 | }, |
| 1037 | .ensure_err_payload_void => { | 1037 | .ensure_err_union_payload_void => { |
| 1038 | try sema.zirEnsureErrPayloadVoid(block, inst); | 1038 | try sema.zirEnsureErrUnionPayloadVoid(block, inst); |
| 1039 | i += 1; | 1039 | i += 1; |
| 1040 | continue; | 1040 | continue; |
| 1041 | }, | 1041 | }, |
| ... | @@ -3100,6 +3100,33 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com | ... | @@ -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 | fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 3130 | fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 3104 | const tracy = trace(@src()); | 3131 | const tracy = trace(@src()); |
| 3105 | defer tracy.end(); | 3132 | defer tracy.end(); |
| ... | @@ -7681,24 +7708,6 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE | ... | @@ -7681,24 +7708,6 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE |
| 7681 | return block.addTyOp(.unwrap_errunion_err_ptr, result_ty, operand); | 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 | fn zirFunc( | 7711 | fn zirFunc( |
| 7703 | sema: *Sema, | 7712 | sema: *Sema, |
| 7704 | block: *Block, | 7713 | block: *Block, |
src/Zir.zig+5-6| ... | @@ -402,6 +402,8 @@ pub const Inst = struct { | ... | @@ -402,6 +402,8 @@ pub const Inst = struct { |
| 402 | /// Emits a compile error if an error is ignored. | 402 | /// Emits a compile error if an error is ignored. |
| 403 | /// Uses the `un_node` field. | 403 | /// Uses the `un_node` field. |
| 404 | ensure_result_non_error, | 404 | ensure_result_non_error, |
| 405 | /// Emits a compile error error union payload is not void. | ||
| 406 | ensure_err_union_payload_void, | ||
| 405 | /// Create a `E!T` type. | 407 | /// Create a `E!T` type. |
| 406 | /// Uses the `pl_node` field with `Bin` payload. | 408 | /// Uses the `pl_node` field with `Bin` payload. |
| 407 | error_union_type, | 409 | error_union_type, |
| ... | @@ -646,9 +648,6 @@ pub const Inst = struct { | ... | @@ -646,9 +648,6 @@ pub const Inst = struct { |
| 646 | /// Given a pointer to an error union value, returns the error code. No safety checks. | 648 | /// Given a pointer to an error union value, returns the error code. No safety checks. |
| 647 | /// Uses the `un_node` field. | 649 | /// Uses the `un_node` field. |
| 648 | err_union_code_ptr, | 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 | /// An enum literal. Uses the `str_tok` union field. | 651 | /// An enum literal. Uses the `str_tok` union field. |
| 653 | enum_literal, | 652 | enum_literal, |
| 654 | /// A switch expression. Uses the `pl_node` union field. | 653 | /// A switch expression. Uses the `pl_node` union field. |
| ... | @@ -1060,6 +1059,7 @@ pub const Inst = struct { | ... | @@ -1060,6 +1059,7 @@ pub const Inst = struct { |
| 1060 | .elem_val_node, | 1059 | .elem_val_node, |
| 1061 | .ensure_result_used, | 1060 | .ensure_result_used, |
| 1062 | .ensure_result_non_error, | 1061 | .ensure_result_non_error, |
| 1062 | .ensure_err_union_payload_void, | ||
| 1063 | .@"export", | 1063 | .@"export", |
| 1064 | .export_value, | 1064 | .export_value, |
| 1065 | .field_ptr, | 1065 | .field_ptr, |
| ... | @@ -1113,7 +1113,6 @@ pub const Inst = struct { | ... | @@ -1113,7 +1113,6 @@ pub const Inst = struct { |
| 1113 | .err_union_code_ptr, | 1113 | .err_union_code_ptr, |
| 1114 | .ptr_type, | 1114 | .ptr_type, |
| 1115 | .overflow_arithmetic_ptr, | 1115 | .overflow_arithmetic_ptr, |
| 1116 | .ensure_err_payload_void, | ||
| 1117 | .enum_literal, | 1116 | .enum_literal, |
| 1118 | .merge_error_sets, | 1117 | .merge_error_sets, |
| 1119 | .error_union_type, | 1118 | .error_union_type, |
| ... | @@ -1282,7 +1281,7 @@ pub const Inst = struct { | ... | @@ -1282,7 +1281,7 @@ pub const Inst = struct { |
| 1282 | .dbg_block_end, | 1281 | .dbg_block_end, |
| 1283 | .ensure_result_used, | 1282 | .ensure_result_used, |
| 1284 | .ensure_result_non_error, | 1283 | .ensure_result_non_error, |
| 1285 | .ensure_err_payload_void, | 1284 | .ensure_err_union_payload_void, |
| 1286 | .set_eval_branch_quota, | 1285 | .set_eval_branch_quota, |
| 1287 | .atomic_store, | 1286 | .atomic_store, |
| 1288 | .store, | 1287 | .store, |
| ... | @@ -1615,6 +1614,7 @@ pub const Inst = struct { | ... | @@ -1615,6 +1614,7 @@ pub const Inst = struct { |
| 1615 | .elem_val_node = .pl_node, | 1614 | .elem_val_node = .pl_node, |
| 1616 | .ensure_result_used = .un_node, | 1615 | .ensure_result_used = .un_node, |
| 1617 | .ensure_result_non_error = .un_node, | 1616 | .ensure_result_non_error = .un_node, |
| 1617 | .ensure_err_union_payload_void = .un_node, | ||
| 1618 | .error_union_type = .pl_node, | 1618 | .error_union_type = .pl_node, |
| 1619 | .error_value = .str_tok, | 1619 | .error_value = .str_tok, |
| 1620 | .@"export" = .pl_node, | 1620 | .@"export" = .pl_node, |
| ... | @@ -1677,7 +1677,6 @@ pub const Inst = struct { | ... | @@ -1677,7 +1677,6 @@ pub const Inst = struct { |
| 1677 | .err_union_payload_unsafe_ptr = .un_node, | 1677 | .err_union_payload_unsafe_ptr = .un_node, |
| 1678 | .err_union_code = .un_node, | 1678 | .err_union_code = .un_node, |
| 1679 | .err_union_code_ptr = .un_node, | 1679 | .err_union_code_ptr = .un_node, |
| 1680 | .ensure_err_payload_void = .un_tok, | ||
| 1681 | .enum_literal = .str_tok, | 1680 | .enum_literal = .str_tok, |
| 1682 | .switch_block = .pl_node, | 1681 | .switch_block = .pl_node, |
| 1683 | .switch_cond = .un_node, | 1682 | .switch_cond = .un_node, |
src/print_zir.zig+1-1| ... | @@ -162,6 +162,7 @@ const Writer = struct { | ... | @@ -162,6 +162,7 @@ const Writer = struct { |
| 162 | .load, | 162 | .load, |
| 163 | .ensure_result_used, | 163 | .ensure_result_used, |
| 164 | .ensure_result_non_error, | 164 | .ensure_result_non_error, |
| 165 | .ensure_err_union_payload_void, | ||
| 165 | .ret_node, | 166 | .ret_node, |
| 166 | .ret_load, | 167 | .ret_load, |
| 167 | .resolve_inferred_alloc, | 168 | .resolve_inferred_alloc, |
| ... | @@ -235,7 +236,6 @@ const Writer = struct { | ... | @@ -235,7 +236,6 @@ const Writer = struct { |
| 235 | 236 | ||
| 236 | .ref, | 237 | .ref, |
| 237 | .ret_tok, | 238 | .ret_tok, |
| 238 | .ensure_err_payload_void, | ||
| 239 | .closure_capture, | 239 | .closure_capture, |
| 240 | .switch_capture_tag, | 240 | .switch_capture_tag, |
| 241 | => try self.writeUnTok(stream, inst), | 241 | => try self.writeUnTok(stream, inst), |
test/behavior/error.zig+1-1| ... | @@ -7,7 +7,7 @@ const mem = std.mem; | ... | @@ -7,7 +7,7 @@ const mem = std.mem; |
| 7 | /// A more basic implementation of std.testing.expectError which | 7 | /// A more basic implementation of std.testing.expectError which |
| 8 | /// does not require formatter/printing support | 8 | /// does not require formatter/printing support |
| 9 | fn expectError(expected_err: anyerror, observed_err_union: anytype) !void { | 9 | fn expectError(expected_err: anyerror, observed_err_union: anytype) !void { |
| 10 | if (observed_err_union) { | 10 | if (observed_err_union) |_| { |
| 11 | return error.TestExpectedError; | 11 | return error.TestExpectedError; |
| 12 | } else |err| if (err == expected_err) { | 12 | } else |err| if (err == expected_err) { |
| 13 | return; // Success | 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 '|_|' | ||