all: fix registration of methods with the same name on different generic structs
parent
730b2a9263
commit
43fee6b3d5
|
@ -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'
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(', ')
|
||||||
|
|
|
@ -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(', ')
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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