Expand description
Value-word core: every DSL value is a fixed-size word (a TypeDescriptor
vtable pointer plus a 64-bit ValuePayload) interpreted through that
vtable.
There is exactly one representation for every type. Payloads that fit in
64 bits (integers, floats, booleans, handles, and host scalars annotated
#[oxdock_type(inline)]) ride directly in the payload; everything else
rides behind a thin pointer to either an owned Box<T> (exclusive heaps:
STRING, PATH, DURATION, PIPE, most host types) or a shared
Arc<T> (shared heaps: LIST, MAP, and host types annotated
#[oxdock_type(shared)]). The vtable owns the lifecycle (clone, drop)
and operations (eq, fmt), so Clone/Drop/PartialEq/Display on
Value delegate instead of matching. There are no dynamic trait
objects anywhere in this path: every hook is a monomorphic function
pointer reached directly, with no table lookup and no lock.
Descriptors are canonical singletons: each #[oxdock_type] struct gets
one &'static TypeDescriptor (built at compile time, shared by every
word of that type), so words carry their own vtable and no registry of
any kind exists. The ten startup types (INT, FLOAT, STRING, BOOL,
LIST, MAP, PATH, DURATION, PIPE, HANDLE) are ordinary Rust
structs annotated with #[oxdock_type], exactly as host types are. Name
directories (which descriptor answers for "TAG") live per execution
state in oxdock-core, never here: this module knows types, not names.
Ownership discipline (load-bearing, Miri-verified in
crates/oxdock-core/tests/miri_value_words.rs):
- Exclusive heap
Values own their box exactly once.cloneallocates a new box;dropfrees it. No sharing, no aliasing. Because each box holds a concrete sizedT, its pointer is thin: no double-boxing, no fat pointer casts, no metadata to lose. - Shared heap
Values (LIST,MAP) co-own anArc<T>buffer.clonebumps the strong count inO(1)with no allocation;dropreleases one count and frees only the final word’s drop. Because the DSL exposes no interior mutability, aliases, or reference syntax, container graphs are strictly acyclic trees, so refcounting reclaims deterministically with no tracing collector. Mutable access goes only throughValue::read_heap_mut, which detaches (clones the buffer) whenever the strong count exceeds 1, so a writer always exclusively owns a private buffer and clones never observe each other’s writes. Deriving&mutfrom a payload any other way is unsound. - Pointer casts are always
Box::into_raw/Box::from_raw(exclusive) orArc::into_raw/Arc::from_rawplusArc::increment_strong_count(shared) round trips on the same concrete payload type, which preserves provenance. Inline words never touch the pointer domain; heap words never touch the integer domain. - Minting a word with a descriptor built for a different Rust type
misdirects the vtable and is unsound. The
mint_*constructors document this contract; hosts mint through the payload type’s ownOxDockType::descriptor(), which cannot mismatch by construction.
Structs§
- Type
Descriptor - Vtable for one type: lifecycle plus operations. All hooks are plain
function pointers (never closures) so descriptors stay
Copyand the global table hands them out by value. Every hook documents the payload domain it expects; calling one with a foreign payload is unsound, and every call site is a single choke point reviewed with the layout. - Value
Traits§
- OxDock
Type - Export hook for a DSL payload type, implemented by
#[oxdock_type]on the payload struct itself. The canonical descriptor singleton backs every word of the type; user code never names a generated symbol.
Functions§
- clone_
boxed ⚠ - Heap
clone: deep-copy the box. - clone_
copy ⚠ - Inline
clone: payloads are plain bytes. - clone_
shared ⚠ - Shared-heap
clone: bump theArcstrong count, sharing the buffer.O(1)with no allocation. - drop_
boxed ⚠ - Heap
drop: free the box. - drop_
noop ⚠ - Inline
drop: nothing owns anything. - drop_
shared ⚠ - Shared-heap
drop: release oneArcstrong count, freeing the buffer only when the final word drops. - eq_
boxed ⚠ - Heap
eq: compare the boxed values. - eq_
inline ⚠ - Inline
eq: reconstruct both sides and compare. - eq_
shared ⚠ - Shared-heap
eq: compare the shared values. - fmt_
boxed ⚠ - Heap
fmt: render the boxed value. - fmt_
inline ⚠ - Inline
fmt: reconstruct and render. - fmt_
shared ⚠ - Shared-heap
fmt: render the shared value. - load_
inline ⚠ - Reconstruct a
Copyvalue from an inline payload. - startup_
descriptors - Canonical descriptors of the ten startup types, in a fixed order, for seeding per-state name directories and static rendering (docs-gen). Each entry is the payload struct’s own singleton: no table, no lock.
- store_
inline - Copy a
Copyvalue’s bytes into a payload. Panics whenTexceeds 64 bits: such types must use the heap path. - type_
anchor - Anchor of a type’s reference section, derived from its name the way the Markdown slugger derives it from the doc title.
- unshare_
boxed ⚠ - Exclusive-heap
unshare: the box is already uniquely owned, so the payload is returned unchanged with no allocation. - unshare_
inline ⚠ - Inline
unshare: inline words hold bytes, not a heap buffer, so there is nothing to hand out mutably. Panics: reaching this hook meansValue::read_heap_mutwas called with an inline descriptor, a caller bug (mirrorsstore_inline’s size assert). - unshare_
shared ⚠ - Shared-heap
unshare: detach on write. When the strong count is 1 the payload is returned unchanged (in-place, no allocation); otherwise the buffer is cloned, the word is rewritten to the private buffer, and the other clones keep the original. Either way the returned pointer addresses a buffer this word uniquely owns.
Unions§
- Value
Payload - Payload half of a
Valueword: either the value’s bytes inline or a thin pointer to an ownedBox<T>(exclusive heaps) or a sharedArc<T>(shared heaps), as the type’s descriptor dictates. Inline and pointer domains never mix for a givenTypeDescriptor.