diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index a470dbf737..a4e59df410 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -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 diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index c204a64180..c43cb7e0a0 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -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 + } } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index f8fdf70791..47e48dd458 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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' { diff --git a/vlib/v/tests/generics_with_cascaded_multiple_nested_generics_fn_test.v b/vlib/v/tests/generics_with_cascaded_multiple_nested_generics_fn_test.v new file mode 100644 index 0000000000..89d93a6c39 --- /dev/null +++ b/vlib/v/tests/generics_with_cascaded_multiple_nested_generics_fn_test.v @@ -0,0 +1,35 @@ +fn gfn1(var T) T { + return var +} + +fn gfn2(var T) T { + return gfn1(var) +} + +fn gfn3(var T) T { + return gfn2(var) +} + +fn gfn4(var T) T { + return gfn3(var) +} + +// don't give concrete types +fn gfn2_infer(var T) T { + return gfn1(var) +} + +fn gfn3_infer(var T) T { + return gfn2_infer(var) +} + +fn gfn4_infer(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 +}