Rewritten 2026-08-11. Part 2 of the original issue is fixed and has been removed — leaving it would send whoever picks this up chasing a module-resolution bug that no longer exists. The line anchors in part 1 have been refreshed, and the two divergences that are actually left have been added. See the comment below for the sweep this is based on.
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. This issue tracks what is left.
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/src → src/self_host/ and cargo build.
1. Higher-order functions (Fn(...) parameters)
Still reproduces exactly as filed. The host prints 12; --self-host fails at parse with Expected ',' or ')' in parameter list.
fn dbl(x: Int) -> Int
x * 2
fn applyTwice(f: Fn(Int) -> Int, x: Int) -> Int
f(f(x))
Two layers, both unchanged:
- Parser:
skipTypeExpr in self_hosted/domain/parsermatch.av:224 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 at self_hosted/domain/eval/common.av:23 when an identifier resolves to a fn, but never consumed. callWithArgs / callWithArgsNormal (self_hosted/domain/eval/core.av:387 / :394) resolve the callee only as a top-level fn or a builtin and do not receive env, so a call f(x) where f is a local bound to ValFnRef falls through to callBuiltin("f", …) and fails. Both the named-env path (evalCall, core.av:608) and the slot-env path (evalCallSlot, core.av:1047) would need it, and the resolver may also reject a param as a call target.
The self-host is slot-resolved and was not designed for first-class fn values; this is a feature add, not a patch.
Worth weighing before anyone starts. Exactly one file in the 112-file example corpus uses an Fn(...) parameter — examples/formal/oracle_independent_products.av:11 — and it has no main, so it is not even in the parity sweep. examples/core/lambda.av does run under --self-host, but it is a lambda-calculus interpreter over a type Term and exercises no Aver first-class functions at all. So this is a real gap with almost no corpus pressure behind it.
2. Multi-line ? documentation continuation
examples/refinement/natural_app.av fails under --self-host with Expected expression, got Indent. Bisected to a ? doc string followed by an indented continuation line, the shape fn add uses in examples/refinement/natural/natural.av:
fn add(a: Nat, b: Nat) -> Nat
? "line one"
"line two"
exposes [f], exposes opaque [Nat] and verify ... law ... given all parse fine — it is only the continuation. Small and self-contained, and fixing it takes natural_app.av to parity.
3. Literal smart-constructor discharge is fenced off
examples/data/json.av diverges because the self-host refuses 0.28's literal smart-constructor discharge, loudly and deliberately — reject_literal_refinement_discharge at src/main/commands.rs:1292, with the reasoning at :1277-1291. Fail-closed with a message, not a silent miscompile, so this is correct behaviour rather than a bug.
It does raise a standing question this issue should carry: whether the self-host is meant to track host typing rules as they land. The note at commands.rs:1287 puts the cost plainly — mirroring the discharge "is not the three syntactic predicates the literal-divisor rule needed; it is the whole recognizer". That is a tax on every future discharge rule, and it should be decided rather than absorbed.
Verification
Corpus parity sweep over every examples/**/*.av with a fn main, host versus --self-host, diffing stdout:
- 43 exercised, 37 exact match
- of the 6 differing:
date.av is clock nondeterminism, and app.av / app_dot.av / pricing_app.av differ only by a host-side stderr warning about unchecked dependency-module verify blocks — their stdout is byte-identical
- leaving 2 genuine:
natural_app.av (section 2) and json.av (section 3)
Regen gate: AVER_SELF_HOST_REGEN=1 cargo test --test rust_self_host_regen -- --ignored.
A VM-vs-self-host corpus audit during 0.24 prep found the self-hosted interpreter (
self_hosted/*.av, compiled to Rust undersrc/self_host/) lags the other backends on several language features. 0.24 "Divide" closed the tractable ones. This issue tracks what is left.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 copyself_hosted/out/src→src/self_host/andcargo build.1. Higher-order functions (
Fn(...)parameters)Still reproduces exactly as filed. The host prints 12;
--self-hostfails at parse withExpected ',' or ')' in parameter list.Two layers, both unchanged:
skipTypeExprinself_hosted/domain/parsermatch.av:224handlesTkIdentandTkLParenbut has noTkFncase, so aFn(A) -> Bparameter type never parses. Add aTkFnbranch that skipsFn ( types ) -> ret. (TkFn/TkArrowtokens already exist.)ValFnRefis created atself_hosted/domain/eval/common.av:23when an identifier resolves to a fn, but never consumed.callWithArgs/callWithArgsNormal(self_hosted/domain/eval/core.av:387/:394) resolve the callee only as a top-level fn or a builtin and do not receiveenv, so a callf(x)wherefis a local bound toValFnReffalls through tocallBuiltin("f", …)and fails. Both the named-env path (evalCall,core.av:608) and the slot-env path (evalCallSlot,core.av:1047) would need it, and the resolver may also reject a param as a call target.The self-host is slot-resolved and was not designed for first-class fn values; this is a feature add, not a patch.
Worth weighing before anyone starts. Exactly one file in the 112-file example corpus uses an
Fn(...)parameter —examples/formal/oracle_independent_products.av:11— and it has nomain, so it is not even in the parity sweep.examples/core/lambda.avdoes run under--self-host, but it is a lambda-calculus interpreter over atype Termand exercises no Aver first-class functions at all. So this is a real gap with almost no corpus pressure behind it.2. Multi-line
?documentation continuationexamples/refinement/natural_app.avfails under--self-hostwithExpected expression, got Indent. Bisected to a?doc string followed by an indented continuation line, the shapefn adduses inexamples/refinement/natural/natural.av:exposes [f],exposes opaque [Nat]andverify ... law ... givenall parse fine — it is only the continuation. Small and self-contained, and fixing it takesnatural_app.avto parity.3. Literal smart-constructor discharge is fenced off
examples/data/json.avdiverges because the self-host refuses 0.28's literal smart-constructor discharge, loudly and deliberately —reject_literal_refinement_dischargeatsrc/main/commands.rs:1292, with the reasoning at:1277-1291. Fail-closed with a message, not a silent miscompile, so this is correct behaviour rather than a bug.It does raise a standing question this issue should carry: whether the self-host is meant to track host typing rules as they land. The note at
commands.rs:1287puts the cost plainly — mirroring the discharge "is not the three syntactic predicates the literal-divisor rule needed; it is the whole recognizer". That is a tax on every future discharge rule, and it should be decided rather than absorbed.Verification
Corpus parity sweep over every
examples/**/*.avwith afn main, host versus--self-host, diffing stdout:date.avis clock nondeterminism, andapp.av/app_dot.av/pricing_app.avdiffer only by a host-side stderr warning about unchecked dependency-module verify blocks — their stdout is byte-identicalnatural_app.av(section 2) andjson.av(section 3)Regen gate:
AVER_SELF_HOST_REGEN=1 cargo test --test rust_self_host_regen -- --ignored.