Skip to content

Support top-level bindings in the self-host pipeline - #809

Merged
jasisz merged 1 commit into
mainfrom
fix/self-host-top-level-bindings
Aug 8, 2026
Merged

Support top-level bindings in the self-host pipeline#809
jasisz merged 1 commit into
mainfrom
fix/self-host-top-level-bindings

Conversation

@jasisz

@jasisz jasisz commented Aug 8, 2026

Copy link
Copy Markdown
Owner

A binding written outside any fn 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. The self-hosted interpreter evaluated those statements into a throwaway environment and then called main with an empty one, so this program worked on the VM and failed under aver run --self-host with undefined variable: greeting:

greeting = "hello"

fn main() -> Unit
    ! [Console.print]
    Console.print(greeting)

What changed

  • FnStore carries a globals map next to the function table, with withGlobals / lookupGlobal accessors.
  • evalTopLevelStmts threads 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.
  • The unresolved-name fallback consults top-level bindings before function references, matching the host, where a binding sharing a function's name wins when the name is read as a value.

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_host was 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.rs gains VM/self-host parity pairs covering a binding used in main, 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

  • The wasm-gc backend traps on any program with a top-level binding. Pre-existing and untouched here, so no wasm-gc test is added.
  • The self-host HttpServer callback 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

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>
@jasisz
jasisz merged commit b035ccf into main Aug 8, 2026
35 checks passed
@jasisz
jasisz deleted the fix/self-host-top-level-bindings branch August 8, 2026 12:57
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