From 744ac80d3623353722245e29854383465f5c194b Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Mon, 4 Nov 2024 17:03:07 -0300 Subject: [PATCH] checker: fix missing info about generic fn var usage without concrete types (fix #22733, #22734) (#22743) --- vlib/v/checker/checker.v | 15 +++++++++++-- vlib/v/checker/fn.v | 10 ++++++--- .../generic_fn_infinite_loop_limit_err.out | 19 ++++++++++++----- .../tests/generic_fn_var_unresolved_err.out | 21 +++++++++++++++++++ .../tests/generic_fn_var_unresolved_err.vv | 12 +++++++++++ 5 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 vlib/v/checker/tests/generic_fn_var_unresolved_err.out create mode 100644 vlib/v/checker/tests/generic_fn_var_unresolved_err.vv diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 118d8705f..23c04857d 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -121,8 +121,9 @@ mut: is_last_stmt bool prevent_sum_type_unwrapping_once bool // needed for assign new values to sum type, stopping unwrapping then using_new_err_struct bool - need_recheck_generic_fns bool // need recheck generic fns because there are cascaded nested generic fn - inside_sql bool // to handle sql table fields pseudo variables + need_recheck_generic_fns bool // need recheck generic fns because there are cascaded nested generic fn + generic_fns map[string]bool // register generic fns that needs recheck once + inside_sql bool // to handle sql table fields pseudo variables inside_selector_expr bool inside_interface_deref bool inside_decl_rhs bool @@ -3833,6 +3834,10 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type { info := node.info as ast.IdentFn if func := c.table.find_fn(node.name) { if func.generic_names.len > 0 { + if node.concrete_types.len == 0 { + c.error('`${node.name}` is a generic fn, you should pass its concrete types, e.g. ${node.name}[int]', + node.pos) + } concrete_types := node.concrete_types.map(c.unwrap_generic(it)) if concrete_types.all(!it.has_flag(.generic)) { c.table.register_fn_concrete_types(func.fkey(), concrete_types) @@ -4006,6 +4011,12 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type { } // Non-anon-function object (not a call), e.g. `onclick(my_click)` if func := c.table.find_fn(name) { + if func.generic_names.len > 0 { + if node.concrete_types.len == 0 { + c.error('`${node.name}` is a generic fn, you should pass its concrete types, e.g. ${node.name}[int]', + node.pos) + } + } return c.resolve_var_fn(func, mut node, name) } } diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 4b73e75c6..ac058e1e8 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -38,8 +38,12 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) { // This is done so that all generic function calls can // have a chance to populate c.table.fn_generic_types with // the correct concrete types. - c.file.generic_fns << node - c.need_recheck_generic_fns = true + fkey := node.fkey() + if !c.generic_fns[fkey] { + c.need_recheck_generic_fns = true + c.generic_fns[fkey] = true + c.file.generic_fns << node + } return } node.ninstances++ @@ -2764,7 +2768,7 @@ fn (mut c Checker) post_process_generic_fns() ! { fkey := node.fkey() all_generic_fns[fkey]++ if all_generic_fns[fkey] > generic_fn_cutoff_limit_per_fn { - c.error('generic function visited more than ${generic_fn_cutoff_limit_per_fn} times', + c.error('${fkey} generic function visited more than ${generic_fn_cutoff_limit_per_fn} times', node.pos) return error('fkey: ${fkey}') } diff --git a/vlib/v/checker/tests/generic_fn_infinite_loop_limit_err.out b/vlib/v/checker/tests/generic_fn_infinite_loop_limit_err.out index baff17394..3f5257a4f 100644 --- a/vlib/v/checker/tests/generic_fn_infinite_loop_limit_err.out +++ b/vlib/v/checker/tests/generic_fn_infinite_loop_limit_err.out @@ -5,6 +5,20 @@ vlib/v/checker/tests/generic_fn_infinite_loop_limit_err.vv:6:2: warning: unused | ~~~ 7 | // ff1 := f1[int] <-- is a valid usage with generic return types that are not generic structs 8 | } +vlib/v/checker/tests/generic_fn_infinite_loop_limit_err.vv:6:9: error: `f1` is a generic fn, you should pass its concrete types, e.g. f1[int] + 4 | + 5 | fn main() { + 6 | ff1 := f1 // <-- missing e.g. `[int]` + | ~~ + 7 | // ff1 := f1[int] <-- is a valid usage with generic return types that are not generic structs + 8 | } +vlib/v/checker/tests/generic_fn_infinite_loop_limit_err.vv:6:9: error: `f1` is a generic fn, you should pass its concrete types, e.g. f1[int] + 4 | + 5 | fn main() { + 6 | ff1 := f1 // <-- missing e.g. `[int]` + | ~~ + 7 | // ff1 := f1[int] <-- is a valid usage with generic return types that are not generic structs + 8 | } vlib/v/checker/tests/generic_fn_infinite_loop_limit_err.vv:6:9: error: cannot assign `f1` as a generic function variable 4 | 5 | fn main() { @@ -12,8 +26,3 @@ vlib/v/checker/tests/generic_fn_infinite_loop_limit_err.vv:6:9: error: cannot as | ~~ 7 | // ff1 := f1[int] <-- is a valid usage with generic return types that are not generic structs 8 | } -vlib/v/checker/tests/generic_fn_infinite_loop_limit_err.vv:1:1: error: generic function visited more than 10000 times - 1 | fn f1[T](x T, i int) T { - | ~~~~~~~~~~~~~~~~~~~~~~ - 2 | return x - 3 | } diff --git a/vlib/v/checker/tests/generic_fn_var_unresolved_err.out b/vlib/v/checker/tests/generic_fn_var_unresolved_err.out new file mode 100644 index 000000000..ab8e4f700 --- /dev/null +++ b/vlib/v/checker/tests/generic_fn_var_unresolved_err.out @@ -0,0 +1,21 @@ +vlib/v/checker/tests/generic_fn_var_unresolved_err.vv:8:7: error: `func` is a generic fn, you should pass its concrete types, e.g. func[int] + 6 | + 7 | fn main() { + 8 | _ := func + | ~~~~ + 9 | _ := { + 10 | 'test': func +vlib/v/checker/tests/generic_fn_var_unresolved_err.vv:8:7: error: `func` is a generic fn, you should pass its concrete types, e.g. func[int] + 6 | + 7 | fn main() { + 8 | _ := func + | ~~~~ + 9 | _ := { + 10 | 'test': func +vlib/v/checker/tests/generic_fn_var_unresolved_err.vv:10:11: error: `func` is a generic fn, you should pass its concrete types, e.g. func[int] + 8 | _ := func + 9 | _ := { + 10 | 'test': func + | ~~~~ + 11 | } + 12 | } diff --git a/vlib/v/checker/tests/generic_fn_var_unresolved_err.vv b/vlib/v/checker/tests/generic_fn_var_unresolved_err.vv new file mode 100644 index 000000000..c9e25c012 --- /dev/null +++ b/vlib/v/checker/tests/generic_fn_var_unresolved_err.vv @@ -0,0 +1,12 @@ +module main + +fn func[T](arg string, mut val T) int { + return 2 +} + +fn main() { + _ := func + _ := { + 'test': func + } +} -- 2.39.5