Iter 16b.3: post-typecheck lift for LetRec with Let-bound captures
Adds path-2 from the 16b.2 planning entry. Desugar now defers
LetRecs whose captures include Term::Let-bound names; a new
ailang-check::lift_letrecs pass runs after typecheck and uses the
elaborated env to resolve capture types. ailang-check learns a real
Term::LetRec typing rule in synth and verify_tail_positions.
- desugar: Term::LetRec arm gains a defer-arm; helpers promoted to
pub for reuse by the lift pass; find_non_callee_use moved before
classification.
- ailang-check::synth/verify_tail_positions: real LetRec rules
(effect-subset, locals install, recursive name in body+in_term).
- ailang-check::lift.rs (new, 720 LOC): post-typecheck lift with
post-order traversal, env-walk for capture-type resolution,
fast-path skip when no LetRec is present.
- ail::main.rs: build path now does load → check → desugar →
lift_letrecs → codegen.
- examples/local_rec_let_capture.{ailx,ail.json}: new fixture
capturing a let-bound `threshold` in a recursive helper.
- e2e + check unit tests: 106 → 110 (+4).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -701,6 +701,7 @@ conversion in JOURNAL). A `seq` term evaluates `lhs` for its effects
|
||||
├─ resolve names + assign hashes
|
||||
├─ desugar (AST → AST, Iter 16a)
|
||||
├─ typecheck (HM, effect rows)
|
||||
├─ lift_letrecs (post-typecheck AST → AST, Iter 16b.3)
|
||||
├─ lower to MIR (SSA-like, named SSA values)
|
||||
├─ emit LLVM IR (.ll)
|
||||
└─ clang -O2 *.ll -o binary (links libgc for @GC_malloc)
|
||||
@@ -716,6 +717,17 @@ in the `check` entry point continues to hash from the *original*
|
||||
on-disk module, not the desugared one, so `ail diff` and `ail manifest`
|
||||
report identities that match the canonical JSON the user is editing.
|
||||
|
||||
The **lift_letrecs** pass (`ailang-check::lift_letrecs`, Iter 16b.3)
|
||||
runs **after** typecheck and **before** codegen, but only on the
|
||||
`build` / `run` paths — the `check` subcommand stops at typecheck
|
||||
and never sees a lifted module. It eliminates every `Term::LetRec`
|
||||
that the desugar pass left in place (the case where at least one
|
||||
capture is `Term::Let`-bound, so its type is only knowable after
|
||||
inference). The output is a module with synthetic `<hint>$lr_N`
|
||||
top-level fns appended, ready for codegen. Synthetic FnDefs added
|
||||
by this pass do **not** appear in `CheckedModule.symbols` — same
|
||||
invariant as the 16b.2 lifts in desugar.
|
||||
|
||||
## CLI
|
||||
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user