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

pull/9975/head
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

@ -87,6 +87,7 @@ mut:
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
}