## Remaining `for` iteration statements

**Priority:** P2
**Status:** open — classic `for` and declaration `for…of` are implemented;
`for…in` and assignment-target iteration remain open.
Admitting those remaining forms is a language change that needs formal
language-design approval first
([DESIGN.md §12](../../doc/DESIGN.md#12-preserve-harmless-javascript-conventions))

The `for…of` implementation plan builds on [PR #3589](https://github.com/functionalscript/functionalscript/pull/3589)
and its [fold proposal](../../doc/sergey-shandar/for-of.md). The implementation PRs
should follow this TODO through the [stack workflow](../../doc/SESSION.md).

### Problem

The compiler admits classic `for (init; condition; step)` and `for (;;)`
through the implemented [loop contract](../control-flow/loops/README.md).
The declaration `for…of` implementation follows the plan below. This issue
continues to own the remaining iteration forms.
The generic syntax AST can represent a form before FunctionalScript admits it;
the dependency graph needs that boundary even for modules that cannot compile.
They were the most common first refusal:
at `442ea8f`, 43 of the 279 refused modules stop at a `for`, 18 of them among
the 98 the self-hosting loader depends on
([where self-hosting stands](../../nanvm/lib/todo/mvp-roadmap.md#where-self-hosting-stands)).
At that revision, the authored `module.f.mjs` files held 126 `for` statements
in 60 files, in three forms:

| Count | Form | Example |
| ---: | --- | --- |
| 93 | `for…of` | `for (const item of items) { … }` (`fjs/edag/value/array`) |
| 30 | a counting loop | `for (let i = 0; i < source.length; i++) { … }` (`fjs/compiler/tokenizer`) |
| 3 | `for (;;)` | `for (;;) { … }` (`fjs/js/tokenizer`) |

Many of their bodies use what general loops add — `break`, `continue`, a
`return` inside the loop, more than one variable — and every form lowers to a
loop, so the forms build on the completed
[loop integration](../../fjs/compiler/architecture.md#loop-integration).

### Proposal

Each form is surface syntax over the admitted general `while`, with no new
EDAG node.

- Implemented: `for (;;) { … }` is `while (true) { … }`.
- Implemented: `for (let i = a; c; u) { … }` is `let i = a; while (c) { …; u }`, with
  `continue` running `u` before the test and `i` scoped to the loop.
  JavaScript gives each iteration a fresh `i`, which a function captures; the
  write-based [capture rule](../bindings/variables/README.md#capture-compatibility) refuses a
  capture of a reassigned variable, so that difference is refused rather than
  answered differently. Compound assignments such as `i += 1` are admitted;
  `i++` and `i--` remain part of [assignments](./3430-assignments.md).
  Named `let` declarations without initializers start at primitive `undefined`,
  as they do outside a header ([variables](../bindings/variables/README.md#declarations-without-initializers)).
  Empty clauses, initialized `const` declarations, assignment and call
  initializers, and assignment and call steps use the same lowering.
- `for…of` lowers to the fold helper proposed in
  [PR #3589](https://github.com/functionalscript/functionalscript/pull/3589),
  using existing array spread, calls and structured loops. Its input kinds are
  arrays and strings, matching the existing spread contract. The detailed
  implementation plan is below.
- `for…in` remains separate open work. This plan does not admit it.

### `for…of` implementation plan

#### Input and iteration

Evaluate the source expression once at loop entry, then materialize it with
`const items = [...source]`, as in the [fold proposal](../../doc/sergey-shandar/for-of.md).
Use the existing [array/call spread](../values/arrays/README.md#spread)
operation for validation and expansion:

- Arrays supply their elements in index order, preserving element identities.
- Strings supply code points as strings: `[...'a😀']` is `['a', '😀']`.
  Preserve lone surrogates as JavaScript does.
- Every other FunctionalScript value fails during expansion, before the first
  body evaluation. No separate type guard or iterable protocol is needed.

The fold walks the resulting array by index. This design intentionally
materializes the whole input, including a copy of an array, before calling
the body. It costs O(n) work and storage even if the first iteration exits.
FunctionalScript inputs are immutable arrays or strings, so no mutable source
or user-defined iterator behavior has to be preserved. Reusing spread also
avoids a second Unicode decoder or a dependency on `list.next` in this lowering.

#### State and continuations

Keep the helper internal to compiler lowering; `tryFold` is the proposal's
descriptive name, not a change to the existing nullable
[`list.tryFold`](../../fjs/types/list/README.md).

Generate `f(item, state)` and the normal continuation `c(state)`.
The state is one aggregate containing the current values selected by the
existing binding analysis, in declaration-introduction order. Reuse the
current loop input/result signatures and packing rules rather than adding a
second write/capture analysis. Support zero state values and state beyond the
fixed function-parameter limit. Immutable captures remain references. Construct
initial state from the binding versions after evaluating the source expression,
including any admitted writes there; preserve existing lazy-demand rules while
packing that state.

The helper's step result is `[stop, result]`. Its meaning depends on the
resolved source transfer:

| Source path | Step result or action |
| --- | --- |
| Body fallthrough or `continue` targeting this loop | `[false, nextState]` |
| `break` targeting this loop | `[true, c(currentState)]` |
| `return value` targeting the owning source function | `[true, value]` |
| `throw value` | Propagate the failure; do not call `c`. |

A continuing step replaces the state before the next iteration. A stopped
step returns its payload directly, without calling `c` again. Exhaustion,
including empty input, calls `c(state)` exactly once. Do not evaluate the
continuation while constructing the helper or its initial state.

As a concrete lowering example:

```js
let sum = 0;
for (const item of source) {
    if (item < 0) { break; }
    sum = sum + item;
}
return sum * 2;
```

has the following fold structure, with generated fresh names:

```js
const c = state => state[0] * 2;
return tryFold(
    (item, state) => item < 0
        ? [true, c(state)]
        : [false, [state[0] + item]],
    c,
    source,
    [0],
);
```

The helper's internal callback boundary must not change source legality or
ownership. Retain fresh per-iteration bindings, lexical shadowing, the existing
write-based capture rules, and checks on unreachable source. The admitted heads
are braced `const`/`let` declarations using the existing binding-pattern and
lazy-default rules. Assignment-target heads remain separate syntax work.
Module loops keep their existing rules, including the refusal of module-level
`return`.

Preserve function, loop and switch target identities through nested generated
helpers. A nested source function keeps its own returns; a switch break keeps
its switch target. Compose with the existing
[scoped completion transport](../../fjs/edag/from_fast/loops.md#enclosing-function-returns)
at nested boundaries: an inner helper returns to its caller, and the caller
performs its own transition. Do not create mutually recursive helper calls by
invoking an enclosing loop's transition from an inner continuation.

Keep the normal continuation shared rather than copying its body into every
exit. Pass the current state explicitly so it cannot capture a stale version.
Reached initializers, assignments and failures retain source evaluation order,
even when their results are unused. Transfers skip all following work on
their path.

#### Compiler integration and evidence

Follow the existing [compiler pipeline](../../fjs/compiler/architecture.md):
source admission resolves bindings and transfer targets; sugar represents the
fold through eager spread, an indexed structured loop and scoped exits. Its
normal result supplies the continuation's state, while owning returns keep
their source-function target. This is the fold contract expressed through
existing FAST, without introducing a public helper or duplicating a normal
continuation. Graph construction consumes the explicit signatures and
completion facts. Target writers consume
the common prepared representation. No new EDAG operation or handwritten
backend Unicode implementation is required.

Proofs should accompany the implementation steps and cover:

- Empty and nonempty arrays and strings; BMP characters, surrogate pairs and
  lone surrogates; dynamic non-iterable values and source evaluation once,
  including source-expression writes observed by an empty-input continuation.
- Fallthrough, continue and break after state updates; owning return and throw;
  exactly-once normal continuation and skipped work after an exit.
- Nested loops, switches and source functions with their resolved targets;
  module loops, shadowing, fresh iteration snapshots and capture refusals.
- Zero and many state values, required failures in otherwise unused work,
  long-running loops with bounded stack, and shared continuation graph growth.
- Agreement between JavaScript source execution, represented evaluation and
  supported Rust/NaNVM output, with stable source/graph round trips under the
  existing normalization contract.

### Benefits and drawbacks

Benefits: `for…of` and the counting loop are the ordinary JavaScript
spellings of the loops the standard modules write, and the largest group of
first refusals; each form lowers onto the existing structured loop.

Drawbacks: additional header and binding forms to parse and prove; the
fold's eager spread allocates and traverses the entire input before the body;
generated callbacks and continuations must preserve source ownership, failure
order and bounded-stack execution. The admitted kinds match existing spread,
so arbitrary host iterables are outside this proposal. The counting loop's
per-iteration binding remains subject to the existing capture rule.

Rewriting iteration modules to index-based `while` or classic `for` is the
alternative, and costs `for…of` its readability.

### Generic `for…of` syntax

The syntax-only foundation covers braced declaration loops:

```js
for (const item of items) { consume(item); }
for (let [key, value] of entries) { consume(key, value); }
```

The AST retains the declaration kind and binding target, the iterable expression,
the body and the starting token. Classic `for` keeps its existing representation.
The contextual separator `of` remains a valid ordinary identifier elsewhere.

`parseSyntax` can form this AST, so `fjs deps` can inspect such modules without
executing or resolving them. The syntax-only increment originally retained an
explicit FunctionalScript refusal. The admitted iteration semantics now follow
the implementation above, using the existing spread and loop operations.
Assignment-target iteration, `for…in` and `for await…of` remain separate syntax
work.

This expands AST coverage for repository source while keeping the unresolved
iteration policy separate. The cost is a syntax form whose later admission
still needs its own design and proofs.

`sergey-shandar` requested this syntax-only increment in the
[working session](https://chatgpt.com/local/01a1908b-f970-713f-b7cb-e16f7ffb2006)
and confirmed that it is only AST support. This records no approval for FJS
iteration semantics.

### Classic `for` authorization and scope

`sergey-shandar`, the language designer named in
[DESIGN.md §12](../../doc/DESIGN.md#12-preserve-harmless-javascript-conventions),
explicitly requested this implementation in the
[working session](https://chatgpt.com/local/01a1890d-59f3-7124-b8e8-a763dc98c003):

> Implement desugaring from `for (init, cond, step)` to `while`.

The accompanying example uses `for (let i = 0; i < count; i += 1)`.
That request is the authorization used for classic-for desugaring. The
implemented header variants are documented in the
[loop contract](../control-flow/loops/README.md#classic-for): `let` declarations,
initialized `const`, assignment or call initializers and steps, optional clauses,
and terminal `for (;;)`, subject to the existing source and capture rules.
The later [named `let` approval](../bindings/variables/README.md#design-approval)
also permits an omitted initializer in a classic `for` header.
That classic-for request did not cover `for…of`, `for…in`, `++` or `--`.

No separate formal language-design approval comment is recorded for those
header variants beyond the linked implementation request. The earlier
[general-loop approval](../control-flow/loops/README.md#design-approval)
covers the `while` and binding extension; it does not approve classic `for`.

### `for…of` implementation authorization

`sergey-shandar` explicitly requested the implementation in the
[working session](https://chatgpt.com/local/01a19e8c-c68a-733e-86e5-9dde72899035):

> Then implement the `for-of` in a new PR stacked on top of 3590.

This request authorizes implementing the spread-based fold proposal above
under [DESIGN.md §12](../../doc/DESIGN.md#12-preserve-harmless-javascript-conventions).

### Tasks

- [x] Form a generic AST for braced `const`/`let` declaration `for…of` loops;
      retain explicit FJS refusal and prove `fjs deps` uses the syntax boundary.
- [x] Record formal language-design approval for the `for…of` proposal,
      including its admitted header forms, array/string input kinds and eager
      spread, with a link under
      [DESIGN.md §12](../../doc/DESIGN.md#12-preserve-harmless-javascript-conventions).
- [x] `for (;;)` and the counting loop lower to the existing loop; the
      [loops](../control-flow/loops/README.md) page and
      [refused constructs](./refused-constructs.md) updated.
- [x] Implement the shared internal fold lowering with spread-based entry,
      explicit aggregate state and the continuation contract above. Include
      helper/sugar proofs for Unicode, invalid inputs, state and exits.
- [x] Admit the approved `for…of` headers in grammar, syntax and source
      resolution, preserving binding/capture rules and resolved targets.
      Route them through the shared lowering and include parser, execution,
      bounded-stack and output/round-trip proofs in the same change.
- [x] Document the implemented source contract and update refused constructs;
      recount the modules this unblocks and rename those that then compile,
      updating importers and running their scoped checks.
- [ ] Propose and obtain separate approval for `for…in` and assignment-target
      iteration; keep this issue open for those remaining forms.

### Related

- [PR #3589](https://github.com/functionalscript/functionalscript/pull/3589) and
  [the fold notes](../../doc/sergey-shandar/for-of.md) — the design this plan builds on.
- [Variables](../bindings/variables/README.md) and
  [loops](../control-flow/loops/README.md) — current source contracts.
- [Loop integration](../../fjs/compiler/architecture.md#loop-integration) —
  the shared pipeline these forms can reuse.
- [3430 assignments](./3430-assignments.md) — `++` and `+=`.
- [MVP roadmap](../../nanvm/lib/todo/mvp-roadmap.md#where-self-hosting-stands)
  — what self-hosting waits on.
