all: cleanup generics call_expr (#9856)

pull/9862/head
yuyi 2021-04-24 14:44:15 +08:00 committed by GitHub
parent a832bb609a
commit 8ccdae6188
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 99 additions and 98 deletions

View File

@ -420,8 +420,8 @@ pub mut:
receiver_type Type // User
return_type Type
should_be_skipped bool
generic_types []Type
generic_list_pos token.Position
concrete_types []Type // concrete types, e.g. <int, string>
concrete_list_pos token.Position
free_receiver bool // true if the receiver expression needs to be freed
scope &Scope
from_embed_type Type // holds the type of the embed that the method is called from

View File

@ -479,8 +479,8 @@ pub fn (mut c Checker) infer_fn_generic_types(f ast.Fn, mut call_expr ast.CallEx
mut inferred_types := []ast.Type{}
for gi, gt_name in f.generic_names {
// skip known types
if gi < call_expr.generic_types.len {
inferred_types << call_expr.generic_types[gi]
if gi < call_expr.concrete_types.len {
inferred_types << call_expr.concrete_types[gi]
continue
}
mut typ := ast.void_type
@ -569,7 +569,7 @@ pub fn (mut c Checker) infer_fn_generic_types(f ast.Fn, mut call_expr ast.CallEx
println('inferred `$f.name<$s>`')
}
inferred_types << typ
call_expr.generic_types << typ
call_expr.concrete_types << typ
}
c.table.register_fn_generic_types(f.name, inferred_types)
}

View File

@ -1430,17 +1430,17 @@ fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ ast.Type, call_exp
}
}
fn (mut c Checker) check_return_generics_struct(return_type ast.Type, mut call_expr ast.CallExpr, generic_types []ast.Type) {
fn (mut c Checker) check_return_generics_struct(return_type ast.Type, mut call_expr ast.CallExpr, concrete_types []ast.Type) {
rts := c.table.get_type_symbol(return_type)
if rts.info is ast.Struct {
if rts.info.is_generic {
mut nrt := '$rts.name<'
mut c_nrt := '${rts.name}_T_'
for i in 0 .. call_expr.generic_types.len {
gts := c.table.get_type_symbol(call_expr.generic_types[i])
for i in 0 .. call_expr.concrete_types.len {
gts := c.table.get_type_symbol(call_expr.concrete_types[i])
nrt += gts.name
c_nrt += gts.name
if i != call_expr.generic_types.len - 1 {
if i != call_expr.concrete_types.len - 1 {
nrt += ','
c_nrt += '_'
}
@ -1452,18 +1452,18 @@ fn (mut c Checker) check_return_generics_struct(return_type ast.Type, mut call_e
call_expr.return_type = ast.new_type(idx).derive(return_type)
} else {
mut fields := rts.info.fields.clone()
if rts.info.generic_types.len == generic_types.len {
if rts.info.generic_types.len == concrete_types.len {
generic_names := rts.info.generic_types.map(c.table.get_type_symbol(it).name)
for i, _ in fields {
if t_typ := c.table.resolve_generic_to_concrete(fields[i].typ,
generic_names, generic_types)
generic_names, concrete_types)
{
fields[i].typ = t_typ
}
}
mut info := rts.info
info.is_generic = false
info.concrete_types = generic_types.clone()
info.concrete_types = concrete_types.clone()
info.parent_type = return_type
info.fields = fields
stru_idx := c.table.register_type_symbol(ast.TypeSymbol{
@ -1499,18 +1499,18 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type {
if left_type_sym.kind in [.sum_type, .interface_] && method_name == 'type_name' {
return ast.string_type
}
mut has_generic_generic := false // x.foo<T>() instead of x.foo<int>()
mut generic_types := []ast.Type{}
for generic_type in call_expr.generic_types {
if generic_type.has_flag(.generic) {
has_generic_generic = true
generic_types << c.unwrap_generic(generic_type)
mut has_generic := false // x.foo<T>() instead of x.foo<int>()
mut concrete_types := []ast.Type{}
for concrete_type in call_expr.concrete_types {
if concrete_type.has_flag(.generic) {
has_generic = true
concrete_types << c.unwrap_generic(concrete_type)
} else {
generic_types << generic_type
concrete_types << concrete_type
}
}
if has_generic_generic {
c.table.register_fn_generic_types(call_expr.name, generic_types)
if has_generic {
c.table.register_fn_generic_types(call_expr.name, concrete_types)
}
// TODO: remove this for actual methods, use only for compiler magic
// FIXME: Argument count != 1 will break these
@ -1625,7 +1625,7 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type {
return method.return_type
}
if method.generic_names.len > 0 && method.return_type.has_flag(.generic) {
c.check_return_generics_struct(method.return_type, mut call_expr, generic_types)
c.check_return_generics_struct(method.return_type, mut call_expr, concrete_types)
} else {
call_expr.return_type = method.return_type
}
@ -1749,24 +1749,24 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type {
} else {
call_expr.receiver_type = method.params[0].typ
}
if method.generic_names.len != call_expr.generic_types.len {
if method.generic_names.len != call_expr.concrete_types.len {
// no type arguments given in call, attempt implicit instantiation
c.infer_fn_generic_types(method, mut call_expr)
}
if call_expr.generic_types.len > 0 && method.return_type != 0 {
if call_expr.concrete_types.len > 0 && method.return_type != 0 {
if typ := c.table.resolve_generic_to_concrete(method.return_type, method.generic_names,
call_expr.generic_types)
call_expr.concrete_types)
{
call_expr.return_type = typ
return typ
}
}
if call_expr.generic_types.len > 0 && method.generic_names.len == 0 {
c.error('a non generic function called like a generic one', call_expr.generic_list_pos)
if call_expr.concrete_types.len > 0 && method.generic_names.len == 0 {
c.error('a non generic function called like a generic one', call_expr.concrete_list_pos)
}
if call_expr.generic_types.len > method.generic_names.len {
c.error('too many generic parameters got $call_expr.generic_types.len, expected $method.generic_names.len',
call_expr.generic_list_pos)
if call_expr.concrete_types.len > method.generic_names.len {
c.error('too many generic parameters got $call_expr.concrete_types.len, expected $method.generic_names.len',
call_expr.concrete_list_pos)
}
if method.generic_names.len > 0 {
return call_expr.return_type
@ -1960,22 +1960,22 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
// TODO: impl typeof properly (probably not going to be a fn call)
return ast.string_type
}
mut has_generic_generic := false // foo<T>() instead of foo<int>()
mut generic_types := []ast.Type{}
for generic_type in call_expr.generic_types {
if generic_type.has_flag(.generic) {
has_generic_generic = true
generic_types << c.unwrap_generic(generic_type)
mut has_generic := false // foo<T>() instead of foo<int>()
mut concrete_types := []ast.Type{}
for concrete_type in call_expr.concrete_types {
if concrete_type.has_flag(.generic) {
has_generic = true
concrete_types << c.unwrap_generic(concrete_type)
} else {
generic_types << generic_type
concrete_types << concrete_type
}
}
if has_generic_generic {
if has_generic {
if c.mod != '' && !fn_name.starts_with('${c.mod}.') {
// Need to prepend the module when adding a generic type to a function
c.table.register_fn_generic_types(c.mod + '.' + fn_name, generic_types)
c.table.register_fn_generic_types(c.mod + '.' + fn_name, concrete_types)
} else {
c.table.register_fn_generic_types(fn_name, generic_types)
c.table.register_fn_generic_types(fn_name, concrete_types)
}
}
if fn_name == 'json.encode' {
@ -2061,7 +2061,7 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
if c.pref.is_script && !found {
os_name := 'os.$fn_name'
if f := c.table.find_fn(os_name) {
if f.generic_names.len == call_expr.generic_types.len {
if f.generic_names.len == call_expr.concrete_types.len {
c.table.fn_generic_types[os_name] = c.table.fn_generic_types['${call_expr.mod}.$call_expr.name']
}
call_expr.name = os_name
@ -2111,16 +2111,16 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
&& !func.is_unsafe {
c.error('cannot call a function that does not have a body', call_expr.pos)
}
for generic_type in call_expr.generic_types {
c.ensure_type_exists(generic_type, call_expr.generic_list_pos) or {}
for concrete_type in call_expr.concrete_types {
c.ensure_type_exists(concrete_type, call_expr.concrete_list_pos) or {}
}
if func.generic_names.len > 0 && call_expr.args.len == 0 && call_expr.generic_types.len == 0 {
if func.generic_names.len > 0 && call_expr.args.len == 0 && call_expr.concrete_types.len == 0 {
c.error('no argument generic function must add concrete types, e.g. foo<int>()',
call_expr.pos)
return func.return_type
}
if func.generic_names.len > 0 && func.return_type.has_flag(.generic) {
c.check_return_generics_struct(func.return_type, mut call_expr, generic_types)
c.check_return_generics_struct(func.return_type, mut call_expr, concrete_types)
} else {
call_expr.return_type = func.return_type
}
@ -2259,7 +2259,7 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
call_arg.pos)
}
}
if func.generic_names.len != call_expr.generic_types.len {
if func.generic_names.len != call_expr.concrete_types.len {
// no type arguments given in call, attempt implicit instantiation
c.infer_fn_generic_types(func, mut call_expr)
}
@ -2273,9 +2273,10 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
c.expected_type = param.typ
typ := c.check_expr_opt_call(call_arg.expr, c.expr(call_arg.expr))
if param.typ.has_flag(.generic) && func.generic_names.len == call_expr.generic_types.len {
if param.typ.has_flag(.generic)
&& func.generic_names.len == call_expr.concrete_types.len {
if unwrap_typ := c.table.resolve_generic_to_concrete(param.typ, func.generic_names,
call_expr.generic_types)
call_expr.concrete_types)
{
c.check_expected_call_arg(typ, unwrap_typ, call_expr.language) or {
c.error('$err.msg in argument ${i + 1} to `$fn_name`', call_arg.pos)
@ -2284,21 +2285,21 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
}
}
}
if call_expr.generic_types.len > 0 && func.return_type != 0 {
if call_expr.concrete_types.len > 0 && func.return_type != 0 {
if typ := c.table.resolve_generic_to_concrete(func.return_type, func.generic_names,
call_expr.generic_types)
call_expr.concrete_types)
{
call_expr.return_type = typ
return typ
}
}
if call_expr.generic_types.len > 0 && func.generic_names.len == 0 {
c.error('a non generic function called like a generic one', call_expr.generic_list_pos)
if call_expr.concrete_types.len > 0 && func.generic_names.len == 0 {
c.error('a non generic function called like a generic one', call_expr.concrete_list_pos)
}
if call_expr.generic_types.len > func.generic_names.len {
c.error('too many generic parameters got $call_expr.generic_types.len, expected $func.generic_names.len',
call_expr.generic_list_pos)
if call_expr.concrete_types.len > func.generic_names.len {
c.error('too many generic parameters got $call_expr.concrete_types.len, expected $func.generic_names.len',
call_expr.concrete_list_pos)
}
if func.generic_names.len > 0 {
return call_expr.return_type

View File

@ -1599,11 +1599,11 @@ pub fn (mut f Fmt) call_expr(node ast.CallExpr) {
}
fn (mut f Fmt) write_generic_if_require(node ast.CallExpr) {
if node.generic_types.len > 0 {
if node.concrete_types.len > 0 {
f.write('<')
for i, generic_type in node.generic_types {
is_last := i == node.generic_types.len - 1
f.write(f.table.type_to_str(generic_type))
for i, concrete_type in node.concrete_types {
is_last := i == node.concrete_types.len - 1
f.write(f.table.type_to_str(concrete_type))
if !is_last {
f.write(', ')
}

View File

@ -113,7 +113,7 @@ mut:
hotcode_fn_names []string
embedded_files []ast.EmbeddedFile
cur_fn ast.FnDecl
cur_generic_types []ast.Type // `int`, `string`, etc in `foo<T>()`
cur_concrete_types []ast.Type // current concrete types, e.g. <int, string>
sql_i int
sql_stmt_name string
sql_bind_name string
@ -3202,7 +3202,7 @@ fn (mut g Gen) expr(node ast.Expr) {
fn (mut g Gen) type_name(type_ ast.Type) {
mut typ := type_
if typ.has_flag(.generic) {
typ = g.cur_generic_types[0]
typ = g.cur_concrete_types[0]
}
s := g.table.type_to_str(typ)
g.write('_SLIT("${util.strip_main_name(s)}")')
@ -5945,14 +5945,14 @@ fn (mut g Gen) go_expr(node ast.GoExpr) {
mut expr := node.call_expr
mut name := expr.name // util.no_dots(expr.name)
// TODO: fn call is duplicated. merge with fn_call().
for i, generic_type in expr.generic_types {
if generic_type != ast.void_type && generic_type != 0 {
for i, concrete_type in expr.concrete_types {
if concrete_type != ast.void_type && concrete_type != 0 {
// Using _T_ to differentiate between get<string> and get_string
// `foo<int>()` => `foo_T_int()`
if i == 0 {
name += '_T'
}
name += '_' + g.typ(generic_type)
name += '_' + g.typ(concrete_type)
}
}
if expr.is_method {

View File

@ -127,18 +127,18 @@ fn (mut g Gen) gen_fn_decl(node ast.FnDecl, skip bool) {
// if g.fileis('vweb.v') {
// println('\ngen_fn_decl() $node.name $node.is_generic $g.cur_generic_type')
// }
if node.generic_names.len > 0 && g.cur_generic_types.len == 0 { // need the cur_generic_type check to avoid inf. recursion
if node.generic_names.len > 0 && g.cur_concrete_types.len == 0 { // need the cur_concrete_type check to avoid inf. recursion
// loop thru each generic type and generate a function
for generic_types in g.table.fn_generic_types[node.name] {
for concrete_types in g.table.fn_generic_types[node.name] {
if g.pref.is_verbose {
syms := generic_types.map(g.table.get_type_symbol(it))
syms := concrete_types.map(g.table.get_type_symbol(it))
the_type := syms.map(node.name).join(', ')
println('gen fn `$node.name` for type `$the_type`')
}
g.cur_generic_types = generic_types
g.cur_concrete_types = concrete_types
g.gen_fn_decl(node, skip)
}
g.cur_generic_types = []
g.cur_concrete_types = []
return
}
cur_fn_save := g.cur_fn
@ -173,12 +173,12 @@ fn (mut g Gen) gen_fn_decl(node ast.FnDecl, skip bool) {
name = c_name(name)
}
mut type_name := g.typ(node.return_type)
if g.cur_generic_types.len > 0 {
if g.cur_concrete_types.len > 0 {
// foo<T>() => foo_T_int(), foo_T_string() etc
// Using _T_ to differentiate between get<string> and get_string
name += '_T'
for generic_type in g.cur_generic_types {
gen_name := g.typ(generic_type)
for concrete_type in g.cur_concrete_types {
gen_name := g.typ(concrete_type)
name += '_' + gen_name
}
}
@ -477,7 +477,7 @@ fn (mut g Gen) call_expr(node ast.CallExpr) {
pub fn (mut g Gen) unwrap_generic(typ ast.Type) ast.Type {
if typ.has_flag(.generic) {
if t_typ := g.table.resolve_generic_to_concrete(typ, g.cur_fn.generic_names, g.cur_generic_types) {
if t_typ := g.table.resolve_generic_to_concrete(typ, g.cur_fn.generic_names, g.cur_concrete_types) {
return t_typ
}
}
@ -654,14 +654,14 @@ fn (mut g Gen) method_call(node ast.CallExpr) {
}
}
}
for i, generic_type in node.generic_types {
if generic_type != ast.void_type && generic_type != 0 {
for i, concrete_type in node.concrete_types {
if concrete_type != ast.void_type && concrete_type != 0 {
// Using _T_ to differentiate between get<string> and get_string
// `foo<int>()` => `foo_T_int()`
if i == 0 {
name += '_T'
}
name += '_' + g.typ(generic_type)
name += '_' + g.typ(concrete_type)
}
}
// TODO2
@ -832,13 +832,13 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
panic('cgen: obf name "$key" not found, this should never happen')
}
}
for i, generic_type in node.generic_types {
for i, concrete_type in node.concrete_types {
// Using _T_ to differentiate between get<string> and get_string
// `foo<int>()` => `foo_T_int()`
if i == 0 {
name += '_T'
}
name += '_' + g.typ(generic_type)
name += '_' + g.typ(concrete_type)
}
// TODO2
// cgen shouldn't modify ast nodes, this should be moved
@ -1072,7 +1072,7 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
g.write('/*af arg*/' + name)
}
} else {
if node.generic_types.len > 0 && arg.expr.is_auto_deref_var() && !arg.is_mut
if node.concrete_types.len > 0 && arg.expr.is_auto_deref_var() && !arg.is_mut
&& !expected_types[i].is_ptr() {
g.write('*')
}
@ -1104,7 +1104,7 @@ fn (mut g Gen) call_args(node ast.CallExpr) {
varg_type_name := g.table.type_to_str(varg_type)
for i, fn_gen_name in fn_def.generic_names {
if fn_gen_name == varg_type_name {
arr_info.elem_type = node.generic_types[i]
arr_info.elem_type = node.concrete_types[i]
break
}
}

View File

@ -26,27 +26,27 @@ pub fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr
p.expecting_type = true // Makes name_expr() parse the type `User` in `json.decode(User, txt)`
or_kind = .block
}
//
old_expr_mod := p.expr_mod
defer {
p.expr_mod = old_expr_mod
}
p.expr_mod = ''
//
mut generic_types := []ast.Type{}
mut generic_list_pos := p.tok.position()
mut concrete_types := []ast.Type{}
mut concrete_list_pos := p.tok.position()
if p.tok.kind == .lt {
// `foo<int>(10)`
p.expr_mod = ''
generic_types = p.parse_generic_type_list()
generic_list_pos = generic_list_pos.extend(p.prev_tok.position())
concrete_types = p.parse_generic_type_list()
concrete_list_pos = concrete_list_pos.extend(p.prev_tok.position())
// In case of `foo<T>()`
// T is unwrapped and registered in the checker.
full_generic_fn_name := if fn_name.contains('.') { fn_name } else { p.prepend_mod(fn_name) }
has_generic_generic := generic_types.filter(it.has_flag(.generic)).len > 0
has_generic_generic := concrete_types.filter(it.has_flag(.generic)).len > 0
if !has_generic_generic {
// will be added in checker
p.table.register_fn_generic_types(full_generic_fn_name, generic_types)
p.table.register_fn_generic_types(full_generic_fn_name, concrete_types)
}
}
p.check(.lpar)
@ -95,8 +95,8 @@ pub fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr
mod: p.mod
pos: pos
language: language
generic_types: generic_types
generic_list_pos: generic_list_pos
concrete_types: concrete_types
concrete_list_pos: concrete_list_pos
or_block: ast.OrExpr{
stmts: or_stmts
kind: or_kind

View File

@ -2229,18 +2229,18 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
}
// Method call
// TODO move to fn.v call_expr()
mut generic_types := []ast.Type{}
mut generic_list_pos := p.tok.position()
mut concrete_types := []ast.Type{}
mut concrete_list_pos := p.tok.position()
if is_generic_call {
// `g.foo<int>(10)`
generic_types = p.parse_generic_type_list()
generic_list_pos = generic_list_pos.extend(p.prev_tok.position())
concrete_types = p.parse_generic_type_list()
concrete_list_pos = concrete_list_pos.extend(p.prev_tok.position())
// In case of `foo<T>()`
// T is unwrapped and registered in the checker.
has_generic_generic := generic_types.filter(it.has_flag(.generic)).len > 0
has_generic_generic := concrete_types.filter(it.has_flag(.generic)).len > 0
if !has_generic_generic {
// will be added in checker
p.table.register_fn_generic_types(field_name, generic_types)
p.table.register_fn_generic_types(field_name, concrete_types)
}
}
if p.tok.kind == .lpar {
@ -2280,8 +2280,8 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
name_pos: name_pos
pos: pos
is_method: true
generic_types: generic_types
generic_list_pos: generic_list_pos
concrete_types: concrete_types
concrete_list_pos: concrete_list_pos
or_block: ast.OrExpr{
stmts: or_stmts
kind: or_kind