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

checker: fix generics with cascaded multi nested generic fn (fix #3815) (#9965)

This commit is contained in:
yuyi 2021-05-03 00:30:57 +08:00 committed by GitHub
parent ae22967d1d
commit 581fe375cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 63 additions and 12 deletions

View File

@ -932,13 +932,14 @@ pub fn (t &Table) mktyp(typ Type) Type {
}
}
pub fn (mut t Table) register_fn_concrete_types(fn_name string, types []Type) {
pub fn (mut t Table) register_fn_concrete_types(fn_name string, types []Type) bool {
mut a := t.fn_generic_types[fn_name]
if types in a {
return
return false
}
a << types
t.fn_generic_types[fn_name] = a
return true
}
// TODO: there is a bug when casting sumtype the other way if its pointer

View File

@ -571,5 +571,7 @@ pub fn (mut c Checker) infer_fn_generic_types(f ast.Fn, mut call_expr ast.CallEx
inferred_types << typ
call_expr.concrete_types << typ
}
c.table.register_fn_concrete_types(f.name, inferred_types)
if c.table.register_fn_concrete_types(f.name, inferred_types) {
c.need_recheck_generic_fns = true
}
}

View File

@ -83,10 +83,11 @@ mut:
main_fn_decl_node ast.FnDecl
match_exhaustive_cutoff_limit int = 10
// TODO: these are here temporarily and used for deprecations; remove soon
using_new_err_struct bool
inside_selector_expr bool
inside_println_arg bool
inside_decl_rhs bool
using_new_err_struct bool
inside_selector_expr bool
inside_println_arg bool
inside_decl_rhs bool
need_recheck_generic_fns bool // need recheck generic fns because there are cascaded nested generic fn
}
pub fn new_checker(table &ast.Table, pref &pref.Preferences) Checker {
@ -197,8 +198,8 @@ pub fn (mut c Checker) check_files(ast_files []ast.File) {
// post process generic functions. must be done after all files have been
// checked, to eunsure all generic calls are processed as this information
// is needed when the generic type is auto inferred from the call argument
// Check 2 times (in order to check nested generics fn)
for _ in 0 .. 2 {
// Check more times if there are more new registered fn concrete types
for {
for i in 0 .. ast_files.len {
file := unsafe { &ast_files[i] }
if file.generic_fns.len > 0 {
@ -206,6 +207,12 @@ pub fn (mut c Checker) check_files(ast_files []ast.File) {
c.post_process_generic_fns()
}
}
if c.need_recheck_generic_fns {
c.need_recheck_generic_fns = false
continue
} else {
break
}
}
// restore the original c.file && c.mod after post processing
c.change_current_file(last_file)
@ -1657,7 +1664,9 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type {
}
}
if has_generic {
c.table.register_fn_concrete_types(call_expr.name, concrete_types)
if c.table.register_fn_concrete_types(call_expr.name, concrete_types) {
c.need_recheck_generic_fns = true
}
}
// TODO: remove this for actual methods, use only for compiler magic
// FIXME: Argument count != 1 will break these
@ -2117,11 +2126,15 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
}
}
if has_generic {
mut no_exists := true
if c.mod != '' && !fn_name.contains('.') {
// Need to prepend the module when adding a generic type to a function
c.table.register_fn_concrete_types(c.mod + '.' + fn_name, concrete_types)
no_exists = c.table.register_fn_concrete_types(c.mod + '.' + fn_name, concrete_types)
} else {
c.table.register_fn_concrete_types(fn_name, concrete_types)
no_exists = c.table.register_fn_concrete_types(fn_name, concrete_types)
}
if no_exists {
c.need_recheck_generic_fns = true
}
}
if fn_name == 'json.encode' {

View File

@ -0,0 +1,35 @@
fn gfn1<T>(var T) T {
return var
}
fn gfn2<T>(var T) T {
return gfn1<T>(var)
}
fn gfn3<T>(var T) T {
return gfn2<T>(var)
}
fn gfn4<T>(var T) T {
return gfn3<T>(var)
}
// don't give concrete types
fn gfn2_infer<T>(var T) T {
return gfn1(var)
}
fn gfn3_infer<T>(var T) T {
return gfn2_infer(var)
}
fn gfn4_infer<T>(var T) T {
return gfn3_infer(var)
}
fn test_generics_with_cascaded_multiple_nested_generics_fn() {
println(gfn4(1234))
assert gfn4(1234) == 1234
println(gfn4_infer(1234))
assert gfn4_infer(1234) == 1234
}