decisions(memory): loop arena sizing ×bound now, escape-analysis slot reuse deferred post-MA4 (counter) #51
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/051-decisions-memory-loop-arena-sizing-bound-now-e"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
decisions(memory): loop arena sizing ×bound now, escape-analysis slot reuse deferred post-MA4 (counter)
Along the way:
Touches 47 files:
current-state.md,docs/impl/03-parser.md,docs/impl/08-diagnostics.md,docs/impl/memory-model/arenas.md,docs/syntax.md,plan.mdand 41 more.Split. The decision in
lower_foris a pure function now, with its own tests insrc/rust/crates/ck_resolve/src/lower/loops_lower.rs.Nothing outside calls
iterable. It is pub(crate) insrc/rust/crates/ck_syntax/src/loops.rsnow.Done, and I added the test for
src/rust/crates/ck_resolve/src/lower/loops_lower.rsthat would have caught it.Mostly there, but the question on
src/rust/crates/ck_resolve/src/lower/loops_lower.rsneeds an answer before this lands.@ -0,0 +59,4 @@/// iterable lowers before it in source order (it is evaluated outside/// the binder's scope — the resolver's N3 walk already scoped it that/// way). Missing pieces (parser recovery) keep spanned placeholders.pub(super) fn lower_for(&mut self, node: &SyntaxNode, span: Span) -> ExprId {lower_forreads, decides and writes in one function. The deciding part is the piece I would want to test on its own. Worth splitting?@ -0,0 +110,4 @@/// The iterable expression (the node after `in`, resolved before the/// binder is introduced).#[must_use]pub fn iterable(&self) -> Option<&'a SyntaxNode> {iterableis public. Does anything outside the crate call it, or is it pub because that was easier? Every public item is a promise.Good. The second pass on
src/rust/crates/ck_resolve/src/lower/loops_lower.rsis what I hoped for.The loop front-end of plan.md row 14a item (1), design owner-locked in decisions-scheduler-model.md "Loops": the language gets `for` + `while<N>`, and NO unbounded loop exists — indefinite execution is a scheduled actor, never a spin. Front-end ONLY; the CFG back-edge lowering is MA1.2. Parser: `while<N>` bound = mandatory grammar (`WhileExpr ::= while WhileBound Expr Block`, `WhileBound ::= < IntLit >`), a `WhileBound` CST node mirroring rec<N>'s RecBound shape and diagnostics — bare `while` = hard E0123 (the policy-carrying error; recovery still parses condition + body), E0124 missing bound literal, E0125 missing `>`. The bound slot opens only DIRECTLY after the keyword, so a comparison condition never collides. `for` already parsed (slice 16); ranges already parse as iterables (slice 23). CST wrappers (ck_syntax::loops, split per R3): WhileExpr (bound reader = the recursion_depth shape), RangeExpr (start/end/inclusive), ForStmt moved in. HIR: ExprKind::{While, For, Range} — the While bound N is CARRIED (MA1.2's sizing multiplier + back-edge consume it; no runtime check built), the For binder is the shared Pattern language primitive, a Range is deliberately NOT a first-class value. Resolver lowering (ck_resolve lower/loops_lower.rs, split per R3): a statement-position `for` lowers as a statement, never the block tail — previously it was silently tail-overwritten as Unsupported. Typecheck (ck_types::loops, synthesis-only per plan §5 — no metavariables, no solving): `while` condition must be Bool (E0732, fail-soft on Unknown); `for` iterable = a bounded List<T,N> (element type flows to the binder) or an integer range with manifest Int endpoints (E0734), anything else = E0733; a range outside the for-iterable slot = E0735; the for binder rides the `let` rules — Binder/Wildcard direct, destructures through check_let_pattern (irrefutability E0731, binder types flow from the element type). Honest lowering gap: a loop reaching ck_lower is a real spanned LowerError::LoopUnsupported (verified end-to-end: ckc run exits 1 with the spanned error) — never a silently-dropped statement. Docs, same commit: docs/syntax.md §Loops rewritten to the owner decision (the "no while in v0" rule superseded, dated); docs/impl/03-parser.md slice-22 grammar + E0120–E0125 table rows (E0120–E0122 were missing — FS5.2 doc drift fixed) + codes prose; docs/impl/08-diagnostics.md rows E0732–E0735 + the E0731 for-binder note; plan.md row 14a marked front-end LANDED; current-state.md §3 updated. Tests: 7 new parser unit tests (bound node, AST reader, E0123 x2, E0124, E0125), existing while tests re-bounded; 3 resolver lowering tests (bound carried, for-as-statement, range node); 9 new type goldens + 1 resolve golden (clean while/for/range/destructure; E0731/E0732/E0733/E0734/E0735), all trap-covered — the untrapped-diagnostics ratchet cap is UNCHANGED. R3/R4 held by splitting (ck_syntax/loops.rs, ck_resolve/loops_lower.rs, ck_types/loops.rs) + one justified R4 allowlist entry (LowerError's Display: one arm per variant by design). tests/lex/program_effects.cktest: the bless run canonicalized its trap-section position; content unchanged. Gates (from src/rust): cargo fmt --check clean; cargo clippy --all-targets -- -D warnings 0 warnings; cargo test --workspace exit 0 (incl. conformance + architecture + the container e2e corpus); cargo build --workspace clean. 0 warnings, 0 errors. ckc run tests/corpus/helloWorld unchanged: hello, world, exit 0, 10 000-byte freestanding ELF.Closes the MA1.1 right-path gap the coordinator's probes found: a statement-position `while<8> c { … }` followed by more statements or a tail expression failed to parse (E0036 cascade), because `while` was still slice-22's value-producing PRIMARY (needed `;` as an ExprStmt) while `for` was a block-bodied statement. That contradicted docs/syntax.md §Loops' own examples and the uniform-rules lock — the two loop primitives must terminate identically. Parser: `while` moved from `parse_primary` to `try_parse_stmt_keyword` beside `for` — `WhileStmt ::= while WhileBound Expr Block`, the closing `}` is the statement terminator, no `;`. The slice-22 expression production is RETIRED (a `while` as a let-RHS no longer parses; nothing is lost — the loop was always Unit-valued, and a block ending in a while statement returns Unit exactly as the old tail reading did). SyntaxKind::WhileExpr → WhileStmt across lexer/parser/syntax/resolve (file while_expr.rs → while_stmt.rs; ck_syntax wrapper WhileExpr → WhileStmt). E0123 recovery is now ONE diagnostic — the statement terminator ends the loop, so the bare `while 1 == 2 { let x = 1; }` probe reports E0123 alone, no cascading E0036 (pinned by test). Resolver: lower_block routes ForStmt | WhileStmt through one `lower_loop_stmt` helper (loops_lower.rs) — an expression STATEMENT, never the block's tail. HIR/typecheck/ck_lower unchanged (ExprKind::While was already statement-agnostic; the LoopUnsupported honesty gate holds). Tests: while_stmt.rs rewritten to the statement grammar — statement position followed by another statement AND by a tail expression (the exact probe shapes), expression position rejected, E0123-alone recovery shape, E0123/E0124/E0125, bound node + AST reader, condition Pratt composition, byte-completeness; a for-mid-block parser test pins the `for` twin. New golden tests/types/while_statement_position.cktest (the probe source, blessed clean); the resolver while-lowering test moved to statement position + tail assertion. Conformance ratchet cap unchanged. Docs, same commit: docs/syntax.md Stmt grammar grew WhileStmt ("Seven shapes, one terminator: every statement except CatchStmt, ForStmt, and WhileStmt ends with `;`"), §Loops states both loops are block-bodied statements; docs/impl/03-parser.md slice-22 section rewritten (WhileExpr → WhileStmt, the retirement + re-home dated), codes prose + table rows updated (parse_while_stmt), lib.rs stable-code range corrected E0009..E0125; plan.md row 14a + current-state.md §3 note MA1.1b. Gates (from src/rust): cargo fmt --check clean; cargo clippy --all-targets -- -D warnings 0 warnings; cargo test --workspace exit 0 (conformance + architecture R3/R4 + container e2e green; ck_resolve lower/mod.rs back under the cap via the lower_loop_stmt consolidation). 0 warnings, 0 errors. Live: probe 1 (`while<8> 1 == 2 { let x = 1; } 42`) checks clean; probe 2 (bare while) = exactly one E0123; helloWorld unchanged (hello, world, exit 0).