mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
checker: add a hard to reach limit of 1_000_000 iterations for resolving all generics (usually <10 are enough) (#18477)
This commit is contained in:
parent
9c5aeb62b2
commit
017cc6738b
@ -20,6 +20,8 @@ const (
|
||||
expr_level_cutoff_limit = 40
|
||||
stmt_level_cutoff_limit = 40
|
||||
iface_level_cutoff_limit = 100
|
||||
generic_fn_cutoff_limit_per_fn = 10_000 // how many times post_process_generic_fns, can visit the same function before bailing out
|
||||
generic_fn_postprocess_iterations_cutoff_limit = 1000_000 // how many times the compiler will try to resolve all remaining generic functions
|
||||
)
|
||||
|
||||
pub const (
|
||||
@ -323,7 +325,7 @@ pub fn (mut c Checker) check_files(ast_files []&ast.File) {
|
||||
// is needed when the generic type is auto inferred from the call argument.
|
||||
// we may have to loop several times, if there were more concrete types found.
|
||||
mut post_process_generic_fns_iterations := 0
|
||||
for {
|
||||
post_process_iterations_loop: for post_process_generic_fns_iterations <= checker.generic_fn_postprocess_iterations_cutoff_limit {
|
||||
$if trace_post_process_generic_fns_loop ? {
|
||||
eprintln('>>>>>>>>> recheck_generic_fns loop iteration: ${post_process_generic_fns_iterations}')
|
||||
}
|
||||
@ -334,7 +336,7 @@ pub fn (mut c Checker) check_files(ast_files []&ast.File) {
|
||||
file.generic_fns.map(it.name).str())
|
||||
}
|
||||
c.change_current_file(file)
|
||||
c.post_process_generic_fns()
|
||||
c.post_process_generic_fns() or { break post_process_iterations_loop }
|
||||
}
|
||||
}
|
||||
if !c.need_recheck_generic_fns {
|
||||
|
@ -2210,16 +2210,23 @@ fn (mut c Checker) set_node_expected_arg_types(mut node ast.CallExpr, func &ast.
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut c Checker) post_process_generic_fns() {
|
||||
fn (mut c Checker) post_process_generic_fns() ! {
|
||||
mut all_generic_fns := map[string]int{}
|
||||
// Loop thru each generic function concrete type.
|
||||
// Check each specific fn instantiation.
|
||||
for i in 0 .. c.file.generic_fns.len {
|
||||
mut node := c.file.generic_fns[i]
|
||||
c.mod = node.mod
|
||||
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',
|
||||
node.pos)
|
||||
return error('fkey: ${fkey}')
|
||||
}
|
||||
gtypes := c.table.fn_generic_types[fkey]
|
||||
$if trace_post_process_generic_fns ? {
|
||||
eprintln('> post_process_generic_fns ${node.mod} | ${node.name} | fkey: ${fkey} | gtypes: ${gtypes}')
|
||||
eprintln('> post_process_generic_fns ${node.mod} | ${node.name} | fkey: ${fkey} | gtypes: ${gtypes} | c.file.generic_fns.len: ${c.file.generic_fns.len}')
|
||||
}
|
||||
for concrete_types in gtypes {
|
||||
c.table.cur_concrete_types = concrete_types
|
||||
|
12
vlib/v/checker/tests/generic_fn_infinite_loop_limit_err.out
Normal file
12
vlib/v/checker/tests/generic_fn_infinite_loop_limit_err.out
Normal file
@ -0,0 +1,12 @@
|
||||
vlib/v/checker/tests/generic_fn_infinite_loop_limit_err.vv:6:2: warning: unused variable: `ff1`
|
||||
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:1:1: error: generic function visited more than 10000 times
|
||||
1 | fn f1[T](x T, i int) T {
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~
|
||||
2 | return x
|
||||
3 | }
|
@ -0,0 +1,8 @@
|
||||
fn f1[T](x T, i int) T {
|
||||
return x
|
||||
}
|
||||
|
||||
fn main() {
|
||||
ff1 := f1 // <-- missing e.g. `[int]`
|
||||
// ff1 := f1[int] <-- is a valid usage with generic return types that are not generic structs
|
||||
}
|
Loading…
Reference in New Issue
Block a user