Iter 16b.2 — LetRec captures of fn-params (path-1 safe subset)
Lift 16b.1's no-capture restriction. The desugar pass's `scope` becomes a `BTreeMap<String, ScopeEntry>` carrying `KnownType` for fn/Lam-params, sentinels for Let-/Match-bindings. LetRec captures of `KnownType` names are lifted: the synthetic top-level fn's signature gets the capture types appended, and every call site of the LetRec name is rewritten via `subst_call_with_extras` to pass the captures positionally. Captures from let-bindings, match-arm patterns, polymorphic enclosing fns, and name-as-value uses are rejected at desugar with panics pointing at 16b.3-16b.7. Demo: `sum_below(n)` recurses via `(let-rec loop (params i) ... (body ... uses n ...) (in (app loop 1)))`. Lifts to `loop$lr_0(i: Int, n: Int) -> Int` with `(app loop X)` rewritten to `(app loop$lr_0 X n)`. Outputs 0, 10, 45. Tests: 103 -> 106 (+1 e2e local_rec_capture_demo, +2 desugar unit; the 16b.1 capture-panic test was repurposed into the positive fn-param-capture test). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -450,6 +450,23 @@ fn local_rec_factorial_demo() {
|
||||
assert_eq!(lines, vec!["1", "6", "120"]);
|
||||
}
|
||||
|
||||
/// Iter 16b.2: LetRec capture of a fn-param (path-1 safe subset).
|
||||
/// Property protected: the desugar pass lifts a LetRec whose body
|
||||
/// captures one or more enclosing-scope names (here: `n` from
|
||||
/// `sum_below`'s params) into a synthetic top-level fn whose
|
||||
/// signature has the capture types appended, and rewrites every
|
||||
/// call site of the LetRec name in body and in_term to pass the
|
||||
/// captures positionally as extra args. Without 16b.2, the
|
||||
/// 16b.1-era panic ("would capture") would fire and the build
|
||||
/// would fail. Three call sites: sum_below(1)=0, sum_below(5)=10,
|
||||
/// sum_below(10)=45.
|
||||
#[test]
|
||||
fn local_rec_capture_demo() {
|
||||
let stdout = build_and_run("local_rec_capture.ail.json");
|
||||
let lines: Vec<&str> = stdout.lines().collect();
|
||||
assert_eq!(lines, vec!["0", "10", "45"]);
|
||||
}
|
||||
|
||||
/// Guards `ail diff`: a modified body changes the hash of `sum`, while
|
||||
/// `main` stays unchanged. Expects exit code 1, `changed` contains exactly
|
||||
/// `sum`, `unchanged` contains `main`, `added`/`removed` empty.
|
||||
|
||||
Reference in New Issue
Block a user