From 86619338dd2955e1d58a8a365409221e386a00fe Mon Sep 17 00:00:00 2001 From: SheatNoisette <45624871+SheatNoisette@users.noreply.github.com> Date: Sat, 3 Jan 2026 08:11:41 +0100 Subject: [PATCH] wasm: fix infinite loop using continue in C-like for loops (#26250) --- vlib/v/gen/wasm/gen.v | 29 +++++--- vlib/v/gen/wasm/tests/control_flow.vv | 54 ++++++++++++++ vlib/v/gen/wasm/tests/control_flow.vv.out | 8 +- vlib/v/gen/wasm/tests/labeled_for_inc.vv | 78 ++++++++++++++++++++ vlib/v/gen/wasm/tests/labeled_for_inc.vv.out | 9 +++ 5 files changed, 165 insertions(+), 13 deletions(-) create mode 100644 vlib/v/gen/wasm/tests/labeled_for_inc.vv create mode 100644 vlib/v/gen/wasm/tests/labeled_for_inc.vv.out diff --git a/vlib/v/gen/wasm/gen.v b/vlib/v/gen/wasm/gen.v index bd6f2d80c..d3abcc9e7 100644 --- a/vlib/v/gen/wasm/gen.v +++ b/vlib/v/gen/wasm/gen.v @@ -1093,26 +1093,31 @@ pub fn (mut g Gen) expr_stmt(node ast.Stmt, expected ast.Type) { loop := g.func.c_loop([], []) { - g.loop_breakpoint_stack << LoopBreakpoint{ - c_continue: loop - c_break: block - name: node.label - } + continue_block := g.func.c_block([], []) + { + g.loop_breakpoint_stack << LoopBreakpoint{ + c_continue: continue_block + c_break: block + name: node.label + } - if node.has_cond { - g.expr(node.cond, ast.bool_type) - g.func.eqz(.i32_t) - g.func.c_br_if(block) // !cond, goto end - } + if node.has_cond { + g.expr(node.cond, ast.bool_type) + g.func.eqz(.i32_t) + g.func.c_br_if(block) // !cond, goto end + } - g.expr_stmts(node.stmts, ast.void_type) + g.expr_stmts(node.stmts, ast.void_type) + + g.loop_breakpoint_stack.pop() + } + g.func.c_end(continue_block) if node.has_inc { g.expr_stmt(node.inc, ast.void_type) } g.func.c_br(loop) - g.loop_breakpoint_stack.pop() } g.func.c_end(loop) } diff --git a/vlib/v/gen/wasm/tests/control_flow.vv b/vlib/v/gen/wasm/tests/control_flow.vv index 19d32a89d..94838bfab 100644 --- a/vlib/v/gen/wasm/tests/control_flow.vv +++ b/vlib/v/gen/wasm/tests/control_flow.vv @@ -94,6 +94,51 @@ fn infcfor() int { return 0 } +fn continuecfor() int { + mut val := 0 + + for i := 0; i < 10; i += 2 { + if i == 6 { + continue + } + val += i + } + return val +} + +fn continuecfor_multiple() int { + mut val := 0 + + for i := 0; i < 10; i += 2 { + if i == 6 { + continue + } + // Two continues + if i == 2 { + continue + } + val += i + } + return val +} + +fn continuec_twofor_multiple() int { + mut val := 0 + + for i := 0; i < 10; i += 2 { + if i == 6 { + continue + } + for j := -12; j < 10; j += 1 { + if j == 6 { + continue + } + val += i + } + } + return val +} + fn main() { println('--- func()') println(func(10, true)) @@ -121,4 +166,13 @@ fn main() { println('--- infcfor()') println(infcfor()) + + println('--- continuecfor()') + println(continuecfor()) + + println('--- continuecfor_multiple()') + println(continuecfor_multiple()) + + println('--- continuec_twofor_multiple()') + println(continuec_twofor_multiple()) } diff --git a/vlib/v/gen/wasm/tests/control_flow.vv.out b/vlib/v/gen/wasm/tests/control_flow.vv.out index f990171f8..4c0fa8112 100644 --- a/vlib/v/gen/wasm/tests/control_flow.vv.out +++ b/vlib/v/gen/wasm/tests/control_flow.vv.out @@ -16,4 +16,10 @@ 100 99 --- infcfor() -10 \ No newline at end of file +10 +--- continuecfor() +14 +--- continuecfor_multiple() +12 +--- continuec_twofor_multiple() +294 \ No newline at end of file diff --git a/vlib/v/gen/wasm/tests/labeled_for_inc.vv b/vlib/v/gen/wasm/tests/labeled_for_inc.vv new file mode 100644 index 000000000..f7ab4119d --- /dev/null +++ b/vlib/v/gen/wasm/tests/labeled_for_inc.vv @@ -0,0 +1,78 @@ +fn labeled_break_continue_with_inc() int { + // Should not infinite loop + outer: for i := 4; true; i++ { + for { + if i < 7 { + continue outer + } else { + break outer + } + } + } + return 0 +} + +fn labeled_break_with_inc() int { + mut sum := 0 + outer: for i := 0; i < 10; i++ { + for j := 0; j < 5; j++ { + sum += j + if i == 3 && j == 2 { + break outer + } + } + if sum == 10 { + return i + } + } + return sum +} + +fn triple_nested_labeled_continue() (int, int) { + mut val1 := 0 + mut val2 := 0 + outer: for i := 0; i < 5; i++ { + middle: for j := 0; j < 3; j++ { + for k := 0; k < 2; k++ { + if i == 1 && j == 1 { + continue outer + } + val2 += 1 + if i == 2 && j == 2 { + continue middle + } + val1 += 1 + } + } + } + return val1, val2 +} + +fn labeled_continue_deep_nest() int { + mut result := 0 + outer: for i := 0; i < 3; i++ { + for j := 0; j < 2; j++ { + for k := 0; k < 2; k++ { + if k == 1 && i < 2 { + result++ + continue outer + } + result++ + } + } + } + return result +} + +fn main() { + println('--- labeled_break_continue_with_inc()') + println(labeled_break_continue_with_inc()) + println('--- labeled_break_with_inc()') + println(labeled_break_with_inc()) + println('--- triple_nested_labeled_continue()') + a, b := triple_nested_labeled_continue() + println(a) + println(b) + println('--- labeled_continue_deep_nest()') + println(labeled_continue_deep_nest()) +} diff --git a/vlib/v/gen/wasm/tests/labeled_for_inc.vv.out b/vlib/v/gen/wasm/tests/labeled_for_inc.vv.out new file mode 100644 index 000000000..dbffa6be6 --- /dev/null +++ b/vlib/v/gen/wasm/tests/labeled_for_inc.vv.out @@ -0,0 +1,9 @@ +--- labeled_break_continue_with_inc() +0 +--- labeled_break_with_inc() +0 +--- triple_nested_labeled_continue() +24 +25 +--- labeled_continue_deep_nest() +8 \ No newline at end of file -- 2.39.5