Memory Model
Overview of the three tiers
| # | Tier | Owns | Reclaim strategy | Determinism |
|---|---|---|---|---|
| 1 | Atomic Reference Counting (ARC) | Mutable heap: objects, dynamic collections, OS handles | Immediate free on last reference release | Immediate, synchronous |
| 2 | Region-Based Arena | Transient rule/block locals | Single-instruction unwind on scope exit or zap | Immediate, scope-bounded |
| 3 | Compacting Generational GC | Immutable strings, slices, rope buffers | Background two-space copying compaction | Amortized, lock-free |
Tier 1 — Atomic Reference Counting (ARC)
ARC is the default management for mutable dynamic structures, object instances, and system resources (files, channels, sockets). It guarantees immediate, deterministic resource cleanup when the last reference is released.
Allocation scope
- Objects (object)
- Dynamic collections (array, map, list, set)
- System OS handles (files, channels, sockets)
Header layout (16 bytes)
Every ARC-allocated object carries a standardized 16-byte header preceding a variable-width payload:
+-------------------+-------------------+-----------------------+
| RefCount (64bit) | TypeID (32bit) | Flags / Metadata(32) |
+-------------------+-------------------+-----------------------+
| Payload Data (Variable Width) |
+---------------------------------------------------------------+
Operational semantics
- On assignment / copy: AtomicInc(RefHeader.RefCount)
- On scope exit / re-assignment: if AtomicDec(RefHeader.RefCount) == 0 then DestructAndFree(Object)
- Destructor execution is immediate and synchronous, releasing backing native resources (e.g. file descriptors) on the calling thread.
Tier 2 — Region-Based Arena & zap
Every rule (and nested block frame) allocates a light, stack-bound Region Arena (2 KB initial chunk). Short-lived locals allocate directly from the active arena offset with no per-object heap allocation (ptr = arena.top; arena.top += size).
Region cleanup
On block termination or an iteration jump (return, done, next), the arena offset is unwound in a single instruction (arena.top = arena.base), instantly reclaiming all region-allocated memory.
Manual deallocation (zap)
zap identifier;
In Hot Zones (performance-critical loops), zap explicitly invalidates the pointer and resets the target memory slot immediately.
- Static analysis invariant: the compiler performs lifetime tracking. Reading or writing an identifier after zap in the same control-flow graph is a compile-time error E0801: AccessAfterZap.
- Runtime safety guard: in non-optimized debug builds (-d), zap zero-fills the target pointer slot, so a later dereference raises Panic: UseAfterZap.
Tier 3 — Compacting Generational GC (immutable strings & ropes)
A special-purpose garbage collector is dedicated to immutable string literals, dynamic string concatenations, and rope nodes. It guarantees efficient interning, zero fragmentation, and lock-free thread-safe sharing.
GC roots
- Thread execution stacks
- Region Arenas
- ARC object payload pointers that point into the String Heap
Compaction
The generational GC runs independently on background threads. Because string payloads are strictly immutable, compaction uses two-space copying without requiring write barriers on reader threads.
Assignment & mutability semantics
Two assignment operators govern how values are written; their behavior depends on the storage class of the target (native vs boxed vs reference).
| Operator | Meaning | Effect |
|---|---|---|
| := | Assign | With new: allocate a new storage location. With let: update the existing value in place. |
| :: | Clone | Boxed/objects: deep copy (structural clone) into an independent heap payload. Primitive natives: equivalent to :=. |
Value modification rules (let with :=)
- Native variable → value mutated in place.
- Boxed variable → the boxed value is changed in place.
- Reference on the right-hand side → value is transferred by deep copy.
Mutability modifiers
new identifier ((∈) | in) type_specifier ; (* declare *)
let identifier (:= | ::) expression ; (* mutable update *)
alter identifier ... (* modify an existing element *)
- new — declare and allocate a fresh storage location.
- let — update the value of an existing variable.
- alter — modify an existing variable in place (explicit mutation).
Formal EBNF
(* Memory Management Statements *)
zap_statement ::= "zap" identifier ";" ;
new_statement ::= "new" identifier [ ":" type_specifier ]
[ ( ":=" | "::" ) expression ] ";" ;
let_statement ::= "let" identifier ( ":=" | "::" ) expression ";" ;
(* Variable Mutability Modifiers *)
variable_decl ::= "new" identifier ( "∈" | "in" ) type_specifier ";" ;
mutability_prefix ::= "let" | "alter" ;
Concurrency & cross-thread boundaries
- Thread isolation: spawned tasks (begin) run in isolated Region Arenas.
- Immutable sharing: GC-managed immutable strings and ropes cross thread boundaries without locks or reference copies.
- Mutable ARC transfer: passing a mutable ARC object to a spawned thread increments its atomic reference count (LOCK XADD).
- Parallel error isolation: an uncaught exception or panic in a worker thread freezes that thread's local Region Arena and captures diagnostic context into the thread-local $trial handle, preventing corruption of parent state.
Diagnostics & safety protocols
| Code | Violation | Mitigation / diagnostic action |
|---|---|---|
| E0801 | AccessAfterZap: use of identifier after zap | Compile-time fatal error with source line location; runtime Panic: UseAfterZap under -d. |
| E0802 | RegionEscape: pointer escapes Region Arena to outer scope | Compiler automatically promotes the allocation from Region to ARC Heap. |
| E0803 | ArcCycleLeak: unhandled cycle in an ARC structure | Static lifetime analysis, or runtime leak warning under -d. |
| E0804 | CrossThreadUnsharedMutation: invalid cross-thread mutation of an unshared reference | Compile-time data-race restriction. |