You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`BREAK` and `CONTINUE` were lexer-reserved tokens with no parser
production consuming them — any use produced a syntax error. Surfaced
by standards#284 (TS->AS port) which had to restructure several
loops into combined-guard / sentinel-boolean forms to work around it.
Now wired through the full pipeline:
- **ast.ml**: `ExprBreak of Span.t`, `ExprContinue of Span.t`.
- **parser.mly**: `BREAK`/`CONTINUE` added to `expr_assign` next to
`RETURN`/`RESUME`. Diverging prefix expressions, no operand role.
- **resolve.ml**: pass-through (no name resolution needed). Touched
both `resolve_expr` and `lower_expr` arms.
- **typecheck.ml**: synth returns `ty_never` (matching `ExprReturn`).
Loop-context check via new `ctx.in_loop : mutable bool` flipped on
`StmtWhile`/`StmtFor` body entry and restored on exit. Misuse
outside a loop yields a new `NotInLoop of string` type-error with a
message naming the keyword and citing #459.
- **always_diverges**: both new cases return true (a loop-body `break`
exits the body normally, no value produced — same diverging shape
as `return`).
- **borrow.ml**: pass-through in span lookup, visit-recurse, free-var
collection, and the main checker (no expression carried, no borrow
state mutated).
- **quantity.ml**, **effect_sites.ml**: pass-through (no resources
used, no call sites involved).
- **codegen_deno.ml**, **js_codegen.ml**: statement-position emission
lowers to bare JS `break;` / `continue;`. Expression-position IIFE
fallback is a defensive stub — legal AffineScript places these
inside a loop body, so the statement path (`gen_stmt_expr`) is what
fires. The wasm/GC/other-backend codegens fall through their
existing wildcard arms; full backend support is out of scope for
this PR (file separately if a non-JS target needs it).
Regression fixture `tests/codegen-deno/loop_break_continue.affine`
+ harness exercise 14 assertions across:
- `while` + `break` (threshold-driven early exit)
- `while` + `continue` (skip-evens accumulator)
- `for` + `break` (find-first-match index)
- `for` + `continue` (count-positive filter)
- Edge cases: break on first iteration, no-break path, empty array
Verified:
- `./tools/run_codegen_deno_tests.sh`: 15/15 harnesses green
- `dune test`: 352/352 unit tests green
- Misuse check: `break;` outside a loop emits the new `NotInLoop`
error with the expected message ("`break` used outside a loop body
(#459). `break` and `continue` must be lexically enclosed by a
`while` or `for` loop.").
Closes#459
Refs hyperpolymath/standards#284 (workarounds documented in the
"Seam findings" section that surfaced this gap)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
0 commit comments