authorgravatar for kkhaike@gmail.comkkHAIKE <kkhaike@gmail.com> 2022-09-15 16:33:56+08:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-09-21 20:20:05+03:00
log183127733c8ab51c05f4d4b5cee2a1497272172a
tree85cd3267f1f67c77bb9b7ff250a32f3931974335
parent0799e9856220cde895858ac7ae90ecf66e0b19f2

AstGen: make loop body's ResultLoc .none

Fixes #12555 Fixes #12551 Fixes #12455

5 files changed, 49 insertions(+), 37 deletions(-)

src/AstGen.zig+10-37
...@@ -1864,15 +1864,8 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn...@@ -1864,15 +1864,8 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
1864 }1864 }
1865 block_gz.break_count += 1;1865 block_gz.break_count += 1;
18661866
1867 // The loop scope has a mechanism to prevent rvalue() from emitting a
1868 // store to the result location for the loop body (since it is continues
1869 // rather than returning a result from the loop) but here is a `break`
1870 // which needs to override this behavior.
1871 const prev_rvalue_noresult = parent_gz.rvalue_noresult;
1872 parent_gz.rvalue_noresult = .none;
1873 const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_loc, rhs, node);1867 const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_loc, rhs, node);
1874 const search_index = @intCast(Zir.Inst.Index, astgen.instructions.len);1868 const search_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
1875 parent_gz.rvalue_noresult = prev_rvalue_noresult;
18761869
1877 try genDefers(parent_gz, scope, parent_scope, .normal_only);1870 try genDefers(parent_gz, scope, parent_scope, .normal_only);
18781871
...@@ -2193,7 +2186,7 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod...@@ -2193,7 +2186,7 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
2193 .assign_add_wrap => try assignOp(gz, scope, statement, .addwrap),2186 .assign_add_wrap => try assignOp(gz, scope, statement, .addwrap),
2194 .assign_mul => try assignOp(gz, scope, statement, .mul),2187 .assign_mul => try assignOp(gz, scope, statement, .mul),
2195 .assign_mul_wrap => try assignOp(gz, scope, statement, .mulwrap),2188 .assign_mul_wrap => try assignOp(gz, scope, statement, .mulwrap),
2196 2189
2197 .grouped_expression => {2190 .grouped_expression => {
2198 inner_node = node_data[statement].lhs;2191 inner_node = node_data[statement].lhs;
2199 continue;2192 continue;
...@@ -5789,7 +5782,6 @@ fn whileExpr(...@@ -5789,7 +5782,6 @@ fn whileExpr(
5789 // make scope now but don't stack on parent_gz until loop_scope5782 // make scope now but don't stack on parent_gz until loop_scope
5790 // gets unstacked after cont_expr is emitted and added below5783 // gets unstacked after cont_expr is emitted and added below
5791 var then_scope = parent_gz.makeSubBlock(&continue_scope.base);5784 var then_scope = parent_gz.makeSubBlock(&continue_scope.base);
5792 then_scope.markAsLoopBody(loop_scope);
5793 then_scope.instructions_top = GenZir.unstacked_top;5785 then_scope.instructions_top = GenZir.unstacked_top;
5794 defer then_scope.unstack();5786 defer then_scope.unstack();
57955787
...@@ -5889,7 +5881,7 @@ fn whileExpr(...@@ -5889,7 +5881,7 @@ fn whileExpr(
5889 if (dbg_var_name) |some| {5881 if (dbg_var_name) |some| {
5890 try then_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);5882 try then_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
5891 }5883 }
5892 const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr);5884 const then_result = try expr(&then_scope, then_sub_scope, .none, while_full.ast.then_expr);
5893 _ = try addEnsureResult(&then_scope, then_result, while_full.ast.then_expr);5885 _ = try addEnsureResult(&then_scope, then_result, while_full.ast.then_expr);
58945886
5895 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);5887 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
...@@ -6067,7 +6059,6 @@ fn forExpr(...@@ -6067,7 +6059,6 @@ fn forExpr(
6067 }6059 }
60686060
6069 var then_scope = parent_gz.makeSubBlock(&cond_scope.base);6061 var then_scope = parent_gz.makeSubBlock(&cond_scope.base);
6070 then_scope.markAsLoopBody(loop_scope);
6071 defer then_scope.unstack();6062 defer then_scope.unstack();
60726063
6073 try then_scope.addDbgBlockBegin();6064 try then_scope.addDbgBlockBegin();
...@@ -6129,7 +6120,7 @@ fn forExpr(...@@ -6129,7 +6120,7 @@ fn forExpr(
6129 break :blk &index_scope.base;6120 break :blk &index_scope.base;
6130 };6121 };
61316122
6132 const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr);6123 const then_result = try expr(&then_scope, then_sub_scope, .none, for_full.ast.then_expr);
6133 _ = try addEnsureResult(&then_scope, then_result, for_full.ast.then_expr);6124 _ = try addEnsureResult(&then_scope, then_result, for_full.ast.then_expr);
61346125
6135 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);6126 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
...@@ -9465,25 +9456,19 @@ fn rvalue(...@@ -9465,25 +9456,19 @@ fn rvalue(
9465 }9456 }
9466 },9457 },
9467 .ptr => |ptr_inst| {9458 .ptr => |ptr_inst| {
9468 if (gz.rvalue_noresult != ptr_inst) {9459 _ = try gz.addPlNode(.store_node, src_node, Zir.Inst.Bin{
9469 _ = try gz.addPlNode(.store_node, src_node, Zir.Inst.Bin{9460 .lhs = ptr_inst,
9470 .lhs = ptr_inst,9461 .rhs = result,
9471 .rhs = result,9462 });
9472 });
9473 }
9474 return result;9463 return result;
9475 },9464 },
9476 .inferred_ptr => |alloc| {9465 .inferred_ptr => |alloc| {
9477 if (gz.rvalue_noresult != alloc) {9466 _ = try gz.addBin(.store_to_inferred_ptr, alloc, result);
9478 _ = try gz.addBin(.store_to_inferred_ptr, alloc, result);
9479 }
9480 return result;9467 return result;
9481 },9468 },
9482 .block_ptr => |block_scope| {9469 .block_ptr => |block_scope| {
9483 if (gz.rvalue_noresult != block_scope.rl_ptr) {9470 block_scope.rvalue_rl_count += 1;
9484 block_scope.rvalue_rl_count += 1;9471 _ = try gz.addBin(.store_to_block_ptr, block_scope.rl_ptr, result);
9485 _ = try gz.addBin(.store_to_block_ptr, block_scope.rl_ptr, result);
9486 }
9487 return result;9472 return result;
9488 },9473 },
9489 }9474 }
...@@ -10163,7 +10148,6 @@ const GenZir = struct {...@@ -10163,7 +10148,6 @@ const GenZir = struct {
10163 rl_ptr: Zir.Inst.Ref = .none,10148 rl_ptr: Zir.Inst.Ref = .none,
10164 /// When a block has a type result location, here it is.10149 /// When a block has a type result location, here it is.
10165 rl_ty_inst: Zir.Inst.Ref = .none,10150 rl_ty_inst: Zir.Inst.Ref = .none,
10166 rvalue_noresult: Zir.Inst.Ref = .none,
10167 /// Keeps track of how many branches of a block did not actually10151 /// Keeps track of how many branches of a block did not actually
10168 /// consume the result location. astgen uses this to figure out10152 /// consume the result location. astgen uses this to figure out
10169 /// whether to rely on break instructions or writing to the result10153 /// whether to rely on break instructions or writing to the result
...@@ -11558,17 +11542,6 @@ const GenZir = struct {...@@ -11558,17 +11542,6 @@ const GenZir = struct {
11558 try gz.astgen.instructions.append(gpa, .{ .tag = .dbg_block_end, .data = undefined });11542 try gz.astgen.instructions.append(gpa, .{ .tag = .dbg_block_end, .data = undefined });
11559 try gz.instructions.insert(gpa, gz.instructions.items.len - 1, new_index);11543 try gz.instructions.insert(gpa, gz.instructions.items.len - 1, new_index);
11560 }11544 }
11561
11562 /// Control flow does not fall through the "then" block of a loop; it continues
11563 /// back to the while condition. This prevents `rvalue` from
11564 /// adding an invalid store to the result location of `then_scope`.
11565 fn markAsLoopBody(gz: *GenZir, loop_scope: GenZir) void {
11566 gz.rvalue_noresult = switch (loop_scope.break_result_loc) {
11567 .ptr, .inferred_ptr => |ptr| ptr,
11568 .block_ptr => |block| block.rl_ptr,
11569 else => .none,
11570 };
11571 }
11572};11545};
1157311546
11574/// This can only be for short-lived references; the memory becomes invalidated11547/// This can only be for short-lived references; the memory becomes invalidated
test/behavior.zig+2
...@@ -81,10 +81,12 @@ test {...@@ -81,10 +81,12 @@ test {
81 _ = @import("behavior/bugs/11179.zig");81 _ = @import("behavior/bugs/11179.zig");
82 _ = @import("behavior/bugs/11181.zig");82 _ = @import("behavior/bugs/11181.zig");
83 _ = @import("behavior/bugs/11213.zig");83 _ = @import("behavior/bugs/11213.zig");
84 _ = @import("behavior/bugs/11816.zig");
84 _ = @import("behavior/bugs/12003.zig");85 _ = @import("behavior/bugs/12003.zig");
85 _ = @import("behavior/bugs/12033.zig");86 _ = @import("behavior/bugs/12033.zig");
86 _ = @import("behavior/bugs/12430.zig");87 _ = @import("behavior/bugs/12430.zig");
87 _ = @import("behavior/bugs/12486.zig");88 _ = @import("behavior/bugs/12486.zig");
89 _ = @import("behavior/bugs/12551.zig");
88 _ = @import("behavior/bugs/12680.zig");90 _ = @import("behavior/bugs/12680.zig");
89 _ = @import("behavior/bugs/12776.zig");91 _ = @import("behavior/bugs/12776.zig");
90 _ = @import("behavior/bugs/12786.zig");92 _ = @import("behavior/bugs/12786.zig");
test/behavior/bugs/11816.zig created+13
...@@ -0,0 +1,13 @@
1const std = @import("std");
2const builtin = @import("builtin");
3
4test {
5 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
6
7 var x: u32 = 3;
8 const val: usize = while (true) switch (x) {
9 1 => break 2,
10 else => x -= 1,
11 };
12 try std.testing.expect(val == 2);
13}
test/behavior/bugs/12551.zig created+10
...@@ -0,0 +1,10 @@
1const std = @import("std");
2const builtin = @import("builtin");
3
4test {
5 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
6
7 try std.testing.expect(for ([1]u8{0}) |x| {
8 if (x == 0) break true;
9 } else false);
10}
test/cases/compile_errors/unused_value_in_switch_in_loop.zig created+14
...@@ -0,0 +1,14 @@
1export fn entry() void {
2 var a: u32 = 0;
3 while (true) switch (a) {
4 0 => 2,
5 1 => a = 0,
6 else => break,
7 };
8}
9
10// error
11// backend=stage2
12// target=native
13//
14// :3:18: error: incompatible types: 'comptime_int' and 'void'