diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index aea8fd9cf4..5ba2140eb2 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -8,7 +8,7 @@ import ( v.table ) -pub type TypeDecl = AliasTypeDecl | SumTypeDecl +pub type TypeDecl = AliasTypeDecl | SumTypeDecl | FnTypeDecl pub type Expr = InfixExpr | IfExpr | StringLiteral | IntegerLiteral | CharLiteral | FloatLiteral | Ident | CallExpr | BoolLiteral | StructInit | ArrayInit | SelectorExpr | PostfixExpr | @@ -128,15 +128,17 @@ pub: pub struct Arg { pub: - typ table.Type - name string + + name string + is_mut bool + typ table.Type } pub struct FnDecl { pub: name string stmts []Stmt - typ table.Type + return_type table.Type args []Arg is_deprecated bool is_pub bool @@ -232,9 +234,9 @@ pub: scope &Scope } -pub struct IdentFunc { +pub struct IdentFn { pub mut: - return_type table.Type + typ table.Type } pub struct IdentVar { @@ -244,7 +246,7 @@ pub mut: is_static bool } -pub type IdentInfo = IdentFunc | IdentVar +pub type IdentInfo = IdentFn | IdentVar pub enum IdentKind { unresolved @@ -459,6 +461,13 @@ pub: sub_types []table.Type } +pub struct FnTypeDecl { +pub: + name string + is_pub bool + typ table.Type +} + pub struct DeferStmt { pub: stmts []Stmt diff --git a/vlib/v/ast/str.v b/vlib/v/ast/str.v index 1f6e3d98ba..7bc9176936 100644 --- a/vlib/v/ast/str.v +++ b/vlib/v/ast/str.v @@ -48,10 +48,10 @@ pub fn (node &FnDecl) str(t &table.Table) string { } } f.write(')') - if node.typ != table.void_type { + if node.return_type != table.void_type { // typ := t.type_to_str(node.typ) // if typ.starts_with(' - f.write(' ' + t.type_to_str(node.typ)) + f.write(' ' + t.type_to_str(node.return_type)) } return f.str() } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index e2c6783860..7966fbf6f4 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -204,6 +204,19 @@ pub fn (c mut Checker) call_expr(call_expr mut ast.CallExpr) table.Type { f = f1 } } + // check for arg (var) of fn type + if !found { + scope := c.file.scope.innermost(call_expr.pos.pos) + if var := scope.find_var(fn_name) { + if var.typ != 0 { + vts := c.table.get_type_symbol(var.typ) + if vts.kind == .function { + f = vts.info as table.Fn + found = true + } + } + } + } if !found { c.error('unknown fn: $fn_name', call_expr.pos) } @@ -277,11 +290,17 @@ pub fn (c mut Checker) method_call_expr(method_call_expr mut ast.MethodCallExpr) return info.elem_type } if method := c.table.type_find_method(typ_sym, name) { + if method_call_expr.args.len < method.args.len-1 { + c.error('too few arguments in call to `${typ_sym.name}.$name`', method_call_expr.pos) + } + else if !method.is_variadic && method_call_expr.args.len > method.args.len+1 { + c.error('too many arguments in call to `${typ_sym.name}.$name` ($method_call_expr.args.len instead of $method.args.len)', method_call_expr.pos) + } // if name == 'clone' { // println('CLONE nr args=$method.args.len') // } for i, arg_expr in method_call_expr.args { - c.expected_type = method.args[i].typ + c.expected_type = method.args[i+1].typ c.expr(arg_expr) } method_call_expr.receiver_type = method.args[0].typ @@ -492,7 +511,7 @@ fn (c mut Checker) stmt(node ast.Stmt) { c.expr(it.expr) } ast.FnDecl { - c.fn_return_type = it.typ + c.fn_return_type = it.return_type for stmt in it.stmts { c.stmt(stmt) } @@ -697,8 +716,8 @@ pub fn (c mut Checker) ident(ident mut ast.Ident) table.Type { } // second use, already resovled in unresovled branch else if ident.kind == .function { - info := ident.info as ast.IdentFunc - return info.return_type + info := ident.info as ast.IdentFn + return info.typ } // Handle indents with unresolved types during the parsing step // (declared after first usage) @@ -719,12 +738,13 @@ pub fn (c mut Checker) ident(ident mut ast.Ident) table.Type { } // Function object (not a call), e.g. `onclick(my_click)` if func := c.table.find_fn(name) { - ident.name = name + fn_type := c.table.find_or_register_fn_type(func) + ident.name = name ident.kind = .function - ident.info = ast.IdentFunc{ - return_type: func.return_type + ident.info = ast.IdentFn{ + typ: fn_type } - return func.return_type + return fn_type } } // TODO @@ -889,6 +909,7 @@ pub fn (c mut Checker) enum_val(node ast.EnumVal) table.Type { typ := c.table.get_type_symbol(table.Type(typ_idx)) // println('tname=$typ.name') if typ.kind != .enum_ { + println('# $typ.kind.str()') c.error('not an enum', node.pos) } // info := typ.info as table.Enum diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 1dfb4b6b7d..3c7bbaf743 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -245,7 +245,7 @@ fn (g mut Gen) stmt(node ast.Stmt) { g.write('return') // multiple returns if it.exprs.len > 1 { - styp := g.typ(g.fn_decl.typ) + styp := g.typ(g.fn_decl.return_type) g.write(' ($styp){') for i, expr in it.exprs { g.write('.arg$i=') @@ -375,8 +375,8 @@ fn (g mut Gen) gen_fn_decl(it ast.FnDecl) { if name.starts_with('_op_') { name = op_to_fn_name(name) } - // type_name := g.table.type_to_str(it.typ) - type_name := g.typ(it.typ) + // type_name := g.table.type_to_str(it.return_type) + type_name := g.typ(it.return_type) g.write('$type_name ${name}(') g.definitions.write('$type_name ${name}(') } diff --git a/vlib/v/gen/jsgen.v b/vlib/v/gen/jsgen.v index 4611a665b8..ec6c904fe0 100644 --- a/vlib/v/gen/jsgen.v +++ b/vlib/v/gen/jsgen.v @@ -37,7 +37,7 @@ pub fn (g mut JsGen) writeln(s string) { fn (g mut JsGen) stmt(node ast.Stmt) { match node { ast.FnDecl { - type_sym := g.table.get_type_symbol(it.typ) + type_sym := g.table.get_type_symbol(it.return_type) g.write('/** @return { $type_sym.name } **/\nfunction ${it.name}(') for arg in it.args { arg_type_sym := g.table.get_type_symbol(arg.typ) diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 07971e83b4..fe0e2a5e03 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -81,27 +81,18 @@ fn (p mut Parser) fn_decl() ast.FnDecl { is_method = true p.next() rec_name = p.check_name() - if p.tok.kind == .key_mut { - rec_mut = true - } + rec_mut = p.tok.kind == .key_mut + // if rec_mut { + // p.check(.key_mut) + // } + // TODO: talk to alex, should mut be parsed with the type like this? + // or should it be a property of the arg, like this ptr/mut becomes indistinguishable rec_type = p.parse_type() - args << table.Var{ - // Receiver is the first arg - typ: rec_type - name: rec_name - } ast_args << ast.Arg{ name: rec_name + is_mut: rec_mut typ: rec_type } - // p.table.register_var(table.Var{ - // name: rec_name - // typ: rec_type - // }) - p.scope.register_var(ast.Var{ - name: rec_name - typ: rec_type - }) p.check(.rpar) } mut name := '' @@ -126,6 +117,7 @@ fn (p mut Parser) fn_decl() ast.FnDecl { for ast_arg in ast_args { var := table.Var{ name: ast_arg.name + is_mut: ast_arg.is_mut typ: ast_arg.typ } args << var @@ -136,9 +128,9 @@ fn (p mut Parser) fn_decl() ast.FnDecl { // p.table.register_var(var) } // Return type - mut typ := table.void_type + mut return_type := table.void_type if p.tok.kind.is_start_of_type() { - typ = p.parse_type() + return_type = p.parse_type() } // Register if is_method { @@ -147,7 +139,7 @@ fn (p mut Parser) fn_decl() ast.FnDecl { type_sym.register_method(table.Fn{ name: name args: args - return_type: typ + return_type: return_type }) } else { @@ -160,7 +152,7 @@ fn (p mut Parser) fn_decl() ast.FnDecl { p.table.register_fn(table.Fn{ name: name args: args - return_type: typ + return_type: return_type is_variadic: is_variadic is_c: is_c }) @@ -175,7 +167,7 @@ fn (p mut Parser) fn_decl() ast.FnDecl { return ast.FnDecl{ name: name stmts: stmts - typ: typ + return_type: return_type args: ast_args is_deprecated: is_deprecated is_pub: is_pub @@ -202,6 +194,10 @@ fn (p mut Parser) fn_args() ([]ast.Arg,bool) { mut arg_no := 1 for p.tok.kind != .rpar { arg_name := 'arg_$arg_no' + is_mut := p.tok.kind == .key_mut + if is_mut { + p.check(.key_mut) + } if p.tok.kind == .ellipsis { p.check(.ellipsis) is_variadic = true @@ -218,6 +214,7 @@ fn (p mut Parser) fn_args() ([]ast.Arg,bool) { } args << ast.Arg{ name: arg_name + is_mut: is_mut typ: arg_type } } @@ -231,7 +228,8 @@ fn (p mut Parser) fn_args() ([]ast.Arg,bool) { p.check(.comma) arg_names << p.check_name() } - if p.tok.kind == .key_mut { + is_mut := p.tok.kind == .key_mut + if is_mut { p.check(.key_mut) } if p.tok.kind == .ellipsis { @@ -245,6 +243,7 @@ fn (p mut Parser) fn_args() ([]ast.Arg,bool) { for arg_name in arg_names { args << ast.Arg{ name: arg_name + is_mut: is_mut typ: typ } // if typ.typ.kind == .variadic && p.tok.kind == .comma { diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index 29c4ffddbf..d432f04649 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -66,15 +66,32 @@ pub fn (p mut Parser) parse_multi_return_type() table.Type { return table.new_type(idx) } -pub fn (p mut Parser) parse_fn_type() table.Type { - // p.warn('parrse fn') +// given anon name based off signature when `name` is blank +pub fn (p mut Parser) parse_fn_type(name string) table.Type { + // p.warn('parse fn') p.check(.key_fn) - // p.fn_decl() - p.fn_args() - if p.tok.kind.is_start_of_type() { - p.parse_type() + ast_args, is_variadic := p.fn_args() + mut args := []table.Var + for ast_arg in ast_args { + arg := table.Var{ + name: ast_arg.name + is_mut: ast_arg.is_mut + typ: ast_arg.typ + } + args << arg } - return table.int_type + mut return_type := table.void_type + if p.tok.kind.is_start_of_type() { + return_type = p.parse_type() + } + func := table.Fn{ + name: name + args: args + is_variadic: is_variadic + return_type: return_type + } + idx := p.table.find_or_register_fn_type(func) + return table.new_type(idx) } pub fn (p mut Parser) parse_type() table.Type { @@ -137,7 +154,7 @@ pub fn (p mut Parser) parse_any_type(is_c, is_ptr bool) table.Type { match p.tok.kind { // func .key_fn { - return p.parse_fn_type() + return p.parse_fn_type('') } // array .lsbr { diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 371cb8d73d..7d46f140d4 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -1727,6 +1727,16 @@ fn (p mut Parser) type_decl() ast.TypeDecl { sub_types: sum_variants } } + // function type: `type mycallback fn(string, int)` + if p.tok.kind == .key_fn { + fn_name := p.prepend_mod(name) + fn_type := p.parse_fn_type(fn_name) + return ast.FnTypeDecl{ + name: fn_name + is_pub: is_pub + typ: fn_type + } + } // type MyType int parent_type := p.parse_type() pid := table.type_idx(parent_type) diff --git a/vlib/v/table/atype_symbols.v b/vlib/v/table/atype_symbols.v index 71c6024e6a..46d617c00d 100644 --- a/vlib/v/table/atype_symbols.v +++ b/vlib/v/table/atype_symbols.v @@ -8,7 +8,7 @@ import ( ) pub type TypeInfo = Array | ArrayFixed | Map | Struct | -MultiReturn | Alias | Enum | SumType +MultiReturn | Alias | Enum | SumType | Fn pub struct TypeSymbol { pub: @@ -87,6 +87,7 @@ pub enum Kind { sum_type alias enum_ + function } pub fn (t &TypeSymbol) str() string { diff --git a/vlib/v/table/table.v b/vlib/v/table/table.v index fbe355c1f9..35b04b4bd4 100644 --- a/vlib/v/table/table.v +++ b/vlib/v/table/table.v @@ -60,6 +60,25 @@ pub fn (t mut Table) register_global(name string, typ Type) { } } +// used to compare fn's & for naming anon fn's +pub fn (f &Fn) signature() string { + mut sig := '' + for i, arg in f.args { + // TODO: for now ignore mut/pts in sig for now + typ := type_set_nr_muls(arg.typ, 0) + // if arg.is_mut { + // sig += 'mut_' + // } + // sig += '$arg.typ' + sig += '$typ' + if i < f.args.len-1 { + sig += '_' + } + } + sig += '_$f.return_type' + return sig +} + pub fn (t &Table) find_fn(name string) ?Fn { f := t.fns[name] if f.name.str != 0 { @@ -360,6 +379,20 @@ pub fn (t mut Table) find_or_register_multi_return(mr_typs []Type) int { return t.register_type_symbol(mr_type) } +pub fn (t mut Table) find_or_register_fn_type(f Fn) int { + name := if f.name.len > 0 { + f.name + } + else { + 'anon_$f.signature()' + } + return t.register_type_symbol(TypeSymbol{ + kind: .function + name: name + info: f + }) +} + pub fn (t mut Table) add_placeholder_type(name string) int { ph_type := TypeSymbol{ kind: .placeholder @@ -440,12 +473,20 @@ pub fn (t &Table) check(got, expected Type) bool { return true } } - else if exp_type_sym.kind == .sum_type { + if exp_type_sym.kind == .sum_type { sum_info := exp_type_sym.info as SumType if got in sum_info.variants { return true } } + // fn type + if got_type_sym.kind == .function && exp_type_sym.kind == .function { + got_fn := got_type_sym.info as Fn + exp_fn := exp_type_sym.info as Fn + if got_fn.signature() == exp_fn.signature() { + return true + } + } if got_idx != exp_idx { // && got.typ.name != expected.typ.name*/ return false