Status: Accepted Related: ADR-003 D1 (the interface this is the Nikaia side of), ADR-001 D4 (Stage 0 is a transpiler — this is that transpiler), ADR-296 (the lowering construct by construct), ADR-300 (diagnostics, which need the generated source to be readable), ADR-021 D9 (how a backend that is not here refuses), ADR-260 D3 (the verbs that lower or interpret one file)
A .nika file has to become a program. Nikaia does not own a code generator and
does not want one: Rust’s own is better than anything this project could build,
and Rust’s borrow checker is what lets Nikaia promise memory safety without
implementing it (ADR-002 §3).
So the question is not how to generate machine code but what to hand rustc.
Two answers were available — a data structure, or text — and the cost of getting
it wrong is the same either way: two ways of producing a program means double the
maintenance and guaranteed drift between what is debugged and what is shipped.
crates/nikaia/src/emit takes the Nikaia AST and writes Rust, in a single pass.
The mapping for a construct — a let to a let, a grammar to a grammar!
invocation — exists in exactly one place, and that place is the only code
generator in the toolchain. rustc then compiles the text like any other Rust.
This is the whole of ADR-003 D1’s interface on the Nikaia side and
the whole of ADR-001 D4’s “Stage 0 is a transpiler”. There is no
second exit, no second representation and no alternative to contrast it with:
the only other way to run a file is nikaia interpret (ADR-260
D3), which emits nothing at all.
The lowering is syntactic and name for name (ADR-296 D17).
Where Nikaia and Rust disagree about what a name means, the emitted program says
so by not compiling, rather than the emitter guessing — and
ADR-300 is what puts that complaint back on the .nika line that
caused it.
The generated Rust is not a debugging view of some other artifact: it is what
rustc is given, byte for byte, and what --explain maps diagnostics back
through. So “readable” is a requirement on the one code generator rather than a
second feature that could drift from it.
Two consequences are worth naming, because both shaped the emitter:
SourceMap beside the code. A transpiler that emitted
only code could be told about errors only in a file nobody wrote, which is the
failure ADR-300 exists to prevent..nika file is the
source.The toolchain depends on rustc the way every program does — a command line and
a file — and on no internal of it. A new Rust release costs what it costs any
other crate in the ecosystem, and a Nikaia binary links libc and nothing
exotic, so it runs anywhere that Rust’s own output runs.
Text is parsed twice: once by the grammar, once by rustc. That round trip is
under one percent of the instructions a full compile retires, and the share
falls as a program grows, because every .nika file becomes a mod in one Rust
crate and the fixed cost is paid once.