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}