diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-10-02 20:15:03 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-10-02 20:15:03 -0700 |
| commit | c4df9bf56f204f63f4e87255933ba453d69d0182 (patch) | |
| tree | 647cec61769d5723385f8ac9b1392b3c935a219b /src | |
| parent | 61a53a587558ff1fe1b0ec98bb424022885edccf (diff) | |
| download | zig-c4df9bf56f204f63f4e87255933ba453d69d0182.tar.gz zig-c4df9bf56f204f63f4e87255933ba453d69d0182.zip | |
AstGen: fix `while` and `for` with unreachable bodies
Companion commit to 61a53a587558ff1fe1b0ec98bb424022885edccf.
This commit also moves over a bunch of behavior test cases to the
passing-for-stage2 section.
Diffstat (limited to 'src')
| -rw-r--r-- | src/AstGen.zig | 19 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 10 |
2 files changed, 23 insertions, 6 deletions
diff --git a/src/AstGen.zig b/src/AstGen.zig index 54534b1e5a..79608fedf3 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -5537,8 +5537,10 @@ fn whileExpr( }); } - loop_scope.break_count += 1; const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr); + if (!then_scope.endsWithNoReturn()) { + loop_scope.break_count += 1; + } try checkUsed(parent_gz, &then_scope.base, then_sub_scope); var else_scope = parent_gz.makeSubBlock(&continue_scope.base); @@ -5549,7 +5551,6 @@ fn whileExpr( src: Ast.Node.Index, result: Zir.Inst.Ref, } = if (else_node != 0) blk: { - loop_scope.break_count += 1; const sub_scope = s: { if (while_full.error_token) |error_token| { const tag: Zir.Inst.Tag = if (payload_is_ref) @@ -5576,6 +5577,9 @@ fn whileExpr( } }; const e = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node); + if (!else_scope.endsWithNoReturn()) { + loop_scope.break_count += 1; + } try checkUsed(parent_gz, &else_scope.base, sub_scope); break :blk .{ .src = else_node, @@ -5746,8 +5750,10 @@ fn forExpr( break :blk &index_scope.base; }; - loop_scope.break_count += 1; const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr); + if (!then_scope.endsWithNoReturn()) { + loop_scope.break_count += 1; + } try checkUsed(parent_gz, &then_scope.base, then_sub_scope); var else_scope = parent_gz.makeSubBlock(&cond_scope.base); @@ -5758,11 +5764,14 @@ fn forExpr( src: Ast.Node.Index, result: Zir.Inst.Ref, } = if (else_node != 0) blk: { - loop_scope.break_count += 1; const sub_scope = &else_scope.base; + const else_result = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node); + if (!else_scope.endsWithNoReturn()) { + loop_scope.break_count += 1; + } break :blk .{ .src = else_node, - .result = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node), + .result = else_result, }; } else .{ .src = for_full.ast.then_expr, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 761dd2a8bc..500aa482fa 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1605,7 +1605,15 @@ pub const FuncGen = struct { self.builder.positionBuilderAtEnd(loop_block); try self.genBody(body); - _ = self.builder.buildBr(loop_block); + // TODO instead of this logic, change AIR to have the property that + // every block is guaranteed to end with a noreturn instruction. + // Then we can simply rely on the fact that a repeat or break instruction + // would have been emitted already. Also the main loop in genBody can + // be while(true) instead of for(body), which will eliminate 1 branch on + // a hot path. + if (body.len == 0 or !self.air.typeOfIndex(body[body.len - 1]).isNoReturn()) { + _ = self.builder.buildBr(loop_block); + } return null; } |
