The runtime beneath both execution paths
LLVM can emit scalar arithmetic directly. Lists, class lifetime, worker transfer, text storage, and source-aware traps use the native runtime shared by compiled programs and the test interpreter.
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
luce_rt_* C exportsBoth execution paths call the same runtime operations for lists, text, objects, workers, and traps. A differential test can then focus on whether native code and MIR dispatch invoke those operations in the same order with the same values.
The runtime modules
heap.zig + value.zig24-byte values, object rows, generations, strong counts, weak handles, allocation, and trap state.
containers.zigLists, maps, arrays, builders, element ownership, bounds, and immutable roots.
text.zigUTF-8 and bytes storage, inline text, validation, conversions, and scalar operations.
operators.zigChecked arithmetic, comparison, numeric conversion, parsing, formatting, and math primitives.
Files, sockets, and graphicsRuntime-owned resource handles installed from optional host service channels.
workers.zigPrivate runtimes, graph transfer, thread nursery, effect lock, task result, and join.
trace.zigTrap state, bounded call frames, stable locations, and one completed report.
exports.zigThe C ABI that generated objects call and effect metadata verifies.
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 holds object-table handles containing an index and generation. This indirection lets a list reallocate its buffer while aliases keep the same handle, prevents an old weak handle from matching a reused row, and lets the runtime 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 has its own run status. Callers can therefore distinguish a recoverable language error, a program trap, and exhausted machine resources.
The library stands below the compiler
Dependency arrows point from the compiler and runners toward the runtime. Shared enum vocabulary—trap codes, error codes, operators, and service failures—lives under support/. This keeps front-end parser and analyzer code out of the library linked into every artifact.