Skip to content

Lift map/flt/fld closure-bind to native VM dispatch (PR A of ILO-45)#619

Merged
danieljohnmorris merged 1 commit into
nextfrom
feature/lift-ctx-hofs
May 22, 2026
Merged

Lift map/flt/fld closure-bind to native VM dispatch (PR A of ILO-45)#619
danieljohnmorris merged 1 commit into
nextfrom
feature/lift-ctx-hofs

Conversation

@danieljohnmorris
Copy link
Copy Markdown
Collaborator

Summary

PR A of ILO-45 — first slice of the actual tree-walker eval-loop deletion. Lifts map fn ctx xs / flt fn ctx xs / fld fn ctx xs init from tree-bridge dispatch to native VM dispatch via OP_CALL_DYN. Cranelift inherits via the existing jit_call_dyn helper.

The bridge for these three HOFs called eval_body (the tree-walker's expression evaluator) per-element to run the user's closure-bind callback. After this PR they dispatch natively. Three down, five HOFs (srt 3, rsrt 2, rsrt 3, ct 2, ct 3) plus the non-HOF bridge entries to go before the eval loop can actually be deleted.

Why now

PR #613 shipped the user-visible promise (no Engine::Tree, no --run-tree, runtime renamed) but kept eval_body alive because the bridge for closure-bind HOFs called into it. This PR starts unwinding that dependency.

ILO-234's "bridge is the right abstraction" framing held for non-HOF builtins (regex / fmt / fs / crypto / calendar — cheap round-trip, no eval involvement). For HOFs the round-trip goes through call_functioneval_body, which is the load-bearing path that keeps the tree-walker alive. Lifting these natively is the only way to actually delete it.

What's in the diff

Single commit, two files:

  • src/vm/mod.rs
    • Three new arms in compile_call: (Builtin::Map, 3), (Builtin::Flt, 3), (Builtin::Fld, 4). Each emits OP_FOREACHPREP / OP_CALL_DYN / OP_LISTAPPEND (or fld's accumulator refresh) in the same shape as the existing 2-arg arms. Reg layout: res + 2 or 3 contiguous arg slots. flt 3 keeps the bool typecheck + WRAPERR/PANIC_UNWRAP error path from flt 2.
    • is_tree_bridge_eligible: dropped the three entries; Srt, 3 and Rsrt, 3 retained pending PR B.
  • examples/closure-bind.@
    • Lifted engine-skip: jit directive (closure-bind HOFs now work on JIT)
    • Added above-demo (flt 3) and weighted-demo (fld 4) with assertable output

Cranelift inherits via the existing OP_CALL_DYN codegen — no Cranelift changes needed.

Test plan

  • cargo test --release --features cranelift — full suite green, 3348+ unit tests pass, no regressions
  • examples/closure-bind.@ runs identically across default / --vm / --jit for all four demo functions
  • cargo test --release --features cranelift --test examples_engines passes
  • rust-review subagent run on the diff — no blockers found

Follow-ups

  • PR B: srt 3 / rsrt 2 / rsrt 3 — needs OP_SRT_BY_KEY desc-flag extension or new OP_RSRT_BY_KEY
  • PR C: ct 2 / ct 3 — needs finalizer or inline counter loop
  • PR D: audit non-HOF bridge entries (regex/fmt/fs/crypto/calendar/etc) for transitive eval_body reachability and lift any that need it
  • PR E: actual deletion of eval_body/eval_stmt/eval_expr/Env/trampoline/ACTIVE_AST_PROGRAM (headline LoC win)

PR A of ILO-45. Adds three new compiler arms in src/vm/mod.rs that emit
native OP_CALL_DYN loops for the closure-bind ctx variants:

- map fn ctx xs   argc=2, per-iter call(item, ctx)
- flt fn ctx xs   argc=2, per-iter call(item, ctx), bool typecheck + LISTAPPEND
- fld fn ctx xs init  argc=3, per-iter call(acc, item, ctx)

Removes the three arms from is_tree_bridge_eligible. Cranelift JIT and
AOT inherit via the existing OP_CALL_DYN codegen (jit_call_dyn), so this
fixes the closure-bind gap on JIT that examples/closure-bind.@ had been
engine-skipping since PR 3c.

Reg layout mirrors the existing 2-arg HOF arms: res + N contiguous arg
slots, with the same contiguity assertions. Closure captures auto-append
after the user args via the dispatcher's closure path at mod.rs:9785,
matching the tree-walker's [item, ctx, ...captures] / [acc, item, ctx,
...captures] call_args order at runtime/mod.rs:5737 / :5958.

srt 3 and rsrt 3 still bridge pending PR B's finalizer-opcode extension.
ct 2/3 and rsrt 2 are PR C.

Test plan: examples/closure-bind.@ now exercises map/flt/fld closure-bind
across default / --vm / --jit. All four assertions match (main, prices-
demo, above-demo, weighted-demo). Full suite green: 3348 unit tests pass.
@danieljohnmorris danieljohnmorris merged commit b0637e9 into next May 22, 2026
4 checks passed
@danieljohnmorris danieljohnmorris deleted the feature/lift-ctx-hofs branch May 22, 2026 00:01
@codecov
Copy link
Copy Markdown

codecov Bot commented May 22, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant