aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-10-02 20:15:03 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-10-02 20:15:03 -0700
commitc4df9bf56f204f63f4e87255933ba453d69d0182 (patch)
tree647cec61769d5723385f8ac9b1392b3c935a219b /src
parent61a53a587558ff1fe1b0ec98bb424022885edccf (diff)
downloadzig-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.zig19
-rw-r--r--src/codegen/llvm.zig10
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;
}