Skip to content

Commit dd68cb1

Browse files
authored
checker: infer generic fn values from call args (#27432)
1 parent 5a1b7b0 commit dd68cb1

2 files changed

Lines changed: 99 additions & 7 deletions

File tree

‎vlib/v/checker/fn.v‎

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2290,7 +2290,8 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
22902290
c.table.cur_concrete_types)
22912291
param = unwrapped
22922292
}
2293-
param.typ = c.resolve_call_arg_param_type(call_arg, param, func.generic_names, concrete_types)
2293+
param.typ = c.resolve_call_arg_param_type(call_arg, param, func.generic_names,
2294+
concrete_types)
22942295
// registers if the arg must be passed by ref to disable auto deref args
22952296
call_arg.should_be_ptr = param.typ.is_ptr() && !param.is_mut
22962297
if func.is_variadic && call_arg.expr is ast.ArrayDecompose {
@@ -3650,8 +3651,11 @@ fn (mut c Checker) method_call(mut node ast.CallExpr, mut continue_check &bool)
36503651
}
36513652

36523653
for i, mut arg in node.args {
3654+
mut exp_arg_param := call_arg_param_for_fn(method, i, true)
3655+
variadic_start := variadic_call_arg_start_idx(method, true)
3656+
has_typed_variadic := method.is_variadic && !method.is_c_variadic
36533657
if i > 0 || exp_arg_typ == ast.no_type {
3654-
exp_arg_typ = call_arg_param_for_fn(method, i, true).typ
3658+
exp_arg_typ = exp_arg_param.typ
36553659
if !c.inside_recheck {
36563660
arg.ct_expr = c.comptime.is_comptime(arg.expr)
36573661
}
@@ -3670,7 +3674,8 @@ fn (mut c Checker) method_call(mut node ast.CallExpr, mut continue_check &bool)
36703674
parent_sym := c.table.sym(parent_type)
36713675
if parent_sym.info is ast.Struct && parent_sym.info.is_generic {
36723676
if f := parent_sym.find_method(method_name) {
3673-
exp_arg_typ = call_arg_param_for_fn(f, i, true).typ
3677+
exp_arg_param = call_arg_param_for_fn(f, i, true)
3678+
exp_arg_typ = exp_arg_param.typ
36743679
}
36753680
}
36763681
}
@@ -3698,8 +3703,19 @@ fn (mut c Checker) method_call(mut node ast.CallExpr, mut continue_check &bool)
36983703
else {}
36993704
}
37003705
}
3701-
exp_arg_typ = c.resolve_short_syntax_call_arg_type(arg, exp_arg_typ,
3702-
resolved_method_generic_names, resolved_method_concrete_types)
3706+
exp_arg_typ = if exp_arg_typ == exp_arg_param.typ {
3707+
c.resolve_call_arg_param_type(arg, exp_arg_param, resolved_method_generic_names,
3708+
resolved_method_concrete_types)
3709+
} else {
3710+
c.resolve_short_syntax_call_arg_type(arg, exp_arg_typ, resolved_method_generic_names,
3711+
resolved_method_concrete_types)
3712+
}
3713+
if has_typed_variadic && i >= variadic_start {
3714+
variadic_sym := c.table.sym(exp_arg_typ)
3715+
if variadic_sym.info is ast.Array {
3716+
exp_arg_typ = variadic_sym.info.elem_type
3717+
}
3718+
}
37033719
exp_arg_sym := c.table.sym(exp_arg_typ)
37043720
c.expected_type = exp_arg_typ
37053721

