diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 99ac0ff21b..e6afd204c3 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1432,6 +1432,10 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { c.fail_if_immutable(call_expr.left) // call_expr.is_mut = true } + if (!left_type_sym.is_builtin() && method.mod != 'builtin') && method.language == .v + && method.no_body { + c.error('cannot call a method that does not have a body', call_expr.pos) + } if method.return_type == table.void_type && method.ctdefine.len > 0 && method.ctdefine !in c.pref.compile_defines { call_expr.should_be_skipped = true @@ -1726,6 +1730,9 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type { // builtin C.m*, C.s* only - temp c.warn('function `$f.name` must be called from an `unsafe` block', call_expr.pos) } + if f.mod != 'builtin' && f.language == .v && f.no_body { + c.error('cannot call a function that does not have a body', call_expr.pos) + } for generic_type in call_expr.generic_types { sym := c.table.get_type_symbol(generic_type) if sym.kind == .placeholder { diff --git a/vlib/v/checker/tests/fn_call_no_body.out b/vlib/v/checker/tests/fn_call_no_body.out new file mode 100644 index 0000000000..376fc56c8a --- /dev/null +++ b/vlib/v/checker/tests/fn_call_no_body.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/fn_call_no_body.vv:7:9: error: cannot call a method that does not have a body + 5 | + 6 | fn main() { + 7 | Foo(0).f() + | ~~~ + 8 | f() + 9 | } +vlib/v/checker/tests/fn_call_no_body.vv:8:2: error: cannot call a function that does not have a body + 6 | fn main() { + 7 | Foo(0).f() + 8 | f() + | ~~~ + 9 | } diff --git a/vlib/v/checker/tests/fn_call_no_body.vv b/vlib/v/checker/tests/fn_call_no_body.vv new file mode 100644 index 0000000000..1058977f84 --- /dev/null +++ b/vlib/v/checker/tests/fn_call_no_body.vv @@ -0,0 +1,9 @@ +type Foo = int +fn (_ Foo) f() + +fn f() + +fn main() { + Foo(0).f() + f() +} diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 1aa2d7b781..4c3b2bd301 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -315,6 +315,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl { return_type = p.parse_type() } mut type_sym_method_idx := 0 + no_body := p.tok.kind != .lcbr // Register if is_method { mut type_sym := p.table.get_type_symbol(rec_type) @@ -345,6 +346,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl { is_pub: is_pub is_deprecated: is_deprecated is_unsafe: is_unsafe + no_body: no_body mod: p.mod attrs: p.attrs }) @@ -369,6 +371,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl { is_pub: is_pub is_deprecated: is_deprecated is_unsafe: is_unsafe + no_body: no_body mod: p.mod attrs: p.attrs language: language @@ -378,7 +381,6 @@ fn (mut p Parser) fn_decl() ast.FnDecl { // Body p.cur_fn_name = name mut stmts := []ast.Stmt{} - no_body := p.tok.kind != .lcbr body_start_pos := p.peek_tok.position() if p.tok.kind == .lcbr { p.inside_fn = true diff --git a/vlib/v/table/table.v b/vlib/v/table/table.v index fe3b40d7e3..90b9274bc5 100644 --- a/vlib/v/table/table.v +++ b/vlib/v/table/table.v @@ -32,6 +32,7 @@ pub: is_deprecated bool is_unsafe bool is_placeholder bool + no_body bool mod string ctdefine string // compile time define. myflag, when [if myflag] tag attrs []Attr diff --git a/vlib/v/table/types.v b/vlib/v/table/types.v index 8ed20e561f..2bbd4005df 100644 --- a/vlib/v/table/types.v +++ b/vlib/v/table/types.v @@ -573,6 +573,11 @@ pub fn (t &TypeSymbol) is_primitive() bool { return t.is_number() || t.is_pointer() || t.is_string() } +[inline] +pub fn (t &TypeSymbol) is_builtin() bool { + return t.mod == 'builtin' +} + // for debugging/errors only, perf is not an issue pub fn (k Kind) str() string { k_str := match k {