checker: fix regression of anon fns that have loops with break/continue

pull/10952/head^2
Delyan Angelov 2021-07-25 10:38:37 +03:00
parent 744a753b47
commit 0bcb955258
No known key found for this signature in database
GPG Key ID: 66886C0F12D595ED
3 changed files with 55 additions and 9 deletions

View File

@ -4609,7 +4609,7 @@ fn (mut c Checker) branch_stmt(node ast.BranchStmt) {
if c.inside_defer {
c.error('`$node.kind.str()` is not allowed in defer statements', node.pos)
}
if c.in_for_count == 0 || c.inside_anon_fn {
if c.in_for_count == 0 {
c.error('$node.kind.str() statement not within a loop', node.pos)
}
if node.label.len > 0 {
@ -7798,13 +7798,6 @@ fn (mut c Checker) evaluate_once_comptime_if_attribute(mut a ast.Attr) bool {
}
fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
c.fn_level++
defer {
c.fn_level--
}
//
c.returns = false
if node.generic_names.len > 0 && c.table.cur_concrete_types.len == 0 {
// Just remember the generic function for now.
// It will be processed later in c.post_process_generic_fns,
@ -7815,6 +7808,30 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
c.file.generic_fns << node
return
}
// save all the state that fn_decl or inner statements/expressions
// could potentially modify, since functions can be nested, due to
// anonymous function support, and ensure that it is restored, when
// fn_decl returns:
prev_fn_scope := c.fn_scope
prev_in_for_count := c.in_for_count
prev_inside_defer := c.inside_defer
prev_inside_unsafe := c.inside_unsafe
prev_inside_anon_fn := c.inside_anon_fn
prev_returns := c.returns
c.fn_level++
c.in_for_count = 0
c.inside_defer = false
c.inside_unsafe = false
c.returns = false
defer {
c.fn_level--
c.returns = prev_returns
c.inside_anon_fn = prev_inside_anon_fn
c.inside_unsafe = prev_inside_unsafe
c.inside_defer = prev_inside_defer
c.in_for_count = prev_in_for_count
c.fn_scope = prev_fn_scope
}
// Check generics fn/method without generic type parameters
mut need_generic_names := false
if node.generic_names.len == 0 {
@ -8041,7 +8058,6 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
}
}
}
c.returns = false
node.source_file = c.file
}

View File

@ -19,6 +19,20 @@ vlib/v/checker/tests/goto_label.vv:8:8: error: unknown label `f2`
| ~~
9 | goto a1
10 | a1:
vlib/v/checker/tests/goto_label.vv:9:8: error: `goto` requires `unsafe` (consider using labelled break/continue)
7 | goto f1
8 | goto f2
9 | goto a1
| ~~
10 | a1:
11 | goto a1
vlib/v/checker/tests/goto_label.vv:11:8: error: `goto` requires `unsafe` (consider using labelled break/continue)
9 | goto a1
10 | a1:
11 | goto a1
| ~~
12 | }
13 | f2:
vlib/v/checker/tests/goto_label.vv:14:7: error: unknown label `a1`
12 | }
13 | f2:

View File

@ -0,0 +1,16 @@
fn test_anon_functions_can_have_loops_with_breaks() {
mut res := 123
for true {
x := fn () int {
for x in 0 .. 10 {
println(x)
break
}
return 3
}()
println('$x')
res = x
break
}
assert res == 3
}