Issue #583: Self-hosting emit: the rest of the core (E9 U9-U10, E10, E11)

Open and answer this issue on GitHub.

selfhosting, compiler, ready · opened 2026-10-12 · updated 2026-10-12 · 0 comments

How it came up

Part of #125 / #558. emit/mod.rs is moved to Nikaia in sequential units through a host trait (EmitHost), decided by a read-only architecture review. The plan, the method form, the oracle and the risks are in docs/selfhosting-plan.md, section “E9 in units”. Cost and speed of the runs so far: docs/tokenspeed.md.

Today (develop, 0.0.848)

Done: E1-E8 and E9 U0a-U8 (Out, EmitState/EmitWalk/EmitWorld, EmitFlow/EmitScope, the layer below the core, leaves, reads, match, args, calls, places, flow): commits 0bb1721 … f46ac49c. scripts/self_hosting.py shows the share.

To build (strictly one unit at a time; package.rs and the version files are shared)

  1. U9 tools/emit_core_stmt.nika: all 15 arms of a_stmt (Let, Comptime, Assign (5 guarded arms together, original order), While, For, Break, Return, Expr …), one Nikaia function per variant; the Rust a_stmt stays the dispatcher until U10.
  2. U10 tools/emit_core_expr.nika: the remaining arms of expr_as_written (literals, Variable x6, Dsl, Path, Block, Unsafe, Overlap, Select, If), the methods expr, expr_after_the_copy, expr_as_written, block_opening_with, block_plain, stmt, a_stmt. Replace every host.expr( / host.block_opening_with( by self.emit_core_* and delete those trait methods; the Rust expr/block/stmt stay as adapters for the layer above until E10. Measure with callgrind (Ir and mispredictions, --branch-sim=yes) before U0 and after U10, as the plan says; no callgrind before U10 (each crossing allocates an EmitScope).
  3. E10 the layer above (function, item, function_body, program, grammar, fold) as tools/emit_items_core.nika with Declared/MethodOf as owned structs.
  4. E11 emit/mod.rs shrinks to the adapter (with_contracts, entry points, SourceMap, Build, EmitRefused to anyhow); update scripts/self_hosting.py.

Per unit (see the plan’s task template): lower-std with the previous binary and the new one, package.rs identical except the additions; scripts/c6/oracle.py OLD NEW . 0 differences; clippy, fmt, the tests the moved code touches + examples, sysroot, self_hosting, refusal_lines, emit_deep_nesting after the final rebase; python3 scripts/bump.py new/rebase-fix/check; no wip commits; at most ~120 turns per agent.

Known traps: a host method must not touch self.walk(..) (pass the walk in); take_lend/insert_after_first_brace stay Rust (no String::pop/insert_str in std); reached_copy is not restored after a thrown error (no try/finally); prover strings use rust_string where Rust used {:?} (differs only for exotic control characters).

The commit that finishes E11 says Fixes #<this issue>.

Back to Open Issues