Expand description
The interned small-Int range (§4.3).
§4.3’s uniform object model is normative “even if later optimizations intern
small integers, use tagged pointers, or eliminate allocations through escape
analysis” — provided such an optimization “preserves reference and aliasing
semantics”. For Int there are none to preserve, and the language already
ships the existence proof: Unit and Bool are interned singletons, so
every true in every program is one object
(crate::immortal::Immortals).
Why sharing an Int is unobservable. There is no identity operator in
the language — praxis_hir’s BinOp is arithmetic, comparison and the two
logical connectives, and nothing else. == on Int lowers to Inst::IntCmp
over extracted payloads; the structural fallback praxis_struct_eq has no
pointer fast path either. Map/Set/Counter keys go through
DynamicKey, whose eq does open with a
pointer comparison — but that is a fast path for structural equality, and
int_equals is reflexive, so sharing an object can only make it fire more
often, never change the answer. And an Int payload is never written after
its allocation: Inst::StoreScalar has no builder site and the backend’s arm
for it is a documented no-op.
Why not Float or Text. Float fails the reflexivity argument that
carries DynamicKey’s fast path — float_equals is IEEE, so NaN ≠ NaN — and
interning it would make two separately-written NaN literals compare equal as
map keys. Text fails a different test: TextPayload::Owned(OwnedText) is
not Copy, and Heap::alloc_immortal requires Copy
because an immortal is invisible to Heap’s Drop — an immortal Text
would leak its Box<str> at teardown.
Char passes every leg of the argument above, and crate::small_char is
the second interned scalar range (ADR-107): char_equals is a reflexive
u32 ==, a CharPayload is Copy, and ASCII is a bounded set. It is a
separate module rather than a second constant here because the two ranges
have different consumers — this one is read by three crates and by generated
code, and that one only by the runtime.
This module is the one statement of the range. praxis-mir asks
index_of whether a literal is in range at compile time and praxis-runtime
asks it again at run time; the Cranelift backend derives the element offset
from the same SMALL_INT_MIN. A second spelling of the bounds anywhere
would let the compiler emit a table read for a value the table does not hold.
Constants§
- INLINE_
INTERN_ SITE - Everything the Cranelift backend bakes in to answer an in-range
Intinline, as one value (ADR-113). - SMALL_
INT_ COUNT - How many
Ints the table holds — the length ofImmortals::small_intsand the bound every index derived fromindex_ofrespects. - SMALL_
INT_ MAX - The highest
Intthe runtime interns. - SMALL_
INT_ MIN - The lowest
Intthe runtime interns. - SMALL_
INT_ STRIDE - The size of one table element, for the backend’s element-offset arithmetic.
Functions§
- index_
of v’s index in the interned table, orNoneifvis outside the range.