Skip to main content

shape_value/
slot.rs

1//! ValueSlot: 8-byte raw value storage for TypedObject fields
2//!
3//! Each slot stores exactly 8 bytes of raw bits. Simple types (f64, i64, bool)
4//! use their native bit representation. Heap types are stored as the raw
5//! pointer of an `Arc<T>` produced by `Arc::into_raw`, where `T` is the
6//! exact typed payload (e.g. `String`, `HashMapData`, `Decimal`; for
7//! array fields post-V3-S5, `TypedArray<f64>`/`TypedArray<i64>` /etc.
8//! per the v2-raw per-element-kind carrier shape). Drop dispatch
9//! consults the schema's `FieldType` /
10//! `NativeKind` to pick the matching `Arc::decrement_strong_count`; there
11//! is no `Box<HeapValue>` wrapping in new code.
12//!
13//! The slot itself does NOT self-describe its type. TypedObject's `heap_mask`
14//! bitmap identifies which slots contain heap pointers (bit N set = slot N is heap).
15//!
16//! ADR-006 §2.4: per-FieldType constructors. Per-HeapValue-variant
17//! constructors mirror the typed `Arc<T>` payloads on `HeapValue`.
18//! `from_heap` is `#[deprecated]` transitional — new code uses the typed
19//! constructors below. See `docs/adr/006-value-and-memory-model.md`.
20
21// V3-S5 ckpt-4 (2026-05-15): `TypedArrayData` import deleted — the enum
22// was retired at ckpt-1 per W12-typed-array-data-deletion-audit §3.5 +
23// ADR-006 §2.7.24 Q25.A SUPERSEDED. The `from_typed_array(Arc<
24// TypedArrayData>)` constructor below is deleted in lockstep; slot
25// construction for array fields cascade-breaks here for v2-raw
26// `TypedArray<T>` per-element-kind constructor rebuild in a downstream
27// wave (e.g. `from_typed_array_f64(Arc<TypedArray<f64>>)` /
28// `from_typed_array_i64(...)` per the new monomorphic carrier shape).
29use crate::heap_value::{
30    AtomicData, ChannelData, DequeData, HashMapData, HashSetData, HeapValue, IoHandleData,
31    LazyData, MutexData, NativeViewData, PriorityQueueData, RangeData, TypedObjectStorage,
32};
33use crate::datatable::DataTable;
34use std::sync::Arc;
35
36/// A raw 8-byte value slot for TypedObject field storage.
37#[repr(transparent)]
38#[derive(Copy, Clone)]
39pub struct ValueSlot(u64);
40
41impl ValueSlot {
42    /// Store a f64 as raw IEEE 754 bits.
43    pub fn from_number(n: f64) -> Self {
44        Self(n.to_bits())
45    }
46
47    /// Store an i64 as raw two's complement bits. Full 64-bit range, no precision loss.
48    pub fn from_int(i: i64) -> Self {
49        Self(i as u64)
50    }
51
52    /// Store a u64 directly. Only meaningful when the FieldType is known to be U64.
53    pub fn from_u64(v: u64) -> Self {
54        Self(v)
55    }
56
57    /// Read as u64 (caller must know this slot is u64 type).
58    pub fn as_u64(&self) -> u64 {
59        self.0
60    }
61
62    /// Store a bool as 1/0.
63    pub fn from_bool(b: bool) -> Self {
64        Self(if b { 1 } else { 0 })
65    }
66
67    /// Store None as zero bits.
68    pub fn none() -> Self {
69        Self(0)
70    }
71
72    /// Store any HeapValue on the heap. The caller MUST set the corresponding
73    /// bit in `heap_mask` so Drop knows to free this.
74    ///
75    /// Without `gc` feature: allocates via Box (freed by drop_heap).
76    /// With `gc` feature: allocates via GcHeap (freed by garbage collector).
77    ///
78    // ADR-005 / ADR-006: transitional API. New code uses per-FieldType
79    // constructors (`from_string_arc(Arc<String>)`,
80    // `from_typed_array(Arc<TypedArrayData>)`, `from_decimal(Arc<Decimal>)`,
81    // ...) that store typed pointers directly without `Box<HeapValue>`
82    // wrapping. The `from_heap_arc(Arc<HeapValue>)` shape is explicitly
83    // forbidden (ADR-006 §13, Q6 ruling): per-FieldType constructors only.
84    // See docs/adr/006-value-and-memory-model.md.
85    #[cfg(not(feature = "gc"))]
86    #[deprecated(
87        note = "Box<HeapValue> wrapping. Use a per-FieldType constructor \
88                (`from_string_arc`, `from_typed_object`, \
89                `from_decimal`, `from_bigint`, `from_hashmap`, ...). \
90                Array fields: V3-S5 ckpt-4 deleted `from_typed_array` along \
91                with the `TypedArrayData` enum; per-element-kind \
92                `from_typed_array_<T>(Arc<TypedArray<T>>)` constructors \
93                are the v2-raw replacement (downstream wave rebuild). \
94                See ADR-006 §2.4 + W12 audit §3.5/§B."
95    )]
96    pub fn from_heap(value: HeapValue) -> Self {
97        let ptr = Box::into_raw(Box::new(value)) as u64;
98        Self(ptr)
99    }
100
101    /// Store any HeapValue on the GC heap.
102    #[cfg(feature = "gc")]
103    #[deprecated(
104        note = "Box<HeapValue> wrapping. Use a per-FieldType constructor \
105                (`from_string_arc`, `from_typed_object`, \
106                `from_decimal`, `from_bigint`, `from_hashmap`, ...). \
107                Array fields: V3-S5 ckpt-4 deleted `from_typed_array` along \
108                with the `TypedArrayData` enum; per-element-kind \
109                `from_typed_array_<T>(Arc<TypedArray<T>>)` constructors \
110                are the v2-raw replacement (downstream wave rebuild). \
111                See ADR-006 §2.4 + W12 audit §3.5/§B."
112    )]
113    pub fn from_heap(value: HeapValue) -> Self {
114        let heap = shape_gc::thread_gc_heap();
115        let ptr = heap.alloc(value) as u64;
116        Self(ptr)
117    }
118
119    // ── Per-FieldType typed constructors (ADR-006 §2.4) ─────────────────────
120    //
121    // Each constructor consumes a typed `Arc<T>` and stores its `Arc::into_raw`
122    // pointer. Drop dispatch (in `TypedObjectStorage::Drop` per ADR-006 §2.5)
123    // consults the schema's `FieldType` → `NativeKind` to pick the matching
124    // `Arc::decrement_strong_count::<T>` — no `Box<HeapValue>` materialization.
125    //
126    // The `from_heap_arc(Arc<HeapValue>)` catch-all is explicitly forbidden
127    // (ADR-006 §13, Q6 ruling). Add new typed constructors here when a new
128    // heap shape genuinely needs one — never a polymorphic `Arc<HeapValue>`
129    // entry point.
130
131    /// Store an `Arc<String>` directly. Mirrors `FieldType::String` /
132    /// `NativeKind::String` / `HeapValue::String(Arc<String>)`.
133    pub fn from_string_arc(s: Arc<String>) -> Self {
134        Self(Arc::into_raw(s) as u64)
135    }
136
137    // V3-S5 ckpt-4 (2026-05-15): `from_typed_array(a: Arc<TypedArrayData>)`
138    // constructor DELETED. The `TypedArrayData` enum + `TypedBuffer<T>` /
139    // `AlignedTypedBuffer` wrapper layer are retired wholesale at
140    // ckpt-1..ckpt-4 per W12-typed-array-data-deletion-audit §3.5 + §B
141    // + ADR-006 §2.7.24 Q25.A SUPERSEDED. Replacement (downstream wave):
142    // per-element-kind constructors — `from_typed_array_f64(Arc<
143    // TypedArray<f64>>)`, `from_typed_array_i64(Arc<TypedArray<i64>>)`,
144    // etc. — matching the v2-raw monomorphic flat-struct carrier shape
145    // at `crate::v2::typed_array::TypedArray<T>` per
146    // `docs/runtime-v2-spec.md`. Refusal #1 binding.
147
148    /// Store an `Arc<TypedObjectStorage>` directly. Mirrors
149    /// `FieldType::Object(_)` / `NativeKind::Ptr(HeapKind::TypedObject)` /
150    /// `HeapValue::TypedObject(Arc<TypedObjectStorage>)` (post Step 4 /
151    /// ADR-006 §2.3).
152    ///
153    /// **Wave 2 Agent D1 (2026-05-14): legacy transitional constructor.**
154    /// Per ADR-006 §2.3 amendment, `TypedObjectStorage` grew a `HeapHeader`
155    /// at offset 0 to support v2-raw raw-pointer carriers
156    /// (`from_typed_object_raw`). This `Arc`-shaped constructor remains the
157    /// canonical Wave-2-pre-D2 entry point; the 18 production construction
158    /// sites migrate to `from_typed_object_raw` in D2 (Round 2). After D2's
159    /// close, the last caller is gone and this constructor is deleted
160    /// alongside the legacy `impl Drop for TypedObjectStorage` path. Slot
161    /// bits stored are `Arc::into_raw(arc) as u64` — refcount-managed by
162    /// Rust `Arc` at offset -16, NOT by the on-header refcount at offset 0
163    /// (which sits unused at 1 for the Arc lifetime).
164    pub fn from_typed_object(o: Arc<TypedObjectStorage>) -> Self {
165        Self(Arc::into_raw(o) as u64)
166    }
167
168    /// Store a raw `*const TypedObjectStorage` directly. Mirrors
169    /// `FieldType::Object(_)` / `NativeKind::Ptr(HeapKind::TypedObject)`.
170    ///
171    /// **Wave 2 Agent D1 (2026-05-14): v2-raw raw-pointer constructor.**
172    /// Per ADR-006 §2.3 amendment + audit §4.3 Obstacle O-3.a resolution,
173    /// `TypedObjectStorage` carries a `HeapHeader` at offset 0; the v2-raw
174    /// allocator `TypedObjectStorage::_new` returns `*mut TypedObjectStorage`
175    /// with refcount=1 on the header. The slot bits stored are the raw
176    /// pointer (NOT `Arc::into_raw`); refcount discipline goes through
177    /// `v2_retain` / `v2_release` via the `HeapElement` trait. Drop runs
178    /// at refcount=0 via `TypedObjectStorage::_drop` (NOT Rust `Arc::drop`).
179    ///
180    /// Callers (Wave 2 Agent D2, Round 2): replace the legacy pattern
181    /// ```ignore
182    /// let arc = Arc::new(TypedObjectStorage::new(schema_id, slots, mask, kinds));
183    /// let slot = ValueSlot::from_typed_object(arc);
184    /// ```
185    /// with the v2-raw pattern
186    /// ```ignore
187    /// let ptr = TypedObjectStorage::_new(schema_id, slots, mask, kinds);
188    /// let slot = ValueSlot::from_typed_object_raw(ptr);
189    /// ```
190    pub fn from_typed_object_raw(ptr: *const TypedObjectStorage) -> Self {
191        Self(ptr as u64)
192    }
193
194    /// Store an `Arc<rust_decimal::Decimal>` directly. Mirrors
195    /// `FieldType::Decimal` / `HeapValue::Decimal(Arc<Decimal>)` (post Step 3).
196    pub fn from_decimal(d: Arc<rust_decimal::Decimal>) -> Self {
197        Self(Arc::into_raw(d) as u64)
198    }
199
200    /// Store a v2-raw `*const StringObj` carrier pointer directly. ADR-006
201    /// §2.7.5 amendment (Wave 2 Agent B W12-StringV2-DecimalV2-NativeKind-
202    /// additions, 2026-05-14): paired with `NativeKind::StringV2` per the
203    /// audit §H.4 H-c decision. Slot bits = `ptr as u64`; refcount discipline
204    /// goes through `v2_retain` / `v2_release` against the `HeapHeader` at
205    /// offset 0 of the `StringObj` (NOT `Arc::increment_strong_count` —
206    /// `StringObj` is a manually-allocated `repr(C)` 24-byte carrier per
207    /// `v2/string_obj.rs`, not an `Arc<String>` allocation).
208    ///
209    /// Caller's construction-side contract: `ptr` MUST point to a live
210    /// `StringObj` whose refcount has been incremented to claim the share
211    /// this slot now owns (e.g. via `v2_retain` at the producer site).
212    pub fn from_string_v2_ptr(ptr: *const crate::v2::string_obj::StringObj) -> Self {
213        Self(ptr as u64)
214    }
215
216    /// Store a v2-raw `*const DecimalObj` carrier pointer directly. ADR-006
217    /// §2.7.5 amendment (Wave 2 Agent B W12-StringV2-DecimalV2-NativeKind-
218    /// additions, 2026-05-14): mirror of `from_string_v2_ptr` for the
219    /// `DecimalObj` sibling per `v2/decimal_obj.rs`. Slot bits = `ptr as u64`;
220    /// refcount via `v2_retain` / `v2_release` against the `HeapHeader` at
221    /// offset 0 of the `DecimalObj`.
222    pub fn from_decimal_v2_ptr(ptr: *const crate::v2::decimal_obj::DecimalObj) -> Self {
223        Self(ptr as u64)
224    }
225
226    /// Store an `Arc<i64>` (BigInt payload) directly. Mirrors
227    /// `HeapValue::BigInt(Arc<i64>)` (post Step 3).
228    pub fn from_bigint(b: Arc<i64>) -> Self {
229        Self(Arc::into_raw(b) as u64)
230    }
231
232    /// Store an `Arc<HashMapKindedRef>` directly. Mirrors
233    /// `HeapValue::HashMap(HashMapKindedRef)`.
234    ///
235    /// **Wave 2 Round 3b C2-joint ckpt-2 (2026-05-14):** signature flipped
236    /// from `Arc<HashMapData>` (non-generic) to `Arc<HashMapKindedRef>`
237    /// (per-V enum carrier wrapped in Arc) per ADR-006 §2.7.24 Q25.B
238    /// SUPERSEDED. Single Arc indirection — the inner enum's variant tag
239    /// carries the per-V discriminator at the carrier layer, and the
240    /// 4-table-lockstep arms in `kinded_slot.rs` + `stack.rs` retain/release
241    /// the outer `Arc<HashMapKindedRef>` (single-discriminator preserved
242    /// per ADR-005 §1; per-V Arc<HashMapData<V>> retire occurs at
243    /// HashMapKindedRef's Drop on refcount-0 of the outer Arc).
244    pub fn from_hashmap(h: Arc<crate::heap_value::HashMapKindedRef>) -> Self {
245        Self(Arc::into_raw(h) as u64)
246    }
247
248    /// Store an `Arc<HashSetData>` directly. Mirrors
249    /// `HeapValue::HashSet(Arc<HashSetData>)`. ADR-006 §2.7.15 / Q16
250    /// amendment (Wave 13 W13-hashset-rebuild) — Set is a HashMap
251    /// sibling, full-`HeapValue` arm shape.
252    pub fn from_hashset(h: Arc<HashSetData>) -> Self {
253        Self(Arc::into_raw(h) as u64)
254    }
255
256    /// Store an `Arc<DequeData>` directly. Mirrors
257    /// `HeapValue::Deque(Arc<DequeData>)`. ADR-006 §2.7.19 / Q20
258    /// amendment (Wave 15 W15-deque) — Deque is a HashSet sibling,
259    /// full-`HeapValue` arm shape.
260    pub fn from_deque(d: Arc<DequeData>) -> Self {
261        Self(Arc::into_raw(d) as u64)
262    }
263
264    /// Store an `Arc<ChannelData>` directly. Mirrors
265    /// `HeapValue::Channel(Arc<ChannelData>)`. ADR-006 §2.7.20 / Q21
266    /// amendment (Wave 15 W15-channel-rebuild). Channel is a
267    /// concurrency primitive; the inner `ChannelData` carries
268    /// `Mutex<ChannelInner>` so two `Arc<ChannelData>` shares of the
269    /// same channel observe each other's `send` / `recv`.
270    pub fn from_channel(c: Arc<ChannelData>) -> Self {
271        Self(Arc::into_raw(c) as u64)
272    }
273
274    /// Store an `Arc<MutexData>` directly. Mirrors
275    /// `HeapValue::Mutex(Arc<MutexData>)`. ADR-006 §2.7.25 amendment
276    /// (Wave 17 W17-concurrency, 2026-05-11). Same typed-Arc shape as
277    /// Channel; `MutexData` carries `Mutex<MutexInner>` for
278    /// interior-mutability sharing.
279    pub fn from_mutex(m: Arc<MutexData>) -> Self {
280        Self(Arc::into_raw(m) as u64)
281    }
282
283    /// Store an `Arc<AtomicData>` directly. Mirrors
284    /// `HeapValue::Atomic(Arc<AtomicData>)`. ADR-006 §2.7.25 amendment
285    /// (Wave 17 W17-concurrency, 2026-05-11). i64-only at landing.
286    pub fn from_atomic(a: Arc<AtomicData>) -> Self {
287        Self(Arc::into_raw(a) as u64)
288    }
289
290    /// Store an `Arc<LazyData>` directly. Mirrors
291    /// `HeapValue::Lazy(Arc<LazyData>)`. ADR-006 §2.7.25 amendment
292    /// (Wave 17 W17-concurrency, 2026-05-11). Closure-call path
293    /// unlocked by W17-make-closure (`aa47364`).
294    pub fn from_lazy(l: Arc<LazyData>) -> Self {
295        Self(Arc::into_raw(l) as u64)
296    }
297
298    /// Store an `Arc<PriorityQueueData>` directly. Mirrors
299    /// `HeapValue::PriorityQueue(Arc<PriorityQueueData>)`. ADR-006
300    /// §2.7.18 / Q19 amendment (Wave 15 W15-priority-queue) —
301    /// PriorityQueue is a HashSet sibling, full-`HeapValue` arm shape.
302    pub fn from_priority_queue(p: Arc<PriorityQueueData>) -> Self {
303        Self(Arc::into_raw(p) as u64)
304    }
305
306    /// Store an `Arc<RangeData>` directly. Mirrors
307    /// `HeapValue::Range(Arc<RangeData>)`. ADR-006 §2.7.23 / Q24
308    /// amendment (W15-range, 2026-05-10).
309    pub fn from_range(r: Arc<RangeData>) -> Self {
310        Self(Arc::into_raw(r) as u64)
311    }
312
313    /// Store an `Arc<ContentNode>` directly. Mirrors
314    /// `HeapValue::Content(Arc<ContentNode>)`. ADR-006 §2.3 (W18.6 R8 W3
315    /// 2026-05-24 — supervisor D3+D4): paired with `KindedSlot::from_content`
316    /// to land the `Content.text(...)` / `Content.code(...)` etc.
317    /// user-facing constructor surface and the `Display.display() -> content`
318    /// return-type discipline.
319    pub fn from_content(c: Arc<crate::content::ContentNode>) -> Self {
320        Self(Arc::into_raw(c) as u64)
321    }
322
323    /// Store an `Arc<ResultData>` directly. Mirrors
324    /// `HeapValue::Result(Arc<ResultData>)`. ADR-006 §2.7.17 / Q18
325    /// amendment (Wave 14 W14-variant-codegen).
326    pub fn from_result(r: Arc<crate::heap_value::ResultData>) -> Self {
327        Self(Arc::into_raw(r) as u64)
328    }
329
330    /// Store an `Arc<TraitObjectStorage>` directly. Mirrors
331    /// `HeapValue::TraitObject(Arc<TraitObjectStorage>)`. ADR-006
332    /// §2.7.24 / Q25.C amendment (Wave 17 W17-trait-object-storage,
333    /// 2026-05-11). Re-introduces the bulldozer-deleted trait-object
334    /// carrier under the typed-Arc shape — `TraitObjectStorage` holds
335    /// an `Arc<TypedObjectStorage>` data half + an `Arc<VTable>`
336    /// vtable half. The `Box<u64>` data-half is explicitly forbidden
337    /// per §Q25.E #3 (kind-blind raw-bits storage).
338    ///
339    /// **Wave 2 Agent E (2026-05-14): legacy transitional constructor.**
340    /// Per ADR-006 §Q25.C.5 amendment, `TraitObjectStorage` grew a
341    /// `HeapHeader` at offset 0 to support v2-raw raw-pointer carriers
342    /// (`from_trait_object_raw`). This `Arc`-shaped constructor remains
343    /// a valid entry point during the Wave 2 Round 2 transition; slot
344    /// bits stored are `Arc::into_raw(arc) as u64` — refcount-managed
345    /// by Rust `Arc` at offset -16, NOT by the on-header refcount at
346    /// offset 0 (which sits unused at 1 for the Arc lifetime).
347    pub fn from_trait_object(t: Arc<crate::heap_value::TraitObjectStorage>) -> Self {
348        Self(Arc::into_raw(t) as u64)
349    }
350
351    /// Store a raw `*const TraitObjectStorage` directly. Mirrors
352    /// `NativeKind::Ptr(HeapKind::TraitObject)`.
353    ///
354    /// **Wave 2 Agent E (2026-05-14): v2-raw raw-pointer constructor.**
355    /// Per ADR-006 §Q25.C.5 amendment + audit §4.3 Obstacle O-3.a
356    /// resolution, `TraitObjectStorage` carries a `HeapHeader` at offset 0;
357    /// the v2-raw allocator `TraitObjectStorage::_new` returns
358    /// `*mut TraitObjectStorage` with refcount=1 on the header. The slot
359    /// bits stored are the raw pointer (NOT `Arc::into_raw`); refcount
360    /// discipline goes through `v2_retain` / `v2_release` via the
361    /// `HeapElement` trait. Drop runs at refcount=0 via
362    /// `TraitObjectStorage::_drop` (NOT Rust `Arc::drop`).
363    ///
364    /// The inner `value: Arc<TypedObjectStorage>` field stays Arc-typed
365    /// in E's Round 2 scope per dispatch contract; D2's lockstep flip
366    /// handles the inner shift to `*mut TypedObjectStorage` once the
367    /// TypedObjectStorage Arc-path retires.
368    pub fn from_trait_object_raw(
369        ptr: *const crate::heap_value::TraitObjectStorage,
370    ) -> Self {
371        Self(ptr as u64)
372    }
373
374    /// Store an `Arc<OptionData>` directly. Mirrors
375    /// `HeapValue::Option(Arc<OptionData>)`. ADR-006 §2.7.17 / Q18
376    /// amendment (Wave 14 W14-variant-codegen).
377    pub fn from_option(o: Arc<crate::heap_value::OptionData>) -> Self {
378        Self(Arc::into_raw(o) as u64)
379    }
380
381    /// Store an `Arc<DataTable>` directly. Mirrors
382    /// `HeapValue::DataTable(Arc<DataTable>)`.
383    pub fn from_data_table(t: Arc<DataTable>) -> Self {
384        Self(Arc::into_raw(t) as u64)
385    }
386
387    /// Store an `Arc<IoHandleData>` directly. Mirrors
388    /// `HeapValue::IoHandle(Arc<IoHandleData>)`.
389    pub fn from_io_handle(h: Arc<IoHandleData>) -> Self {
390        Self(Arc::into_raw(h) as u64)
391    }
392
393    /// Store an `Arc<NativeViewData>` directly. Mirrors
394    /// `HeapValue::NativeView(Arc<NativeViewData>)` (post Step 3, where
395    /// `NativeView` migrates from `Box<NativeViewData>` to `Arc`).
396    pub fn from_native_view(v: Arc<NativeViewData>) -> Self {
397        Self(Arc::into_raw(v) as u64)
398    }
399
400    /// Store a primitive `char` codepoint. Mirrors `HeapValue::Char(char)`
401    /// — kept inline (not heap) but exposed under the per-FieldType naming
402    /// scheme so call sites converge on a single constructor pattern.
403    pub fn from_char(c: char) -> Self {
404        Self(c as u64)
405    }
406
407    /// Read as `char` (caller must know this slot is `char` type).
408    pub fn as_char(&self) -> Option<char> {
409        char::from_u32(self.0 as u32)
410    }
411
412    /// Read as f64 (caller must know this slot is f64 type).
413    pub fn as_f64(&self) -> f64 {
414        f64::from_bits(self.0)
415    }
416
417    /// Read as i64 (caller must know this slot is i64 type).
418    pub fn as_i64(&self) -> i64 {
419        self.0 as i64
420    }
421
422    /// Read as bool (caller must know this slot is bool type).
423    pub fn as_bool(&self) -> bool {
424        self.0 != 0
425    }
426
427    /// Read as heap HeapValue reference (caller must know this slot is a heap pointer).
428    /// Returns a reference to the pointed-to HeapValue.
429    pub fn as_heap_value(&self) -> &HeapValue {
430        let ptr = self.0 as *const HeapValue;
431        unsafe { &*ptr }
432    }
433
434    /// Raw bits for simple copy.
435    pub fn raw(&self) -> u64 {
436        self.0
437    }
438
439    /// Construct from raw bits.
440    pub fn from_raw(bits: u64) -> Self {
441        Self(bits)
442    }
443
444    /// Drop the heap value. MUST only be called on heap slots.
445    ///
446    /// Without `gc` feature: frees via Box deallocation.
447    /// With `gc` feature: no-op (GC handles deallocation).
448    ///
449    /// # Safety
450    /// Caller must ensure this slot actually contains a valid heap pointer.
451    #[cfg(not(feature = "gc"))]
452    pub unsafe fn drop_heap(&mut self) {
453        if self.0 != 0 {
454            let ptr = self.0 as *mut HeapValue;
455            let _ = unsafe { Box::from_raw(ptr) };
456            self.0 = 0;
457        }
458    }
459
460    /// Drop the heap value (GC path: no-op).
461    #[cfg(feature = "gc")]
462    pub unsafe fn drop_heap(&mut self) {
463        // No-op: garbage collector handles deallocation
464        self.0 = 0;
465    }
466
467    /// Clone a heap slot by cloning the pointed-to HeapValue into a new Box.
468    ///
469    /// Without `gc` feature: deep clones into a new Box allocation.
470    /// With `gc` feature: bitwise copy (GC tracks all references).
471    ///
472    /// # Safety
473    /// Caller must ensure this slot actually contains a valid heap pointer.
474    #[cfg(not(feature = "gc"))]
475    pub unsafe fn clone_heap(&self) -> Self {
476        if self.0 == 0 {
477            return Self(0);
478        }
479        let ptr = self.0 as *const HeapValue;
480        let cloned = unsafe { (*ptr).clone() };
481        // Transitional: `clone_heap` is part of the Box<HeapValue> drop/clone
482        // path that Phase 1.A retires alongside `from_heap`. Suppressing the
483        // deprecation warning at this single call site keeps the build clean
484        // while the call-site migration runs in Phase 1.B.
485        #[allow(deprecated)]
486        Self::from_heap(cloned)
487    }
488
489    /// Clone a heap slot (GC path: bitwise copy).
490    #[cfg(feature = "gc")]
491    pub unsafe fn clone_heap(&self) -> Self {
492        // Under GC, just copy the pointer — GC traces all live references
493        Self(self.0)
494    }
495}
496
497impl std::fmt::Debug for ValueSlot {
498    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
499        write!(f, "ValueSlot(0x{:016x})", self.0)
500    }
501}
502
503#[cfg(test)]
504mod tests {
505    use super::*;
506    use std::sync::Arc;
507
508    #[test]
509    fn test_number_roundtrip() {
510        let slot = ValueSlot::from_number(3.14);
511        assert_eq!(slot.as_f64(), 3.14);
512    }
513
514    #[test]
515    fn test_int_roundtrip() {
516        let slot = ValueSlot::from_int(-42);
517        assert_eq!(slot.as_i64(), -42);
518
519        let slot = ValueSlot::from_int(i64::MAX);
520        assert_eq!(slot.as_i64(), i64::MAX);
521
522        let slot = ValueSlot::from_int(i64::MIN);
523        assert_eq!(slot.as_i64(), i64::MIN);
524    }
525
526    #[test]
527    fn test_bool_roundtrip() {
528        assert!(ValueSlot::from_bool(true).as_bool());
529        assert!(!ValueSlot::from_bool(false).as_bool());
530    }
531
532    /// ADR-006 §2.4: `from_string_arc` round-trips an `Arc<String>` raw
533    /// pointer through the slot without `Box<HeapValue>` wrapping.
534    #[test]
535    fn test_from_string_arc_roundtrip() {
536        let s: Arc<String> = Arc::new("hello".to_string());
537        let raw_before = Arc::as_ptr(&s) as u64;
538        let slot = ValueSlot::from_string_arc(s);
539        assert_eq!(slot.raw(), raw_before, "slot stores Arc::into_raw pointer");
540        // Reclaim the Arc to avoid a leak in the test.
541        unsafe {
542            let _ = Arc::<String>::from_raw(slot.raw() as *const String);
543        }
544    }
545
546    /// ADR-006 §2.4: `from_decimal` accepts an `Arc<rust_decimal::Decimal>`
547    /// directly — no `HeapValue::Decimal` materialization at the slot
548    /// boundary.
549    #[test]
550    fn test_from_decimal_roundtrip() {
551        let d: Arc<rust_decimal::Decimal> =
552            Arc::new(rust_decimal::Decimal::new(123, 2));
553        let raw_before = Arc::as_ptr(&d) as u64;
554        let slot = ValueSlot::from_decimal(d);
555        assert_eq!(slot.raw(), raw_before);
556        unsafe {
557            let _ =
558                Arc::<rust_decimal::Decimal>::from_raw(slot.raw() as *const _);
559        }
560    }
561
562    #[test]
563    #[allow(deprecated)] // `from_heap` is the Phase 1.A transitional API under test
564    fn test_heap_string_roundtrip() {
565        let original = HeapValue::String(Arc::new("hello".to_string()));
566        let slot = ValueSlot::from_heap(original.clone());
567        let recovered = slot.as_heap_value();
568        match recovered {
569            HeapValue::String(s) => assert_eq!(s.as_str(), "hello"),
570            other => panic!("Expected HeapValue::String, got {:?}", other),
571        }
572        // Clean up
573        unsafe {
574            let mut slot = slot;
575            slot.drop_heap();
576        }
577    }
578}