From 95ea2586097a24ce00d0987f29af93f0ac2987e8 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 15 Apr 2026 02:13:43 +0300 Subject: [PATCH] checker: fix failed type inference for fold with lambda expression (fixes #25934) --- vlib/v/checker/fn.v | 8 +++++- vlib/v/checker/lambda_expr.v | 28 ++++++++----------- .../generics/generic_lambda_inference_test.v | 11 ++++++++ 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index e604d7a11..fcf102e52 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -3830,10 +3830,16 @@ fn (c &Checker) generic_lambda_concrete_types(lambda &ast.LambdaExpr, caller_gen return concrete_types } -fn (mut c Checker) handle_generic_lambda_arg(node &ast.CallExpr, caller_generic_names []string, mut lambda ast.LambdaExpr) { +fn (mut c Checker) handle_generic_lambda_arg(node &ast.CallExpr, generic_names []string, mut lambda ast.LambdaExpr) { // Calling fn is generic and lambda arg also is generic if node.concrete_types.len > 0 && lambda.func != unsafe { nil } && lambda.func.decl.generic_names.len > 0 { + if generic_names.len == node.concrete_types.len { + unsafe { + mut p := &[]string(&lambda.func.decl.generic_names) + *p = generic_names.clone() + } + } lambda.call_ctx = unsafe { node } lambda_concrete_types := c.generic_lambda_concrete_types(lambda, caller_generic_names, node.concrete_types) diff --git a/vlib/v/checker/lambda_expr.v b/vlib/v/checker/lambda_expr.v index 12d3ae4e5..101cfdffa 100644 --- a/vlib/v/checker/lambda_expr.v +++ b/vlib/v/checker/lambda_expr.v @@ -39,14 +39,12 @@ pub fn (mut c Checker) lambda_expr(mut node ast.LambdaExpr, exp_typ ast.Type) as return ast.void_type } mut params := []ast.Param{} - mut generic_types := map[ast.Type]bool{} + mut generic_names := []string{} for idx, mut x in node.params { eparam := exp_sym.info.func.params[idx] eparam_type := eparam.typ c.lambda_expr_fix_type_of_param(mut node, mut x, eparam_type) - if eparam_type.has_flag(.generic) { - generic_types[eparam_type] = true - } + c.lambda_expr_push_generic_names(mut generic_names, eparam_type) params << ast.Param{ pos: x.pos name: x.name @@ -66,19 +64,7 @@ pub fn (mut c Checker) lambda_expr(mut node ast.LambdaExpr, exp_typ ast.Type) as is_variadic := false return_type := exp_sym.info.func.return_type return_type_pos := node.pos - if return_type.has_flag(.generic) { - generic_types[return_type] = true - } - - mut generic_names := []string{} - for t, _ in generic_types { - gtnames := c.table.generic_type_names(t) - for x in gtnames { - if x !in generic_names { - generic_names << x - } - } - } + c.lambda_expr_push_generic_names(mut generic_names, return_type) mut stmts := []ast.Stmt{} mut has_return := false @@ -143,6 +129,14 @@ pub fn (mut c Checker) lambda_expr(mut node ast.LambdaExpr, exp_typ ast.Type) as return exp_typ } +fn (mut c Checker) lambda_expr_push_generic_names(mut generic_names []string, typ ast.Type) { + for generic_name in c.table.generic_type_names(typ) { + if generic_name !in generic_names { + generic_names << generic_name + } + } +} + pub fn (mut c Checker) lambda_expr_fix_type_of_param(mut node ast.LambdaExpr, mut pident ast.Ident, ptype ast.Type) { if mut v := node.scope.find_var(pident.name) { v.is_arg = true diff --git a/vlib/v/tests/generics/generic_lambda_inference_test.v b/vlib/v/tests/generics/generic_lambda_inference_test.v index 4026839f2..31502f617 100644 --- a/vlib/v/tests/generics/generic_lambda_inference_test.v +++ b/vlib/v/tests/generics/generic_lambda_inference_test.v @@ -1,3 +1,5 @@ +import arrays + const result = ['0: a', '1: b', '2: c', '3: d'] fn mapi[T, U](arr []T, callback fn (int, T) U) []U { @@ -19,3 +21,12 @@ fn test_main() { }) assert arr_3 == result } + +fn count_chars_fold(strs []string) int { + return arrays.fold(strs, 0, |acc, s| acc + utf8_str_visible_length(s)) +} + +fn test_lambda_inferred_for_multi_generic_fold_callback() { + input := ['abc', 'def', 'здравей'] + assert count_chars_fold(input) == 13 +} -- 2.39.5