1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

generics: implement method generics (fix #7638) (#7732)

This commit is contained in:
yuyi 2021-01-01 01:00:22 +08:00 committed by GitHub
parent 2bc9ee4d88
commit c3dafad7ef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 86 additions and 1 deletions

View File

@ -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>() T` => `foo<int>() 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

View File

@ -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<string> and get_string
// `foo<int>()` => `foo_T_int()`
name += '_T_' + g.typ(node.generic_type)
}
// TODO2
// g.generate_tmp_autofree_arg_vars(node, name)
//

View File

@ -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

View File

@ -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<int>(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>()`
// 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

View File

@ -0,0 +1,21 @@
struct Point {
mut:
x int
y int
}
fn (mut p Point) translate<T>(x T, y T) {
p.x += x
p.y += y
}
fn test_generic_method() {
mut pot := Point{}
pot.translate<int>(1, 3)
pot.translate(1, 3)
println(pot)
assert pot == Point{
x: 2
y: 6
}
}