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.
|
// 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.
|
// 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 {
|
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)
|
mut sym := t.sym(generic_type)
|
||||||
if sym.name in generic_names {
|
if sym.name in generic_names {
|
||||||
index := generic_names.index(sym.name)
|
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>()
|
// x is Bar<T>, x.foo() -> x.foo<T>()
|
||||||
rec_sym := c.table.sym(node.left_type)
|
rec_sym := c.table.sym(node.left_type)
|
||||||
rec_is_generic := left_type.has_flag(.generic)
|
rec_is_generic := left_type.has_flag(.generic)
|
||||||
|
mut rec_concrete_types := []ast.Type{}
|
||||||
if rec_sym.info is ast.Struct {
|
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
|
if rec_is_generic && node.concrete_types.len == 0
|
||||||
&& method.generic_names.len == rec_sym.info.generic_types.len {
|
&& method.generic_names.len == rec_sym.info.generic_types.len {
|
||||||
node.concrete_types = rec_sym.info.generic_types
|
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
|
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 {
|
for i, mut arg in node.args {
|
||||||
if i > 0 || exp_arg_typ == ast.Type(0) {
|
if i > 0 || exp_arg_typ == ast.Type(0) {
|
||||||
exp_arg_typ = if method.is_variadic && i >= method.params.len - 1 {
|
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)
|
final_arg_sym = c.table.sym(final_arg_typ)
|
||||||
}
|
}
|
||||||
if exp_arg_typ.has_flag(.generic) {
|
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,
|
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
|
exp_arg_typ = exp_utyp
|
||||||
} else {
|
} 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_arg_typ.has_flag(.generic) {
|
||||||
if got_utyp := c.table.resolve_generic_to_concrete(got_arg_typ, method.generic_names,
|
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
|
got_arg_typ = got_utyp
|
||||||
} else {
|
} 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)
|
23 | println(ret4)
|
||||||
24 | }
|
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 {
|
1 | fn get_name<A, B>(a A, b B) string {
|
||||||
2 | return '$a, $b'
|
2 | return '$a, $b'
|
||||||
| ^
|
| ^
|
||||||
3 | }
|
3 | }
|
||||||
4 |
|
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 |
|
6 |
|
||||||
7 | fn (f Foo) get_name<A, B>(a A, b B) string {
|
7 | fn (f Foo) get_name<A, B>(a A, b B) string {
|
||||||
8 | return '$a, $b'
|
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_sym := g.table.sym(got_deref_type)
|
||||||
deref_will_match := expected_type in [got_type, got_deref_type, deref_sym.parent_idx]
|
deref_will_match := expected_type in [got_type, got_deref_type, deref_sym.parent_idx]
|
||||||
got_is_opt := got_type.has_flag(.optional)
|
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('*')
|
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