authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-02 19:09:54-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-02 19:09:54-07:00
log61a53a587558ff1fe1b0ec98bb424022885edccf
tree0e0ad82f7998a473104ec6fa07cb051a1b56a7c3
parentac52e005640e9dc7829356f857a82b0bc3894245

AstGen: fix if, orelse, catch, with unreachable bodies

Before, the system to replace a result location pointer with a traditional break instruction did not notice the case when one of the bodies was unreachable. Now, the emitted ZIR code is improved and simplified in this case, resulting in a new passing behavior test.

3 files changed, 18 insertions(+), 15 deletions(-)

src/AstGen.zig+10-4
...@@ -4891,7 +4891,7 @@ fn orelseCatchExpr(...@@ -4891,7 +4891,7 @@ fn orelseCatchExpr(
4891 // We cannot use `block_scope.break_result_loc` because that has the bare4891 // We cannot use `block_scope.break_result_loc` because that has the bare
4892 // type, whereas this expression has the optional type. Later we make4892 // type, whereas this expression has the optional type. Later we make
4893 // up for this fact by calling rvalue on the else branch.4893 // up for this fact by calling rvalue on the else branch.
4894 const operand = try expr(&block_scope, &block_scope.base, operand_rl, lhs);4894 const operand = try reachableExpr(&block_scope, &block_scope.base, operand_rl, lhs, rhs);
4895 const cond = try block_scope.addUnNode(cond_op, operand, node);4895 const cond = try block_scope.addUnNode(cond_op, operand, node);
4896 const condbr = try block_scope.addCondBr(.condbr, node);4896 const condbr = try block_scope.addCondBr(.condbr, node);
48974897
...@@ -4930,8 +4930,10 @@ fn orelseCatchExpr(...@@ -4930,8 +4930,10 @@ fn orelseCatchExpr(
4930 break :blk &err_val_scope.base;4930 break :blk &err_val_scope.base;
4931 };4931 };
49324932
4933 block_scope.break_count += 1;
4934 const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_loc, rhs);4933 const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_loc, rhs);
4934 if (!else_scope.endsWithNoReturn()) {
4935 block_scope.break_count += 1;
4936 }
4935 try checkUsed(parent_gz, &else_scope.base, else_sub_scope);4937 try checkUsed(parent_gz, &else_scope.base, else_sub_scope);
49364938
4937 // We hold off on the break instructions as well as copying the then/else4939 // We hold off on the break instructions as well as copying the then/else
...@@ -5249,8 +5251,10 @@ fn ifExpr(...@@ -5249,8 +5251,10 @@ fn ifExpr(
5249 }5251 }
5250 };5252 };
52515253
5252 block_scope.break_count += 1;
5253 const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_loc, if_full.ast.then_expr);5254 const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_loc, if_full.ast.then_expr);
5255 if (!then_scope.endsWithNoReturn()) {
5256 block_scope.break_count += 1;
5257 }
5254 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);5258 try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
5255 // We hold off on the break instructions as well as copying the then/else5259 // We hold off on the break instructions as well as copying the then/else
5256 // instructions into place until we know whether to keep store_to_block_ptr5260 // instructions into place until we know whether to keep store_to_block_ptr
...@@ -5264,7 +5268,6 @@ fn ifExpr(...@@ -5264,7 +5268,6 @@ fn ifExpr(
5264 src: Ast.Node.Index,5268 src: Ast.Node.Index,
5265 result: Zir.Inst.Ref,5269 result: Zir.Inst.Ref,
5266 } = if (else_node != 0) blk: {5270 } = if (else_node != 0) blk: {
5267 block_scope.break_count += 1;
5268 const sub_scope = s: {5271 const sub_scope = s: {
5269 if (if_full.error_token) |error_token| {5272 if (if_full.error_token) |error_token| {
5270 const tag: Zir.Inst.Tag = if (payload_is_ref)5273 const tag: Zir.Inst.Tag = if (payload_is_ref)
...@@ -5291,6 +5294,9 @@ fn ifExpr(...@@ -5291,6 +5294,9 @@ fn ifExpr(
5291 }5294 }
5292 };5295 };
5293 const e = try expr(&else_scope, sub_scope, block_scope.break_result_loc, else_node);5296 const e = try expr(&else_scope, sub_scope, block_scope.break_result_loc, else_node);
5297 if (!else_scope.endsWithNoReturn()) {
5298 block_scope.break_count += 1;
5299 }
5294 try checkUsed(parent_gz, &else_scope.base, sub_scope);5300 try checkUsed(parent_gz, &else_scope.base, sub_scope);
5295 break :blk .{5301 break :blk .{
5296 .src = else_node,5302 .src = else_node,
test/behavior/if.zig+8
...@@ -65,3 +65,11 @@ test "labeled break inside comptime if inside runtime if" {...@@ -65,3 +65,11 @@ test "labeled break inside comptime if inside runtime if" {
65 }65 }
66 try expect(answer == 42);66 try expect(answer == 42);
67}67}
68
69test "const result loc, runtime if cond, else unreachable" {
70 const Num = enum { One, Two };
71
72 var t = true;
73 const x = if (t) Num.Two else unreachable;
74 try expect(x == .Two);
75}
test/behavior/if_stage1.zig-11
...@@ -2,17 +2,6 @@ const std = @import("std");...@@ -2,17 +2,6 @@ const std = @import("std");
2const expect = std.testing.expect;2const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;3const expectEqual = std.testing.expectEqual;
44
5test "const result loc, runtime if cond, else unreachable" {
6 const Num = enum {
7 One,
8 Two,
9 };
10
11 var t = true;
12 const x = if (t) Num.Two else unreachable;
13 try expect(x == .Two);
14}
15
16test "if prongs cast to expected type instead of peer type resolution" {5test "if prongs cast to expected type instead of peer type resolution" {
17 const S = struct {6 const S = struct {
18 fn doTheTest(f: bool) !void {7 fn doTheTest(f: bool) !void {