Semantics and typed HIR
The parser can tell that value * 2 is multiplication. Semantics proves which value it is, lands 2 as an i64, chooses checked signed multiplication, and records the overflow rule.
This is where Luce decides language meaning. LLVM is not asked to resolve a name, infer a type, choose a method, or decide whether failure is handled. By the time LLVM appears, those questions are closed.
Why resolution and typing stay together
Resolving xs.append(value) needs the type of xs; typing the call needs the member that resolution found. Flow narrowing changes the type visible at a later expression; call selection changes the effect a containing function must handle. These are mutually recursive questions, so the code presents one semantic stage with concern modules rather than fictional independent passes.
Which declaration does this spelling denote? Is it visible? Has it been shadowed or used before initialization?
What exact representation flows here? Which member, conversion, return shape, or witness applies?
Is an optional narrowed? Does every path return? Can failure propagate? What must be released on each edge?
The two semantic spines
Analyzer
Collects modules, declarations, aliases, layouts, constants, signatures, interfaces, defaults, and the entry point. It settles the world in which bodies will be checked.
FunctionBuilder
Checks one body with scopes, narrowing, local state, temporary lifetime, receiver effects, and an HIR recorder.
Concern files provide small operations over those spines: calls.zig chooses a callable, assign.zig plans a place and replacement, closures.zig plans an environment, initializers.zig proves class fields, and interfaces.zig constructs witnesses.
Checking and recording happen together
The check that chooses meaning records that choice immediately into typed HIR. A numeric literal gains its landed width. A method call gains its resolved target and receiver store behavior. A closure gains its capture kinds. An assignment gains its destination place and the releases that replacement requires.
Nothing in semantics emits a MIR instruction. The stage records structured, typed meaning through one recorder API. Register numbering, basic blocks, constants, and instruction assembly belong downstream.
The last word on source-level rules
This is the only stage that can still explain a source mistake. It enforces immutable bindings, explicit numeric conversions, host access, class construction, interface conformance, recursive layout limits, closure cycles, worker sendability, return coverage, optional narrowing, and visibility.
| Rule | Why MIR cannot reconstruct it |
|---|---|
let cannot be rebound | MIR sees a local protocol, not the author's binding promise. |
| A class cannot cross a worker | The static graph may contain the class transitively inside values and containers. |
| A public signature cannot expose a private type | Visibility is a source-module relation erased before code generation. |
A closure cannot store a direct strong self cycle | The capture plan and destination relationship are clearest at the source expression. |
Failure produces no partial program
Diagnostics are bounded and carry stable codes plus byte spans. When any semantic error exists, analysis returns no Analyzed value. A later stage never receives a tree with known bad names or types and therefore never needs recovery branches for user mistakes.