Skip to content

Commit

Permalink
checker: fix generics with anon generics fn argument (fix #9859) (#9897)
Browse files Browse the repository at this point in the history
* checker: fix generics with anon generics fn argument

* cgen: fix typedef of generics anon fn

* fix check generics argument types
  • Loading branch information
yuyi98 committed Apr 27, 2021
1 parent d8bb939 commit 9f1ac39
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 9 deletions.
18 changes: 10 additions & 8 deletions vlib/v/checker/checker.v
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand All @@ -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)
}
}
Expand All @@ -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
Expand Down
10 changes: 9 additions & 1 deletion vlib/v/gen/c/cgen.v
Expand Up @@ -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 {
Expand Down
34 changes: 34 additions & 0 deletions vlib/v/tests/generics_with_anon_generics_fn_test.v
@@ -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
}

0 comments on commit 9f1ac39

Please sign in to comment.