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
@ -14,12 +14,14 @@ import v.errors
|
|||||||
import v.pkgconfig
|
import v.pkgconfig
|
||||||
|
|
||||||
const (
|
const (
|
||||||
int_min = int(0x80000000)
|
int_min = int(0x80000000)
|
||||||
int_max = int(0x7FFFFFFF)
|
int_max = int(0x7FFFFFFF)
|
||||||
// prevent stack overflows by restricting too deep recursion:
|
// prevent stack overflows by restricting too deep recursion:
|
||||||
expr_level_cutoff_limit = 40
|
expr_level_cutoff_limit = 40
|
||||||
stmt_level_cutoff_limit = 40
|
stmt_level_cutoff_limit = 40
|
||||||
iface_level_cutoff_limit = 100
|
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 (
|
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.
|
// 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.
|
// we may have to loop several times, if there were more concrete types found.
|
||||||
mut post_process_generic_fns_iterations := 0
|
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 ? {
|
$if trace_post_process_generic_fns_loop ? {
|
||||||
eprintln('>>>>>>>>> recheck_generic_fns loop iteration: ${post_process_generic_fns_iterations}')
|
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())
|
file.generic_fns.map(it.name).str())
|
||||||
}
|
}
|
||||||
c.change_current_file(file)
|
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 {
|
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.
|
// Loop thru each generic function concrete type.
|
||||||
// Check each specific fn instantiation.
|
// Check each specific fn instantiation.
|
||||||
for i in 0 .. c.file.generic_fns.len {
|
for i in 0 .. c.file.generic_fns.len {
|
||||||
mut node := c.file.generic_fns[i]
|
mut node := c.file.generic_fns[i]
|
||||||
c.mod = node.mod
|
c.mod = node.mod
|
||||||
fkey := node.fkey()
|
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]
|
gtypes := c.table.fn_generic_types[fkey]
|
||||||
$if trace_post_process_generic_fns ? {
|
$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 {
|
for concrete_types in gtypes {
|
||||||
c.table.cur_concrete_types = concrete_types
|
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…
x
Reference in New Issue
Block a user