Support top-level bindings in the self-host pipeline - #809
Merged
Conversation
A binding written outside any function is a module-level value. The host VM evaluates every top-level statement in source order before calling main and exposes the results to all functions, regardless of where each function appears in the file. The self-hosted interpreter parsed those statements and evaluated them into a throwaway environment, then called main with an empty one, so any program with a top-level binding failed under `aver run --self-host` with "undefined variable" — with no other language feature involved. Give the self-hosted evaluator the same semantics: - FnStore carries a `globals` map alongside the function table, with `withGlobals` / `lookupGlobal` accessors. - `evalTopLevelStmts` threads the environment through the top-level statement list and returns it, and re-attaches it to the store for every statement, so a top-level binding whose value comes from calling a function can still read the bindings above it. - The unresolved-name fallback consults top-level bindings before function references, matching the host, where a binding that shares a name with a function wins when the name is read as a value. Function parameters and match-arm bindings continue to shadow a top-level binding of the same name; a function-local `x = ...` shadowing a top-level `x` is rejected by the host typechecker on both paths, so it never reaches either backend. Regenerated src/self_host through the release regeneration sequence, and added VM/self-host parity tests covering a binding used in main, a binding reading another binding, a binding produced by a function call, both shadowing forms, and a binding sharing a function's name. The wasm-gc backend still traps on top-level bindings; that gap is unchanged and untouched here. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
A binding written outside any
fnis a module-level value. The host VM evaluates every top-level statement in source order before callingmainand exposes the results to all functions. The self-hosted interpreter evaluated those statements into a throwaway environment and then calledmainwith an empty one, so this program worked on the VM and failed underaver run --self-hostwithundefined variable: greeting:What changed
FnStorecarries aglobalsmap next to the function table, withwithGlobals/lookupGlobalaccessors.evalTopLevelStmtsthreads the environment through the top-level statement list and returns it, re-attaching it to the store for each statement, so a binding whose value comes from a function call can read the bindings above it.Semantics were derived from the host VM with probe programs first: source order, visible to functions defined before the binding, forward references and function-local rebinding rejected by the typechecker on both paths, parameters and match-arm bindings shadow normally.
src/self_hostwas regenerated through the release regeneration sequence; the diff is confined to the three evaluator modules. The regeneration gate passes (AVER_SELF_HOST_REGEN=1 cargo test --features runtime --test rust_self_host_regen -- --ignored, corpus parity 3/3). No unrelated drift showed up in the regenerated tree.Tests
tests/cross_backend_stress.rsgains VM/self-host parity pairs covering a binding used inmain, a binding reading another binding, a binding produced by a function call, shadowing by a parameter and by a match-arm binding, and a binding sharing a function's name.Known gaps, unchanged
HttpServercallback bridge builds its own function store without globals, so a handler reading a top-level binding still fails. Previously the whole program failed, so this is strictly narrower.🤖 Generated with Claude Code