diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 84477febfa..bee00752a0 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -1153,8 +1153,12 @@ pub fn (t &Table) mktyp(typ Type) Type { } } +pub fn (mut t Table) register_fn_generic_types(fn_name string) { + t.fn_generic_types[fn_name] = [][]Type{} +} + pub fn (mut t Table) register_fn_concrete_types(fn_name string, types []Type) bool { - mut a := t.fn_generic_types[fn_name] + mut a := t.fn_generic_types[fn_name] or { return false } if types in a { return false } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 304110fb7b..e695e88e78 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1986,17 +1986,15 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { // c.error('`void` type has no methods', node.left.position()) return ast.void_type } - mut has_generic := false // x.foo() instead of x.foo() mut concrete_types := []ast.Type{} for concrete_type in node.concrete_types { if concrete_type.has_flag(.generic) { - has_generic = true concrete_types << c.unwrap_generic(concrete_type) } else { concrete_types << concrete_type } } - if has_generic { + if concrete_types.len > 0 { if c.table.register_fn_concrete_types(node.name, concrete_types) { c.need_recheck_generic_fns = true } @@ -2539,13 +2537,17 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) c.error('generic fn using generic types cannot be called outside of generic fn', node.pos) } - if has_generic { + if concrete_types.len > 0 { mut no_exists := true - if c.mod != '' && !fn_name.contains('.') { - // Need to prepend the module when adding a generic type to a function - no_exists = c.table.register_fn_concrete_types(c.mod + '.' + fn_name, concrete_types) - } else { + if fn_name.contains('.') { no_exists = c.table.register_fn_concrete_types(fn_name, concrete_types) + } else { + no_exists = c.table.register_fn_concrete_types(c.mod + '.' + fn_name, concrete_types) + // if the generic fn does not exist in the current fn calling module, continue + // to look in builtin module + if !no_exists { + no_exists = c.table.register_fn_concrete_types(fn_name, concrete_types) + } } if no_exists { c.need_recheck_generic_fns = true diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index caba6d7222..9f40dff6fb 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -40,14 +40,6 @@ pub fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr p.expr_mod = '' concrete_types = p.parse_concrete_types() concrete_list_pos = concrete_list_pos.extend(p.prev_tok.position()) - // In case of `foo()` - // T is unwrapped and registered in the checker. - full_generic_fn_name := if fn_name.contains('.') { fn_name } else { p.prepend_mod(fn_name) } - has_generic := concrete_types.any(it.has_flag(.generic)) - if !has_generic { - // will be added in checker - p.table.register_fn_concrete_types(full_generic_fn_name, concrete_types) - } } p.check(.lpar) args := p.call_args() @@ -528,6 +520,9 @@ fn (mut p Parser) fn_decl() ast.FnDecl { scope: p.scope label_names: p.label_names } + if generic_names.len > 0 { + p.table.register_fn_generic_types(name) + } p.label_names = [] p.close_scope() return fn_decl