ast, checker, cgen: fix error for generic method with generic fn type argument (fix #14239) (#14333)

master
yuyi 2022-05-08 01:22:20 +08:00 committed by GitHub
parent 724e7f037a
commit b04d46770b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 67 additions and 36 deletions

View File

@ -1519,6 +1519,9 @@ pub fn (t Table) does_type_implement_interface(typ Type, inter_typ Type) bool {
// Even map[string]map[string]T can be resolved.
// This is used for resolving the generic return type of CallExpr white `unwrap_generic` is used to resolve generic usage in FnDecl.
pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_names []string, concrete_types []Type) ?Type {
if generic_names.len != concrete_types.len {
return none
}
mut sym := t.sym(generic_type)
if sym.name in generic_names {
index := generic_names.index(sym.name)

View File

@ -1222,7 +1222,9 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
// x is Bar<T>, x.foo() -> x.foo<T>()
rec_sym := c.table.sym(node.left_type)
rec_is_generic := left_type.has_flag(.generic)
mut rec_concrete_types := []ast.Type{}
if rec_sym.info is ast.Struct {
rec_concrete_types = rec_sym.info.concrete_types.clone()
if rec_is_generic && node.concrete_types.len == 0
&& method.generic_names.len == rec_sym.info.generic_types.len {
node.concrete_types = rec_sym.info.generic_types
@ -1304,11 +1306,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
no_type_promotion = true
}
}
// if method_name == 'clone' {
// println('CLONE nr args=$method.args.len')
// }
// node.args << method.args[0].typ
// node.exp_arg_types << method.args[0].typ
for i, mut arg in node.args {
if i > 0 || exp_arg_typ == ast.Type(0) {
exp_arg_typ = if method.is_variadic && i >= method.params.len - 1 {
@ -1341,8 +1339,13 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
final_arg_sym = c.table.sym(final_arg_typ)
}
if exp_arg_typ.has_flag(.generic) {
method_concrete_types := if method.generic_names.len == rec_concrete_types.len {
rec_concrete_types
} else {
concrete_types
}
if exp_utyp := c.table.resolve_generic_to_concrete(exp_arg_typ, method.generic_names,
concrete_types)
method_concrete_types)
{
exp_arg_typ = exp_utyp
} else {
@ -1351,7 +1354,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
if got_arg_typ.has_flag(.generic) {
if got_utyp := c.table.resolve_generic_to_concrete(got_arg_typ, method.generic_names,
concrete_types)
method_concrete_types)
{
got_arg_typ = got_utyp
} else {

View File

@ -1,7 +0,0 @@
vlib/v/checker/tests/generic_parameter_on_method.vv:15:15: error: cannot use `&Type<int>` as `Type<>` in argument 1 to `ContainerType<int>.contains`
13 | fn main() {
14 | con := ContainerType<int>{typ: &Type<int>{0}}
15 | con.contains(con.typ)
| ~~~~~~~
16 | println(con)
17 | }

View File

@ -1,17 +0,0 @@
struct Type<T> {
value T
}
struct ContainerType<T> {
typ &Type<T>
}
fn (instance &ContainerType<T>) contains(typ Type<T>) {
println(typ)
}
fn main() {
con := ContainerType<int>{typ: &Type<int>{0}}
con.contains(con.typ)
println(con)
}

View File

@ -26,16 +26,16 @@ vlib/v/checker/tests/generics_fn_arguments_count_err.vv:22:22: error: expected 2
| ~~~~~~~~~~~~~~~~~~
23 | println(ret4)
24 | }
vlib/v/checker/tests/generics_fn_arguments_count_err.vv:2:15: error: no known default format for type `B`
vlib/v/checker/tests/generics_fn_arguments_count_err.vv:2:11: error: no known default format for type `A`
1 | fn get_name<A, B>(a A, b B) string {
2 | return '$a, $b'
| ^
| ^
3 | }
4 |
vlib/v/checker/tests/generics_fn_arguments_count_err.vv:8:15: error: no known default format for type `B`
vlib/v/checker/tests/generics_fn_arguments_count_err.vv:8:11: error: no known default format for type `A`
6 |
7 | fn (f Foo) get_name<A, B>(a A, b B) string {
8 | return '$a, $b'
| ^
| ^
9 | }
10 |

View File

@ -2238,7 +2238,8 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
deref_sym := g.table.sym(got_deref_type)
deref_will_match := expected_type in [got_type, got_deref_type, deref_sym.parent_idx]
got_is_opt := got_type.has_flag(.optional)
if deref_will_match || got_is_opt || expr.is_auto_deref_var() {
if deref_will_match || got_is_opt || expr.is_auto_deref_var()
|| expected_type.has_flag(.generic) {
g.write('*')
}
}

View File

@ -0,0 +1,25 @@
struct Type<T> {
value T
}
struct ContainerType<T> {
typ &Type<T>
}
fn (instance &ContainerType<T>) contains(typ Type<T>) bool {
println(typ)
if instance.typ == typ {
return true
} else {
return false
}
}
fn test_generic_fn_call_with_reference_argument() {
con := ContainerType<int>{
typ: &Type<int>{0}
}
ret := con.contains(con.typ)
println(con)
assert ret
}

View File

@ -0,0 +1,23 @@
struct Foo<T> {
}
fn (f Foo<T>) do(name string, d fn (T), v T) T {
println('running ' + name)
d(v)
println('ran ' + name)
return v
}
fn test_generics_method_with_generic_anon_fn_argument() {
f1 := Foo<string>{}
r1 := f1.do('foo', fn (s string) {
println('s value is ' + s)
}, 'bar')
assert r1 == 'bar'
f2 := Foo<int>{}
r2 := f2.do('bar', fn (s int) {
println('s value is $s')
}, 22)
assert r2 == 22
}