From 7f5560689aeb0e2ab84c5e3dc48f84247bd5bdc3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 28 Jun 2021 14:16:15 -0700 Subject: [PATCH] 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. --- src/AstGen.zig | 59 +++++++++++++++++------------------------ test/compile_errors.zig | 16 +++++++---- 2 files changed, 36 insertions(+), 39 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index c74106ef44..15b5b88d9a 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -261,6 +261,23 @@ fn typeExpr(gz: *GenZir, scope: *Scope, type_node: ast.Node.Index) InnerError!Zi return expr(gz, scope, .{ .ty = .type_type }, type_node); } +/// Same as `expr` but fails with a compile error if the result type is `noreturn`. +fn reachableExpr( + gz: *GenZir, + scope: *Scope, + rl: ResultLoc, + node: ast.Node.Index, + src_node: ast.Node.Index, +) InnerError!Zir.Inst.Ref { + const result_inst = try expr(gz, scope, rl, node); + if (gz.refIsNoReturn(result_inst)) { + return gz.astgen.failNodeNotes(src_node, "unreachable code", .{}, &[_]u32{ + try gz.astgen.errNoteNode(node, "control flow is diverted here", .{}), + }); + } + return result_inst; +} + fn lvalExpr(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref { const astgen = gz.astgen; const tree = astgen.tree; @@ -2331,8 +2348,7 @@ fn varDecl( const result_loc: ResultLoc = if (var_decl.ast.type_node != 0) .{ .ty = try typeExpr(gz, scope, var_decl.ast.type_node), } else .none; - const init_inst = try expr(gz, scope, result_loc, var_decl.ast.init_node); - try astgen.checkVarInitExpr(gz.*, node, var_decl.ast.init_node, init_inst, "local constant"); + const init_inst = try reachableExpr(gz, scope, result_loc, var_decl.ast.init_node, node); const sub_scope = try block_arena.create(Scope.LocalVal); sub_scope.* = .{ @@ -2383,8 +2399,7 @@ fn varDecl( init_scope.rl_ptr = alloc; } const init_result_loc: ResultLoc = .{ .block_ptr = &init_scope }; - const init_inst = try expr(&init_scope, &init_scope.base, init_result_loc, var_decl.ast.init_node); - try astgen.checkVarInitExpr(init_scope, node, var_decl.ast.init_node, init_inst, "local constant"); + const init_inst = try reachableExpr(&init_scope, &init_scope.base, init_result_loc, var_decl.ast.init_node, node); const zir_tags = astgen.instructions.items(.tag); const zir_datas = astgen.instructions.items(.data); @@ -2486,8 +2501,7 @@ fn varDecl( resolve_inferred_alloc = alloc; break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc } }; }; - const init_inst = try expr(gz, scope, var_data.result_loc, var_decl.ast.init_node); - try astgen.checkVarInitExpr(gz.*, node, var_decl.ast.init_node, init_inst, "local variable"); + _ = try reachableExpr(gz, scope, var_data.result_loc, var_decl.ast.init_node, node); if (resolve_inferred_alloc != .none) { _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node); } @@ -6618,14 +6632,14 @@ fn as( const dest_type = try typeExpr(gz, scope, lhs); switch (rl) { .none, .none_or_ref, .discard, .ref, .ty => { - const result = try expr(gz, scope, .{ .ty = dest_type }, rhs); + const result = try reachableExpr(gz, scope, .{ .ty = dest_type }, rhs, node); return rvalue(gz, rl, result, node); }, .ptr, .inferred_ptr => |result_ptr| { - return asRlPtr(gz, scope, rl, result_ptr, rhs, dest_type); + return asRlPtr(gz, scope, rl, node, result_ptr, rhs, dest_type); }, .block_ptr => |block_scope| { - return asRlPtr(gz, scope, rl, block_scope.rl_ptr, rhs, dest_type); + return asRlPtr(gz, scope, rl, node, block_scope.rl_ptr, rhs, dest_type); }, } } @@ -6679,6 +6693,7 @@ fn asRlPtr( parent_gz: *GenZir, scope: *Scope, rl: ResultLoc, + src_node: ast.Node.Index, result_ptr: Zir.Inst.Ref, operand_node: ast.Node.Index, dest_type: Zir.Inst.Ref, @@ -6692,7 +6707,7 @@ fn asRlPtr( defer as_scope.instructions.deinit(astgen.gpa); as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr); - const result = try expr(&as_scope, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node); + const result = try reachableExpr(&as_scope, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node, src_node); const parent_zir = &parent_gz.instructions; if (as_scope.rvalue_rl_count == 1) { // Busted! This expression didn't actually need a pointer. @@ -9607,27 +9622,3 @@ fn advanceSourceCursor(astgen: *AstGen, source: []const u8, end: usize) void { astgen.source_line = line; astgen.source_column = column; } - -fn checkVarInitExpr( - astgen: *AstGen, - gz: GenZir, - var_node: ast.Node.Index, - init_node: ast.Node.Index, - init_inst: Zir.Inst.Ref, - var_name_text: []const u8, -) !void { - if (gz.refIsNoReturn(init_inst)) { - return astgen.failNodeNotes( - var_node, - "useless {s}", - .{var_name_text}, - &[_]u32{ - try astgen.errNoteNode( - init_node, - "control flow is diverted here", - .{}, - ), - }, - ); - } -} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 0c04f70a86..6a17551ca7 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -4831,7 +4831,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ const a = return; \\} , &[_][]const u8{ - "tmp.zig:2:5: error: useless local constant", + "tmp.zig:2:5: error: unreachable code", "tmp.zig:2:15: note: control flow is diverted here", }); @@ -5058,6 +5058,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { _ = f(); } , &[_][]const u8{ "tmp.zig:2:12: error: unreachable code", + "tmp.zig:2:21: note: control flow is diverted here", }); cases.add("invalid builtin fn", @@ -7143,9 +7144,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry8() void { \\ var h = (Foo {}).bar; \\} - \\export fn entry9() void { - \\ var z: noreturn = return; - \\} \\const Opaque = opaque {}; \\const Foo = struct { \\ fn bar(self: *const Foo) void {} @@ -7161,7 +7159,15 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:17:4: error: variable of type 'Opaque' not allowed", "tmp.zig:20:4: error: variable of type 'type' must be const or comptime", "tmp.zig:23:4: error: variable of type '(bound fn(*const Foo) void)' must be const or comptime", - "tmp.zig:26:22: error: unreachable code", + }); + + cases.add("variable with type 'noreturn'", + \\export fn entry9() void { + \\ var z: noreturn = return; + \\} + , &[_][]const u8{ + "tmp.zig:2:5: error: unreachable code", + "tmp.zig:2:23: note: control flow is diverted here", }); cases.add("wrong types given to atomic order args in cmpxchg",