diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 43da13e413..e6d216b526 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -1570,6 +1570,7 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name } } func.name = '' + func.generic_names = [] idx := t.find_or_register_fn_type(func, true, false) if has_generic { return new_type(idx).derive_add_muls(generic_type).set_flag(.generic) @@ -2163,18 +2164,11 @@ pub fn (mut t Table) generic_insts_to_concrete() { } } FnType { - // TODO: Cache function's generic types (parameters and return type) like Struct and Interface etc. do? mut parent_info := parent.info as FnType mut function := parent_info.func - mut generic_types := []Type{cap: function.params.len + 1} - generic_types << function.params.filter(it.typ.has_flag(.generic)).map(it.typ) - if function.return_type.has_flag(.generic) { - generic_types << function.return_type - } - generic_names := t.get_generic_names(generic_types) for mut param in function.params { if param.typ.has_flag(.generic) { - if t_typ := t.resolve_generic_to_concrete(param.typ, generic_names, + if t_typ := t.resolve_generic_to_concrete(param.typ, function.generic_names, info.concrete_types) { param.typ = t_typ @@ -2183,11 +2177,12 @@ pub fn (mut t Table) generic_insts_to_concrete() { } if function.return_type.has_flag(.generic) { if t_typ := t.resolve_generic_to_concrete(function.return_type, - generic_names, info.concrete_types) + function.generic_names, info.concrete_types) { function.return_type = t_typ } } + function.generic_names = [] sym.info = FnType{ ...parent_info func: function diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index 451cf08fb9..2d744d938e 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -229,7 +229,7 @@ pub fn (mut p Parser) parse_multi_return_type() ast.Type { } // given anon name based off signature when `name` is blank -pub fn (mut p Parser) parse_fn_type(name string) ast.Type { +pub fn (mut p Parser) parse_fn_type(name string, generic_types []ast.Type) ast.Type { p.check(.key_fn) for attr in p.attrs { @@ -273,6 +273,7 @@ pub fn (mut p Parser) parse_fn_type(name string) ast.Type { is_variadic: is_variadic return_type: return_type return_type_pos: return_type_pos + generic_names: generic_types.map(p.table.sym(it).name) is_method: false attrs: p.attrs } @@ -543,7 +544,7 @@ pub fn (mut p Parser) parse_any_type(language ast.Language, is_ptr bool, check_d match p.tok.kind { .key_fn { // func - return p.parse_fn_type('') + return p.parse_fn_type('', []ast.Type{}) } .lsbr, .nilsbr { // array diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 0a3d45d525..246d7230b6 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -4013,7 +4013,7 @@ fn (mut p Parser) type_decl() ast.TypeDecl { if p.tok.kind == .key_fn && p.is_fn_type_decl() { // function type: `type mycallback = fn(string, int)` fn_name := p.prepend_mod(name) - fn_type := p.parse_fn_type(fn_name) + fn_type := p.parse_fn_type(fn_name, generic_types) p.table.sym(fn_type).is_pub = is_pub type_pos = type_pos.extend(p.tok.pos()) comments = p.eat_comments(same_line: true) diff --git a/vlib/v/tests/concrete_type_as_generic_type_test.v b/vlib/v/tests/concrete_type_as_generic_fn_type_1_test.v similarity index 72% rename from vlib/v/tests/concrete_type_as_generic_type_test.v rename to vlib/v/tests/concrete_type_as_generic_fn_type_1_test.v index bf046db393..89642344e0 100644 --- a/vlib/v/tests/concrete_type_as_generic_type_test.v +++ b/vlib/v/tests/concrete_type_as_generic_fn_type_1_test.v @@ -1,8 +1,8 @@ -type Fn = fn (T) +type Fn[T] = fn (T) -type FnReturn = fn (T) R +type FnReturn[T, R] = fn (T) R -type FnMultiReturn = fn (I) (O, R) +type FnMultiReturn[I, O, R] = fn (I) (O, R) fn func_fn_concrete() Fn[string] { return fn (_s string) {} @@ -12,12 +12,11 @@ fn func_fn_dynamic[T]() Fn[T] { return fn [T](_t T) {} } -// FIXME: FnReturn[string, string] fails to stencil -// fn func_fn_return_concrete() FnReturn[string, string] { -// return fn (s string) string { -// return s -// } -// } +fn func_fn_return_concrete() FnReturn[string, string] { + return fn (s string) string { + return s + } +} fn func_fn_return_dynamic[T, R]() FnReturn[T, R] { return fn [T, R](t T) R { @@ -35,6 +34,7 @@ fn test_concrete_function_type_as_generic_type() { func_fn_concrete()('V') func_fn_dynamic[string]()('V') + func_fn_return_concrete()('V') assert func_fn_return_dynamic[string, int]()('100') == 100 s1, s2 := func_fn_multi_return_concrete()('VLang') diff --git a/vlib/v/tests/concrete_type_as_generic_fn_type_2_test.v b/vlib/v/tests/concrete_type_as_generic_fn_type_2_test.v new file mode 100644 index 0000000000..fc0915f65f --- /dev/null +++ b/vlib/v/tests/concrete_type_as_generic_fn_type_2_test.v @@ -0,0 +1,33 @@ +struct ParseResult[T] { + result T + rest string +} + +type ParseFunction[T] = fn (string) !ParseResult[T] + +fn literal(l string) ParseFunction[string] { + return fn [l] (input string) !ParseResult[string] { + if !input.starts_with(l) { + return error(input) + } + + return ParseResult[string]{ + result: l + rest: input.all_after_first(l) + } + } +} + +fn test_concrete_function_type_as_generic_type() { + l_func := literal('start') + val1 := l_func('start test') or { ParseResult[string]{} } + val2 := l_func('test') or { ParseResult[string]{} } + + println(val1) + assert val1.result == 'start' + assert val1.rest == ' test' + + println(val2) + assert val2.result == '' + assert val2.rest == '' +}