authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-08-02 20:44:14+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-08-03 16:45:33+03:00
logd1d24b426dd8f12e6d643f45fcb6bb11dddaa8ef
tree11db2665f9af9c29081145c0c74d9728731ffe7c
parentaa78ebaf95af5a3587194d8dbcb101a49c0bb898

AstGen: check loop bodies and else branches for unused result


5 files changed, 133 insertions(+), 52 deletions(-)

src/AstGen.zig+55-12
......@@ -768,12 +768,12 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
768768 .if_simple => return ifExpr(gz, scope, rl.br(), node, tree.ifSimple(node)),
769769 .@"if" => return ifExpr(gz, scope, rl.br(), node, tree.ifFull(node)),
770770
771 .while_simple => return whileExpr(gz, scope, rl.br(), node, tree.whileSimple(node)),
772 .while_cont => return whileExpr(gz, scope, rl.br(), node, tree.whileCont(node)),
773 .@"while" => return whileExpr(gz, scope, rl.br(), node, tree.whileFull(node)),
771 .while_simple => return whileExpr(gz, scope, rl.br(), node, tree.whileSimple(node), false),
772 .while_cont => return whileExpr(gz, scope, rl.br(), node, tree.whileCont(node), false),
773 .@"while" => return whileExpr(gz, scope, rl.br(), node, tree.whileFull(node), false),
774774
775 .for_simple => return forExpr(gz, scope, rl.br(), node, tree.forSimple(node)),
776 .@"for" => return forExpr(gz, scope, rl.br(), node, tree.forFull(node)),
775 .for_simple => return forExpr(gz, scope, rl.br(), node, tree.forSimple(node), false),
776 .@"for" => return forExpr(gz, scope, rl.br(), node, tree.forFull(node), false),
777777
778778 .slice_open => {
779779 const lhs = try expr(gz, scope, .ref, node_datas[node].lhs);
......@@ -2152,6 +2152,7 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
21522152 const astgen = gz.astgen;
21532153 const tree = astgen.tree;
21542154 const node_tags = tree.nodes.items(.tag);
2155 const node_data = tree.nodes.items(.data);
21552156
21562157 if (statements.len == 0) return;
21572158
......@@ -2178,8 +2179,10 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
21782179 },
21792180 );
21802181 }
2181 switch (node_tags[statement]) {
2182 // zig fmt: off
2182 var inner_node = statement;
2183 while (true) {
2184 switch (node_tags[inner_node]) {
2185 // zig fmt: off
21832186 .global_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.globalVarDecl(statement)),
21842187 .local_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.localVarDecl(statement)),
21852188 .simple_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.simpleVarDecl(statement)),
......@@ -2204,9 +2207,23 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
22042207 .assign_add_wrap => try assignOp(gz, scope, statement, .addwrap),
22052208 .assign_mul => try assignOp(gz, scope, statement, .mul),
22062209 .assign_mul_wrap => try assignOp(gz, scope, statement, .mulwrap),
2210
2211 .grouped_expression => {
2212 inner_node = node_data[statement].lhs;
2213 continue;
2214 },
22072215
2208 else => noreturn_src_node = try unusedResultExpr(gz, scope, statement),
2216 .while_simple => _ = try whileExpr(gz, scope, .discard, inner_node, tree.whileSimple(inner_node), true),
2217 .while_cont => _ = try whileExpr(gz, scope, .discard, inner_node, tree.whileCont(inner_node), true),
2218 .@"while" => _ = try whileExpr(gz, scope, .discard, inner_node, tree.whileFull(inner_node), true),
2219
2220 .for_simple => _ = try forExpr(gz, scope, .discard, inner_node, tree.forSimple(inner_node), true),
2221 .@"for" => _ = try forExpr(gz, scope, .discard, inner_node, tree.forFull(inner_node), true),
2222
2223 else => noreturn_src_node = try unusedResultExpr(gz, scope, inner_node),
22092224 // zig fmt: on
2225 }
2226 break;
22102227 }
22112228 }
22122229
......@@ -2245,6 +2262,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
22452262 // We need to emit an error if the result is not `noreturn` or `void`, but
22462263 // we want to avoid adding the ZIR instruction if possible for performance.
22472264 const maybe_unused_result = try expr(gz, scope, .none, statement);
2265 return addEnsureResult(gz, maybe_unused_result, statement);
2266}
2267
2268fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: Ast.Node.Index) InnerError!Ast.Node.Index {
22482269 var noreturn_src_node: Ast.Node.Index = 0;
22492270 const elide_check = if (refToIndex(maybe_unused_result)) |inst| b: {
22502271 // Note that this array becomes invalid after appending more items to it
......@@ -5648,6 +5669,7 @@ fn whileExpr(
56485669 rl: ResultLoc,
56495670 node: Ast.Node.Index,
56505671 while_full: Ast.full.While,
5672 is_statement: bool,
56515673) InnerError!Zir.Inst.Ref {
56525674 const astgen = parent_gz.astgen;
56535675 const tree = astgen.tree;
......@@ -5818,6 +5840,8 @@ fn whileExpr(
58185840 try then_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
58195841 }
58205842 const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr);
5843 _ = try addEnsureResult(&then_scope, then_result, while_full.ast.then_expr);
5844
58215845 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
58225846 try then_scope.addDbgBlockEnd();
58235847
......@@ -5860,7 +5884,11 @@ fn whileExpr(
58605884 // control flow apply to outer loops; not this one.
58615885 loop_scope.continue_block = 0;
58625886 loop_scope.break_block = 0;
5863 const e = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node);
5887 const else_result = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node);
5888 if (is_statement) {
5889 _ = try addEnsureResult(&else_scope, else_result, else_node);
5890 }
5891
58645892 if (!else_scope.endsWithNoReturn()) {
58655893 loop_scope.break_count += 1;
58665894 }
......@@ -5868,7 +5896,7 @@ fn whileExpr(
58685896 try else_scope.addDbgBlockEnd();
58695897 break :blk .{
58705898 .src = else_node,
5871 .result = e,
5899 .result = else_result,
58725900 };
58735901 } else .{
58745902 .src = while_full.ast.then_expr,
......@@ -5881,7 +5909,7 @@ fn whileExpr(
58815909 }
58825910 }
58835911 const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
5884 return finishThenElseBlock(
5912 const result = try finishThenElseBlock(
58855913 parent_gz,
58865914 rl,
58875915 node,
......@@ -5896,6 +5924,10 @@ fn whileExpr(
58965924 cond_block,
58975925 break_tag,
58985926 );
5927 if (is_statement) {
5928 _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
5929 }
5930 return result;
58995931}
59005932
59015933fn forExpr(
......@@ -5904,6 +5936,7 @@ fn forExpr(
59045936 rl: ResultLoc,
59055937 node: Ast.Node.Index,
59065938 for_full: Ast.full.While,
5939 is_statement: bool,
59075940) InnerError!Zir.Inst.Ref {
59085941 const astgen = parent_gz.astgen;
59095942
......@@ -6047,6 +6080,8 @@ fn forExpr(
60476080 };
60486081
60496082 const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr);
6083 _ = try addEnsureResult(&then_scope, then_result, for_full.ast.then_expr);
6084
60506085 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
60516086 try then_scope.addDbgBlockEnd();
60526087
......@@ -6064,6 +6099,10 @@ fn forExpr(
60646099 loop_scope.continue_block = 0;
60656100 loop_scope.break_block = 0;
60666101 const else_result = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node);
6102 if (is_statement) {
6103 _ = try addEnsureResult(&else_scope, else_result, else_node);
6104 }
6105
60676106 if (!else_scope.endsWithNoReturn()) {
60686107 loop_scope.break_count += 1;
60696108 }
......@@ -6082,7 +6121,7 @@ fn forExpr(
60826121 }
60836122 }
60846123 const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
6085 return finishThenElseBlock(
6124 const result = try finishThenElseBlock(
60866125 parent_gz,
60876126 rl,
60886127 node,
......@@ -6097,6 +6136,10 @@ fn forExpr(
60976136 cond_block,
60986137 break_tag,
60996138 );
6139 if (is_statement) {
6140 _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
6141 }
6142 return result;
61006143}
61016144
61026145fn switchExpr(
test/cases/compile_errors/for_loop_body_expression_ignored.zig created+35
......@@ -0,0 +1,35 @@
1fn returns() usize {
2 return 2;
3}
4export fn f1() void {
5 for ("hello") |_| returns();
6}
7export fn f2() void {
8 var x: anyerror!i32 = error.Bad;
9 for ("hello") |_| returns() else unreachable;
10 _ = x;
11}
12export fn f3() void {
13 for ("hello") |_| {} else true;
14}
15export fn f4() void {
16 const foo = for ("hello") |_| returns() else true;
17 _ = foo;
18}
19
20// error
21// backend=stage2
22// target=native
23//
24// :5:30: error: value of type 'usize' ignored
25// :5:30: note: all non-void values must be used
26// :5:30: note: this error can be suppressed by assigning the value to '_'
27// :9:30: error: value of type 'usize' ignored
28// :9:30: note: all non-void values must be used
29// :9:30: note: this error can be suppressed by assigning the value to '_'
30// :13:31: error: value of type 'bool' ignored
31// :13:31: note: all non-void values must be used
32// :13:31: note: this error can be suppressed by assigning the value to '_'
33// :16:42: error: value of type 'usize' ignored
34// :16:42: note: all non-void values must be used
35// :16:42: note: this error can be suppressed by assigning the value to '_'
test/cases/compile_errors/stage1/obj/for_loop_body_expression_ignored.zig deleted-18
......@@ -1,18 +0,0 @@
1fn returns() usize {
2 return 2;
3}
4export fn f1() void {
5 for ("hello") |_| returns();
6}
7export fn f2() void {
8 var x: anyerror!i32 = error.Bad;
9 for ("hello") |_| returns() else unreachable;
10 _ = x;
11}
12
13// error
14// backend=stage1
15// target=native
16//
17// tmp.zig:5:30: error: expression value is ignored
18// tmp.zig:9:30: error: expression value is ignored
test/cases/compile_errors/stage1/obj/while_loop_body_expression_ignored.zig deleted-22
......@@ -1,22 +0,0 @@
1fn returns() usize {
2 return 2;
3}
4export fn f1() void {
5 while (true) returns();
6}
7export fn f2() void {
8 var x: ?i32 = null;
9 while (x) |_| returns();
10}
11export fn f3() void {
12 var x: anyerror!i32 = error.Bad;
13 while (x) |_| returns() else |_| unreachable;
14}
15
16// error
17// backend=stage1
18// target=native
19//
20// tmp.zig:5:25: error: expression value is ignored
21// tmp.zig:9:26: error: expression value is ignored
22// tmp.zig:13:26: error: expression value is ignored
test/cases/compile_errors/while_loop_body_expression_ignored.zig created+43
......@@ -0,0 +1,43 @@
1fn returns() usize {
2 return 2;
3}
4export fn f1() void {
5 while (true) returns();
6}
7export fn f2() void {
8 var x: ?i32 = null;
9 while (x) |_| returns();
10}
11export fn f3() void {
12 var x: anyerror!i32 = error.Bad;
13 while (x) |_| returns() else |_| unreachable;
14}
15export fn f4() void {
16 var a = true;
17 while (a) {} else true;
18}
19export fn f5() void {
20 var a = true;
21 const foo = while (a) returns() else true;
22 _ = foo;
23}
24
25// error
26// backend=stage2
27// target=native
28//
29// :5:25: error: value of type 'usize' ignored
30// :5:25: note: all non-void values must be used
31// :5:25: note: this error can be suppressed by assigning the value to '_'
32// :9:26: error: value of type 'usize' ignored
33// :9:26: note: all non-void values must be used
34// :9:26: note: this error can be suppressed by assigning the value to '_'
35// :13:26: error: value of type 'usize' ignored
36// :13:26: note: all non-void values must be used
37// :13:26: note: this error can be suppressed by assigning the value to '_'
38// :17:23: error: value of type 'bool' ignored
39// :17:23: note: all non-void values must be used
40// :17:23: note: this error can be suppressed by assigning the value to '_'
41// :21:34: error: value of type 'usize' ignored
42// :21:34: note: all non-void values must be used
43// :21:34: note: this error can be suppressed by assigning the value to '_'