c218d875 · core f63c1e69 · built 2026-10-26 22:39 UTCBuiltin SymbolIds are a hard-coded list of 21 names; other std declarations are numbered per program
Limitation
SymbolTable::new pre-seeds exactly 21 std::core names as Builtin ids: 18 types
(boolean, String, Char, the sized integers and floats, List, Option,
Result, Range, RangeInclusive) and 3 aspects (Display, Iterable,
From). The list is the Builtin enum, declared once by a macro (variant, std::core name,
kind); the SYM_TYPE_* and SYM_ASPECT_* constants are aliases of its variants. There are no
numbers to keep apart any more (ADR-0065). Every other std::core
declaration, for example Copy and Drop, is interned by the name resolver
like user code. intern_all_symbols sorts every declaration by (module path, name) before interning (metel-core#1129), so an id does not depend on the
order modules were loaded; but it is the declaration's rank in that sorted list,
so it shifts whenever a program adds or removes a declaration. Code that needs
these std items looks them up by (module, name) (coherence.rs does this for
Copy and Drop).
Impact
- Builtin identity has two sources of truth: the Rust
Builtinenum here and thestd::coremodule file (ADR-0039). Adding a std type that the runtime must register under a fixed id means editing both; a test checks that each builtin is either declared instd::coreor one of the intrinsic types, so drift is caught, not prevented. - The ids of std declarations outside the 21 are stable for a given program whatever the load order, but they are ranks in that program's sorted declaration list, so they cannot be treated as well-known across programs.
None of this is observable from a Metel program. It is read from the code, so there is no reproducing fixture.
Affects
arch.name-resolution.requirement-1metel-frontend/src/identity/symbols.rs(SymbolTable::new,Builtin, theSYM_*constants)
Resolution
Resolved by metel-core#1542, #1546 (PR #1548) and #1559.
- #1542 and #1546: the 21 builtins are one
Builtinenum, declared once by abuiltins!macro (variant,std::corename, kind); theSYM_*constants are aliases of its variants, andSymbolTable::newand the evaluator's lookups read from the enum. After ADR-0065 step 1 the builtins carry no numbers, so the hand-numbered ids and the unused reserved ranges are gone. A test checks that each builtin is either declared in the embeddedstd::coreor one of the 15 intrinsic types (not both, not neither), so the enum andcore.mtlcannot drift unnoticed. - #1559: every other std declaration is a
Stdid (ADR-0065 Decision 5), numbered from the embedded stdlib alone: the module's position in the embedded table and the declaration's rank among that module's own sorted(namespace, name)keys. It no longer shifts when a program adds or removes a declaration, and it is the same in every program and whichever std modules are loaded. User declarations stayDeclared. Code that needsCopyorDropstill looks them up by(module, namespace, name), which is now stable.
What stays by design: the Builtin list is written by hand in Rust. Fifteen of its entries are intrinsic
types (i64, String, ...) that no core.mtl declaration describes, so there is nothing to generate them
from, and the other six are checked against core.mtl by the drift test. That is the single place a
builtin is added, and a mismatch fails a test; it is not a limitation of the resolver.