From 0bcb955258bc5bafc4e804391ec64a2d22f14911 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sun, 25 Jul 2021 10:38:37 +0300 Subject: [PATCH] checker: fix regression of anon fns that have loops with break/continue --- vlib/v/checker/checker.v | 34 ++++++++++++++----- vlib/v/checker/tests/goto_label.out | 14 ++++++++ .../nested_anonfunc_and_for_break_test.v | 16 +++++++++ 3 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 vlib/v/tests/nested_anonfunc_and_for_break_test.v diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index e7cb960b83..3ec8b8a283 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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 } diff --git a/vlib/v/checker/tests/goto_label.out b/vlib/v/checker/tests/goto_label.out index a2591ad85b..9312085af5 100644 --- a/vlib/v/checker/tests/goto_label.out +++ b/vlib/v/checker/tests/goto_label.out @@ -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: diff --git a/vlib/v/tests/nested_anonfunc_and_for_break_test.v b/vlib/v/tests/nested_anonfunc_and_for_break_test.v new file mode 100644 index 0000000000..6e3fdd28fc --- /dev/null +++ b/vlib/v/tests/nested_anonfunc_and_for_break_test.v @@ -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 +}