Storage and Runtime Model

Storage Model (v0.1)

Storage representation is an implementation detail, but the runtime should follow these rules.

  • Semantics stay value-based (no observable pointer identity).
  • Layout for sized types is deterministic (size, align, field offsets).
  • Nested sized structs are stored inline.
  • Built-in indirection type constructor: *T.
  • *T is non-nullable and represented as the address of one heap allocation containing T inline.
  • Option[*T] carries nullable box references at the language level and uses option enum layout with pointer niche optimization in backends.
  • User-defined recursive/non-stack-allocatable types must use explicit *T at recursion/indirection points.
  • v0.1 uses no implicit boxing for user-defined type recursion.

Inline vs Handle-Backed

  • Inline storage is used for small/sized copyable values in locals and parameters.
  • Built-in aggregates (String, slices, arrays) manage their own internal storage and do not require *T.
  • Pointer-backed storage (*T) is used for explicit user-directed indirection and recursive graph-like user-defined data.
  • The immediate payload of *T must not itself be a built-in heap-handle type.

Copyable and Linear Storage

  • Copyable heap-backed values use eager copies at copy boundaries.
  • v0.1 rule: non-stack-allocatable linear user-defined values require explicit *T indirection.
  • Linear values are move-only, but borrow safety still applies.

Iteration and Move-Out

  • Iteration does not move elements out directly.
  • Consuming traversal is expressed by moving the container binding (for (<-collection) |x|).

Runtime Representation

Value Categories

  • Immediate: inline scalar (i64, Bool, small enums).
  • Heap object: pointer to cell with metadata header.

Heap Header (minimum)

  • Type tag/layout id.
  • Flags.

Current implementation status:

  • Generated Rust pointer copies clone pointee values into fresh cells.
  • Generated WASM pointer copies allocate fresh cells, and release frees pointer cells on drop.
  • Pointer release also recursively walks heap-backed pointee payloads before freeing the outer pointer cell.
  • Generated WASM runtime strings are stored in linear memory; literals stay static and owned string values are freed on drop.
  • Generated WASM array storage frees backing allocations on drop.
  • Array release walks nested pointer/string/array elements before freeing the outer storage.
  • Nested tuple/struct aggregate fields are recursively walked during release, then aggregate storage is freed.

Interpreter/VM Requirements

Minimum conceptual bytecode operations:

  • ASSIGN_COPY dst, src
  • ASSIGN_MOVE dst, src
  • BORROW_RO dst, src
  • BORROW_MUT dst, src
  • DROP x
  • field/index mutation ops

The VM must emit precise drops at all scope/control-flow exits.

Runtime IR Boundary (v0.1 direction)

  • Runtime execution must not rely on source-level TypeExpr annotations.
  • Type/mode validation is performed in a checker pass before runtime IR generation.
  • Checker failures are hard errors and must stop execution.
  • Runtime IR carries only execution data (locals, blocks, ops, control-flow edges), plus dynamic runtime values.
  • Runtime checks remain for dynamic semantics (use-after-move, borrow exclusivity/lifetime, Bool condition enforcement).

Runtime IR Shape (Wasm-like, custom)

  • Use block + terminator CFG shape similar to Wasm control flow.
  • Keep ownership effects explicit as IR ops (Copy, Move, BorrowRo, BorrowMut, Drop).
  • Prefer dense entity IDs (cranelift_entity) and contiguous storage to minimize pointer chasing.
  • Resolve call targets to function IDs during lowering (avoid runtime name lookup in hot paths).