1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

ast, parser: fix generic fntype to concrete types (fix #17982) (#18025)

This commit is contained in:
yuyi
2023-04-23 08:44:16 +08:00
committed by GitHub
parent cd90bc65b8
commit d8167b8966
5 changed files with 50 additions and 21 deletions

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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')

View File

@ -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 == ''
}