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

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

This commit is contained in:
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
}
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 {
if node.is_method {
return '${int(node.receiver_type)}.$node.name'

View File

@ -89,6 +89,7 @@ pub:
is_main bool // `fn main(){}`
is_test bool // `fn test_abc(){}`
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.
mod string
file string
@ -97,6 +98,7 @@ pub:
return_type_pos token.Position
pub mut:
return_type Type
receiver_type Type // != 0, when .is_method == true
name string
params []Param
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 {
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)
for method in parent_sym.methods {
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 {

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

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')
last_file := c.file
// post process generic functions. must be done after all files have been
// checked, to ensure all generic calls are processed as this information
// 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
// checked, to ensure all generic calls are processed, as this information
// is needed when the generic type is auto inferred from the call argument.
// we may have to loop several times, if there were more concrete types found.
mut post_process_generic_fns_iterations := 0
for {
$if trace_post_process_generic_fns_loop ? {
eprintln('>>>>>>>>> recheck_generic_fns loop iteration: $post_process_generic_fns_iterations')
}
for file in ast_files {
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.post_process_generic_fns()
}
@ -304,6 +312,10 @@ pub fn (mut c Checker) check_files(ast_files []&ast.File) {
break
}
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
c.change_current_file(last_file)

View File

@ -7,6 +7,11 @@ import v.util
import v.token
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 {
// Just remember the generic function for now.
// 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 {
mut no_exists := true
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 {
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
// to look in builtin module
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 {
@ -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 {
// TOOD: test this hack more extensively
os_name := 'os.$fn_name'
if f := c.table.find_fn(os_name) {
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
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 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
}
}
@ -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
c.infer_fn_generic_types(method, mut node)
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
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 {
mut node := c.file.generic_fns[i]
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 ? {
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 {
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
for imethod in inter_sym.info.methods {
if inferred_types !in c.table.fn_generic_types[imethod.name] {
c.table.fn_generic_types[imethod.name] << inferred_types
im_fkey := imethod.fkey()
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

View File

@ -475,6 +475,12 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
b.writeln('\n// THE END.')
g.timers.show('cgen common')
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 { g.free_builders() }
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
// 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 {
syms := concrete_types.map(g.table.sym(it))
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
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 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 {
syms := concrete_types.map(g.table.sym(it))
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_test: is_test
is_keep_alive: is_keep_alive
is_method: true
receiver_type: rec.typ
//
attrs: p.attrs
is_conditional: conditional_ctdefine_idx != -1
@ -452,6 +454,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
is_main: is_main
is_test: is_test
is_keep_alive: is_keep_alive
is_method: false
//
attrs: p.attrs
is_conditional: conditional_ctdefine_idx != -1
@ -531,7 +534,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
label_names: p.label_names
}
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.close_scope()
@ -679,6 +682,7 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
params: args
is_variadic: is_variadic
return_type: return_type
is_method: false
}
name := 'anon_fn_${p.unique_prefix}_${p.table.fn_type_signature(func)}_$p.tok.pos'
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
return_type: return_type
return_type_pos: return_type_pos
is_method: false
}
// MapFooFn typedefs are manually added in cheaders.v
// 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
is_variadic: is_variadic
is_pub: true
is_method: true
receiver_type: typ
}
ts.register_method(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
}