@@ -3712,8 +3728,6 @@ fn (mut c Checker) method_call(mut node ast.CallExpr, mut continue_check &bool)
37123728
arg.pos)
37133729
}
37143730
}
3715-
variadic_start := variadic_call_arg_start_idx(method, true)
3716-
has_typed_variadic := method.is_variadic && !method.is_c_variadic
37173731
if has_typed_variadic && got_arg_typ.has_flag(.variadic) && node.args.len - 1 > i {
37183732
c.error('when forwarding a variadic variable, it must be the final argument', arg.pos)
37193733
}
@@ -3951,7 +3965,41 @@ fn (mut c Checker) method_call(mut node ast.CallExpr, mut continue_check &bool)
39513965
concrete_types = node.concrete_types.map(c.unwrap_generic(it))
39523966
}
39533967
if method_generic_names_len > 0 && node.concrete_types.len > 0 {
3968+
variadic_start := variadic_call_arg_start_idx(method, true)
3969+
has_typed_variadic := method.is_variadic && !method.is_c_variadic
39543970
for i, mut arg in node.args {
3971+
param := call_arg_param_for_fn(method, i, true)
3972+
if method_generic_names_len == node.concrete_types.len && param.typ.has_flag(.generic) {
3973+
if unwrap_typ := c.table.convert_generic_param_type(param, method.generic_names,
3974+
concrete_types)
3975+
{
3976+
mut expected_fn_typ := unwrap_typ
3977+
if has_typed_variadic && i >= variadic_start {
3978+
unwrap_sym := c.table.sym(unwrap_typ)
3979+
if unwrap_sym.info is ast.Array {
3980+
expected_fn_typ = unwrap_sym.info.elem_type
3981+
}
3982+
}
3983+
if c.table.final_sym(expected_fn_typ).kind == .function
3984+
&& arg.expr !is ast.LambdaExpr && arg.expr !is ast.AnonFn {
3985+
if mut arg.expr is ast.Ident {
3986+
if arg.expr.concrete_types.any(it.has_flag(.generic)
3987+
|| (it != 0 && c.table.sym(it).kind == .placeholder))
3988+
{
3989+
arg.expr.concrete_types = []ast.Type{}
3990+
}
3991+
}
3992+
old_expected_type := c.expected_type
3993+
c.expected_type = expected_fn_typ
3994+
arg_typ := c.check_expr_option_or_result_call(arg.expr,
3995+
c.expr(mut arg.expr))
3996+
c.expected_type = old_expected_type
3997+
arg.typ = arg_typ
3998+
node.args[i].typ = arg_typ
3999+
node.args[i].expr = arg.expr
4000+
}
4001+
}
4002+
}
39554003
if mut arg.expr is ast.LambdaExpr {
39564004
c.handle_generic_lambda_arg(node, method.generic_names, mut arg.expr)
39574005
} else if mut arg.expr is ast.AnonFn {

‎vlib/v/tests/generics/generic_fn_value_inference_test.v‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,47 @@ fn generic_fn_arg_run[T](h fn (T) int) int {
2222
fn test_generic_fn_value_is_monomorphized_from_generic_call_arg() {
2323
assert generic_fn_arg_run[GenericFnArgFoo](generic_fn_arg_handler) == 42
2424
}
25+
26+
struct GenericFnArgRunner {}
27+
28+
fn (r GenericFnArgRunner) run[T](h fn (T) int) int {
29+
_ := r
30+
return h(T{}) + 1
31+
}
32+
33+
fn test_generic_fn_value_is_monomorphized_from_generic_method_call_arg() {
34+
assert GenericFnArgRunner{}.run[GenericFnArgFoo](generic_fn_arg_handler) == 42
35+
}
36+
37+
fn generic_fn_arg_named_handler[U](x U) int {
38+
_ := x
39+
return 41
40+
}
41+
42+
fn (r GenericFnArgRunner) run_inferred[T](x T, h fn (T) int) int {
43+
_ := r
44+
return h(x) + 1
45+
}
46+
47+
fn test_generic_fn_value_is_monomorphized_from_inferred_generic_method_call_arg() {
48+
assert GenericFnArgRunner{}.run_inferred(GenericFnArgFoo{}, generic_fn_arg_named_handler) == 42
49+
}
50+
51+
fn (r GenericFnArgRunner) run_variadic[T](handlers ...fn (T) int) int {
52+
_ := r
53+
return handlers[0](T{}) + 1
54+
}
55+
56+
fn test_generic_fn_value_is_monomorphized_from_variadic_generic_method_call_arg() {
57+
assert GenericFnArgRunner{}.run_variadic[GenericFnArgFoo](generic_fn_arg_named_handler) == 42
58+
}
59+
60+
fn (r GenericFnArgRunner) run_variadic_inferred[T](x T, handlers ...fn (T) int) int {
61+
_ := r
62+
return handlers[0](x) + 1
63+
}
64+
65+
fn test_generic_fn_value_is_monomorphized_from_inferred_variadic_generic_method_call_arg() {
66+
assert GenericFnArgRunner{}.run_variadic_inferred(GenericFnArgFoo{},
67+
generic_fn_arg_named_handler) == 42
68+
}

0 commit comments

Comments
 (0)