all: fix registration of methods with the same name on different generic structs

pull/12987/head
Delyan Angelov 2021-12-28 19:42:31 +02:00
parent 730b2a9263
commit 43fee6b3d5
No known key found for this signature in database
GPG Key ID: 66886C0F12D595ED
13 changed files with 103 additions and 18 deletions

View File

@ -26,6 +26,13 @@ pub fn (node &FnDecl) fkey() string {
return node.name return node.name
} }
pub fn (node &Fn) fkey() string {
if node.is_method {
return '${int(node.receiver_type)}.$node.name'
}
return node.name
}
pub fn (node &CallExpr) fkey() string { pub fn (node &CallExpr) fkey() string {
if node.is_method { if node.is_method {
return '${int(node.receiver_type)}.$node.name' return '${int(node.receiver_type)}.$node.name'

View File

@ -89,6 +89,7 @@ pub:
is_main bool // `fn main(){}` is_main bool // `fn main(){}`
is_test bool // `fn test_abc(){}` is_test bool // `fn test_abc(){}`
is_keep_alive bool // passed memory must not be freed (by GC) before function returns is_keep_alive bool // passed memory must not be freed (by GC) before function returns
is_method bool // true for `fn (x T) name()`, and for interface declarations (which are also for methods)
no_body bool // a pure declaration like `fn abc(x int)`; used in .vh files, C./JS. fns. no_body bool // a pure declaration like `fn abc(x int)`; used in .vh files, C./JS. fns.
mod string mod string
file string file string
@ -97,6 +98,7 @@ pub:
return_type_pos token.Position return_type_pos token.Position
pub mut: pub mut:
return_type Type return_type Type
receiver_type Type // != 0, when .is_method == true
name string name string
params []Param params []Param
source_fn voidptr // set in the checker, while processing fn declarations source_fn voidptr // set in the checker, while processing fn declarations
@ -1608,7 +1610,7 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr
} }
if final_concrete_types.len > 0 { if final_concrete_types.len > 0 {
for method in ts.methods { for method in ts.methods {
t.register_fn_concrete_types(method.name, final_concrete_types) t.register_fn_concrete_types(method.fkey(), final_concrete_types)
} }
} }
} }
@ -1730,7 +1732,7 @@ pub fn (mut t Table) generic_insts_to_concrete() {
parent_sym := t.sym(parent_info.parent_type) parent_sym := t.sym(parent_info.parent_type)
for method in parent_sym.methods { for method in parent_sym.methods {
if method.generic_names.len == info.concrete_types.len { if method.generic_names.len == info.concrete_types.len {
t.register_fn_concrete_types(method.name, info.concrete_types) t.register_fn_concrete_types(method.fkey(), info.concrete_types)
} }
} }
} else { } else {

View File

@ -851,7 +851,7 @@ pub fn (mut c Checker) infer_fn_generic_types(func ast.Fn, mut node ast.CallExpr
node.concrete_types << typ node.concrete_types << typ
} }
if c.table.register_fn_concrete_types(func.name, inferred_types) { if c.table.register_fn_concrete_types(func.fkey(), inferred_types) {
c.need_recheck_generic_fns = true c.need_recheck_generic_fns = true
} }
} }

View File

