EIR VM: hard-error instead of silent () on unbound symbols, min/max/assoc misuse, and non-exhaustive match#78
Conversation
…ssoc misuse, and non-exhaustive match The default backend silently produced unit in several places the legacy interpreter errors loudly. Now, matching the interp's wording: - Unbound symbols lower to a Built::UnboundSym trap that raises "unbound symbol '<name>'" (with span) instead of a string literal. - Binary [min 1 2] / [max 5 3] error "min requires a vector"; empty vectors error "min: empty vector"; vector min/max now also compare floats. assoc on a non-map errors "assoc requires a map" (#20, #21). - A match with no matching arm raises "no match arm matched value: <v>" via Built::MatchFail carrying the scrutinee. Wasm/native compile the new builtins to their existing unit fallback, preserving behavior. Convergences surfaced by the loud path: - First-class binary operators wrap in an arity-2 closure, so [fold xs 0 +] now agrees with the interp (pinned divergence retired); and-or-value.oo flips to expect-fail: wasm. - First-class arity-N ADT constructors wrap in a real closure. - Const.* physics constants resolve in lowering (physics.oo used the silent field-on-string path). New VmErrorKind variants (UnboundSymbol, NoMatch, BuiltinType) carry stable class() names for trace/verify. Coverage added in backend_parity.rs (silent_unit_traps_error_loudly). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Choji review — Looks good This PR converts several silent-unit paths in the EIR VM into hard errors (unbound symbols, binary min/max misuse, assoc on non-map, non-exhaustive match) and fixes two correctness gaps (first-class binary operators wrap as arity-2 closures, physics constants resolve at lowering time). The implementation is clean and well-tested. I found one narrow correctness concern with NaN handling in the new min/max comparator, but it is a pre-existing edge case that the old code also mishandled (it silently dropped non-numeric elements), so it does not block the PR. No blockers. Correctness
The old code filtered to 1 minor finding
Choji ran your change live — checks failed. No live preview was run for this backend/compiler change. The verification check (
Checks — failures
No issues found in the running app. Was this review useful? Rate the findings → — your thumbs up or down trains Choji on what's worth flagging. Reviewed |
Review follow-up: the NaN-keyed comparator silently ignored non-numeric elements (and pinned `best` to a leading non-numeric one). Non-numeric elements now raise "min: non-numeric element", consistent with the loud-over-silent policy of this branch. Note: the interp diverges here (its generic value_cmp orders strings); pinned in the parity test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
…riant sets Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
What shippedThis is a purely internal change with no user-visible effect. The EIR execution backend now produces the same error messages as the legacy interpreter when programs reference undefined names, misuse built-in functions, or write incomplete match expressions — previously those situations silently returned an empty value instead of failing. No behavior visible to end users has changed. Plain-English summary generated by Choji from this pull request. |
What
The EIR VM (default
loon runbackend) silently produced()in places the legacy interpreter errors loudly. This brings the VM to the loud behavior, with error wording matching the interp so both backends fail the same way:[reduce f 0 #[1 2 3]]returned()and calling a missing builtin gave a vague "value is not callable". It now emits aBuilt::UnboundSymtrap that raisesunbound symbol '<name>'with a span.[min 1 2]errorsmin requires a vector,[min #[]]errorsmin: empty vector, andassocon a non-map errorsassoc requires a map— never unit. Vector min/max also now compare floats, not just ints.Built::MatchFailcarrying the scrutinee, raisingno match arm matched value: <v>.New
VmErrorKindvariants (UnboundSymbol,NoMatch,BuiltinType) have stableclass()names for trace/verify, following the precedent of the earlier VM effect-op hard-error change.Convergences the loud path surfaced
[fold xs 0 +]prints10on both backends — the pinnedfold-builtin-argdivergence in backend_parity.rs is retired as CONVERGED, andand-or-value.ooflips fromexpect-fail: legacytoexpect-fail: wasm(the VM now matches legacy; wasm codegen has no operator values).[map Some xs]) wrap in a real closure instead of the bogus string path.Const.*physics constants (legacy-only until now) resolve in lowering —physics.oowas silently field-accessing a string.Reviewer notes
sqrt; the VM still lacks it but now at least names the gap (unbound symbol 'sqrt'). Porting it, and wiring the type checker intoloon runfor static unbound-symbol detection, are follow-ups.silent_unit_traps_error_loudlytest incrates/loon-lang/tests/backend_parity.rsasserts all error cases on both backends (message substrings on EIR) plus the working counterparts for agreement.cargo test --workspaceis green.🤖 Generated with Claude Code