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:
parent
730b2a9263
commit
43fee6b3d5
@ -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'
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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(', ')
|
||||
|
@ -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(', ')
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
32
vlib/v/tests/generics_method_on_generic_structs_test.v
Normal file
32
vlib/v/tests/generics_method_on_generic_structs_test.v
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user