iter it.2: structural-guardedness checker + first real Diverge effect
Iteration-discipline milestone, 2 of 3. Strictly additive (nothing tail-related removed; that is it.3). New whole-body pass verify_structural_recursion sibling of verify_tail_positions (DD-1): smaller-set algorithm with implicit candidate inference + unconstrained accumulators (DD-2, foldl=structural), self/mutual via inline ADT-family union-find (DD-3), it.2-only tail==false grandfather. CheckError::NonStructuralRecursion. term_contains_loop (stops at Term::Lam, DD-4) injects Diverge so existing UndeclaredEffect enforces it, no new variant; lam-arrow + LetRec sub-effect sites wired. DESIGN.md Decision 3 synced. Four it.1 loop fixtures gained !Diverge. Two spec-premise boundary defects surfaced + resolved within the additive invariant (corpus clean, check not weakened), recorded as corrected it.3 corpus-migration scope: (1) the "21 tail-app fixtures" grandfather premise under-counts the corpus — no-ADT-candidate counter recursions have no structural position to verify, deferred to it.3; (2) two RC-regression fixtures joined the spec's transitional tail-app grandfather as the other 20 do (RC==GC guards verified still green). cargo test --workspace 622/0; 9 acceptance pins non-vacuous. Specfda9b78, planbc9f512.
This commit is contained in:
+20
-4
@@ -164,8 +164,16 @@ Advantages:
|
||||
|
||||
The default is total, pure functions. Effects are declared as a set in the
|
||||
function type: `(Int) -> Int ![IO]`. The effect set is row-polymorphic
|
||||
(`![IO | r]`). In the MVP only the effects `IO` and `Diverge` (for infinite
|
||||
loops) are wired up.
|
||||
(`![IO | r]`). Two effects are wired up: `IO` (observable side effects,
|
||||
raised by `do`-operations), and `Diverge` (non-termination). As of iter
|
||||
it.2 (2026-05-15) `Diverge` is no longer nominal: it is the effect carried
|
||||
by any function whose body contains a `loop` (or that calls a
|
||||
`Diverge`-declaring function), surfaced through the existing
|
||||
declared-vs-raised reconciliation (an undeclared `Diverge` is the existing
|
||||
`UndeclaredEffect`, no new diagnostic). Structural recursion is pure and
|
||||
total and carries no effect — the author who wants a `!Diverge`-free
|
||||
signature is structurally pulled toward structural recursion and pays
|
||||
`!Diverge` only when genuinely writing an unbounded `loop`.
|
||||
|
||||
This is the most important LLM property: when I read a function, I can trust
|
||||
its signature without reading the body.
|
||||
@@ -2446,8 +2454,16 @@ parse, print, prose-project, round-trip, typecheck (binder typing,
|
||||
recur arity/type unification, recur tail-position) and codegen
|
||||
(loop-header block with one phi per binder, `recur` as a back-edge
|
||||
`br`) without removing or modifying the existing `tail-app`/`tail-do`
|
||||
paths. The structural-recursion restriction and the `Diverge`
|
||||
effect land in it.2; `tail-app`/`tail-do` are retired in it.3. See
|
||||
paths. As of iter it.2 (2026-05-15) the structural-recursion
|
||||
guardedness restriction and the `Diverge` effect are in effect:
|
||||
a non-structural recursion-by-call is the compile error
|
||||
`NonStructuralRecursion` (directing the author to `(loop …)` /
|
||||
`recur`), and any fn whose body contains a `Term::Loop` (or calls a
|
||||
`Diverge`-declaring callee) must declare `!Diverge` in its effect
|
||||
row — structural recursion stays pure, total, and effect-free. A
|
||||
transitional grandfather exempts still-`tail: true`-marked recursive
|
||||
calls so the corpus type-checks unchanged through it.2.
|
||||
`tail-app`/`tail-do` are retired in it.3. See
|
||||
`docs/specs/2026-05-15-iteration-discipline.md`.
|
||||
|
||||
**`Literal`**:
|
||||
|
||||
Reference in New Issue
Block a user