From da1f22c128a004525a142124f1f29efd1236990d Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 11 Mar 2026 11:40:55 +0300 Subject: [PATCH] checker: cannot return a function from a generic function (fixes #22011) --- vlib/v/ast/table.v | 34 ++++++++------ ...eturn_closure_generic_struct_result_test.v | 44 +++++++++++++++++++ 2 files changed, 64 insertions(+), 14 deletions(-) create mode 100644 vlib/v/tests/generics/return_closure_generic_struct_result_test.v diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 2677682de..bfb795116 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -1933,23 +1933,29 @@ pub fn (mut t Table) convert_generic_type(generic_type Type, generic_names []str FnType { mut func := sym.info.func mut has_generic := false - if func.return_type.has_flag(.generic) { - if typ := t.convert_generic_type(func.return_type, generic_names, to_types) { - func.return_type = typ - if typ.has_flag(.generic) { - has_generic = true - } - } + orig_return_type := func.return_type + if typ := t.convert_generic_type(func.return_type, generic_names, to_types) { + func.return_type = typ + } + if t.sym(func.return_type).kind == .placeholder { + func.return_type = t.unwrap_generic_type_ex(orig_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() for mut param in func.params { - if param.typ.has_flag(.generic) { - if typ := t.convert_generic_param_type(param, generic_names, to_types) { - param.typ = typ - if typ.has_flag(.generic) { - has_generic = true - } - } + orig_param_type := param.typ + if typ := t.convert_generic_param_type(param, generic_names, to_types) { + param.typ = typ + } + if t.sym(param.typ).kind == .placeholder { + param.typ = t.unwrap_generic_type_ex(orig_param_type, generic_names, + to_types, true) + } + 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 otyp := t.convert_generic_type(param.orig_typ, generic_names, to_types) { diff --git a/vlib/v/tests/generics/return_closure_generic_struct_result_test.v b/vlib/v/tests/generics/return_closure_generic_struct_result_test.v new file mode 100644 index 000000000..b5d213102 --- /dev/null +++ b/vlib/v/tests/generics/return_closure_generic_struct_result_test.v @@ -0,0 +1,44 @@ +struct ParserResult[O] { + i []u8 + o O +} + +type Parser[O] = fn ([]u8) !ParserResult[O] + +type Mapper[O] = fn ([]u8) O + +fn take_part(count int) Parser[[]u8] { + return fn [count] (i []u8) !ParserResult[[]u8] { + if i.len < count { + return error('invalid input length') + } + return ParserResult[[]u8]{ + i: i[count..] + o: i[..count] + } + } +} + +fn map_parser[O](p Parser[[]u8], f Mapper[O]) Parser[O] { + return fn [p, f] [O](i []u8) !ParserResult[O] { + r := p(i)! + return ParserResult[O]{ + i: r.i + o: f[O](r.o) + } + } +} + +fn first_byte_value(input []u8) int { + return int(input[0]) +} + +fn test_return_closure_generic_struct_result() { + parser := map_parser[int](take_part(2), first_byte_value) + result := parser('ab_rest'.bytes()) or { + assert false + return + } + assert result.o == int(`a`) + assert result.i == '_rest'.bytes() +} -- 2.39.5