Wado

Wado Compiler

The Wado compiler (wado-compiler/) translates .wado source into a Wasm component. This document is the map: the phases, the IRs between them, and the rules that hold across them. The WEPs linked below say why each phase has its shape. Each module's doc says how it works.

Pipeline

Phase Output Where
Load AST per module loader.rs, lexer.rs, parser.rs, bind.rs
Analyze Symbol table analyze.rs
Resolve Declarations defs.rs, resolve.rs
Annotate Semantics elaborator/
Liveness Reachability elaborator/
Reify TIR per module elaborator/
Check Diagnostics effect_check.rs, resource_move_check.rs
Synthesis TIR synthesis/
Link One flat package link.rs
Monomorphize / Erase Concrete TIR monomorphize/
Lower NIR lower/
Optimize NIR optimize/
WIR Build WIR wir_build/
WIR Optimize WIR wir_optimize/
Codegen Component bytes codegen/

The driver is compile_with_options in src/lib.rs. It loads the modules, compiles any inline providers, and hands the rest to compile_after_load. The LSP runs the same phases up to liveness and stops there.

IRs

IR What it is
AST Surface syntax, kept as written so wado format round-trips.
TIR Typed IR. One per module after reify; one flat package of every item after link.
NIR Normalized IR, what the optimizer rewrites. See WEP: NIR.
WIR Close to Wasm core instructions. See WEP: WIR Layer.

Codegen reads NIR and WIR only, and knows nothing of the phases before them.

Frontend

The parser builds a faithful AST: compound assignments, comparison chains, struct shorthand and comments survive as written. Bind checks local bindings: duplicates in one scope, reads before initialization, and assignments to immutable locals. Whether a name is in scope is answered where it is resolved, in the elaborator.

The loader reads the entry module and everything it imports: the embedded standard library, WASI and Web bindings, local files, package dependencies, remote URLs, Wasm assets, and Kiln output. Each file has one identity however it was imported. See WEP: Module Loader.

Elaboration

The elaborator resolves, infers, and dispatches, then emits TIR (WEP: Elaborator):

The AST stays as the parser built it. Desugaring (loops, compound assignment, assert, matches, templates, namespace prefixes) produces no synthetic AST: annotate records what the rewrite needs on the source node, and reify emits the rewritten TIR. That is why hover, go-to-definition, and rename land on the user's text, from the same facts the batch compiler uses.

Every trait call is resolved statically. By the end of the pipeline each call targets one concrete function, so there is no vtable (WEP: Trait Resolution, WEP: Overload Resolution).

A type parameter is rigid inside the item that declares it: nothing but itself is assignable to it. An inference variable is flexible: it takes the type the solver finds. Each use of a generic signature replaces its parameters with fresh inference variables. No inference variable survives elaboration, and no type parameter survives monomorphization.

Checks

Before synthesis, the compiler checks that every function declares the effects it performs, that default arguments and global initializers are pure, and that no resource is used after it moved (WEP: Effect System, WEP: Ownership Analysis). What a function retains is not checked: lower infers it (WEP: Value Semantics).

Synthesis

Synthesis generates the TIR the user does not write:

Link merges the modules into one package. Compile-time parameters (#[param]) then take their -D values. Monomorphize instantiates each generic item per concrete type argument and expands variadic packs. Then newtypes collapse to their base type and flags to u32. Dispatch needed the distinction, and nothing after it does. Functions nothing reaches are dropped before lower.

Lower

Lower turns TIR into NIR in two halves. First the planner decides how each construct is represented:

Then the translator folds TIR into NIR in one pass.

Backend

WIR build translates NIR to WIR and plans the component's imports, exports, and adapters. WIR optimize runs the Wasm-shaped passes in optimizer.md. Codegen emits the core module with its branch hints, wraps it in a component, embeds imported Wasm assets pruned to what the component uses, and validates the result.

Component Model

The compiler reads WASI and Web interfaces, worlds, and builtins from their declarations in the standard library rather than hardcoding them. A function is imported only if the program calls it and every type in its signature is supported. A canonical operation such as future.read is typed per payload, so each payload type gets its own core import. See WEP: WIT and Wado Mapping.

Kiln

Kiln turns an input file (a schema, a grammar, a Wado dialect) into .wado source. A generator is an ordinary Wado package targeting the core:kiln/generator world. wado-cli builds and runs it, and caches the output by its inputs, options, the name it was invoked by, and the generator's source. The compiler holds only the pure-data half: the invocations, their order, cache keys, and option checks. It redirects an import to the generated source. See WEP: Kiln.

LSP

wado-lsp/ is a thin layer over the compiler's Semantics: each query parses, loads, and elaborates, then reads the facts at the cursor. The engine performs no I/O. The caller supplies the host that loads modules, so the engine runs in VS Code and in the browser alike. That is why wado-compiler must build for wasm32-unknown-unknown. See WEP: LSP Architecture.

Standard Library

The standard library is Wado source under lib/, embedded in the compiler. lib/core/ is written by hand, except lib/core/kiln/. wado-from-idl generates that directory, lib/wasi/, and lib/web/. Deterministic math is a bundled core Wasm module, linked into the component and pruned to the functions called (WEP: Deterministic libm).

The world selects the allocator, and --allocator overrides it.

Allocator Default for Behaviour
bump CLI and other worlds Never reclaims.
freelist HTTP service, libraries Reuses freed blocks.
debug Test world Never reuses freed memory, and poisons it with 0xFF.

Known Limitations