Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

checker: support nested labeled for statements #21658

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions vlib/v/checker/checker.v
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ mut:
cur_orm_ts ast.TypeSymbol
cur_anon_fn &ast.AnonFn = unsafe { nil }
vmod_file_content string // needed for @VMOD_FILE, contents of the file, *NOT its path**
loop_label string // set when inside a labelled for loop
loop_labels []string // filled, when inside labelled for loops: `a_label: for x in 0..10 {`
vweb_gen_types []ast.Type // vweb route checks
timers &util.Timers = util.get_timers()
comptime_info_stack []comptime.ComptimeInfo // stores the values from the above on each $for loop, to make nesting them easier
Expand Down Expand Up @@ -183,7 +183,7 @@ fn (mut c Checker) reset_checker_state_at_start_of_new_file() {
c.inside_sql = false
c.cur_orm_ts = ast.TypeSymbol{}
c.prevent_sum_type_unwrapping_once = false
c.loop_label = ''
c.loop_labels = []
c.using_new_err_struct = false
c.inside_selector_expr = false
c.inside_interface_deref = false
Expand Down Expand Up @@ -2015,16 +2015,15 @@ fn (mut c Checker) check_enum_field_integer_literal(expr ast.IntegerLiteral, is_
}

@[inline]
fn (mut c Checker) check_loop_label(label string, pos token.Pos) {
fn (mut c Checker) check_loop_labels(label string, pos token.Pos) {
if label == '' {
// ignore
return
}
if c.loop_label.len != 0 {
c.error('nesting of labelled `for` loops is not supported', pos)
if label in c.loop_labels {
c.error('the loop label was already defined before', pos)
return
}
c.loop_label = label
c.loop_labels << label
}

fn (mut c Checker) stmt(mut node ast.Stmt) {
Expand Down Expand Up @@ -2231,7 +2230,7 @@ fn (mut c Checker) branch_stmt(node ast.BranchStmt) {
}
}
if node.label.len > 0 {
if node.label != c.loop_label {
if node.label !in c.loop_labels {
c.error('invalid label name `${node.label}`', node.pos)
}
}
Expand Down
18 changes: 9 additions & 9 deletions vlib/v/checker/for.v
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import v.token

fn (mut c Checker) for_c_stmt(mut node ast.ForCStmt) {
c.in_for_count++
prev_loop_label := c.loop_label
prev_loop_labels := c.loop_labels
if node.has_init {
c.stmt(mut node.init)
}
Expand All @@ -22,15 +22,15 @@ fn (mut c Checker) for_c_stmt(mut node ast.ForCStmt) {
}
c.stmt(mut node.inc)
}
c.check_loop_label(node.label, node.pos)
c.check_loop_labels(node.label, node.pos)
c.stmts(mut node.stmts)
c.loop_label = prev_loop_label
c.loop_labels = prev_loop_labels
c.in_for_count--
}

fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) {
c.in_for_count++
prev_loop_label := c.loop_label
prev_loop_labels := c.loop_labels
mut typ := c.expr(mut node.cond)
if node.key_var.len > 0 && node.key_var != '_' {
c.check_valid_snake_case(node.key_var, 'variable name', node.pos)
Expand Down Expand Up @@ -259,15 +259,15 @@ fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) {
}
}
}
c.check_loop_label(node.label, node.pos)
c.check_loop_labels(node.label, node.pos)
c.stmts(mut node.stmts)
c.loop_label = prev_loop_label
c.loop_labels = prev_loop_labels
c.in_for_count--
}

fn (mut c Checker) for_stmt(mut node ast.ForStmt) {
c.in_for_count++
prev_loop_label := c.loop_label
prev_loop_labels := c.loop_labels
c.expected_type = ast.bool_type
if node.cond !is ast.EmptyExpr {
typ := c.expr(mut node.cond)
Expand All @@ -288,9 +288,9 @@ fn (mut c Checker) for_stmt(mut node ast.ForStmt) {
}
// TODO: update loop var type
// how does this work currently?
c.check_loop_label(node.label, node.pos)
c.check_loop_labels(node.label, node.pos)
c.stmts(mut node.stmts)
c.loop_label = prev_loop_label
c.loop_labels = prev_loop_labels
c.in_for_count--
if c.smartcast_mut_pos != token.Pos{} {
c.smartcast_mut_pos = token.Pos{}
Expand Down
7 changes: 0 additions & 7 deletions vlib/v/checker/tests/labelled_break_continue.out
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,3 @@ vlib/v/checker/tests/labelled_break_continue.vv:16:10: error: invalid label name
| ~~~~~
17 | }
18 | }
vlib/v/checker/tests/labelled_break_continue.vv:21:11: error: nesting of labelled `for` loops is not supported
19 | // check nested loops (not supported ATM)
20 | L3: for ;; i++ {
21 | L4: for {
| ^
22 | if i < 17 {continue L3}
23 | else {break L3}
91 changes: 91 additions & 0 deletions vlib/v/tests/nested_fors_with_labels_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
const good = [
'> x: 0 | y: 0 | z: 0',
'> x: 0 | y: 0 | z: 1',
'> x: 0 | y: 1 | z: 0',
'> x: 0 | y: 1 | z: 1',
'> x: 1 | y: 0 | z: 0',
'> x: 1 | y: 0 | z: 1',
'> x: 1 | y: 1 | z: 0',
'> x: 1 | y: 1 | z: 1',
]

fn test_labeled_nested_loops_for_in() {
mut values := []string{}
abc: for x in 0 .. 2 {
def: for y in 0 .. 5 {
if y > 1 {
continue abc
}
xyz: for z in 0 .. 10 {
if z > 1 {
continue def
}
values << '> x: ${x} | y: ${y} | z: ${z}'
}
}
}
assert values == good
}

fn test_labeled_nested_loops_for_c_style() {
mut values := []string{}
abc: for x := 0; x < 2; x++ {
def: for y := 0; y < 5; y++ {
if y > 1 {
continue abc
}
xyz: for z := 0; z < 10; z++ {
if z > 1 {
continue def
}
values << '> x: ${x} | y: ${y} | z: ${z}'
}
}
}
assert values == good
}

fn test_labeled_nested_loops_for_in_array() {
mut values := []string{}
x_array := [0, 1]
y_array := [0, 1, 2, 3, 4]
z_array := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
abc: for x in x_array {
def: for y in y_array {
if y > 1 {
continue abc
}
xyz: for z in y_array {
if z > 1 {
continue def
}
values << '> x: ${x} | y: ${y} | z: ${z}'
}
}
}
assert values == good
}

fn test_labeled_nested_loops_for_condition() {
mut values := []string{}
mut x := -1
abc: for x < 1 {
x++
mut y := -1
def: for y < 5 {
y++
if y > 1 {
continue abc
}
mut z := -1
xyz: for z < 10 {
z++
if z > 1 {
continue def
}
values << '> x: ${x} | y: ${y} | z: ${z}'
}
}
}
assert values == good
}
Loading