checker: fix generics with anon generics fn argument (fix #9859) (#9897)

* checker: fix generics with anon generics fn argument

* cgen: fix typedef of generics anon fn

* fix check generics argument types
pull/9900/head
yuyi 2021-04-27 20:25:42 +08:00 committed by GitHub
parent d8bb939072
commit 9f1ac39770
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 53 additions and 9 deletions

View File

@ -1684,7 +1684,7 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type {
if method.generic_names.len > 0 {
continue
}
c.check_expected_call_arg(got_arg_typ, exp_arg_typ, call_expr.language) or {
c.check_expected_call_arg(got_arg_typ, c.unwrap_generic(exp_arg_typ), call_expr.language) 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
// Deleting this condition results in propper V error messages
@ -1758,16 +1758,17 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type {
if method.generic_names.len != call_expr.concrete_types.len {
// no type arguments given in call, attempt implicit instantiation
c.infer_fn_generic_types(method, mut call_expr)
concrete_types = call_expr.concrete_types
}
// resolve return generics struct to concrete type
if method.generic_names.len > 0 && method.return_type.has_flag(.generic) {
c.check_return_generics_struct(method.return_type, mut call_expr, call_expr.concrete_types)
c.check_return_generics_struct(method.return_type, mut call_expr, concrete_types)
} else {
call_expr.return_type = method.return_type
}
if call_expr.concrete_types.len > 0 && method.return_type != 0 {
if typ := c.table.resolve_generic_to_concrete(method.return_type, method.generic_names,
call_expr.concrete_types, false)
concrete_types, false)
{
call_expr.return_type = typ
return typ
@ -2248,7 +2249,7 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
c.type_implements(typ, param.typ, call_arg.expr.position())
continue
}
c.check_expected_call_arg(typ, param.typ, call_expr.language) or {
c.check_expected_call_arg(typ, c.unwrap_generic(param.typ), call_expr.language) 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
// Deleting this condition results in propper V error messages
@ -2275,6 +2276,7 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
if func.generic_names.len != call_expr.concrete_types.len {
// no type arguments given in call, attempt implicit instantiation
c.infer_fn_generic_types(func, mut call_expr)
concrete_types = call_expr.concrete_types
}
if func.generic_names.len > 0 {
for i, call_arg in call_expr.args {
@ -2289,9 +2291,9 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
if param.typ.has_flag(.generic)
&& func.generic_names.len == call_expr.concrete_types.len {
if unwrap_typ := c.table.resolve_generic_to_concrete(param.typ, func.generic_names,
call_expr.concrete_types, false)
concrete_types, false)
{
c.check_expected_call_arg(typ, unwrap_typ, call_expr.language) or {
c.check_expected_call_arg(c.unwrap_generic(typ), unwrap_typ, call_expr.language) or {
c.error('$err.msg in argument ${i + 1} to `$fn_name`', call_arg.pos)
}
}
@ -2300,13 +2302,13 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
}
// resolve return generics struct to concrete type
if func.generic_names.len > 0 && func.return_type.has_flag(.generic) {
c.check_return_generics_struct(func.return_type, mut call_expr, call_expr.concrete_types)
c.check_return_generics_struct(func.return_type, mut call_expr, concrete_types)
} else {
call_expr.return_type = func.return_type
}
if call_expr.concrete_types.len > 0 && func.return_type != 0 {
if typ := c.table.resolve_generic_to_concrete(func.return_type, func.generic_names,
call_expr.concrete_types, false)
concrete_types, false)
{
call_expr.return_type = typ
return typ

View File

@ -853,7 +853,15 @@ pub fn (mut g Gen) write_fn_typesymbol_declaration(sym ast.TypeSymbol) {
func := info.func
is_fn_sig := func.name == ''
not_anon := !info.is_anon
if !info.has_decl && (not_anon || is_fn_sig) && !func.return_type.has_flag(.generic) {
mut has_generic_arg := false
for param in func.params {
if param.typ.has_flag(.generic) {
has_generic_arg = true
break
}
}
if !info.has_decl && (not_anon || is_fn_sig) && !func.return_type.has_flag(.generic)
&& !has_generic_arg {
fn_name := sym.cname
g.type_definitions.write_string('typedef ${g.typ(func.return_type)} (*$fn_name)(')
for i, param in func.params {

View File

@ -0,0 +1,34 @@
struct MyStruct<T> {
arr []T
}
fn (mut s MyStruct<T>) get_data(pos int) T {
return s.arr[pos]
}
fn (mut s MyStruct<T>) iterate(handler fn (T) int) int {
mut sum := 0
mut i := 0
for {
k := s.get_data<T>(i)
sum += handler(k)
i++
if i > 4 {
break
}
}
return sum
}
pub fn consume(data int) int {
return data
}
fn test_generics_with_anon_generics_fn() {
mut s := MyStruct<int>{
arr: [1, 2, 3, 4, 5]
}
y := s.iterate<int>(consume)
println(y)
assert y == 15
}