Skip to content

Commit

Permalink
ast, parser: fix generic fntype to concrete types (fix #17982) (#18025)
Browse files Browse the repository at this point in the history
  • Loading branch information
yuyi98 committed Apr 23, 2023
1 parent cd90bc6 commit d8167b8
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 21 deletions.
13 changes: 4 additions & 9 deletions vlib/v/ast/table.v
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
5 changes: 3 additions & 2 deletions vlib/v/parser/parse_type.v
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/parser/parser.v
Expand Up @@ -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)
Expand Down
@@ -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) {}
Expand All @@ -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 {
Expand All @@ -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')
Expand Down
33 changes: 33 additions & 0 deletions 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 == ''
}

0 comments on commit d8167b8

Please sign in to comment.