A HIR project is valid when its nodes, identities, semantic side tables, and source provenance describe one internally consistent checked program.
Core invariants
For a project that may proceed to specialization:
- every referenced expression, statement, pattern, function, range, type, symbol, and string ID exists in its owning arena or registry;
- every expression has a resolved type;
- symbol expressions refer to resolved symbols, not textual names;
- ordered ranges are in bounds and preserve semantic source order;
- direct and witnessed calls carry consistent dispatch facts where analysis resolved them;
- nominal values have layout information before a consumer requires physical layout;
- every node has a direct or classified synthetic origin;
- function effect rows conservatively include behavior needed by later suspension and host-call analysis;
- error recovery is explicit and accompanied by an error-severity diagnostic.
An error type is permitted only to keep analysis and editor tooling moving after a diagnostic. It is not a backend-compatible “unknown” type.
Local arenas
Semantic analysis can build a LocalHir for each file. Its IDs are one-based,
file-local handles into arenas for:
- expressions and expression lists;
- statements and statement lists;
- patterns and pattern lists;
- functions;
- synthetic symbols and per-call dispatch.
Local IDs cannot be inserted unchanged into the project. Two files can both
have local expression 1, and a node can embed many IDs in children, arms,
ranges, captures, and side tables.
Relocation
Merging computes HirOffsets from the destination arena lengths, then
relocates every embedded identity:
project_id = local_id + destination_offsetThat simple equation is only safe when applied exhaustively. Relocation must cover:
- direct child node IDs;
- every start and end represented by a list range;
- block statements and tails;
- match arms and guards;
- lambda bodies, parameters, and captures;
- loop components;
- synthetic and body-local symbols;
- call-dispatch keys and any symbol payloads they contain.
Relocation walkers use exhaustive matching so adding an HIR variant forces the merge logic to account for it. A default arm that silently leaves unfamiliar IDs unchanged would create cross-file aliases and is not valid recovery.
Files are merged in canonical project order. Parallel checking may finish in any order, but scheduling completion must not determine project IDs or output bytes.
Side-table consistency
Side tables are allowed to be sparse only where their contract says absence is meaningful. Examples:
- a call-dispatch entry can be absent for a genuinely indirect or recovery-path call;
- a specialized-origin entry exists only for a generated specialization;
- a data query plan exists only for a checked data expression.
Absence must never be used to mean “the responsible pass forgot.” Consumers should distinguish optional facts from required facts at their stage gate and emit an invariant diagnostic when a required entry is missing.
Specialization gate
Generic HIR may contain type variables, schemes, generic symbols, and type matches. Monomorphic HIR handed to MIR must satisfy stronger conditions:
- all reachable function signatures and bodies are concrete;
- calls to generic functions name concrete specializations;
- instantiated nominal types have concrete layouts;
- compile-time type matches have selected and retained only their chosen path;
- substitution has updated types embedded in expressions, patterns, dispatch, and side tables.
Unreachable generic definitions may remain in the semantic project for tooling; they are not backend roots.
Recovery gate
HIR construction is diagnostic-first. A malformed source expression can produce a typed error placeholder, wildcard pattern, or synthetic recovery node so sibling declarations continue to be checked. The project remains inspectable but not compilable.
Before specialization or MIR construction, the driver checks diagnostics. Any
error-severity result stops the transition. Lower layers must not turn a
recovery node into zero, unit, unreachable, or an omitted operation merely
to complete an artifact.
Internal persistence
HIR derives serialization for internal compiler uses, including prechecked inputs. This does not make every project field persistent. Derived layouts, post-specialization mappings, options, and caches can be skipped and rebuilt.
A serialized HIR snapshot is valid only under its compiler-defined version and configuration. Numeric IDs have meaning only with the exact registries and arenas serialized beside them; they must never be joined across snapshots by matching raw integers.