From 67c53a94e6c29387653d83b571331247bfab3245 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 25 Mar 2026 16:42:23 +0300 Subject: [PATCH] checker: fix veb closure middleware generic inference error (fixes #23868) --- vlib/v/checker/fn.v | 36 +++++++++++++++++++ ...od_call_with_short_syntax_fn_field_err.out | 6 ++++ ...hod_call_with_short_syntax_fn_field_err.vv | 34 ++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 vlib/v/checker/tests/generic_method_call_with_short_syntax_fn_field_err.out create mode 100644 vlib/v/checker/tests/generic_method_call_with_short_syntax_fn_field_err.vv diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 24d04b7a0..0e1cf7232 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -1887,6 +1887,8 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. c.table.cur_concrete_types) param = unwrapped } + param.typ = c.resolve_short_syntax_call_arg_type(call_arg, param.typ, func.generic_names, + concrete_types) // registers if the arg must be passed by ref to disable auto deref args call_arg.should_be_ptr = param.typ.is_ptr() && !param.is_mut if func.is_variadic && call_arg.expr is ast.ArrayDecompose { @@ -3106,6 +3108,24 @@ fn (mut c Checker) method_call(mut node ast.CallExpr, mut continue_check &bool) param_is_mut = false no_type_promotion = false } + resolved_method_concrete_types := if method_generic_names_len == rec_concrete_types.len { + rec_concrete_types + } else { + concrete_types + } + mut resolved_method_generic_names := method.generic_names.clone() + if resolved_method_generic_names.len == 0 { + match rec_sym.info { + ast.Struct, ast.Interface, ast.SumType { + if rec_sym.info.generic_types.len == resolved_method_concrete_types.len { + resolved_method_generic_names = rec_sym.info.generic_types.map(c.table.sym(it).name) + } + } + else {} + } + } + exp_arg_typ = c.resolve_short_syntax_call_arg_type(arg, exp_arg_typ, resolved_method_generic_names, + resolved_method_concrete_types) exp_arg_sym := c.table.sym(exp_arg_typ) c.expected_type = exp_arg_typ @@ -3388,6 +3408,22 @@ fn (mut c Checker) handle_generic_lambda_arg(node &ast.CallExpr, mut lambda ast. } } +fn (mut c Checker) resolve_short_syntax_call_arg_type(arg ast.CallArg, param_typ ast.Type, generic_names []string, concrete_types []ast.Type) ast.Type { + if !param_typ.has_flag(.generic) || generic_names.len == 0 + || generic_names.len != concrete_types.len { + return param_typ + } + if arg.expr is ast.StructInit { + expr := arg.expr as ast.StructInit + if expr.is_short_syntax && expr.typ == ast.void_type { + return c.table.convert_generic_type(param_typ, generic_names, concrete_types) or { + param_typ + } + } + } + return param_typ +} + fn (mut c Checker) check_unresolved_generic_param(node &ast.CallExpr, arg ast.CallArg) bool { if node.raw_concrete_types.len == 0 && arg.expr is ast.ArrayInit && arg.expr.typ == ast.void_type { diff --git a/vlib/v/checker/tests/generic_method_call_with_short_syntax_fn_field_err.out b/vlib/v/checker/tests/generic_method_call_with_short_syntax_fn_field_err.out new file mode 100644 index 000000000..ecee47440 --- /dev/null +++ b/vlib/v/checker/tests/generic_method_call_with_short_syntax_fn_field_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/generic_method_call_with_short_syntax_fn_field_err.vv:33:10: error: cannot assign to field `handler`: expected `fn (mut Context) bool`, not `Options[Context]` + 31 | fn main() { + 32 | mut app := &App{} + 33 | app.use(handler: create_options(mut app)) + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 34 | } diff --git a/vlib/v/checker/tests/generic_method_call_with_short_syntax_fn_field_err.vv b/vlib/v/checker/tests/generic_method_call_with_short_syntax_fn_field_err.vv new file mode 100644 index 000000000..14bb4d2ed --- /dev/null +++ b/vlib/v/checker/tests/generic_method_call_with_short_syntax_fn_field_err.vv @@ -0,0 +1,34 @@ +struct Context { + ok bool +} + +type Handler[T] = fn (mut T) bool + +@[params] +struct Options[T] { + handler Handler[T] @[required] +} + +struct Middleware[T] {} + +fn (mut m Middleware[T]) use(options Options[T]) {} + +struct App { + Middleware[Context] +mut: + hits int +} + +fn create_options(mut app App) Options[Context] { + return Options[Context]{ + handler: fn [mut app] (mut ctx Context) bool { + app.hits++ + return ctx.ok + } + } +} + +fn main() { + mut app := &App{} + app.use(handler: create_options(mut app)) +} -- 2.39.5