Skip to main content

Value

Enum Value 

Source
pub enum Value {
Show 22 variants Int(i64), Float(f64), Bool(bool), Str(SmolStr), Bytes(Vec<u8>), Unit, List(VecDeque<Value>), Tuple(Vec<Value>), Record { shape_id: u32, fields: Box<IndexMap<SmolStr, Value>>, }, StackRecord { shape_id: u32, slab_start: u32, field_count: u16, }, StackTuple { slab_start: u32, arity: u16, }, ArenaRecord { shape_id: u32, slab_start: u32, field_count: u16, }, ArenaTuple { slab_start: u32, arity: u16, }, Variant { name: String, args: Vec<Value>, }, Closure { fn_id: u32, body_hash: BodyHash, captures: Vec<Value>, }, F64Array { rows: u32, cols: u32, data: Vec<f64>, }, Map(BTreeMap<MapKey, Value>), Set(BTreeSet<MapKey>), Deque(VecDeque<Value>), Actor(Arc<Mutex<ActorCell>>), Ticker(Arc<AtomicBool>), ArrowTable(Arc<RecordBatch>),
}

Variants§

§

Int(i64)

§

Float(f64)

§

Bool(bool)

§

Str(SmolStr)

String value. SmolStr stores strings ≤ 22 bytes inline — no heap allocation for identifiers, HTTP methods, status codes, short keys, etc. Clone of a short SmolStr is a 24-byte stack copy (#389 slice 4).

§

Bytes(Vec<u8>)

§

Unit

§

List(VecDeque<Value>)

§

Tuple(Vec<Value>)

§

Record

Record literal. shape_id is the Program::record_shapes index of the field-name vec the record was built from (#462 slice 2), so the Op::GetField polymorphic IC can match on a single u32 compare instead of walking the IndexMap by name. Records constructed outside the bytecode (JSON decode, SQL row → record, HTTP request mutators, test fixtures) have no compile-time shape and carry NO_SHAPE_ID — the IC unconditionally misses on them and falls through to the existing name walk.

fields is Box<IndexMap> rather than IndexMap inline because the bare IndexMap is ~56B; inlining it plus shape_id would push Value’s enum size from 64B → 72B, which measurably regresses the VM stack push/pop loop (Value is cloned/moved on every push/pop). Boxing keeps Value::Record at 16B and Value at the pre-#462 64B. The indirection on every IndexMap access costs a few ns but the IC drops the field-name string compare on every hit, which is the net win on mono_chain.

shape_id is not part of structural equality (see PartialEq below): two records with identical fields must compare equal regardless of provenance, so a JSON-decoded record equals a compile-time-built one with the same fields.

Fields

§shape_id: u32
§

StackRecord

Frame-local record (#464 step 2). Emitted by Op::AllocStackRecord at sites the escape analysis proved can’t outlive the current call frame. slab_start indexes into Vm::stack_record_arena; the field_count consecutive values starting there are the record’s fields, in Program.record_shapes[shape_id] order (same insertion order as Op::MakeRecord uses, so the polymorphic-IC offset is interoperable with Value::Record).

Op::GetField is the only consumer that knows how to read these — every other observation point (Op::Return, Op::Call, Op::MakeRecord as a field value, …) is an escape op that the analysis prevents this variant from reaching. If a StackRecord ever does reach an unexpected site (escape-analysis bug), it surfaces as a panic at the boundary, not undefined behavior — the arena is plain Vec<Value> in safe Rust.

Size: 4 (shape_id) + 4 (slab_start) + 2 (field_count) = 10 bytes payload + tag, comfortably inside the 64B Value envelope.

Fields

§shape_id: u32
§slab_start: u32
§field_count: u16
§

StackTuple

Frame-local tuple (#464 tuple codegen). The stack-alloc analogue of Value::Tuple, emitted by Op::AllocStackTuple at sites the escape analysis proved can’t outlive the current frame. slab_start indexes into Vm::stack_record_arena (the arena is shared with StackRecord — both are flat Value slabs released together on Op::Return); the arity consecutive values starting there are the tuple elements in positional order.

Like StackRecord, the only consumer that knows how to read these is Op::GetElem — every other observation point (Return, Call, a MakeTuple/MakeRecord field value, equality, JSON) is an escape op the analysis prevents this variant from reaching. An unexpected arrival surfaces as a panic at the boundary, not UB (the arena is safe Vec<Value>).

Fields

§slab_start: u32
§arity: u16
§

ArenaRecord

Request-scoped arena record (#463 slice 2a). Same handle shape as Value::StackRecord but indexes Vm::arena_slab (request lifetime) instead of Vm::stack_record_arena (frame lifetime). Emitted by Op::AllocArenaRecord at sites arena::build_arena_index proves do not escape the request scope opened by EffectHandler::enter_request_scope. Reads via Op::GetField (polymorphic across Record / StackRecord / ArenaRecord).

Inspection paths (to_json, equality, memo hash, generic clone) defensively panic on this variant, same contract as StackRecord. Slice 1’s arena::build_arena_index analysis proves these paths are unreachable in well-routed code (any reach is a soundness bug — analysis or codegen). The scoping doc (docs/design/arena-plumbing.md § “Arena handles MUST be readable at serialization”) flags this as the place where arena diverges from #464: a future slice will materialize arena handles at the response-serialization boundary so the Response’s to_json reads through to the slab. That materialization is out of scope for slice 2a; today arena ops only ship for hand-crafted bytecode tests, which avoid the inspection paths.

Fields

§shape_id: u32
§slab_start: u32
§field_count: u16
§

ArenaTuple

Request-scoped arena tuple (#463 slice 2a). Tuple analogue of ArenaRecord; same lifetime / fallback / inspection-panic contract.

Fields

§slab_start: u32
§arity: u16
§

Variant

Fields

§name: String
§args: Vec<Value>
§

Closure

First-class function value (a lambda + its captured locals). The function’s first captures.len() params bind to captures; the remaining params are supplied at call time.

fn_id is a dense compile-time index into Program::functions for fast dispatch; body_hash is the canonical identity — two closures with identical bytecode bodies compare equal even when their fn_ids differ (which they will, when the source has the same closure literal at two locations). See PartialEq below and #222 for the rationale.

Fields

§fn_id: u32
§body_hash: BodyHash
§captures: Vec<Value>
§

F64Array

Dense row-major f64 matrix. A “fast lane” representation that avoids the per-element Value::Float boxing of Value::List. Used by Core’s native tensor ops (matmul, dot, …) so end-to-end matmul perf hits the §13.7 #1 100ms target without paying for 2M Value boxings at the call boundary.

Fields

§rows: u32
§cols: u32
§data: Vec<f64>
§

Map(BTreeMap<MapKey, Value>)

Persistent map keyed by MapKey (Str or Int). Insertion- independent equality (sorted by BTreeMap’s Ord), so two maps built from the same pairs in different orders compare equal. Restricting keys to two primitive variants keeps Eq + Hash requirements off Value itself, which has closures and floats and can’t be hashed soundly.

§

Set(BTreeSet<MapKey>)

Persistent set with the same key-type discipline as Map.

§

Deque(VecDeque<Value>)

Double-ended queue. O(1) push/pop on both ends; otherwise behaves like List for iteration / equality / JSON shape. Lex’s type system tracks Deque[T] separately from List[T] so users explicitly opt in to deque semantics; the runtime uses this dedicated variant rather than backing a deque on top of Value::List (which would make push_front O(n)).

§

Actor(Arc<Mutex<ActorCell>>)

A handle to a conc.Actor. The Arc<Mutex<ActorCell>> allows cheap cloning and safe concurrent access — the mutex serialises message delivery so the actor processes one message at a time. Two actor handles compare equal iff they point to the same cell (identity equality, not structural equality).

§

Ticker(Arc<AtomicBool>)

A periodic-tick handle returned by conc.every (#445). The AtomicBool is the cancel flag — conc.cancel(t) sets it and the background scheduler thread observes it on its next iteration and exits. Two ticker handles compare equal iff they point to the same cancel flag.

§

ArrowTable(Arc<RecordBatch>)

Apache Arrow RecordBatch — an unboxed columnar table. The “fast lane” representation for lex-frame and any future dataframe code: a Value::ArrowTable with one int64 column of N rows is N×8 bytes of contiguous memory, not N Value::Int(_) enum tags inside a VecDeque. Reductions (arrow.col_sum_int, arrow.col_mean, …) execute as one Rust call over the flat buffer, bypassing the bytecode VM for the inner loop.

Arc makes clone cheap (refcount bump) — Arrow tables are already immutable so structural sharing across closures is safe. Equality is structural over schema + columns.

Implementations§

Source§

impl Value

Source

pub fn as_int(&self) -> i64

Source

pub fn as_float(&self) -> f64

Source

pub fn as_bool(&self) -> bool

Source

pub fn as_str(&self) -> &str

Source

pub fn contains_arena_record(&self) -> bool

Returns true if this value is, or transitively contains, an ArenaRecord or ArenaTuple. Used by the memo gate to skip memoization when request-scoped arena handles are present in the call arguments — such values cannot be safely hashed because the memo cache outlives the request arena (#621).

Source

pub fn to_json(&self) -> Value

Render this Value as a serde_json::Value for emission to CLI output, the agent API, conformance harness reports, etc. Canonical mapping shared across crates; previously every boundary had its own copy.

Encoding:

  • Variant { name, args }{"$variant": name, "args": [...]}
  • F64Array { ... }{"$f64_array": true, rows, cols, data}
  • Closure { body_hash, .. }"<closure HEX8>" (first 8 hex chars of the body hash; equivalent closures across source locations render identically — see #222)
  • Bytes{"$bytes": "deadbeef"} (lowercase hex). Round-trips through from_json. Bare hex strings decode as Str, so the marker is required to disambiguate bytes from a string that happens to look like hex.
  • Map with all-Str keys → JSON object; otherwise array of [key, value] pairs (Int keys can’t be JSON-object keys)
  • Set → JSON array of elements
  • other variants → their natural JSON shape

Note: this form is not round-trippable for traces (see lex-trace’s recorder, which uses a richer marker form).

Source

pub fn from_json(v: &Value) -> Value

Decode a serde_json::Value into a Value. The inverse of to_json for the shapes Lex round-trips:

  • {"$variant": "Name", "args": [...]}Value::Variant
  • {"$bytes": "deadbeef"}Value::Bytes (lowercase hex; an odd-length string or non-hex character falls through to Value::Record, matching the malformed-$variant fallback)
  • JSON object → Value::Record
  • JSON array → Value::List
  • JSON null → Value::Unit
  • JSON string / bool / number → the corresponding scalar

Map, Set, F64Array, and Closure don’t round-trip — they decode as their natural JSON shape (Object / Array / Object / Str respectively), since the CLI / HTTP / VM callers building Values from JSON don’t have those shapes in their input vocabulary.

Source

pub fn record_dynamic(fields: IndexMap<String, Value>) -> Value

Build a Value::Record whose fields don’t come from an Op::MakeRecord site — JSON decode, SQL row → record, host effect handlers, test fixtures, etc. Interns the field-name set in the process-global shape registry (#462 slice 3) so records with the same set of field names share a stable shape_id and hit the same IC slot. Two records with the same fields in different insertion order share a shape_id (the registry sorts the field-name vec before lookup), matching the existing Value::Record structural-equality semantics.

Dynamic shape IDs live in the high half of the u32 range (see crate::shape_registry::DYNAMIC_SHAPE_ID_BASE) so they can’t collide with the per-program shape indices emitted by Op::MakeRecord. Mixed-flavor IC sites (which the slice-2b measurement found at exactly zero occurrences) would still be correct under the IC’s shape-keyed verifier — they’d just churn the cache. Build a Record from a String-keyed host map (JSON decode, SQL rows, builtins). Keys are re-collected into interned SmolStr (#461 field-name interning). The hot bytecode MakeRecord path builds SmolStr-keyed maps directly and never routes through here; callers that already hold an interned map use record_interned.

Source

pub fn record_interned(fields: IndexMap<SmolStr, Value>) -> Value

Build a Record from an already-interned SmolStr-keyed map — used by the http builder chain, which threads SmolStr keys through with_header/with_query/… without round-tripping back to String (#461).

Trait Implementations§

Source§

impl Clone for Value

Source§

fn clone(&self) -> Value

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Value

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for Value

Manual PartialEq for Value (#222). Mirrors the auto-derived implementation for every variant except Closure, which compares on (body_hash, captures) only — fn_id is a dense compile-time index that is not stable across source-location-equivalent closure literals, and including it would defeat the canonicality property the body_hash field exists to provide.

Source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.

Auto Trait Implementations§

§

impl !RefUnwindSafe for Value

§

impl !UnwindSafe for Value

§

impl Freeze for Value

§

impl Send for Value

§

impl Sync for Value

§

impl Unpin for Value

§

impl UnsafeUnpin for Value

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.