Skip to main content
Next (unreleased) · docs c218d875 · core f63c1e69 · built 2026-10-26 22:39 UTC

Name & 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.

Implemented by (2)
  • intern metel-frontend/src/identity/symbols.rs
  • new metel-frontend/src/identity/symbols.rs
Tested by (7)
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.

Implemented by (2)
  • add_explicit metel-frontend/src/pipeline/name_resolution/name_resolver/mod.rs
  • resolve_module metel-frontend/src/pipeline/name_resolution/name_resolver/mod.rs
Tested by (6)
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.

Implemented by (1)
Tested by (2)

Known limitations​

No active limitations are currently recorded for this section. This is a current inventory, not a claim of complete coverage.

Resolved (5)
resolvedLIMIT-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 ↗