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
25052505 .dbg_block_end,
25062506 .ensure_result_used,
25072507 .ensure_result_non_error,
2508 .ensure_err_union_payload_void,
25082509 .@"export",
25092510 .export_value,
25102511 .set_eval_branch_quota,
2511 .ensure_err_payload_void,
25122512 .atomic_store,
25132513 .store,
25142514 .store_node,
......@@ -5492,6 +5492,7 @@ fn ifExpr(
54925492 try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
54935493 break :s &payload_val_scope.base;
54945494 } else {
5495 _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node);
54955496 break :s &then_scope.base;
54965497 }
54975498 } else if (if_full.payload_token) |payload_token| {
......@@ -5829,6 +5830,7 @@ fn whileExpr(
58295830 dbg_var_inst = indexToRef(payload_inst);
58305831 break :s &payload_val_scope.base;
58315832 } else {
5833 _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node);
58325834 break :s &then_scope.base;
58335835 }
58345836 } else if (while_full.payload_token) |payload_token| {
src/Sema.zig+29-20
......@@ -1034,8 +1034,8 @@ fn analyzeBodyInner(
10341034 i += 1;
10351035 continue;
10361036 },
1037 .ensure_err_payload_void => {
1038 try sema.zirEnsureErrPayloadVoid(block, inst);
1037 .ensure_err_union_payload_void => {
1038 try sema.zirEnsureErrUnionPayloadVoid(block, inst);
10391039 i += 1;
10401040 continue;
10411041 },
......@@ -3100,6 +3100,33 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
31003100 }
31013101}
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
31033130fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
31043131 const tracy = trace(@src());
31053132 defer tracy.end();
......@@ -7681,24 +7708,6 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
76817708 return block.addTyOp(.unwrap_errunion_err_ptr, result_ty, operand);
76827709}
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
77027711fn zirFunc(
77037712 sema: *Sema,
77047713 block: *Block,
src/Zir.zig+5-6
......@@ -402,6 +402,8 @@ pub const Inst = struct {
402402 /// Emits a compile error if an error is ignored.
403403 /// Uses the `un_node` field.
404404 ensure_result_non_error,
405 /// Emits a compile error error union payload is not void.
406 ensure_err_union_payload_void,
405407 /// Create a `E!T` type.
406408 /// Uses the `pl_node` field with `Bin` payload.
407409 error_union_type,
......@@ -646,9 +648,6 @@ pub const Inst = struct {
646648 /// Given a pointer to an error union value, returns the error code. No safety checks.
647649 /// Uses the `un_node` field.
648650 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,
652651 /// An enum literal. Uses the `str_tok` union field.
653652 enum_literal,
654653 /// A switch expression. Uses the `pl_node` union field.
......@@ -1060,6 +1059,7 @@ pub const Inst = struct {
10601059 .elem_val_node,
10611060 .ensure_result_used,
10621061 .ensure_result_non_error,
1062 .ensure_err_union_payload_void,
10631063 .@"export",
10641064 .export_value,
10651065 .field_ptr,
......@@ -1113,7 +1113,6 @@ pub const Inst = struct {
11131113 .err_union_code_ptr,
11141114 .ptr_type,
11151115 .overflow_arithmetic_ptr,
1116 .ensure_err_payload_void,
11171116 .enum_literal,
11181117 .merge_error_sets,
11191118 .error_union_type,
......@@ -1282,7 +1281,7 @@ pub const Inst = struct {
12821281 .dbg_block_end,
12831282 .ensure_result_used,
12841283 .ensure_result_non_error,
1285 .ensure_err_payload_void,
1284 .ensure_err_union_payload_void,
12861285 .set_eval_branch_quota,
12871286 .atomic_store,
12881287 .store,
......@@ -1615,6 +1614,7 @@ pub const Inst = struct {
16151614 .elem_val_node = .pl_node,
16161615 .ensure_result_used = .un_node,
16171616 .ensure_result_non_error = .un_node,
1617 .ensure_err_union_payload_void = .un_node,
16181618 .error_union_type = .pl_node,
16191619 .error_value = .str_tok,
16201620 .@"export" = .pl_node,
......@@ -1677,7 +1677,6 @@ pub const Inst = struct {
16771677 .err_union_payload_unsafe_ptr = .un_node,
16781678 .err_union_code = .un_node,
16791679 .err_union_code_ptr = .un_node,
1680 .ensure_err_payload_void = .un_tok,
16811680 .enum_literal = .str_tok,
16821681 .switch_block = .pl_node,
16831682 .switch_cond = .un_node,
src/print_zir.zig+1-1
......@@ -162,6 +162,7 @@ const Writer = struct {
162162 .load,
163163 .ensure_result_used,
164164 .ensure_result_non_error,
165 .ensure_err_union_payload_void,
165166 .ret_node,
166167 .ret_load,
167168 .resolve_inferred_alloc,
......@@ -235,7 +236,6 @@ const Writer = struct {
235236
236237 .ref,
237238 .ret_tok,
238 .ensure_err_payload_void,
239239 .closure_capture,
240240 .switch_capture_tag,
241241 => try self.writeUnTok(stream, inst),
test/behavior/error.zig+1-1
......@@ -7,7 +7,7 @@ const mem = std.mem;
77/// A more basic implementation of std.testing.expectError which
88/// does not require formatter/printing support
99fn expectError(expected_err: anyerror, observed_err_union: anytype) !void {
10 if (observed_err_union) {
10 if (observed_err_union) |_| {
1111 return error.TestExpectedError;
1212 } else |err| if (err == expected_err) {
1313 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 '|_|'