From 24eac4d74d43a4c14048ac2146979b3879b4594c Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Sun, 22 Dec 2024 08:58:45 -0300 Subject: [PATCH] cgen: fix lambda inferring generic type on method call (fix #23221) (#23234) --- vlib/v/checker/check_types.v | 6 +++- vlib/v/checker/fn.v | 25 +++++++++----- .../generics/generic_method_lambda_arg_test.v | 34 +++++++++++++++++++ 3 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 vlib/v/tests/generics/generic_method_lambda_arg_test.v diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index 16796e835e..ca456270ea 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -1125,7 +1125,11 @@ fn (mut c Checker) infer_fn_generic_types(func &ast.Fn, mut node ast.CallExpr) { if typ.has_flag(.generic) { lambda_ret_gt_name := c.table.type_to_str(typ) idx := func.generic_names.index(lambda_ret_gt_name) - typ = node.concrete_types[idx] + if idx < node.concrete_types.len { + typ = node.concrete_types[idx] + } else { + typ = ast.void_type + } } } } diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index af7f3ef8f3..353610cd3d 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -1806,15 +1806,7 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. } if mut call_arg.expr is ast.LambdaExpr { // Calling fn is generic and lambda arg also is generic - if node.concrete_types.len > 0 && call_arg.expr.func != unsafe { nil } - && call_arg.expr.func.decl.generic_names.len > 0 { - call_arg.expr.call_ctx = unsafe { node } - if c.table.register_fn_concrete_types(call_arg.expr.func.decl.fkey(), - node.concrete_types) - { - call_arg.expr.func.decl.ninstances++ - } - } + c.handle_generic_lambda_arg(node, mut call_arg.expr) continue } c.error('${err.msg()} in argument ${i + 1} to `${fn_name}`', call_arg.pos) @@ -2793,6 +2785,10 @@ fn (mut c Checker) method_call(mut node ast.CallExpr, mut continue_check &bool) c.error('${err.msg()} in argument ${i + 1} to `${left_sym.name}.${method_name}`', arg.pos) } + if mut arg.expr is ast.LambdaExpr { + // Calling fn is generic and lambda arg also is generic + c.handle_generic_lambda_arg(node, mut arg.expr) + } param_typ_sym := c.table.sym(exp_arg_typ) if param_typ_sym.kind == .struct && got_arg_typ !in [ast.voidptr_type, ast.nil_type] && !c.check_multiple_ptr_match(got_arg_typ, param.typ, param, arg) { @@ -2871,6 +2867,17 @@ fn (mut c Checker) method_call(mut node ast.CallExpr, mut continue_check &bool) return node.return_type } +fn (mut c Checker) handle_generic_lambda_arg(node &ast.CallExpr, 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 { + lambda.call_ctx = unsafe { node } + if c.table.register_fn_concrete_types(lambda.func.decl.fkey(), node.concrete_types) { + lambda.func.decl.ninstances++ + } + } +} + fn (mut c Checker) spawn_expr(mut node ast.SpawnExpr) ast.Type { ret_type := c.call_expr(mut node.call_expr) if node.call_expr.or_block.kind != .absent { diff --git a/vlib/v/tests/generics/generic_method_lambda_arg_test.v b/vlib/v/tests/generics/generic_method_lambda_arg_test.v new file mode 100644 index 0000000000..7af47dbec1 --- /dev/null +++ b/vlib/v/tests/generics/generic_method_lambda_arg_test.v @@ -0,0 +1,34 @@ +module main + +fn test_main() { + my := MyError{ + path: 'err msg' + } + p := my.to[string](|m| m.path) + p2 := my.to_str(|m| m.path) + println(p) + println(p2) + + assert p == p2 +} + +struct MyError { +pub: + path string +} + +fn (e &MyError) msg() string { + return e.path +} + +fn (e &MyError) code() int { + return 1 +} + +fn (e &MyError) to[T](func fn (MyError) T) T { + return func(e) +} + +fn (e &MyError) to_str(func fn (MyError) string) string { + return func(e) +}