c218d875 · core f63c1e69 · built 2026-10-26 22:39 UTCName & Reference Resolution
The pipeline stage after module loading (Module Loading & Parsing): every top-level declaration across the loaded ModuleGraph gets a stable SymbolId, import/glob visibility is settled per module, and every reference site is classified against that symbol space. Owning crate: metel-frontend. Defined in name_resolver.rs, reference_resolver.rs, and symbols.rs.
This is adjacent to, but distinct from, Resolution (arch.resolution.*, ADR-0054's identity-freeze model in identity/). That section covers structural LocalId/RefId/FieldId/VariantId allocation for lexical bindings and member declarations. This section covers an earlier, different concern: binding a top-level declaration to a SymbolId, and classifying which references point at one. A reader should not conflate the two symbol/identity systems just because both produce stable ids.
Model
Resolution begins by giving every declaration one canonical SymbolId; imports and
aliases then point back to that declaration rather than minting local replacements
(declaration identity). Import precedence is
settled before reference classification, so explicit imports and globs behave
predictably (import scopes).
Reference collection records the identity selected at each use site (references). Path normalization is a following, separate stage; both precede the structural identity freeze described in the Resolution section.
Verifiable architecture claims
Owner metel-frontend unless a requirement names another.
Requirement №1
Every top-level declaration gets a SymbolId from one SymbolTable keyed by (module, namespace, name), so a struct and a function of one name differ, and it keeps that id under any importer. Builtins are Builtin ids, std items Std ids fixed by the stdlib alone, user items Declared.
Requirement №2
An explicit import beats a glob, and two explicit imports of one local name in one namespace are an error. A user glob silently beats the auto-inserted std glob; T0011 fires only when two globs of the same tier export one name.
Requirement №3
Each bare-identifier reference is Res::Def (a top-level declaration, with its SymbolId) or Res::Local (parameter, let/mut, for or match binding); a local shadows a same-named global. Only Def references are recorded, keyed by Span, before the resolution freeze.
Known limitations
No active limitations are currently recorded for this section. This is a current inventory, not a claim of complete coverage.Resolved (5)
LIMIT-NAME-RESOLUTION-001 — std::core had no physical module file (resolved by ADR-0039). std::core had no physical module file; resolved by ADR-0039. Open record ↗resolvedLIMIT-NAME-RESOLUTION-002 — Overload SymbolIds come from a process-global counter that is never reset. Free-function overload ids are allocated from a static counter shared by every compilation in the process, so their values depend on what was compiled before. Open record ↗resolvedLIMIT-NAME-RESOLUTION-003 — The SymbolId space is split between three allocators by convention, with no cross-check. User, overload and block-local ids share one u32 split into ranges by convention; nothing guards the boundaries or overflow, and the table's map is public. Open record ↗resolvedLIMIT-NAME-RESOLUTION-004 — Builtin SymbolIds are a hard-coded list of 21 names; other std declarations are numbered per program. Only 18 builtin types and 3 aspects have fixed ids, kept in Rust beside the std::core module file; every other std declaration gets an id from a per-program sorted pass. Open record ↗resolvedLIMIT-NAME-RESOLUTION-005 — Symbol identity is a (module, name-string) key, so distinct declarations can share one SymbolId. ResolvedNames.symbols keys ids by module path and a name string: every declaration kind shares that key space, and a method's key drops its impl's type arguments, so different impls collide. Open record ↗