diff --git a/cmd/tools/vast/vast.v b/cmd/tools/vast/vast.v index 816ba2c1ce..9511858b95 100644 --- a/cmd/tools/vast/vast.v +++ b/cmd/tools/vast/vast.v @@ -480,6 +480,7 @@ fn (t Tree) fn_decl(node ast.FnDecl) &Node { obj.add('is_pub', t.bool_node(node.is_pub)) obj.add('is_variadic', t.bool_node(node.is_variadic)) obj.add('is_anon', t.bool_node(node.is_anon)) + obj.add('is_noreturn', t.bool_node(node.is_noreturn)) obj.add('is_manualfree', t.bool_node(node.is_manualfree)) obj.add('is_main', t.bool_node(node.is_main)) obj.add('is_test', t.bool_node(node.is_test)) @@ -1393,22 +1394,24 @@ fn (t Tree) ident_fn(node ast.IdentFn) &Node { fn (t Tree) call_expr(node ast.CallExpr) &Node { mut obj := new_object() obj.add('ast_type', t.string_node('CallExpr')) - obj.add('left', t.expr(node.left)) - obj.add('is_method', t.bool_node(node.is_method)) obj.add('mod', t.string_node(node.mod)) obj.add('name', t.string_node(node.name)) obj.add('language', t.enum_node(node.language)) + obj.add('left_type', t.type_node(node.left_type)) + obj.add('receiver_type', t.type_node(node.receiver_type)) + obj.add('return_type', t.type_node(node.return_type)) + obj.add('left', t.expr(node.left)) + obj.add('is_method', t.bool_node(node.is_method)) + obj.add('is_keep_alive', t.bool_node(node.is_keep_alive)) + obj.add('is_noreturn', t.bool_node(node.is_noreturn)) + obj.add('should_be_skipped', t.bool_node(node.should_be_skipped)) + obj.add('free_receiver', t.bool_node(node.free_receiver)) obj.add('scope', t.number_node(int(node.scope))) obj.add('args', t.array_node_call_arg(node.args)) obj.add('expected_arg_types', t.array_node_type(node.expected_arg_types)) obj.add('concrete_types', t.array_node_type(node.concrete_types)) obj.add('or_block', t.or_expr(node.or_block)) - obj.add('left_type', t.type_node(node.left_type)) - obj.add('receiver_type', t.type_node(node.receiver_type)) - obj.add('return_type', t.type_node(node.return_type)) - obj.add('should_be_skipped', t.bool_node(node.should_be_skipped)) obj.add('concrete_list_pos', t.position(node.concrete_list_pos)) - obj.add('free_receiver', t.bool_node(node.free_receiver)) obj.add('from_embed_type', t.type_node(node.from_embed_type)) obj.add('comments', t.array_node_comment(node.comments)) obj.add('pos', t.position(node.pos)) diff --git a/cmd/tools/vfmt.v b/cmd/tools/vfmt.v index 2ba8956879..3995800a95 100644 --- a/cmd/tools/vfmt.v +++ b/cmd/tools/vfmt.v @@ -332,6 +332,7 @@ fn get_compile_name_of_potential_v_project(file string) string { return pfolder } +[noreturn] fn verror(s string) { util.verror('vfmt error', s) } diff --git a/vlib/builtin/builtin.c.v b/vlib/builtin/builtin.c.v index d21afcc621..1f6565491b 100644 --- a/vlib/builtin/builtin.c.v +++ b/vlib/builtin/builtin.c.v @@ -13,7 +13,6 @@ fn vhalt() { [noreturn] pub fn exit(code int) { C.exit(code) - vhalt() } fn vcommithash() string { diff --git a/vlib/builtin/cfns.c.v b/vlib/builtin/cfns.c.v index 101b314a30..ebe7deaf71 100644 --- a/vlib/builtin/cfns.c.v +++ b/vlib/builtin/cfns.c.v @@ -16,7 +16,7 @@ fn C.realloc(a &byte, b int) &byte fn C.free(ptr voidptr) -[trusted] +[noreturn; trusted] fn C.exit(code int) fn C.qsort(base voidptr, items size_t, item_size size_t, cb qsort_callback_func) diff --git a/vlib/os/os_test.v b/vlib/os/os_test.v index 2f51552314..ce670897a4 100644 --- a/vlib/os/os_test.v +++ b/vlib/os/os_test.v @@ -343,12 +343,12 @@ fn test_realpath_does_not_absolutize_non_existing_relative_paths() { } } -fn test_realpath_absolutepath_symlink() { +fn test_realpath_absolutepath_symlink() ? { file_name := 'tolink_file.txt' symlink_name := 'symlink.txt' - mut f := os.create(file_name) or { panic(err) } + mut f := os.create(file_name) ? f.close() - assert os.symlink(file_name, symlink_name) or { panic(err) } + assert os.symlink(file_name, symlink_name) ? rpath := os.real_path(symlink_name) println(rpath) assert os.is_abs_path(rpath) diff --git a/vlib/v/builder/builder.v b/vlib/v/builder/builder.v index 096db1a0fd..b3b5e7b464 100644 --- a/vlib/v/builder/builder.v +++ b/vlib/v/builder/builder.v @@ -461,6 +461,7 @@ fn error_with_pos(s string, fpath string, pos token.Position) { exit(1) } +[noreturn] fn verror(s string) { util.verror('builder error', s) } diff --git a/vlib/v/builder/compile.v b/vlib/v/builder/compile.v index a5ef2d2610..7116efac68 100644 --- a/vlib/v/builder/compile.v +++ b/vlib/v/builder/compile.v @@ -30,7 +30,6 @@ pub fn compile(command string, pref &pref.Preferences) { os.is_writable_folder(output_folder) or { // An early error here, is better than an unclear C error later: verror(err.msg) - exit(1) } // Construct the V object from command line arguments mut b := new_builder(pref) @@ -217,9 +216,7 @@ pub fn (v Builder) get_builtin_files() []string { } } // Panic. We couldn't find the folder. - verror('`builtin/` not included on module lookup path. -Did you forget to add vlib to the path? (Use @vlib for default vlib)') - panic('Unreachable code reached.') + verror('`builtin/` not included on module lookup path.\nDid you forget to add vlib to the path? (Use @vlib for default vlib)') } pub fn (v &Builder) get_user_files() []string { @@ -259,10 +256,7 @@ pub fn (v &Builder) get_user_files() []string { is_test := v.pref.is_test mut is_internal_module_test := false if is_test { - tcontent := os.read_file(dir) or { - verror('$dir does not exist') - exit(0) - } + tcontent := os.read_file(dir) or { verror('$dir does not exist') } slines := tcontent.trim_space().split_into_lines() for sline in slines { line := sline.trim_space() @@ -290,7 +284,6 @@ pub fn (v &Builder) get_user_files() []string { does_exist := os.exists(dir) if !does_exist { verror("$dir doesn't exist") - exit(1) } is_real_file := does_exist && !os.is_dir(dir) resolved_link := if is_real_file && os.is_link(dir) { os.real_path(dir) } else { dir } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index aef270404f..b93f082d6e 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -4756,10 +4756,31 @@ fn (mut c Checker) stmts(stmts []ast.Stmt) { if unreachable.line_nr >= 0 { c.error('unreachable code', unreachable) } + c.find_unreachable_statements_after_noreturn_calls(stmts) c.scope_returns = false c.expected_type = ast.void_type } +pub fn (mut c Checker) find_unreachable_statements_after_noreturn_calls(stmts []ast.Stmt) { + mut prev_stmt_was_noreturn_call := false + for stmt in stmts { + match stmt { + ast.ExprStmt { + if stmt.expr is ast.CallExpr { + if prev_stmt_was_noreturn_call { + c.error('unreachable code after a [noreturn] call', stmt.pos) + return + } + prev_stmt_was_noreturn_call = stmt.expr.is_noreturn + } + } + else { + prev_stmt_was_noreturn_call = false + } + } + } +} + pub fn (mut c Checker) unwrap_generic(typ ast.Type) ast.Type { if typ.has_flag(.generic) { if t_typ := c.table.resolve_generic_to_concrete(typ, c.table.cur_fn.generic_names, diff --git a/vlib/v/checker/tests/noreturn_with_non_empty_loop_at_end.out b/vlib/v/checker/tests/noreturn_with_non_empty_loop_at_end.out index 6e7ec8df98..02fbccc825 100644 --- a/vlib/v/checker/tests/noreturn_with_non_empty_loop_at_end.out +++ b/vlib/v/checker/tests/noreturn_with_non_empty_loop_at_end.out @@ -5,3 +5,9 @@ vlib/v/checker/tests/noreturn_with_non_empty_loop_at_end.vv:4:6: error: [noretur | ^ 5 | break 6 | } +vlib/v/checker/tests/noreturn_with_non_empty_loop_at_end.vv:18:2: error: unreachable code after a [noreturn] call + 16 | eprintln('start') + 17 | abc() + 18 | eprintln('done') + | ~~~~~~~~~~~~~~~~ + 19 | } diff --git a/vlib/v/checker/tests/noreturn_with_return.out b/vlib/v/checker/tests/noreturn_with_return.out index 640c7555c9..d8c3279051 100644 --- a/vlib/v/checker/tests/noreturn_with_return.out +++ b/vlib/v/checker/tests/noreturn_with_return.out @@ -11,3 +11,9 @@ vlib/v/checker/tests/noreturn_with_return.vv:6:2: error: [noreturn] functions sh | ~~~~~~ 7 | } 8 | +vlib/v/checker/tests/noreturn_with_return.vv:18:2: error: unreachable code after a [noreturn] call + 16 | eprintln('start') + 17 | abc() + 18 | eprintln('done') + | ~~~~~~~~~~~~~~~~ + 19 | } diff --git a/vlib/v/checker/tests/noreturn_without_loop_or_another_noreturn_at_end.out b/vlib/v/checker/tests/noreturn_without_loop_or_another_noreturn_at_end.out index afabf5f331..6215215df5 100644 --- a/vlib/v/checker/tests/noreturn_without_loop_or_another_noreturn_at_end.out +++ b/vlib/v/checker/tests/noreturn_without_loop_or_another_noreturn_at_end.out @@ -5,3 +5,9 @@ vlib/v/checker/tests/noreturn_without_loop_or_another_noreturn_at_end.vv:3:2: er | ~~~~~~~~~~~~~ 4 | } 5 | +vlib/v/checker/tests/noreturn_without_loop_or_another_noreturn_at_end.vv:15:2: error: unreachable code after a [noreturn] call + 13 | eprintln('start') + 14 | abc() + 15 | eprintln('done') + | ~~~~~~~~~~~~~~~~ + 16 | } diff --git a/vlib/v/gen/c/cheaders.v b/vlib/v/gen/c/cheaders.v index 7e71687113..1a1e02a588 100644 --- a/vlib/v/gen/c/cheaders.v +++ b/vlib/v/gen/c/cheaders.v @@ -185,6 +185,23 @@ const c_common_macros = ' #endif #endif +#if !defined(VUNREACHABLE) + #if defined(__GNUC__) && !defined(__clang__) + #define V_GCC_VERSION (__GNUC__ * 10000L + __GNUC_MINOR__ * 100L + __GNUC_PATCHLEVEL__) + #if (V_GCC_VERSION >= 40500L) + #define VUNREACHABLE() do { __builtin_unreachable(); } while (0) + #endif + #endif + #if defined(__clang__) && defined(__has_builtin) + #if __has_builtin(__builtin_unreachable) + #define VUNREACHABLE() do { __builtin_unreachable(); } while (0) + #endif + #endif + #ifndef VUNREACHABLE + #define VUNREACHABLE() do { } while (0) + #endif +#endif + //likely and unlikely macros #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) #define _likely_(x) __builtin_expect(x,1) diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 8ca8a3d1ee..926739bfe0 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -527,6 +527,10 @@ fn (mut g Gen) call_expr(node ast.CallExpr) { g.write('\n $cur_line $tmp_opt') } } + if node.is_noreturn { + g.writeln(';') + g.write('VUNREACHABLE()') + } } fn (mut g Gen) method_call(node ast.CallExpr) { diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index f9b83faced..3aa284e46c 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -300,7 +300,7 @@ pub fn (g JsGen) hashes() string { return res } -[inline] +[noreturn] fn verror(msg string) { eprintln('jsgen error: $msg') exit(1) diff --git a/vlib/v/gen/native/amd64.v b/vlib/v/gen/native/amd64.v index d84b9711de..af1bf1d168 100644 --- a/vlib/v/gen/native/amd64.v +++ b/vlib/v/gen/native/amd64.v @@ -802,10 +802,7 @@ fn (mut g Gen) assign_stmt(node ast.AssignStmt) { match node.left_types[i] { 7 { // ast.IndexExpr { ie := node.left[i] as ast.IndexExpr - bracket := name.index('[') or { - verror('bracket expected') - exit(1) - } + bracket := name.index('[') or { verror('bracket expected') } var_name := name[0..bracket] mut dest := g.get_var_offset(var_name) index := ie.index as ast.IntegerLiteral diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 772f59c7e9..4dc0470e5c 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -3218,6 +3218,7 @@ fn (p &Parser) new_true_expr() ast.Expr { } } +[noreturn] fn verror(s string) { util.verror('parser error', s) } diff --git a/vlib/v/scanner/scanner.v b/vlib/v/scanner/scanner.v index 259954feb9..56d09c03d3 100644 --- a/vlib/v/scanner/scanner.v +++ b/vlib/v/scanner/scanner.v @@ -1399,6 +1399,7 @@ fn (mut s Scanner) vet_error(msg string, fix vet.FixKind) { s.vet_errors << ve } +[noreturn] pub fn verror(s string) { util.verror('scanner error', s) }