Skip to main content
v0.14.0

The SymbolId space is split between three allocators by convention, with no cross-check

Limitation​

SymbolId is a u32 newtype whose range is partitioned by comment (symbols.rs): builtins occupy 1-99, 1000 + is the name resolver's, and 0x4000_0000 + is the overload allocator's. A third allocator, in type_engine, hands block-local struct/enum ids from the top of the u32 space counting down (fresh_local_type_id, saturating_sub). The three are independent, and only a unit test asserts one boundary (second.0 < OVERLOAD_SYM_START). At runtime:

  • SymbolTable::intern advances with next_id += 1 on a u32: it panics on overflow in a debug build and wraps in a release build;
  • the overload counter's fetch_add wraps silently;
  • nothing checks that the resolver's range stays below OVERLOAD_SYM_START or that the two ascending allocators stay clear of the descending one;
  • SymbolTable.map is a pub field, so code outside the type can insert an id that bypasses intern and the counter entirely.

Impact​

Purely theoretical at realistic sizes: the resolver range would have to reach about 1.07 billion declarations before it met the overload range. The risk is structural, not practical: identity uniqueness rests on three allocators respecting comment-level ranges, so a future change that adds a fourth allocator, widens a range or inserts into map directly has no safety net. There is no observable symptom, so no reproducing fixture.

Affects​

Resolution​

None yet. Typed allocators (one owner for the whole space, with range checks and a private map) would turn the convention into an invariant.