authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-06-28 14:16:15-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-02 13:26:50-07:00
log7f5560689aeb0e2ab84c5e3dc48f84247bd5bdc3
treeeafe7ba69a4ebffc6c54067f6bf615daf6d89bdb
parentcffa22a658d23bdedbdd7e23853b80d856e43627

AstGen: introduce 'reachableExpr' function

This function can be swapped out for calls to expr() to report a compile error when the expression results in control flow that does not return.

2 files changed, 36 insertions(+), 39 deletions(-)

src/AstGen.zig+25-34
...@@ -261,6 +261,23 @@ fn typeExpr(gz: *GenZir, scope: *Scope, type_node: ast.Node.Index) InnerError!Zi...@@ -261,6 +261,23 @@ fn typeExpr(gz: *GenZir, scope: *Scope, type_node: ast.Node.Index) InnerError!Zi
261 return expr(gz, scope, .{ .ty = .type_type }, type_node);261 return expr(gz, scope, .{ .ty = .type_type }, type_node);
262}262}
263263
264/// Same as `expr` but fails with a compile error if the result type is `noreturn`.
265fn reachableExpr(
266 gz: *GenZir,
267 scope: *Scope,
268 rl: ResultLoc,
269 node: ast.Node.Index,
270 src_node: ast.Node.Index,
271) InnerError!Zir.Inst.Ref {
272 const result_inst = try expr(gz, scope, rl, node);
273 if (gz.refIsNoReturn(result_inst)) {
274 return gz.astgen.failNodeNotes(src_node, "unreachable code", .{}, &[_]u32{
275 try gz.astgen.errNoteNode(node, "control flow is diverted here", .{}),
276 });
277 }
278 return result_inst;
279}
280
264fn lvalExpr(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref {281fn lvalExpr(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref {
265 const astgen = gz.astgen;282 const astgen = gz.astgen;
266 const tree = astgen.tree;283 const tree = astgen.tree;
...@@ -2331,8 +2348,7 @@ fn varDecl(...@@ -2331,8 +2348,7 @@ fn varDecl(
2331 const result_loc: ResultLoc = if (var_decl.ast.type_node != 0) .{2348 const result_loc: ResultLoc = if (var_decl.ast.type_node != 0) .{
2332 .ty = try typeExpr(gz, scope, var_decl.ast.type_node),2349 .ty = try typeExpr(gz, scope, var_decl.ast.type_node),
2333 } else .none;2350 } else .none;
2334 const init_inst = try expr(gz, scope, result_loc, var_decl.ast.init_node);2351 const init_inst = try reachableExpr(gz, scope, result_loc, var_decl.ast.init_node, node);
2335 try astgen.checkVarInitExpr(gz.*, node, var_decl.ast.init_node, init_inst, "local constant");
23362352
2337 const sub_scope = try block_arena.create(Scope.LocalVal);2353 const sub_scope = try block_arena.create(Scope.LocalVal);
2338 sub_scope.* = .{2354 sub_scope.* = .{
...@@ -2383,8 +2399,7 @@ fn varDecl(...@@ -2383,8 +2399,7 @@ fn varDecl(
2383 init_scope.rl_ptr = alloc;2399 init_scope.rl_ptr = alloc;
2384 }2400 }
2385 const init_result_loc: ResultLoc = .{ .block_ptr = &init_scope };2401 const init_result_loc: ResultLoc = .{ .block_ptr = &init_scope };
2386 const init_inst = try expr(&init_scope, &init_scope.base, init_result_loc, var_decl.ast.init_node);2402 const init_inst = try reachableExpr(&init_scope, &init_scope.base, init_result_loc, var_decl.ast.init_node, node);
2387 try astgen.checkVarInitExpr(init_scope, node, var_decl.ast.init_node, init_inst, "local constant");
23882403
2389 const zir_tags = astgen.instructions.items(.tag);2404 const zir_tags = astgen.instructions.items(.tag);
2390 const zir_datas = astgen.instructions.items(.data);2405 const zir_datas = astgen.instructions.items(.data);
...@@ -2486,8 +2501,7 @@ fn varDecl(...@@ -2486,8 +2501,7 @@ fn varDecl(
2486 resolve_inferred_alloc = alloc;2501 resolve_inferred_alloc = alloc;
2487 break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc } };2502 break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc } };
2488 };2503 };
2489 const init_inst = try expr(gz, scope, var_data.result_loc, var_decl.ast.init_node);2504 _ = try reachableExpr(gz, scope, var_data.result_loc, var_decl.ast.init_node, node);
2490 try astgen.checkVarInitExpr(gz.*, node, var_decl.ast.init_node, init_inst, "local variable");
2491 if (resolve_inferred_alloc != .none) {2505 if (resolve_inferred_alloc != .none) {
2492 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);2506 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
2493 }2507 }
...@@ -6618,14 +6632,14 @@ fn as(...@@ -6618,14 +6632,14 @@ fn as(
6618 const dest_type = try typeExpr(gz, scope, lhs);6632 const dest_type = try typeExpr(gz, scope, lhs);
6619 switch (rl) {6633 switch (rl) {
6620 .none, .none_or_ref, .discard, .ref, .ty => {6634 .none, .none_or_ref, .discard, .ref, .ty => {
6621 const result = try expr(gz, scope, .{ .ty = dest_type }, rhs);6635 const result = try reachableExpr(gz, scope, .{ .ty = dest_type }, rhs, node);
6622 return rvalue(gz, rl, result, node);6636 return rvalue(gz, rl, result, node);
6623 },6637 },
6624 .ptr, .inferred_ptr => |result_ptr| {6638 .ptr, .inferred_ptr => |result_ptr| {
6625 return asRlPtr(gz, scope, rl, result_ptr, rhs, dest_type);6639 return asRlPtr(gz, scope, rl, node, result_ptr, rhs, dest_type);
6626 },6640 },
6627 .block_ptr => |block_scope| {6641 .block_ptr => |block_scope| {
6628 return asRlPtr(gz, scope, rl, block_scope.rl_ptr, rhs, dest_type);6642 return asRlPtr(gz, scope, rl, node, block_scope.rl_ptr, rhs, dest_type);
6629 },6643 },
6630 }6644 }
6631}6645}
...@@ -6679,6 +6693,7 @@ fn asRlPtr(...@@ -6679,6 +6693,7 @@ fn asRlPtr(
6679 parent_gz: *GenZir,6693 parent_gz: *GenZir,
6680 scope: *Scope,6694 scope: *Scope,
6681 rl: ResultLoc,6695 rl: ResultLoc,
6696 src_node: ast.Node.Index,
6682 result_ptr: Zir.Inst.Ref,6697 result_ptr: Zir.Inst.Ref,
6683 operand_node: ast.Node.Index,6698 operand_node: ast.Node.Index,
6684 dest_type: Zir.Inst.Ref,6699 dest_type: Zir.Inst.Ref,
...@@ -6692,7 +6707,7 @@ fn asRlPtr(...@@ -6692,7 +6707,7 @@ fn asRlPtr(
6692 defer as_scope.instructions.deinit(astgen.gpa);6707 defer as_scope.instructions.deinit(astgen.gpa);
66936708
6694 as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr);6709 as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr);
6695 const result = try expr(&as_scope, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node);6710 const result = try reachableExpr(&as_scope, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node, src_node);
6696 const parent_zir = &parent_gz.instructions;6711 const parent_zir = &parent_gz.instructions;
6697 if (as_scope.rvalue_rl_count == 1) {6712 if (as_scope.rvalue_rl_count == 1) {
6698 // Busted! This expression didn't actually need a pointer.6713 // Busted! This expression didn't actually need a pointer.
...@@ -9607,27 +9622,3 @@ fn advanceSourceCursor(astgen: *AstGen, source: []const u8, end: usize) void {...@@ -9607,27 +9622,3 @@ fn advanceSourceCursor(astgen: *AstGen, source: []const u8, end: usize) void {
9607 astgen.source_line = line;9622 astgen.source_line = line;
9608 astgen.source_column = column;9623 astgen.source_column = column;
9609}9624}
9610
9611fn checkVarInitExpr(
9612 astgen: *AstGen,
9613 gz: GenZir,
9614 var_node: ast.Node.Index,
9615 init_node: ast.Node.Index,
9616 init_inst: Zir.Inst.Ref,
9617 var_name_text: []const u8,
9618) !void {
9619 if (gz.refIsNoReturn(init_inst)) {
9620 return astgen.failNodeNotes(
9621 var_node,
9622 "useless {s}",
9623 .{var_name_text},
9624 &[_]u32{
9625 try astgen.errNoteNode(
9626 init_node,
9627 "control flow is diverted here",
9628 .{},
9629 ),
9630 },
9631 );
9632 }
9633}
test/compile_errors.zig+11-5
...@@ -4831,7 +4831,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -4831,7 +4831,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
4831 \\ const a = return;4831 \\ const a = return;
4832 \\}4832 \\}
4833 , &[_][]const u8{4833 , &[_][]const u8{
4834 "tmp.zig:2:5: error: useless local constant",4834 "tmp.zig:2:5: error: unreachable code",
4835 "tmp.zig:2:15: note: control flow is diverted here",4835 "tmp.zig:2:15: note: control flow is diverted here",
4836 });4836 });
48374837
...@@ -5058,6 +5058,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -5058,6 +5058,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
5058 \\export fn entry() void { _ = f(); }5058 \\export fn entry() void { _ = f(); }
5059 , &[_][]const u8{5059 , &[_][]const u8{
5060 "tmp.zig:2:12: error: unreachable code",5060 "tmp.zig:2:12: error: unreachable code",
5061 "tmp.zig:2:21: note: control flow is diverted here",
5061 });5062 });
50625063
5063 cases.add("invalid builtin fn",5064 cases.add("invalid builtin fn",
...@@ -7143,9 +7144,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -7143,9 +7144,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
7143 \\export fn entry8() void {7144 \\export fn entry8() void {
7144 \\ var h = (Foo {}).bar;7145 \\ var h = (Foo {}).bar;
7145 \\}7146 \\}
7146 \\export fn entry9() void {
7147 \\ var z: noreturn = return;
7148 \\}
7149 \\const Opaque = opaque {};7147 \\const Opaque = opaque {};
7150 \\const Foo = struct {7148 \\const Foo = struct {
7151 \\ fn bar(self: *const Foo) void {}7149 \\ fn bar(self: *const Foo) void {}
...@@ -7161,7 +7159,15 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -7161,7 +7159,15 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
7161 "tmp.zig:17:4: error: variable of type 'Opaque' not allowed",7159 "tmp.zig:17:4: error: variable of type 'Opaque' not allowed",
7162 "tmp.zig:20:4: error: variable of type 'type' must be const or comptime",7160 "tmp.zig:20:4: error: variable of type 'type' must be const or comptime",
7163 "tmp.zig:23:4: error: variable of type '(bound fn(*const Foo) void)' must be const or comptime",7161 "tmp.zig:23:4: error: variable of type '(bound fn(*const Foo) void)' must be const or comptime",
7164 "tmp.zig:26:22: error: unreachable code",7162 });
7163
7164 cases.add("variable with type 'noreturn'",
7165 \\export fn entry9() void {
7166 \\ var z: noreturn = return;
7167 \\}
7168 , &[_][]const u8{
7169 "tmp.zig:2:5: error: unreachable code",
7170 "tmp.zig:2:23: note: control flow is diverted here",
7165 });7171 });
71667172
7167 cases.add("wrong types given to atomic order args in cmpxchg",7173 cases.add("wrong types given to atomic order args in cmpxchg",