v1.0.0
exile-lang compiles itself. src/ is the exile compiler written in exile:
lexer, parser, typechecker, move pass, escape pass, drop pass, codegen. It is
self-hosted in the strict sense, and the claim is a gate rather than a story —
make bootstrap-fixpoint builds the port with the OCaml compiler, has it emit C
for its own source, compiles that, and has the result emit C for the same
source again. The two outputs are byte-identical (50 681 lines, zero diff). A
non-empty diff fails the build.
The second claim is just as checkable: across all 89 examples, the C emitted by
the exile compiler is byte-identical to the C emitted by the OCaml reference —
whole-file, not modulo whitespace. Every intermediate layer is diffed too
(tokens, AST, typed IR, post-drop IR), including the diagnostics of passes that
emit no code: make selfhost-verify runs the lot, and CI runs it on every push.
That is what 1.0 asserts. It does not assert the OCaml compiler is retired — it
is not. The port reproduces the transformation on valid input; it does not yet
carry the typechecker's diagnostics, so the reference implementation remains the
compiler you hand a broken program to. Closing that gap is the next arc, and
until it closes the two compilers ship together, with the reference as the
oracle every gate above measures against.
Added
- A self-hosted exile compiler in
src/(~10 000 lines of exile), covering
the full pipeline:lex -> parse -> typecheck -> move -> escape -> drop -> codegen make bootstrap-fixpoint— the self-host proof as a build gate: the compiler
built by the compiler must emit byte-identical C for its own sourcemake selfhost-verify— every differential gate in one target (fixpoint,
tokens, lexer errors, AST, parser errors, typed IR, post-drop IR, escape
diagnostics), wired into CI- Boolean literal patterns:
trueandfalseare pattern atoms, matchable
against aboolscrutinee.true | falseis exhaustive without a catch-all
(see 0.11.6 below) str::from_slice(arena, slice)— the string-interning primitive: copy a
Slice<u8>into an arena as a NUL-terminatedstr. A tokenizer holds
identifiers as slices of its source buffer and materialises each one onceexamples/bool_match.exl,examples/str_from_slice.exl
Changed
- The compiler source moved out of
examples/selfhost/intosrc/.examples/
is the language's example catalogue again; the compiler is not an example docs/exile-by-example.mdno longer teaches a hand-writtenfree_treefor
owned recursive types — the drop glue is synthesized. A linear spine (a list)
is torn down with a loop, so it costs constant stack however long it grows; a
genuine tree keeps the recursive form and its depth lives on the C stack
Fixed
- Escape pass: a param beyond the summary bitmask's width degraded to
Unknown, which projects to "every param may carry the return" — so a
&localpassed at an unrelated position poisoned the result of a function that
never returns it, and the pass rejected valid code. The high bit now saturates
("some param at or past 31") and the call site meets over the tail arguments
only, keeping the imprecision where the unrepresentable index actually lives - Move / drop: owned-transfer consume parity across
letand reassignment;
an owned RHS of a field / deref / index assignment is now consumed; a
reassignment drops the old value after the RHS is evaluated, not before - Lift:
if-expression branches lift per-branch, so a block-shaped call
argument hoists correctly instead of tripping an internal error