From 20936a3010684ceb2737808dd2e5944a67c18720 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Tue, 14 Apr 2026 12:45:32 +0300 Subject: [PATCH] parser: Generic closure that returns multi return with array generic type does not work (fixes #17876) --- vlib/v/ast/table.v | 58 ++++++++++++++----- vlib/v/parser/parse_type.v | 39 ++++++++++++- ...eturn_array_multi_return_regression_test.v | 34 +++++++++++ 3 files changed, 115 insertions(+), 16 deletions(-) create mode 100644 vlib/v/tests/generics/generic_closure_return_array_multi_return_regression_test.v diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 0e10c6a6b..e8a9a1a04 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -2364,15 +2364,18 @@ pub fn (mut t Table) convert_generic_type(generic_type Type, generic_names []str mut has_generic := false return_type_sym := t.sym(func.return_type) if func.return_type.has_flag(.generic) + || t.generic_type_names(func.return_type).len > 0 || (return_type_sym.kind == .generic_inst && (return_type_sym.info as GenericInst).concrete_types.any(it.has_flag(.generic))) { if typ := t.convert_generic_type(func.return_type, generic_names, to_types) { func.return_type = typ - typ_sym := t.sym(typ) - if typ.has_flag(.generic) || (typ_sym.kind == .generic_inst - && (typ_sym.info as GenericInst).concrete_types.any(it.has_flag(.generic))) { - has_generic = true - } + } else { + func.return_type = t.unwrap_generic_type_ex(func.return_type, generic_names, + to_types, true) + } + if func.return_type.has_flag(.generic) + || t.generic_type_names(func.return_type).len > 0 { + has_generic = true } } func.params = func.params.clone() @@ -2388,9 +2391,12 @@ pub fn (mut t Table) convert_generic_type(generic_type Type, generic_names []str if param.typ.has_flag(.generic) || t.generic_type_names(param.typ).len > 0 { has_generic = true } - if param.orig_typ.has_flag(.generic) { + if param.orig_typ.has_flag(.generic) || t.generic_type_names(param.orig_typ).len > 0 { if otyp := t.convert_generic_type(param.orig_typ, generic_names, to_types) { param.orig_typ = otyp + } else { + param.orig_typ = t.unwrap_generic_type_ex(param.orig_typ, generic_names, + to_types, true) } } } @@ -3193,7 +3199,17 @@ pub fn (mut t Table) generic_type_names(generic_type Type) []string { names << t.generic_type_names(sym.info.elem_type) } FnType { - names << sym.info.func.generic_names + for param in sym.info.func.params { + generic_names_push_with_filter(mut names, t.generic_type_names(param.typ)) + if param.orig_typ != 0 { + generic_names_push_with_filter(mut names, t.generic_type_names(param.orig_typ)) + } + } + generic_names_push_with_filter(mut names, + t.generic_type_names(sym.info.func.return_type)) + if names.len == 0 { + generic_names_push_with_filter(mut names, sym.info.func.generic_names) + } } MultiReturn { for ret_type in sym.info.types { @@ -3313,17 +3329,20 @@ fn (mut t Table) unwrap_generic_type_ex_with_depth(typ Type, generic_names []str unwrapped_fn.params = unwrapped_fn.params.clone() mut has_generic := false for i, param in unwrapped_fn.params { - if param.typ.has_flag(.generic) { + if param.typ.has_flag(.generic) || t.generic_type_names(param.typ).len > 0 { unwrapped_fn.params[i].typ = t.unwrap_generic_param_type(param, generic_names, concrete_types) has_generic = true } - if param.orig_typ.has_flag(.generic) { + if param.orig_typ.has_flag(.generic) || t.generic_type_names(param.orig_typ).len > 0 { unwrapped_fn.params[i].orig_typ = t.unwrap_generic_type(param.orig_typ, generic_names, concrete_types) } } - if unwrapped_fn.return_type.has_flag(.generic) { + unwrapped_return_sym := t.sym(unwrapped_fn.return_type) + if unwrapped_fn.return_type.has_flag(.generic) + || t.generic_type_names(unwrapped_fn.return_type).len > 0 + || (unwrapped_return_sym.kind == .generic_inst&& (unwrapped_return_sym.info as GenericInst).concrete_types.any(it.has_flag(.generic))) { unwrapped_fn.return_type = t.unwrap_generic_type_ex_with_depth(unwrapped_fn.return_type, generic_names, concrete_types, recheck_concrete_types, depth_guard) has_generic = true @@ -4018,26 +4037,39 @@ pub fn (mut t Table) generic_insts_to_concrete() { mut function := parent_info.func function.params = function.params.clone() for mut param in function.params { - if param.typ.has_flag(.generic) { + if param.typ.has_flag(.generic) || t.generic_type_names(param.typ).len > 0 { if t_typ := t.convert_generic_param_type(param, function.generic_names, info.concrete_types) { param.typ = t_typ + } else { + param.typ = t.unwrap_generic_type_ex(param.typ, + function.generic_names, info.concrete_types, true) } } - if param.orig_typ.has_flag(.generic) { + if param.orig_typ.has_flag(.generic) + || t.generic_type_names(param.orig_typ).len > 0 { if t_typ := t.convert_generic_type(param.orig_typ, function.generic_names, info.concrete_types) { param.orig_typ = t_typ + } else { + param.orig_typ = t.unwrap_generic_type_ex(param.orig_typ, + function.generic_names, info.concrete_types, true) } } } - if function.return_type.has_flag(.generic) { + return_type_sym := t.sym(function.return_type) + if function.return_type.has_flag(.generic) + || t.generic_type_names(function.return_type).len > 0 + || (return_type_sym.kind == .generic_inst&& (return_type_sym.info as GenericInst).concrete_types.any(it.has_flag(.generic))) { if t_typ := t.convert_generic_type(function.return_type, function.generic_names, info.concrete_types) { function.return_type = t_typ + } else { + function.return_type = t.unwrap_generic_type_ex(function.return_type, + function.generic_names, info.concrete_types, true) } } function.generic_names = [] diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index b264488e3..76363f378 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -935,23 +935,56 @@ fn (mut p Parser) find_type_or_add_placeholder(name string, language ast.Languag } else { mut func := sym.info.func func.name = sym_name - func.generic_names = generic_names.clone() - if func.return_type.has_flag(.generic) { + func.params = func.params.clone() + mut remaining_generic_names := []string{} + return_type_sym := p.table.sym(func.return_type) + if func.return_type.has_flag(.generic) + || p.table.generic_type_names(func.return_type).len > 0 + || (return_type_sym.kind == .generic_inst&& (return_type_sym.info as ast.GenericInst).concrete_types.any(it.has_flag(.generic))) { if to_generic_typ := p.table.convert_generic_type(func.return_type, sym.info.func.generic_names, p.init_generic_types) { func.return_type = to_generic_typ + } else { + func.return_type = p.table.unwrap_generic_type_ex(func.return_type, + sym.info.func.generic_names, p.init_generic_types, true) + } + } + for generic_name in p.table.generic_type_names(func.return_type) { + if generic_name !in remaining_generic_names { + remaining_generic_names << generic_name } } for i in 0 .. func.params.len { - if func.params[i].typ.has_flag(.generic) { + if func.params[i].typ.has_flag(.generic) + || p.table.generic_type_names(func.params[i].typ).len > 0 { if to_generic_typ := p.table.convert_generic_type(func.params[i].typ, sym.info.func.generic_names, p.init_generic_types) { func.params[i].typ = to_generic_typ + } else { + func.params[i].typ = p.table.unwrap_generic_type_ex(func.params[i].typ, + sym.info.func.generic_names, p.init_generic_types, true) + } + } + if func.params[i].orig_typ.has_flag(.generic) + || p.table.generic_type_names(func.params[i].orig_typ).len > 0 { + if to_generic_typ := p.table.convert_generic_type(func.params[i].orig_typ, + sym.info.func.generic_names, p.init_generic_types) + { + func.params[i].orig_typ = to_generic_typ + } else { + func.params[i].orig_typ = p.table.unwrap_generic_type_ex(func.params[i].orig_typ, + sym.info.func.generic_names, p.init_generic_types, true) + } + } + for generic_name in p.table.generic_type_names(func.params[i].typ) { + if generic_name !in remaining_generic_names { + remaining_generic_names << generic_name } } } + func.generic_names = remaining_generic_names idx = p.table.find_or_register_fn_type(func, false, false) } typ = ast.new_type(idx) diff --git a/vlib/v/tests/generics/generic_closure_return_array_multi_return_regression_test.v b/vlib/v/tests/generics/generic_closure_return_array_multi_return_regression_test.v new file mode 100644 index 000000000..1df6024b3 --- /dev/null +++ b/vlib/v/tests/generics/generic_closure_return_array_multi_return_regression_test.v @@ -0,0 +1,34 @@ +type Issue17876Fn[I, O, R] = fn (I) !(O, R) + +fn issue17876_chain[I, O, R](funcs ...Issue17876Fn[I, O, R]) Issue17876Fn[I, []O, R] { + return fn [funcs] [I, O, R](input I) !([]O, R) { + mut last_input := input + mut results := []O{} + + for function in funcs { + got, remain := function(last_input)! + results << got + last_input = remain + } + + return results, last_input + } +} + +fn issue17876_take_first(input string) !([]string, string) { + if input.len == 0 { + return []string{}, input + } + return [input[..1]], input[1..] +} + +fn test_issue_17876_generic_closure_return_array_multi_return() ! { + results, remain := issue17876_chain[string, []string, string]()('STRING')! + assert results == [][]string{} + assert remain == 'STRING' + + results2, remain2 := + issue17876_chain[string, []string, string](issue17876_take_first)('STRING')! + assert results2 == [['S']] + assert remain2 == 'TRING' +} -- 2.39.5