@ -290,12 +290,20 @@ pub fn (mut c Checker) check_files(ast_files []&ast.File) {
c.timers.start('checker_post_process_generic_fns') c.timers.start('checker_post_process_generic_fns')
last_file := c.file last_file := c.file
// post process generic functions. must be done after all files have been // post process generic functions. must be done after all files have been
// checked, to ensure all generic calls are processed as this information // checked, to ensure all generic calls are processed, as this information
// is needed when the generic type is auto inferred from the call argument // is needed when the generic type is auto inferred from the call argument.
// Check more times if there are more new registered fn concrete types // we may have to loop several times, if there were more concrete types found.
mut post_process_generic_fns_iterations := 0
for { for {
$if trace_post_process_generic_fns_loop ? {
eprintln('>>>>>>>>> recheck_generic_fns loop iteration: $post_process_generic_fns_iterations')
}
for file in ast_files { for file in ast_files {
if file.generic_fns.len > 0 { if file.generic_fns.len > 0 {
$if trace_post_process_generic_fns_loop ? {
eprintln('>> file.path: ${file.path:-40} | file.generic_fns:' +
file.generic_fns.map(it.name).str())
}
c.change_current_file(file) c.change_current_file(file)
c.post_process_generic_fns() c.post_process_generic_fns()
} }
@ -304,6 +312,10 @@ pub fn (mut c Checker) check_files(ast_files []&ast.File) {
break break
} }
c.need_recheck_generic_fns = false c.need_recheck_generic_fns = false
post_process_generic_fns_iterations++
}
$if trace_post_process_generic_fns_loop ? {
eprintln('>>>>>>>>> recheck_generic_fns loop done, iteration: $post_process_generic_fns_iterations')
} }
// restore the original c.file && c.mod after post processing // restore the original c.file && c.mod after post processing
c.change_current_file(last_file) c.change_current_file(last_file)

View File

@ -7,6 +7,11 @@ import v.util
import v.token import v.token
fn (mut c Checker) fn_decl(mut node ast.FnDecl) { fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
$if trace_post_process_generic_fns_types ? {
if node.generic_names.len > 0 {
eprintln('>>> post processing node.name: ${node.name:-30} | $node.generic_names <=> $c.table.cur_concrete_types')
}
}
if node.generic_names.len > 0 && c.table.cur_concrete_types.len == 0 { if node.generic_names.len > 0 && c.table.cur_concrete_types.len == 0 {
// Just remember the generic function for now. // Just remember the generic function for now.
// It will be processed later in c.post_process_generic_fns, // It will be processed later in c.post_process_generic_fns,
@ -429,13 +434,14 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
if concrete_types.len > 0 { if concrete_types.len > 0 {
mut no_exists := true mut no_exists := true
if fn_name.contains('.') { if fn_name.contains('.') {
no_exists = c.table.register_fn_concrete_types(fn_name, concrete_types) no_exists = c.table.register_fn_concrete_types(node.fkey(), concrete_types)
} else { } else {
no_exists = c.table.register_fn_concrete_types(c.mod + '.' + fn_name, concrete_types) no_exists = c.table.register_fn_concrete_types(c.mod + '.' + node.fkey(),
concrete_types)
// if the generic fn does not exist in the current fn calling module, continue // if the generic fn does not exist in the current fn calling module, continue
// to look in builtin module // to look in builtin module
if !no_exists { if !no_exists {
no_exists = c.table.register_fn_concrete_types(fn_name, concrete_types) no_exists = c.table.register_fn_concrete_types(node.fkey(), concrete_types)
} }
} }
if no_exists { if no_exists {
@ -579,10 +585,12 @@ pub fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool)
} }
} }
if !found && c.pref.is_vsh { if !found && c.pref.is_vsh {
// TOOD: test this hack more extensively
os_name := 'os.$fn_name' os_name := 'os.$fn_name'
if f := c.table.find_fn(os_name) { if f := c.table.find_fn(os_name) {
if f.generic_names.len == node.concrete_types.len { if f.generic_names.len == node.concrete_types.len {
c.table.fn_generic_types[os_name] = c.table.fn_generic_types['${node.mod}.$node.name'] node_alias_name := node.fkey()
c.table.fn_generic_types[os_name] = c.table.fn_generic_types[node_alias_name]
} }
node.name = os_name node.name = os_name
found = true found = true
@ -1009,7 +1017,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
} }
} }
if concrete_types.len > 0 { if concrete_types.len > 0 {
if c.table.register_fn_concrete_types(node.name, concrete_types) { if c.table.register_fn_concrete_types(node.fkey(), concrete_types) {
c.need_recheck_generic_fns = true c.need_recheck_generic_fns = true
} }
} }
@ -1298,6 +1306,10 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
// no type arguments given in call, attempt implicit instantiation // no type arguments given in call, attempt implicit instantiation
c.infer_fn_generic_types(method, mut node) c.infer_fn_generic_types(method, mut node)
concrete_types = node.concrete_types concrete_types = node.concrete_types
} else {
if node.concrete_types.len > 0 && !node.concrete_types[0].has_flag(.generic) {
c.table.register_fn_concrete_types(method.fkey(), node.concrete_types)
}
} }
// resolve return generics struct to concrete type // resolve return generics struct to concrete type
if method.generic_names.len > 0 && method.return_type.has_flag(.generic) if method.generic_names.len > 0 && method.return_type.has_flag(.generic)
@ -1431,9 +1443,10 @@ fn (mut c Checker) post_process_generic_fns() {
for i in 0 .. c.file.generic_fns.len { for i in 0 .. c.file.generic_fns.len {
mut node := c.file.generic_fns[i] mut node := c.file.generic_fns[i]
c.mod = node.mod c.mod = node.mod
gtypes := c.table.fn_generic_types[node.name] fkey := node.fkey()
gtypes := c.table.fn_generic_types[fkey]
$if trace_post_process_generic_fns ? { $if trace_post_process_generic_fns ? {
eprintln('> post_process_generic_fns $node.mod | $node.name | $gtypes') eprintln('> post_process_generic_fns $node.mod | $node.name | fkey: $fkey | gtypes: $gtypes')
} }
for concrete_types in gtypes { for concrete_types in gtypes {
c.table.cur_concrete_types = concrete_types c.table.cur_concrete_types = concrete_types

View File

@ -222,8 +222,9 @@ fn (mut c Checker) resolve_generic_interface(typ ast.Type, interface_type ast.Ty
} }
// add concrete types to method // add concrete types to method
for imethod in inter_sym.info.methods { for imethod in inter_sym.info.methods {
if inferred_types !in c.table.fn_generic_types[imethod.name] { im_fkey := imethod.fkey()
c.table.fn_generic_types[imethod.name] << inferred_types if inferred_types !in c.table.fn_generic_types[im_fkey] {
c.table.fn_generic_types[im_fkey] << inferred_types
} }
} }
inter_sym.info.concrete_types = inferred_types inter_sym.info.concrete_types = inferred_types

View File

@ -475,6 +475,12 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
b.writeln('\n// THE END.') b.writeln('\n// THE END.')
g.timers.show('cgen common') g.timers.show('cgen common')
res := b.str() res := b.str()
$if trace_all_generic_fn_keys ? {
gkeys := g.table.fn_generic_types.keys()
for gkey in gkeys {
eprintln('>> g.table.fn_generic_types key: $gkey')
}
}
unsafe { b.free() } unsafe { b.free() }
unsafe { g.free_builders() } unsafe { g.free_builders() }
return res return res

View File

@ -163,7 +163,12 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
// } // }
if node.generic_names.len > 0 && g.cur_concrete_types.len == 0 { // need the cur_concrete_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 // loop thru each generic type and generate a function
for concrete_types in g.table.fn_generic_types[node.name] { nkey := node.fkey()
generic_types_by_fn := g.table.fn_generic_types[nkey]
$if trace_post_process_generic_fns ? {
eprintln('>> gen_fn_decl, nkey: $nkey | generic_types_by_fn: $generic_types_by_fn')
}
for concrete_types in generic_types_by_fn {
if g.pref.is_verbose { if g.pref.is_verbose {
syms := concrete_types.map(g.table.sym(it)) syms := concrete_types.map(g.table.sym(it))
the_type := syms.map(it.name).join(', ') the_type := syms.map(it.name).join(', ')

View File

@ -569,7 +569,7 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl, typ FnGenType) {
node := it node := it
if node.generic_names.len > 0 && g.cur_concrete_types.len == 0 { // need the cur_concrete_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 // loop thru each generic type and generate a function
for concrete_types in g.table.fn_generic_types[node.name] { for concrete_types in g.table.fn_generic_types[node.fkey()] {
if g.pref.is_verbose { if g.pref.is_verbose {
syms := concrete_types.map(g.table.sym(it)) syms := concrete_types.map(g.table.sym(it))
the_type := syms.map(it.name).join(', ') the_type := syms.map(it.name).join(', ')

View File

@ -404,6 +404,8 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
is_main: is_main is_main: is_main
is_test: is_test is_test: is_test
is_keep_alive: is_keep_alive is_keep_alive: is_keep_alive
is_method: true
receiver_type: rec.typ
// //
attrs: p.attrs attrs: p.attrs
is_conditional: conditional_ctdefine_idx != -1 is_conditional: conditional_ctdefine_idx != -1
@ -452,6 +454,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
is_main: is_main is_main: is_main
is_test: is_test is_test: is_test
is_keep_alive: is_keep_alive is_keep_alive: is_keep_alive
is_method: false
// //
attrs: p.attrs attrs: p.attrs
is_conditional: conditional_ctdefine_idx != -1 is_conditional: conditional_ctdefine_idx != -1
@ -531,7 +534,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
label_names: p.label_names label_names: p.label_names
} }
if generic_names.len > 0 { if generic_names.len > 0 {
p.table.register_fn_generic_types(name) p.table.register_fn_generic_types(fn_decl.fkey())
} }
p.label_names = [] p.label_names = []
p.close_scope() p.close_scope()
@ -679,6 +682,7 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
params: args params: args
is_variadic: is_variadic is_variadic: is_variadic
return_type: return_type return_type: return_type
is_method: false
} }
name := 'anon_fn_${p.unique_prefix}_${p.table.fn_type_signature(func)}_$p.tok.pos' name := 'anon_fn_${p.unique_prefix}_${p.table.fn_type_signature(func)}_$p.tok.pos'
keep_fn_name := p.cur_fn_name keep_fn_name := p.cur_fn_name

View File

@ -252,6 +252,7 @@ pub fn (mut p Parser) parse_fn_type(name string) ast.Type {
is_variadic: is_variadic is_variadic: is_variadic
return_type: return_type return_type: return_type
return_type_pos: return_type_pos return_type_pos: return_type_pos
is_method: false
} }
// MapFooFn typedefs are manually added in cheaders.v // MapFooFn typedefs are manually added in cheaders.v
// because typedefs get generated after the map struct is generated // because typedefs get generated after the map struct is generated

View File

@ -584,6 +584,8 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
return_type: method.return_type return_type: method.return_type
is_variadic: is_variadic is_variadic: is_variadic
is_pub: true is_pub: true
is_method: true
receiver_type: typ
} }
ts.register_method(tmethod) ts.register_method(tmethod)
info.methods << tmethod info.methods << tmethod

View File

@ -0,0 +1,32 @@
import datatypes
import math.mathutil
struct Foo {
a int
}
struct Bar<T> {
a int
}
fn (b Bar<T>) pop() {}
fn test_bar_foo_works_even_when_datatypes_is_imported_that_also_has_pop_methods() {
mut a := Bar<Foo>{}
println(a)
assert true
}
fn test_datatypes_can_be_used_without_interfering_with_local_generic_structs() {
mut stack := datatypes.Stack<int>{}
stack.push(1)
println(stack)
assert true
}
fn test_generic_type_inference_on_generic_function_from_another_module_still_works() {
x := -123
a := mathutil.abs(x)
assert x == -123
assert a == 123
}