Skip to content

Commit

Permalink
checker: support nested labeled for statements (vlang#21658)
Browse files Browse the repository at this point in the history
  • Loading branch information
spytheman authored Jun 9, 2024
1 parent dfc0c91 commit 1af7b7c
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 24 deletions.
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
}

0 comments on commit 1af7b7c

Please sign in to comment.