mirror of
https://github.com/vlang/v.git
synced 2025-09-13 17:36:52 -04:00
checker: support nested labeled for statements (#21658)
This commit is contained in:
parent
dfc0c91295
commit
1af7b7c3de
@ -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
|
||||
@ -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
|
||||
@ -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) {
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
@ -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)
|
||||
@ -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)
|
||||
@ -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{}
|
||||
|
@ -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
vlib/v/tests/nested_fors_with_labels_test.v
Normal file
91
vlib/v/tests/nested_fors_with_labels_test.v
Normal file
@ -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…
x
Reference in New Issue
Block a user