diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 3c7e31feeb..fcbdebac3b 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1187,6 +1187,16 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { if left_type_sym.kind == .sum_type && method_name == 'type_name' { return table.string_type } + if call_expr.generic_type.has_flag(.generic) { + if c.mod != '' { + // Need to prepend the module when adding a generic type to a function + // `fn_gen_types['mymod.myfn'] == ['string', 'int']` + c.table.register_fn_gen_type(c.mod + '.' + call_expr.name, c.cur_generic_type) + } else { + c.table.register_fn_gen_type(call_expr.name, c.cur_generic_type) + } + // call_expr.generic_type = c.unwrap_generic(call_expr.generic_type) + } // TODO: remove this for actual methods, use only for compiler magic // FIXME: Argument count != 1 will break these if left_type_sym.kind == .array && @@ -1351,6 +1361,9 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { c.type_implements(got_arg_typ, exp_arg_typ, arg.expr.position()) continue } + if method.is_generic { + continue + } c.check_expected_call_arg(got_arg_typ, exp_arg_typ) or { // str method, allow type with str method if fn arg is string // Passing an int or a string array produces a c error here @@ -1402,6 +1415,36 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { call_expr.receiver_type = method.params[0].typ } call_expr.return_type = method.return_type + if method.is_generic && call_expr.generic_type == table.void_type { + // no type arguments given in call, attempt implicit instantiation + c.infer_fn_types(method, mut call_expr) + } + if call_expr.generic_type != table.void_type && method.return_type != 0 { // table.t_type { + // Handle `foo() T` => `foo() int` => return int + return_sym := c.table.get_type_symbol(method.return_type) + if return_sym.name == 'T' { + mut typ := call_expr.generic_type + typ = typ.set_nr_muls(method.return_type.nr_muls()) + if method.return_type.has_flag(.optional) { + typ = typ.set_flag(.optional) + } + call_expr.return_type = typ + return typ + } else if return_sym.kind == .array { + elem_info := return_sym.info as table.Array + elem_sym := c.table.get_type_symbol(elem_info.elem_type) + if elem_sym.name == 'T' { + idx := c.table.find_or_register_array(call_expr.generic_type, 1) + return table.new_type(idx) + } + } + } + if call_expr.generic_type.is_full() && !method.is_generic { + c.error('a non generic function called like a generic one', call_expr.generic_list_pos) + } + if method.is_generic { + return call_expr.return_type + } return method.return_type } // TODO: str methods diff --git a/vlib/v/gen/fn.v b/vlib/v/gen/fn.v index 2295ac7319..bdf7a87daa 100644 --- a/vlib/v/gen/fn.v +++ b/vlib/v/gen/fn.v @@ -424,6 +424,11 @@ fn (mut g Gen) method_call(node ast.CallExpr) { } } } + if node.generic_type != table.void_type && node.generic_type != 0 { + // Using _T_ to differentiate between get and get_string + // `foo()` => `foo_T_int()` + name += '_T_' + g.typ(node.generic_type) + } // TODO2 // g.generate_tmp_autofree_arg_vars(node, name) // diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 162e895493..78e6992648 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -50,7 +50,7 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp } } p.check(.lpar) - mut args := p.call_args() + args := p.call_args() last_pos := p.tok.position() p.check(.rpar) // ! in mutable methods diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 9cb3003c43..d73d901c2c 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -1427,6 +1427,20 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr { } // Method call // TODO move to fn.v call_expr() + mut generic_type := table.void_type + mut generic_list_pos := p.tok.position() + if p.tok.kind == .lt && p.peek_tok.kind == .name && p.peek_tok2.kind == .gt { + // `g.foo(10)` + p.next() // `<` + generic_type = p.parse_type() + p.check(.gt) // `>` + generic_list_pos = generic_list_pos.extend(p.prev_tok.position()) + // In case of `foo()` + // T is unwrapped and registered in the checker. + if !generic_type.has_flag(.generic) { + p.table.register_fn_gen_type(field_name, generic_type) + } + } if p.tok.kind == .lpar { p.next() args := p.call_args() @@ -1472,6 +1486,8 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr { args: args pos: pos is_method: true + generic_type: generic_type + generic_list_pos: generic_list_pos or_block: ast.OrExpr{ stmts: or_stmts kind: or_kind diff --git a/vlib/v/tests/generics_method_test.v b/vlib/v/tests/generics_method_test.v new file mode 100644 index 0000000000..ef54376d20 --- /dev/null +++ b/vlib/v/tests/generics_method_test.v @@ -0,0 +1,21 @@ +struct Point { +mut: + x int + y int +} + +fn (mut p Point) translate(x T, y T) { + p.x += x + p.y += y +} + +fn test_generic_method() { + mut pot := Point{} + pot.translate(1, 3) + pot.translate(1, 3) + println(pot) + assert pot == Point{ + x: 2 + y: 6 + } +}