Skip to content

self-host parity: higher-order functions + multi-module type resolution #436

Description

@jasisz

A VM-vs-self-host corpus audit during 0.24 prep found the self-hosted interpreter (self_hosted/*.av, compiled to Rust under src/self_host/) lags the other backends on several language features. 0.24 "Divide" closed the tractable ones — Int.div builtin, float-literal lexing inside string interpolation, and replay Unit-output normalization (branch fix-self-host-parity). This issue tracks the two remaining gaps, each a real subsystem rather than a quick patch.

Regen after editing self_hosted/*.av: aver compile self_hosted/main.av --target rust --output self_hosted/out --module-root self_hosted --with-self-host-support --guest-entry runGuestCliProgram --with-replay --policy runtime, then copy self_hosted/out/srcsrc/self_host/ and cargo build.

1. Higher-order functions (Fn(...) parameters)

Repro: a top-level fn passed as a Fn(...) parameter and called indirectly.

fn dbl(x: Int) -> Int
    x * 2
fn applyTwice(f: Fn(Int) -> Int, x: Int) -> Int
    f(f(x))

aver run --self-host fails at parse: Expected ',' or ')' in parameter list. Two layers:

  • Parser: skipTypeExpr in self_hosted/domain/parsermatch.av (~L224) handles TkIdent and TkLParen but has no TkFn case, so a Fn(A) -> B parameter type never parses. Add a TkFn branch that skips Fn ( types ) -> ret. (TkFn/TkArrow tokens already exist.)
  • Eval: ValFnRef is created (self_hosted/domain/eval/common.av:12) when an identifier resolves to a fn, but it is never consumed — there is no path to call a value holding a fn-ref. callWithArgs/callWithArgsNormal (self_hosted/domain/eval/core.av:341/348) resolve the callee only as a top-level fn (lookupFnOption) or a builtin, and don't receive env; a call f(x) where f is a local param bound to ValFnRef falls through to callBuiltin("f", …) and fails. Need: when the callee name is a local var bound to ValFnRef, redirect to the referenced fn — in both the named-env path (evalCall) and the slot-env path (evalCallSlot, core.av:807), and likely in the resolver (slot assignment / call-target resolution may reject a param-as-callee).

The self-host is slot-resolved and was not designed for first-class fn values; this is a feature add.

2. Multi-module type / symbol resolution

  • examples/modules/app.av (and pricing_app.av, app_dot.av) → Argument 1 of 'Console.print': expected String, got E — a cross-module generic type variable E is never instantiated.
  • examples/refinement/natural_app.avUnknown identifier 'Refinement' — a depends [...] module prefix is not resolved.

The self-host typechecker/resolver lacks module-aware symbol resolution: it does not load dependency-module signatures, resolve qualified names across modules, or instantiate generics from a cross-module callee's signature. The host compiler does this (src/types/checker/modules.rs); the self-host has only a basic FnStore.

Verification

  • Corpus parity: run every non-interactive examples/**.av through aver run vs aver run --self-host and diff stdout. After 0.24 batch 1 this is 21/25; these four module/refinement cases are the remaining divergences (target 25/25).
  • Regen gate: AVER_SELF_HOST_REGEN=1 cargo test --test rust_self_host_regen -- --ignored.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions