Luce / engineering
Learn Luce LuciaOS

The runtime beneath both execution paths

Assembly can add two integers by itself. It cannot grow a Luce list, keep a class alive, copy a worker graph, or report a source-aware trap. Those jobs live in the native runtime shared by compiled programs and the test interpreter.

A useful dividing line

If an operation depends only on scalar registers, LLVM can often inline it. If it needs Luce’s heap, object table, host capabilities, or error state, generated assembly calls the Zig runtime.

Where the runtime sits

Two callers, one semantic libraryDispatch differs; behavior does not
Native codecalls the published luce_rt_* C exports
Interpretercalls the Zig runtime surface from its MIR dispatch loop
libluce_rtone heap, value, container, text, operator, resource, worker, and trap implementation

If code generation implemented its own list rules, the test interpreter could quietly become a different language. If only the interpreter had a rule, tests could pass for behavior users never run. Both paths therefore call the same runtime operations.

The runtime modules

The 24-byte value passed around the runtime

Runtime operations exchange a C-layout 24-byte Value. A tag selects the view: scalar bits, inline or outside text, an object handle, absence, a function pair, or another defined representation. Zig callers use a tagged view(); generated code uses constants whose layout is asserted against the Zig struct.

Handles make moving storage safe

A program does not hold raw pointers to collection buffers or class storage. It holds an object-table handle with an index and generation. A list may reallocate its buffer without invalidating every alias; a stale weak handle cannot match a reused row; the runtime can count and inspect all live objects at the end of a run.

Failure at the runtime boundary

A fallible C export returns a status and writes results through out-pointers. A trap is recorded in the runtime once, then propagated through generated frames. Allocation exhaustion is a separate run status rather than a catchable language trap. This keeps language failure, program violations, and machine exhaustion distinguishable.

The library stands below the compiler

No runtime file imports a compiler stage. Shared enum vocabulary—trap codes, error codes, operators, service failures—lives under support/, below both. A library linked into every artifact must be understandable and buildable without pulling the front end into the program.