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
18641864 }
18651865 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;
18731867 const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_loc, rhs, node);
18741868 const search_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
1875 parent_gz.rvalue_noresult = prev_rvalue_noresult;
18761869
18771870 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
21932186 .assign_add_wrap => try assignOp(gz, scope, statement, .addwrap),
21942187 .assign_mul => try assignOp(gz, scope, statement, .mul),
21952188 .assign_mul_wrap => try assignOp(gz, scope, statement, .mulwrap),
2196
2189
21972190 .grouped_expression => {
21982191 inner_node = node_data[statement].lhs;
21992192 continue;
......@@ -5789,7 +5782,6 @@ fn whileExpr(
57895782 // make scope now but don't stack on parent_gz until loop_scope
57905783 // gets unstacked after cont_expr is emitted and added below
57915784 var then_scope = parent_gz.makeSubBlock(&continue_scope.base);
5792 then_scope.markAsLoopBody(loop_scope);
57935785 then_scope.instructions_top = GenZir.unstacked_top;
57945786 defer then_scope.unstack();
57955787
......@@ -5889,7 +5881,7 @@ fn whileExpr(
58895881 if (dbg_var_name) |some| {
58905882 try then_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
58915883 }
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);
58935885 _ = try addEnsureResult(&then_scope, then_result, while_full.ast.then_expr);
58945886
58955887 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
......@@ -6067,7 +6059,6 @@ fn forExpr(
60676059 }
60686060
60696061 var then_scope = parent_gz.makeSubBlock(&cond_scope.base);
6070 then_scope.markAsLoopBody(loop_scope);
60716062 defer then_scope.unstack();
60726063
60736064 try then_scope.addDbgBlockBegin();
......@@ -6129,7 +6120,7 @@ fn forExpr(
61296120 break :blk &index_scope.base;
61306121 };
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);
61336124 _ = try addEnsureResult(&then_scope, then_result, for_full.ast.then_expr);
61346125
61356126 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
......@@ -9465,25 +9456,19 @@ fn rvalue(
94659456 }
94669457 },
94679458 .ptr => |ptr_inst| {
9468 if (gz.rvalue_noresult != ptr_inst) {
9469 _ = try gz.addPlNode(.store_node, src_node, Zir.Inst.Bin{
9470 .lhs = ptr_inst,
9471 .rhs = result,
9472 });
9473 }
9459 _ = try gz.addPlNode(.store_node, src_node, Zir.Inst.Bin{
9460 .lhs = ptr_inst,
9461 .rhs = result,
9462 });
94749463 return result;
94759464 },
94769465 .inferred_ptr => |alloc| {
9477 if (gz.rvalue_noresult != alloc) {
9478 _ = try gz.addBin(.store_to_inferred_ptr, alloc, result);
9479 }
9466 _ = try gz.addBin(.store_to_inferred_ptr, alloc, result);
94809467 return result;
94819468 },
94829469 .block_ptr => |block_scope| {
9483 if (gz.rvalue_noresult != block_scope.rl_ptr) {
9484 block_scope.rvalue_rl_count += 1;
9485 _ = try gz.addBin(.store_to_block_ptr, block_scope.rl_ptr, result);
9486 }
9470 block_scope.rvalue_rl_count += 1;
9471 _ = try gz.addBin(.store_to_block_ptr, block_scope.rl_ptr, result);
94879472 return result;
94889473 },
94899474 }
......@@ -10163,7 +10148,6 @@ const GenZir = struct {
1016310148 rl_ptr: Zir.Inst.Ref = .none,
1016410149 /// When a block has a type result location, here it is.
1016510150 rl_ty_inst: Zir.Inst.Ref = .none,
10166 rvalue_noresult: Zir.Inst.Ref = .none,
1016710151 /// Keeps track of how many branches of a block did not actually
1016810152 /// consume the result location. astgen uses this to figure out
1016910153 /// whether to rely on break instructions or writing to the result
......@@ -11558,17 +11542,6 @@ const GenZir = struct {
1155811542 try gz.astgen.instructions.append(gpa, .{ .tag = .dbg_block_end, .data = undefined });
1155911543 try gz.instructions.insert(gpa, gz.instructions.items.len - 1, new_index);
1156011544 }
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 }
1157211545};
1157311546
1157411547/// This can only be for short-lived references; the memory becomes invalidated
test/behavior.zig+2
......@@ -81,10 +81,12 @@ test {
8181 _ = @import("behavior/bugs/11179.zig");
8282 _ = @import("behavior/bugs/11181.zig");
8383 _ = @import("behavior/bugs/11213.zig");
84 _ = @import("behavior/bugs/11816.zig");
8485 _ = @import("behavior/bugs/12003.zig");
8586 _ = @import("behavior/bugs/12033.zig");
8687 _ = @import("behavior/bugs/12430.zig");
8788 _ = @import("behavior/bugs/12486.zig");
89 _ = @import("behavior/bugs/12551.zig");
8890 _ = @import("behavior/bugs/12680.zig");
8991 _ = @import("behavior/bugs/12776.zig");
9092 _ = @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'