Memory Model
The runtime value representation and the GC-free allocation story.
Runtime values
NG::Value is a variant:
int64_tanddouble— integers (all widths are checked against their static width at runtime) and floatsstring— interned-styleshared_ptrstrings (deep-copied on bind)- arrays, tuples, structs, enums —
shared_ptr-backed aggregates; ordinary copies are deep (deepCopy), so values never alias through copy semantics - references — a (root cell, place steps) view; writes go through the cell, so mutations stay visible after rebinds
- trait views — a reference plus a dispatch-table reference
- opaque —
uint64_thandle tokens fornative funboundaries (type X = native;) - ranges — (start, end) pairs
Ownership at compile time
Copytypes (scalars, strings, tuples, derivedCopytypes) deep-copy on bind/call/return.- Affine nominal types move;
clonecopies explicitly;movemakes the transfer explicit; partial moves are field-aware. impl Dropruns exactly once per initialized value on every scope exit; drop edges are emitted by the lowering.ref/ref mutare scoped views — no returns, no aggregate storage (except recursive-enum self payloads) — and loans release at last use.
Allocation
There is no garbage collector. Allocation happens in two places:
- Aggregate cells — arrays/tuples/structs/enums are
std::shared_ptr-backed; cells are freed when the last value/reference goes away (deterministic, refcounted — not a tracing GC). - Native handles — the
memorystdlib module (allocate/load/store/release/outstanding) manages an embedding-owned slot table; the concreteBoxreleases its handle throughDrop. GenericBox<T>/Gc/Arcare deferred to the runtime-session work.
See the memory management guide for the language-level view.