ast, checker, cgen: fix error for generic method with generic fn type argument (fix #14239) (#14333)
parent
724e7f037a
commit
b04d46770b
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 | }
|
|
@ -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)
|
||||
}
|
|
@ -26,13 +26,13 @@ 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'
|
||||
|
|
|
@ -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('*')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue