feature: mapr off tree-bridge (Phase 2 PR3b, partial)#389
Merged
Conversation
Phase 2 PR3b starts the HOF finalizer migration. mapr 2 previously round-tripped every per-element callback through the tree-bridge (OP_CALL_BUILTIN_TREE), paying a NanVal-to-Value cost per call and depending on the ACTIVE_AST_PROGRAM TLS being live. Now mapr emits its own foreach loop using OP_CALL_DYN per element, with an ISERR check that short-circuits on first Err (out_reg = res and skip the wrap-acc tail), an ISOK typecheck that panics with a mapr-shaped message on a non-Result callback, an OP_UNWRAP of the Ok inner before appending to the acc list, and a final OP_WRAPOK to assemble Ok(L T) on the natural exit. No new opcode is needed since OP_WRAPOK already exists; that's why mapr ships in PR3b ahead of srt/grp/uniqby which all need dedicated finalizer opcodes. The closure-aware OP_CALL_DYN from the Phase 2 PR1/PR2 work means capturing lambdas work here without any further plumbing. The mapr! and mapr!! auto-unwrap forms are handled by emit_result_unwrap on the final out_reg, matching the bridge's behaviour exactly. The remaining HOFs needing finalizer opcodes (srt 2, grp 2, uniqby 2) are deferred to PR3c.
Seven cases across tree, VM, and Cranelift: - non-capturing inline lambda - capturing inline lambda (single capture, multiplier) - named fn-ref - empty input (vacuous Ok empty list) - short-circuit on first Err mid-list - all-Ok run with the same short-circuit-capable predicate - short-circuit on the very first element (acc-empty edge) Plus an examples/mapr-shortcircuit.ilo with -- run / -- out assertions so tests/examples_engines.rs exercises the lift across every engine. The example also serves as in-context learning for agents reaching for mapr in future.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This was referenced May 18, 2026
danieljohnmorris
added a commit
that referenced
this pull request
May 18, 2026
Phase 2 PR3c completes the HOF native dispatch migration. The 2-arg
forms of srt, grp, and uniqby previously round-tripped every per-
element callback through the tree-bridge (OP_CALL_BUILTIN_TREE),
paying a NanVal-to-Value cost per call and depending on the
ACTIVE_AST_PROGRAM TLS being live.
Each now emits its own foreach loop using OP_CALL_DYN per element to
fill two parallel scratch lists (keys + values), then hands them to a
dedicated finalizer opcode:
OP_SRT_BY_KEY (179) — sort vals by key; mixed-type-key fallback
folds to Ordering::Equal, matching the tree
walker exactly.
OP_GRP_BY_KEY (180) — bucket vals into a Map<MapKey, List<value>>;
numeric keys floor to i64; non-finite numbers
and non-t/n/b keys raise a typed runtime err.
OP_UNIQ_BY_KEY (181) — dedup using HashSet<String> with type-prefixed
keys (t:/n:/b:) so values from disjoint
domains never alias, matching the tree
scheme.
Each finalizer ships a free fn that the VM dispatch arm and Cranelift
JIT/AOT helpers share, so the two paths can't drift. The closure-
aware OP_CALL_DYN from Phase 2 PR1/PR2 means capturing lambdas work
here without further plumbing.
A single emit_hof_keyed_finalize helper covers all three HOFs at the
compiler layer; the only per-HOF variation is which finalizer opcode
to emit.
mapr already shipped its native lift in PR3b (#389), so this completes
the Phase 2 HOF migration that started with partition in PR3 (#387).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 2 PR3 (#387) lifted
partition 2off the tree-bridge. This PR extends the migration tomapr 2, the smallest of the four remaining bridge HOFs and the only one that needs no new finalizer opcode (a single existingOP_WRAPOKfinishes the assembly step).The original PR3b scope was all four remaining HOFs (
srt 2,grp 2,uniqby 2,mapr 2). After investigating the surface area (per-HOF new opcode 179-182 + VM dispatch arm + Cranelift JIT helper + AOT helper + tree-bridge eligibility removal + 4-5 cross-engine tests + example, multiplied by four) I time-boxed to the one that was tractable end-to-end with cross-engine parity. The other three are deferred to PR3c with a clear pattern to follow — see below.Manifesto framing:
mapris reached for by agents doing Result-aware data validation pipelines. The bridge round-trip paid a NanVal-to-Value cost per element and pinned the run to a live ACTIVE_AST_PROGRAM TLS. The native lift removes both and aligns mapr with the other native HOFs (map,flt,fld,flatmap,partition).Repro before/after
Before this PR the VM/Cranelift paths re-entered the tree interpreter for each callback. After this PR all three engines run the same
OP_FOREACHPREP/NEXT+OP_CALL_DYN+OP_ISERR/OP_ISOK+OP_UNWRAP+OP_LISTAPPEND+OP_WRAPOKbytecode shape. Output is byte-identical across tree, VM, and Cranelift JIT.What's in the diff
vm: lift mapr 2 off tree-bridge with ISERR short-circuit — new arm in
RegCompiler::compile_exprthat emits the native bytecode shape directly, mirroringpartition 2from feature: HOF native closure dispatch (Phase 2 PR3) #387 plus the short-circuit pattern and theOP_WRAPOKfinalizer.Mapr 2removed fromis_tree_bridge_eligible. Themapr!/mapr!!unwrap forms are handled byemit_result_unwrapon the produced Result, matching the bridge's behaviour. Non-Result callback returns raise throughOP_PANIC_UNWRAPwith a "mapr: fn must return a Result" message that contains the same substrings the tree-walker raises.test: cross-engine coverage for mapr native dispatch —
tests/regression_phase2_hof_finalizers.rswith 7 cases covering non-capturing lambda, capturing lambda, named fn-ref, empty input, short-circuit mid-list, all-Ok run, and short-circuit on the very first element (acc-empty edge). Each case runs on tree / VM / Cranelift JIT. Plusexamples/mapr-shortcircuit.iloso the example harness exercises the lift cross-engine too.Test plan
cargo test --release --features cranelift— full suite green (3180 + the rest)cargo fmt --checkcleancargo clippy --features cranelift --release --tests -- -D warningscleanexamples/mapr-shortcircuit.iloand runs it across every engineFollow-ups (PR3c)
The remaining bridge HOFs that need dedicated finalizer opcodes are deferred to PR3c:
srt 2 fn xs— needsOP_SRT_BY_KEY(slot 179): collect(key, value)pairs via OP_CALL_DYN per element, finalizer sorts in-place with Number/Text/mixed key handling matching the tree-walker.grp 2 fn xs— needsOP_GRP_BY_KEY(slot 180): finalizer buildsMap<MapKey, Vec<value>>using the existingMapKey::Int(n.floor() as i64)for finite numbers + Text fallback for Bool.uniqby 2 fn xs— needsOP_UNIQ_BY_KEY(slot 181): finalizer walks aHashSet<String>with the existing type-prefixed key scheme (n:/t:/b:) and appends only first occurrences. Matches tree-walker semantics exactly; no new MapKey path.Each needs a per-HOF extern "C" Cranelift helper mirroring the partition #387 pattern (
helpers.srt_by_keyetc), plus AOT wiring incompile_cranelift.rs, plus tree-bridge removal. Pattern is established by this PR.No doc update needed
This is a behaviour-restoration lift — the
maprsignature, return type, and semantics are unchanged. SPEC.md / ai.txt / SKILL.md / docs/reference/builtins.md already covermapr fn xs : R (L T) e. Internal refactor only.