c218d875 · core f63c1e69 · built 2026-10-26 22:39 UTCResolution
This section is the Architecture Spec's versioned description of how the
frontend resolves source spellings to stable identities, and the boundary that
binds typechecking, elaboration, and evaluation to those identities instead of
re-deriving meaning from names. It documents the implemented resolved-identity
model described by ADR-0054;
the arch-* requirements below are its checkable claims.
Owning crate: metel-frontend. The model is defined in identity/ (mod.rs, allocate.rs, lexical_path.rs, member.rs, position.rs), and consumed by name_resolver.rs, reference_resolver.rs, typed_ast/, and every later pipeline stage.
Model
The resolution freeze separates source spelling from durable semantic identity. Every reference is total—local or global—and later stages consume that result rather than looking the spelling up again (frozen references). Lexical bindings and uses receive structural identities that survive unrelated edits (lexical identity); fields and variants receive the same treatment at the declaration level (member identity). The walk leaves no code uncovered: a module's bare top-level statements get identities too, and a top-level declaration's span maps to its symbol (coverage).
Editor lookup is intentionally different. A position index maps an ephemeral parsed snapshot back to these identities, but position never becomes a semantic key (position isolation). This distinction lets tools be responsive without making formatting alter program meaning.
Verifiable architecture claims
Owner metel-frontend unless a requirement names another.
Requirement №1
Once inference has solved a body and the frontend has frozen its resolution, no later phase looks anything up by source spelling: every meaning is found by a stable identity, and every value reference has a total Resolution (Global(SymbolId) or Local(LocalId)), with no silent unresolved case.
Requirement №2
Bindings and value references get structural identities (LocalId, RefId) from a parse-driven walk keyed on LexicalPath, not on byte offsets or traversal counters, so reformatting, an earlier sibling binding, or an edit to another body does not change an existing identity.
Requirement №3
Struct fields and enum variants are interned to stable FieldId / VariantId, keyed by (owning type SymbolId, member name) before inference, and threaded onto the typed IR as Option<FieldId> / Option<VariantId> rather than re-derived from a string.
Requirement №4
PositionIndex is the one byte-position-keyed lookup: rebuilt from a parsed snapshot, never persisted, never a semantic input. BindingSpans is a transient construction-time bridge; every durable resolved artifact (ResolutionMap, the frozen IR) is keyed by identity, not position.
Requirement №5
The identity walk covers every body of code, including a module's bare top-level statements (walked as one block under a per-module owner), and maps a top-level declaration's own span to its SymbolId.
Known limitations
LIMIT-RESOLUTION-001 — Resolved-identity model is in-memory only. Resolved identities exist only in memory; nothing persists them across processes. Open record ↗knownLIMIT-RESOLUTION-002 — Inference-time type lookup falls back to a name-approximate, cross-module search. Inference falls back to a name-based, cross-module type lookup where the declaration id is missing, which can confuse same-named types. Open record ↗