authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-03-09 14:05:22+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-10-06 15:39:06+03:00
log0b1dd845d9ac15ade5748dcb6ffa6b868fdfe0c0
treebf69edb5019874be43c15492265ed8418fd71453
parentb626977f45f5175ae26e212b20adbd514649d6e5

stage2: add error for non-void error union payload being ignored

See https://github.com/ziglang/zig/pull/6060#discussion_r471032912

6 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}
31023102
3103fn 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
3103fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {3130fn 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}
76837710
7684fn 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
7702fn zirFunc(7711fn 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 {
235236
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 which7/// A more basic implementation of std.testing.expectError which
8/// does not require formatter/printing support8/// does not require formatter/printing support
9fn expectError(expected_err: anyerror, observed_err_union: anytype) !void {9fn 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; // Success13 return; // Success
test/cases/compile_errors/non_void_error_union_payload_ignored.zig created+25
...@@ -0,0 +1,25 @@
1pub export fn entry1() void {
2 var x: anyerror!usize = 5;
3 if (x) {
4 // foo
5 } else |_| {
6 // bar
7 }
8}
9pub 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 '|_|'