Skip to main content

shape_value/
kinded_slot.rs

1//! `KindedSlot`: caller-side runtime-value carrier (ADR-006 §2.7 / Q7).
2//!
3//! Pairs a raw 8-byte `ValueSlot` with the `NativeKind` that interprets its
4//! bits. Used at GENERIC_CARRIER sites — module bindings, frame info,
5//! suspension state, intrinsic dispatch, output adapters — where the kind
6//! is **not** statically determined by the surrounding `FieldType` /
7//! schema. STATIC_KIND sites continue to use `ValueSlot` directly.
8//!
9//! ## Why a struct, not a sum type
10//!
11//! ADR-005 §1's single-discriminator discipline forbids parallel sum types
12//! whose variants project 1:1 to `HeapKind`. `KindedSlot` is a *struct*, not
13//! a sum type — the kind is data, not a discriminator. `NativeKind` is also
14//! broader than `HeapKind` (it includes raw scalars `Int64`/`Float64`/`Bool`
15//! with no `HeapValue` arm). The kind→heap mapping is many-to-one (heap
16//! arms only), not 1:1.
17//!
18//! ## Why explicit `Drop` / `Clone`, NOT `Copy`
19//!
20//! `ValueSlot` itself is `Copy` (it's a raw `u64`). Putting `KindedSlot` in
21//! a `Vec` would alias-copy the heap pointer on every `push`/`pop`/`clone`
22//! and the default `Vec::drop` would leak refcounts (or, after a clone,
23//! double-free them on the second drop). This is the WB2.4 / WB2.5 bug
24//! class the typed-slot ABI was designed to prevent.
25//!
26//! The reference precedent is `TypedObjectStorage::Drop` in
27//! `heap_value.rs:761-889`: walk a per-slot `NativeKind`, dispatch to the
28//! matching `Arc::decrement_strong_count::<T>`. This module mirrors that
29//! discipline at the carrier-struct level.
30//!
31//! ## Forbidden uses (ADR-006 §2.7.2)
32//!
33//! - Do not use `KindedSlot` where `NativeKind` is statically known
34//!   (would re-introduce kind-tag latency the slot ABI just removed).
35//! - Do not introduce `KindedSlot` *variants* (sum-type form).
36//! - Do not let `KindedSlot` leak into the typed VM↔JIT slot ABI
37//!   (`docs/runtime-v2-spec.md`). The hot stack/JIT path stays
38//!   `ValueSlot`-only with kind threaded through opcodes.
39//!
40//! See `docs/adr/006-value-and-memory-model.md` §2.7.
41
42// ADR-006 §2.7
43// V3-S5 ckpt-4 (2026-05-15): `TypedArrayData` import deleted — the enum
44// was retired at ckpt-1 per W12-typed-array-data-deletion-audit §3.5 +
45// ADR-006 §2.7.24 Q25.A SUPERSEDED. `from_typed_array(Arc<TypedArrayData>)`
46// convenience constructor deleted in lockstep below. The 4-table-lockstep
47// dispatch arms for `HeapKind::TypedArray` in this file's clone/drop
48// dispatch tables (lines ~690 / ~1045 pre-edit) stay until V3-S5 ckpt-5
49// per supervisor 2026-05-15 partition (ckpt-5 territory: 4-table lockstep
50// deletion + U64 relabel + A1 fold).
51use crate::heap_value::{
52    AtomicData, ChannelData, DequeData, HashMapData, HashSetData, HeapKind, HeapValue,
53    IoHandleData, LazyData, MatrixData, MatrixSliceData, MutexData, NativeViewData, OptionData,
54    PriorityQueueData, RangeData, ResultData, TableViewData, TaskGroupData, TemporalData,
55    TraitObjectStorage, TypedObjectStorage,
56};
57use crate::iterator_state::IteratorState;
58use crate::native_kind::NativeKind;
59use crate::reference::RefTarget;
60use crate::slot::ValueSlot;
61use crate::value::FilterNode;
62use std::sync::Arc;
63
64/// Caller-side runtime-value carrier: a `ValueSlot` paired with the
65/// `NativeKind` that interprets it. ADR-006 §2.7.
66///
67/// **Not `Copy`.** Drop and clone dispatch on `kind` to manage heap
68/// refcounts; aliasing copies would leak / double-free.
69#[repr(C)]
70pub struct KindedSlot {
71    pub slot: ValueSlot,
72    pub kind: NativeKind,
73}
74
75impl KindedSlot {
76    /// Construct from an already-owned slot + its kind. The caller must
77    /// ensure the slot's bits are a valid representation of `kind` (e.g.
78    /// for heap kinds, one strong-count share owned by this `KindedSlot`).
79    #[inline]
80    pub fn new(slot: ValueSlot, kind: NativeKind) -> Self {
81        Self { slot, kind }
82    }
83
84    /// Convenience: a numeric `Int64`-kind slot.
85    #[inline]
86    pub fn from_int(i: i64) -> Self {
87        Self::new(ValueSlot::from_int(i), NativeKind::Int64)
88    }
89
90    /// Convenience: a `Float64`-kind slot.
91    #[inline]
92    pub fn from_number(n: f64) -> Self {
93        Self::new(ValueSlot::from_number(n), NativeKind::Float64)
94    }
95
96    /// Convenience: a `Float32`-kind slot. ADR-006 §2.7.5 amendment
97    /// (Round 19 S1.5 W12-nativekind-scalar-additions, 2026-05-14).
98    /// `f32` is a 4-byte scalar; slot bits store the IEEE-754
99    /// single-precision pattern zero-extended into the low 32 bits of
100    /// the 8-byte slot (via `f32::to_bits` reinterpreted as `u64`).
101    /// Drop is a no-op (inline scalar, no `Arc<T>` payload).
102    #[inline]
103    pub fn from_f32(f: f32) -> Self {
104        Self::new(
105            ValueSlot::from_raw(f.to_bits() as u64),
106            NativeKind::Float32,
107        )
108    }
109
110    /// Convenience: a `Bool`-kind slot.
111    #[inline]
112    pub fn from_bool(b: bool) -> Self {
113        Self::new(ValueSlot::from_bool(b), NativeKind::Bool)
114    }
115
116    /// Convenience: a `String`-kind slot from an `Arc<String>`.
117    #[inline]
118    pub fn from_string_arc(s: Arc<String>) -> Self {
119        Self::new(ValueSlot::from_string_arc(s), NativeKind::String)
120    }
121
122    /// Convenience: a `Ptr(HeapKind::TypedObject)`-kind slot from an
123    /// `Arc<TypedObjectStorage>`. **Wave 2 Agent D1 (2026-05-14): legacy
124    /// transitional constructor** — see `ValueSlot::from_typed_object`
125    /// docstring for the Arc-vs-raw-pointer staging. Slot bits are
126    /// `Arc::into_raw(o)`.
127    #[inline]
128    pub fn from_typed_object(o: Arc<TypedObjectStorage>) -> Self {
129        Self::new(
130            ValueSlot::from_typed_object(o),
131            NativeKind::Ptr(HeapKind::TypedObject),
132        )
133    }
134
135    /// Convenience: a `Ptr(HeapKind::TypedObject)`-kind slot from a raw
136    /// `*const TypedObjectStorage`. **Wave 2 Agent D1 (2026-05-14): v2-raw
137    /// raw-pointer constructor.** Per ADR-006 §2.3 amendment + audit §4.3
138    /// O-3.a resolution. Pairs with `TypedObjectStorage::_new` allocator;
139    /// refcount discipline goes through the on-header refcount via
140    /// `v2_retain` / `v2_release` (NOT Rust `Arc::increment/decrement_
141    /// strong_count`). See `ValueSlot::from_typed_object_raw` docstring
142    /// for the full call-site migration pattern.
143    #[inline]
144    pub fn from_typed_object_raw(ptr: *const TypedObjectStorage) -> Self {
145        Self::new(
146            ValueSlot::from_typed_object_raw(ptr),
147            NativeKind::Ptr(HeapKind::TypedObject),
148        )
149    }
150
151    // V3-S5 ckpt-4 (2026-05-15): `from_typed_array(a: Arc<TypedArrayData>)`
152    // DELETED in lockstep with the `TypedArrayData` enum + `TypedBuffer<T>` /
153    // `AlignedTypedBuffer` wrapper layer + `ValueSlot::from_typed_array`
154    // constructor. W12-typed-array-data-deletion-audit §3.5 + §B +
155    // ADR-006 §2.7.24 Q25.A SUPERSEDED. Replacement (downstream wave):
156    // per-element-kind convenience constructors —
157    // `from_typed_array_f64(Arc<TypedArray<f64>>)` /
158    // `from_typed_array_i64(...)` / etc. Refusal #1 binding.
159
160    /// Convenience: a `Ptr(HeapKind::HashMap)`-kind slot.
161    ///
162    /// **Wave 2 Round 3b C2-joint ckpt-2 (2026-05-14):** parameter type
163    /// flipped from `Arc<HashMapData>` (non-generic) to
164    /// `Arc<HashMapKindedRef>` (per-V enum carrier in Arc) per ADR-006
165    /// §2.7.24 Q25.B SUPERSEDED.
166    #[inline]
167    pub fn from_hashmap(h: Arc<crate::heap_value::HashMapKindedRef>) -> Self {
168        Self::new(
169            ValueSlot::from_hashmap(h),
170            NativeKind::Ptr(HeapKind::HashMap),
171        )
172    }
173
174    /// Convenience: a `Ptr(HeapKind::HashSet)`-kind slot. Mirror of
175    /// `from_hashmap` per ADR-006 §2.7.15 / Q16 amendment (Wave 13
176    /// W13-hashset-rebuild). Set is a HashMap sibling — full
177    /// `HeapValue::HashSet(Arc<HashSetData>)` arm, not pure-discriminator.
178    #[inline]
179    pub fn from_hashset(h: Arc<HashSetData>) -> Self {
180        Self::new(
181            ValueSlot::from_hashset(h),
182            NativeKind::Ptr(HeapKind::HashSet),
183        )
184    }
185
186    /// Convenience: a `Ptr(HeapKind::Deque)`-kind slot. Mirror of
187    /// `from_hashset` per ADR-006 §2.7.19 / Q20 amendment (Wave 15
188    /// W15-deque). Deque is a HashSet sibling — full
189    /// `HeapValue::Deque(Arc<DequeData>)` arm, not pure-discriminator.
190    #[inline]
191    pub fn from_deque(d: Arc<DequeData>) -> Self {
192        Self::new(
193            ValueSlot::from_deque(d),
194            NativeKind::Ptr(HeapKind::Deque),
195        )
196    }
197
198    /// Convenience: a `Ptr(HeapKind::Channel)`-kind slot. Mirror of
199    /// `from_hashset` per ADR-006 §2.7.20 / Q21 amendment (Wave 15
200    /// W15-channel-rebuild). Channel is the first concurrency
201    /// primitive to land kinded — full
202    /// `HeapValue::Channel(Arc<ChannelData>)` arm, not pure-discriminator.
203    /// Inner state carries `Mutex<ChannelInner>`; cloning the outer
204    /// `Arc` hands out a fresh endpoint of the same channel.
205    #[inline]
206    pub fn from_channel(c: Arc<ChannelData>) -> Self {
207        Self::new(
208            ValueSlot::from_channel(c),
209            NativeKind::Ptr(HeapKind::Channel),
210        )
211    }
212
213    /// Convenience: a `Ptr(HeapKind::TraitObject)`-kind slot. Mirror
214    /// of `from_typed_object` per ADR-006 §2.7.24 / Q25.C amendment
215    /// (Wave 17 W17-trait-object-storage, 2026-05-11). Full
216    /// `HeapValue::TraitObject(Arc<TraitObjectStorage>)` arm —
217    /// `TraitObjectStorage` is the typed-Arc replacement for the
218    /// bulldozer-deleted `HeapValue::TraitObject { value: Box<u64>,
219    /// vtable: Arc<VTable> }` shape.
220    ///
221    /// **Wave 2 Agent E (2026-05-14): legacy transitional constructor.**
222    /// See `ValueSlot::from_trait_object` docstring for the Arc-vs-raw-pointer
223    /// staging.
224    #[inline]
225    pub fn from_trait_object(t: Arc<TraitObjectStorage>) -> Self {
226        Self::new(
227            ValueSlot::from_trait_object(t),
228            NativeKind::Ptr(HeapKind::TraitObject),
229        )
230    }
231
232    /// Convenience: a `Ptr(HeapKind::TraitObject)`-kind slot from a raw
233    /// `*const TraitObjectStorage`. **Wave 2 Agent E (2026-05-14): v2-raw
234    /// raw-pointer constructor.** Per ADR-006 §Q25.C.5 amendment + audit
235    /// §4.3 O-3.a resolution. Pairs with `TraitObjectStorage::_new`
236    /// allocator; refcount discipline goes through the on-header refcount
237    /// via `v2_retain` / `v2_release` (NOT Rust `Arc::increment/decrement_
238    /// strong_count`). See `ValueSlot::from_trait_object_raw` docstring
239    /// for the full call-site migration pattern.
240    #[inline]
241    pub fn from_trait_object_raw(ptr: *const TraitObjectStorage) -> Self {
242        Self::new(
243            ValueSlot::from_trait_object_raw(ptr),
244            NativeKind::Ptr(HeapKind::TraitObject),
245        )
246    }
247
248    /// Convenience: a `Ptr(HeapKind::Mutex)`-kind slot. Mirror of
249    /// `from_channel` per ADR-006 §2.7.25 amendment (Wave 17
250    /// W17-concurrency, 2026-05-11). Full
251    /// `HeapValue::Mutex(Arc<MutexData>)` arm.
252    #[inline]
253    pub fn from_mutex(m: Arc<MutexData>) -> Self {
254        Self::new(
255            ValueSlot::from_mutex(m),
256            NativeKind::Ptr(HeapKind::Mutex),
257        )
258    }
259
260    /// Convenience: a `Ptr(HeapKind::Atomic)`-kind slot. Mirror of
261    /// `from_channel` per ADR-006 §2.7.25 amendment (Wave 17
262    /// W17-concurrency, 2026-05-11). Full
263    /// `HeapValue::Atomic(Arc<AtomicData>)` arm. i64-only at landing.
264    #[inline]
265    pub fn from_atomic(a: Arc<AtomicData>) -> Self {
266        Self::new(
267            ValueSlot::from_atomic(a),
268            NativeKind::Ptr(HeapKind::Atomic),
269        )
270    }
271
272    /// Convenience: a `Ptr(HeapKind::Lazy)`-kind slot. Mirror of
273    /// `from_channel` per ADR-006 §2.7.25 amendment (Wave 17
274    /// W17-concurrency, 2026-05-11). Full
275    /// `HeapValue::Lazy(Arc<LazyData>)` arm.
276    #[inline]
277    pub fn from_lazy(l: Arc<LazyData>) -> Self {
278        Self::new(
279            ValueSlot::from_lazy(l),
280            NativeKind::Ptr(HeapKind::Lazy),
281        )
282    }
283
284    /// Convenience: a `Ptr(HeapKind::Temporal)`-kind slot. ADR-006
285    /// §2.7.6 / Q8 cardinality amendment (Wave 3 W17-from-temporal-
286    /// instant-constructors, 2026-05-12). Slot bits are
287    /// `Arc::into_raw(Arc<TemporalData>) as u64`; recovery goes through
288    /// the canonical 5-arm receiver-recovery pattern (reconstruct via
289    /// `Arc::<TemporalData>::from_raw`, clone, `into_raw` to restore).
290    /// Mirror of `from_iterator` typed-Arc dispatch shape — `TemporalData`
291    /// is the consolidated DateTime / Duration / TimeSpan / Timeframe /
292    /// TimeReference / DateTimeExpr / DataDateTimeRef carrier per
293    /// `heap_value.rs::TemporalData`. The Drop / Clone arms for
294    /// `HeapKind::Temporal` already dispatch the matching strong-count
295    /// retain/release; this constructor pairs with them by the §2.7.6 /
296    /// Q8 bounded carrier-API rule (one constructor per `NativeKind` heap
297    /// variant, no new heap-variant cardinality introduced).
298    #[inline]
299    pub fn from_temporal(arc: Arc<crate::heap_value::TemporalData>) -> Self {
300        let bits = Arc::into_raw(arc) as u64;
301        Self::new(
302            ValueSlot::from_raw(bits),
303            NativeKind::Ptr(HeapKind::Temporal),
304        )
305    }
306
307    /// Convenience: a `Ptr(HeapKind::Instant)`-kind slot. ADR-006
308    /// §2.7.6 / Q8 cardinality amendment (Wave 3 W17-from-temporal-
309    /// instant-constructors, 2026-05-12). Slot bits are
310    /// `Arc::into_raw(Arc<std::time::Instant>) as u64`; recovery goes
311    /// through the canonical 5-arm receiver-recovery pattern. Mirror of
312    /// `from_temporal` for the `Instant` sibling — `Instant` rides
313    /// `Arc<std::time::Instant>` directly (no `InstantData` wrapper).
314    /// The Drop / Clone arms for `HeapKind::Instant` already dispatch
315    /// the matching strong-count retain/release; this constructor pairs
316    /// with them by the §2.7.6 / Q8 bounded carrier-API rule.
317    #[inline]
318    pub fn from_instant(arc: Arc<std::time::Instant>) -> Self {
319        let bits = Arc::into_raw(arc) as u64;
320        Self::new(
321            ValueSlot::from_raw(bits),
322            NativeKind::Ptr(HeapKind::Instant),
323        )
324    }
325
326    /// Convenience: a `Ptr(HeapKind::Iterator)`-kind slot. Stores the
327    /// `Arc::into_raw` pointer directly per ADR-006 §2.7.16 / Q17 (W13-
328    /// iterator-state).
329    #[inline]
330    pub fn from_iterator(it: Arc<IteratorState>) -> Self {
331        let bits = Arc::into_raw(it) as u64;
332        Self::new(
333            ValueSlot::from_raw(bits),
334            NativeKind::Ptr(HeapKind::Iterator),
335        )
336    }
337
338    /// Convenience: a `Ptr(HeapKind::PriorityQueue)`-kind slot. Mirror
339    /// of `from_hashset` per ADR-006 §2.7.18 / Q19 amendment (Wave 15
340    /// W15-priority-queue). PriorityQueue is a HashSet sibling — full
341    /// `HeapValue::PriorityQueue(Arc<PriorityQueueData>)` arm, not
342    /// pure-discriminator.
343    #[inline]
344    pub fn from_priority_queue(p: Arc<PriorityQueueData>) -> Self {
345        Self::new(
346            ValueSlot::from_priority_queue(p),
347            NativeKind::Ptr(HeapKind::PriorityQueue),
348        )
349    }
350
351    /// Convenience: a `Ptr(HeapKind::Range)`-kind slot. Stores the
352    /// `Arc<RangeData>` directly per ADR-006 §2.7.23 / Q24 (W15-range).
353    /// Slot bits are `Arc::into_raw(Arc<RangeData>) as u64`; recovery
354    /// goes through `slot.as_heap_value()` → `HeapValue::Range(arc)`
355    /// per ADR-005 §1 single-discriminator.
356    #[inline]
357    pub fn from_range(r: Arc<RangeData>) -> Self {
358        Self::new(
359            ValueSlot::from_range(r),
360            NativeKind::Ptr(HeapKind::Range),
361        )
362    }
363
364    /// Convenience: a `Ptr(HeapKind::Result)`-kind slot. ADR-006 §2.7.17 /
365    /// Q18 amendment (Wave 14 W14-variant-codegen). Mirror of
366    /// `from_iterator` typed-Arc dispatch shape.
367    #[inline]
368    pub fn from_result(r: Arc<ResultData>) -> Self {
369        Self::new(
370            ValueSlot::from_result(r),
371            NativeKind::Ptr(HeapKind::Result),
372        )
373    }
374
375    /// Convenience: a `Ptr(HeapKind::Option)`-kind slot. ADR-006 §2.7.17 /
376    /// Q18 amendment (Wave 14 W14-variant-codegen).
377    #[inline]
378    pub fn from_option(o: Arc<OptionData>) -> Self {
379        Self::new(
380            ValueSlot::from_option(o),
381            NativeKind::Ptr(HeapKind::Option),
382        )
383    }
384
385    /// Convenience: a `Ptr(HeapKind::Decimal)`-kind slot.
386    #[inline]
387    pub fn from_decimal(d: Arc<rust_decimal::Decimal>) -> Self {
388        Self::new(
389            ValueSlot::from_decimal(d),
390            NativeKind::Ptr(HeapKind::Decimal),
391        )
392    }
393
394    /// Convenience: a `StringV2`-kind slot from a v2-raw `*const StringObj`
395    /// pointer. ADR-006 §2.7.5 amendment (Wave 2 Agent B W12-StringV2-
396    /// DecimalV2-NativeKind-additions, 2026-05-14): paired with
397    /// `NativeKind::StringV2` per the audit §H.4 H-c decision. Slot bits
398    /// = `ptr as u64`; the `KindedSlot` owns one refcount share on the
399    /// underlying `StringObj` — Drop dispatches the matching `v2_release`
400    /// against the `HeapHeader` at offset 0.
401    ///
402    /// Caller's construction-side contract: `ptr` MUST point to a live
403    /// `StringObj` whose refcount has been incremented (typically via
404    /// `v2_retain` at the producing `op_typed_array_get` site) to claim
405    /// the share this `KindedSlot` now owns. Mirror of `from_string_arc`'s
406    /// "slot owns one strong share" contract — but the underlying retain
407    /// is `v2_retain` against the `repr(C)` HeapHeader, not
408    /// `Arc::increment_strong_count` against an `Arc<String>`.
409    #[inline]
410    pub fn from_string_v2_ptr(ptr: *const crate::v2::string_obj::StringObj) -> Self {
411        Self::new(ValueSlot::from_string_v2_ptr(ptr), NativeKind::StringV2)
412    }
413
414    /// Convenience: a `DecimalV2`-kind slot from a v2-raw `*const DecimalObj`
415    /// pointer. ADR-006 §2.7.5 amendment (Wave 2 Agent B W12-StringV2-
416    /// DecimalV2-NativeKind-additions, 2026-05-14): mirror of
417    /// `from_string_v2_ptr` for the `DecimalObj` sibling. Same construction-
418    /// side contract.
419    #[inline]
420    pub fn from_decimal_v2_ptr(ptr: *const crate::v2::decimal_obj::DecimalObj) -> Self {
421        Self::new(ValueSlot::from_decimal_v2_ptr(ptr), NativeKind::DecimalV2)
422    }
423
424    /// Convenience: a `Ptr(HeapKind::BigInt)`-kind slot.
425    #[inline]
426    pub fn from_bigint(b: Arc<i64>) -> Self {
427        Self::new(ValueSlot::from_bigint(b), NativeKind::Ptr(HeapKind::BigInt))
428    }
429
430    /// Convenience: a `Char`-kind slot. ADR-006 §2.7.5 amendment (Round
431    /// 19 S1.5 W12-nativekind-scalar-additions, 2026-05-14): Char joins
432    /// the scalar bucket — `char` is `Copy + 4-byte` (UTF-32 codepoint),
433    /// no Arc payload. Slot bits store `c as u32` zero-extended into
434    /// the low 32 bits of the 8-byte slot.
435    ///
436    /// Pre-amendment (Round 18 and earlier) this constructor returned
437    /// a slot with kind `NativeKind::Ptr(HeapKind::Char)` — the inline-
438    /// codepoint payload tagged through `HeapKind` for dispatch
439    /// uniformity. The post-amendment shape is a pure scalar variant
440    /// (`NativeKind::Char`), aligning Char with the other 4-byte
441    /// scalars (`Int32` / `UInt32` / `Float32`) per §Q8 carrier-API
442    /// bound (one constructor per scalar variant). The
443    /// `NativeKind::Ptr(HeapKind::Char)` label still exists in source
444    /// (direct `push_kinded(c as u64, NativeKind::Ptr(HeapKind::Char))`
445    /// call-sites have NOT been migrated in this dispatch — a future
446    /// cluster-1 hardening sub-cluster retires that parallel label).
447    /// Drop is a no-op (inline scalar; the `NativeKind::Char` arm in
448    /// `Drop` is part of the inline-scalar group).
449    #[inline]
450    pub fn from_char(c: char) -> Self {
451        Self::new(ValueSlot::from_char(c), NativeKind::Char)
452    }
453
454    /// Convenience: a `Ptr(HeapKind::Matrix)`-kind slot. ADR-006 §2.7.22
455    /// amendment (Round 18 S3 W12-matrix-floatslice-heapkind-exit,
456    /// 2026-05-13). Slot bits are `Arc::into_raw(Arc<MatrixData>) as u64`;
457    /// recovery goes through the canonical reconstruct-clone-restore
458    /// pattern (`Arc::<MatrixData>::from_raw(bits)` → clone → `into_raw`)
459    /// to bump the inner share while preserving the carrier's owned
460    /// outer share. Mirror of `from_iterator` / `from_range` typed-Arc
461    /// dispatch shape — `as_heap_value()` on a Matrix-labeled slot is
462    /// unsound (the slot bits are an `Arc<MatrixData>` pointer, not a
463    /// `*const HeapValue`). The Drop / Clone arms for `HeapKind::Matrix`
464    /// dispatch the matching `Arc::increment/decrement_strong_count
465    /// ::<MatrixData>` retain/release.
466    #[inline]
467    pub fn from_matrix(m: Arc<MatrixData>) -> Self {
468        let bits = Arc::into_raw(m) as u64;
469        Self::new(ValueSlot::from_raw(bits), NativeKind::Ptr(HeapKind::Matrix))
470    }
471
472    /// Convenience: a `Ptr(HeapKind::MatrixSlice)`-kind slot. ADR-006
473    /// §2.7.22 amendment (Round 18 S3 W12-matrix-floatslice-heapkind-exit,
474    /// 2026-05-13). Slot bits are
475    /// `Arc::into_raw(Arc<MatrixSliceData>) as u64`. Same typed-Arc
476    /// pure-discriminator dispatch shape as `from_matrix`. The inner
477    /// `MatrixSliceData { parent, offset, len }` retains a separate
478    /// strong-count share on its parent matrix; cloning the outer share
479    /// does NOT bump the parent — that bump happens at
480    /// `MatrixSliceData::clone` (auto-derived) when the inner struct is
481    /// duplicated under `Arc::make_mut`.
482    #[inline]
483    pub fn from_matrix_slice(s: Arc<MatrixSliceData>) -> Self {
484        let bits = Arc::into_raw(s) as u64;
485        Self::new(
486            ValueSlot::from_raw(bits),
487            NativeKind::Ptr(HeapKind::MatrixSlice),
488        )
489    }
490
491    /// Convenience: a `Ptr(HeapKind::ModuleFn)`-kind slot. Stores the
492    /// `module_fn_id` as raw `u64` slot bits directly (inline-scalar
493    /// payload — no `Arc<T>`, no heap state). Same shape as
494    /// `from_char` / `from_future` per ADR-006 §2.7.26
495    /// (W17-comptime-vm-dispatch).
496    ///
497    /// Construction-side contract: `id` must index a registered entry
498    /// in `VirtualMachine.module_fn_table`. The dispatch shell at
499    /// `executor/call_convention.rs::call_value_immediate_nb` consumes
500    /// the kind label to route the slot's bits to
501    /// `invoke_module_fn_id_stub` at `CallValue` time.
502    #[inline]
503    pub fn from_module_fn_id(id: u64) -> Self {
504        Self::new(
505            ValueSlot::from_raw(id),
506            NativeKind::Ptr(HeapKind::ModuleFn),
507        )
508    }
509
510    /// Convenience: a `String`-kind slot from a `&str`. Allocates a fresh
511    /// `Arc<String>`. Use `from_string_arc` when you already have the
512    /// `Arc<String>` in hand and want to avoid a clone.
513    #[inline]
514    pub fn from_string(s: &str) -> Self {
515        Self::from_string_arc(Arc::new(s.to_string()))
516    }
517
518    /// A null/none-value `KindedSlot`. Bool-kind by convention so the slot
519    /// has a stable interpretation and Drop is a no-op.
520    #[inline]
521    pub fn none() -> Self {
522        Self::new(ValueSlot::none(), NativeKind::Bool)
523    }
524
525    /// Read the inner slot.
526    #[inline]
527    pub fn slot(&self) -> ValueSlot {
528        self.slot
529    }
530
531    /// Read the kind.
532    #[inline]
533    pub fn kind(&self) -> NativeKind {
534        self.kind
535    }
536
537    /// Raw slot bits. Provided for sites that need to peek at the storage
538    /// shape (e.g. wire serialization). Prefer typed accessors.
539    #[inline]
540    pub fn raw(&self) -> u64 {
541        self.slot.raw()
542    }
543
544    // ── Scalar accessors (ADR-006 §2.7.6 / Q8) ────────────────────────────
545    //
546    // One accessor per `NativeKind` *scalar* variant. Each kind-dispatches
547    // on `self.kind` and returns `Some(payload)` only when the kind matches
548    // exactly. Heap variants do NOT get per-variant accessors here; bodies
549    // dispatching on a heap-typed `KindedSlot` use
550    // `kinded_slot.slot.as_heap_value() -> &HeapValue` and pattern-match,
551    // preserving ADR-005 §1's single-discriminator discipline.
552
553    /// Read as `i64` if `self.kind == NativeKind::Int64`, else `None`.
554    #[inline]
555    pub fn as_i64(&self) -> Option<i64> {
556        match self.kind {
557            NativeKind::Int64 => Some(self.slot.as_i64()),
558            _ => None,
559        }
560    }
561
562    /// Read as `f64` if `self.kind == NativeKind::Float64`, else `None`.
563    #[inline]
564    pub fn as_f64(&self) -> Option<f64> {
565        match self.kind {
566            NativeKind::Float64 => Some(self.slot.as_f64()),
567            _ => None,
568        }
569    }
570
571    /// Read as `bool` if `self.kind == NativeKind::Bool`, else `None`.
572    #[inline]
573    pub fn as_bool(&self) -> Option<bool> {
574        match self.kind {
575            NativeKind::Bool => Some(self.slot.as_bool()),
576            _ => None,
577        }
578    }
579
580    /// Read as `char` if `self.kind == NativeKind::Char`, else `None`.
581    /// ADR-006 §2.7.5 amendment (Round 19 S1.5, 2026-05-14): the §Q8
582    /// carrier-API bound binds `as_char` to the new scalar
583    /// `NativeKind::Char` variant. The pre-amendment
584    /// `NativeKind::Ptr(HeapKind::Char)` carrier label is ALSO recognized
585    /// for cross-tier-compatibility — the label still exists in source
586    /// (direct `push_kinded(c as u64, NativeKind::Ptr(HeapKind::Char))`
587    /// call-sites have NOT been migrated in this dispatch); recognizing
588    /// both labels avoids producer/consumer mismatch when those call-
589    /// sites flow values through code paths that materialize as
590    /// `KindedSlot` before consuming `as_char`. Both labels store
591    /// codepoint bits zero-extended in the low 32 bits of the slot, so
592    /// the read is identical in either kind.
593    #[inline]
594    pub fn as_char(&self) -> Option<char> {
595        match self.kind {
596            NativeKind::Char | NativeKind::Ptr(HeapKind::Char) => self.slot.as_char(),
597            _ => None,
598        }
599    }
600
601    /// Read as `f32` if `self.kind == NativeKind::Float32`, else `None`.
602    /// ADR-006 §2.7.5 amendment (Round 19 S1.5, 2026-05-14). Slot bits
603    /// store the IEEE-754 single-precision pattern zero-extended into
604    /// the low 32 bits; `f32::from_bits` reinterprets the low 32 bits.
605    #[inline]
606    pub fn as_f32(&self) -> Option<f32> {
607        match self.kind {
608            NativeKind::Float32 => Some(f32::from_bits(self.slot.raw() as u32)),
609            _ => None,
610        }
611    }
612
613    /// Read as `&str` if `self.kind == NativeKind::String`, else `None`.
614    /// The slot stores an `Arc<String>` raw pointer; this accessor borrows
615    /// the inner `&str` for the lifetime of `&self` (the `KindedSlot` owns
616    /// one strong-count share, so the `Arc` is alive while `&self` lives).
617    #[inline]
618    pub fn as_str(&self) -> Option<&str> {
619        match self.kind {
620            NativeKind::String => {
621                let bits = self.slot.raw();
622                if bits == 0 {
623                    return None;
624                }
625                // SAFETY: per the construction-side contract, `NativeKind::String`
626                // means the slot bits are `Arc::into_raw::<String>` and this
627                // `KindedSlot` owns one strong-count share (so the inner
628                // `String` is alive). The returned `&str` borrows from
629                // `&self`; lifetime is bounded by the slot's ownership.
630                let s: &String = unsafe { &*(bits as *const String) };
631                Some(s.as_str())
632            }
633            _ => None,
634        }
635    }
636}
637
638impl std::fmt::Debug for KindedSlot {
639    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
640        f.debug_struct("KindedSlot")
641            .field("slot", &self.slot)
642            .field("kind", &self.kind)
643            .finish()
644    }
645}
646
647impl Default for KindedSlot {
648    fn default() -> Self {
649        Self::none()
650    }
651}
652
653/// Drop dispatches on `kind` to retire the matching `Arc<T>` strong-count
654/// share. Mirrors `TypedObjectStorage::Drop` in `heap_value.rs:761`.
655impl Drop for KindedSlot {
656    fn drop(&mut self) {
657        let bits = self.slot.raw();
658        if bits == 0 {
659            return;
660        }
661        // SAFETY: per the construction-side contract on every `KindedSlot`
662        // constructor, when `kind` selects a heap arm the slot bits are
663        // the result of `Arc::into_raw::<T>` for the matching `T`. Drop
664        // retires exactly one strong-count share.
665        unsafe {
666            match self.kind {
667                NativeKind::String => {
668                    Arc::decrement_strong_count(bits as *const String);
669                }
670                // Wave 2 Agent B (ADR-006 §2.7.5 amendment, 2026-05-14):
671                // StringV2 / DecimalV2 are v2-raw heap-pointer carriers per
672                // the §H.4 H-c decision. Slot bits are `ptr as u64` where
673                // `ptr: *const StringObj` / `*const DecimalObj`. Refcount
674                // discipline goes through `v2_release` against the
675                // `HeapHeader` at offset 0 of the carrier (NOT
676                // `Arc::decrement_strong_count` — these are manually-
677                // allocated `repr(C)` carriers per `v2/string_obj.rs` /
678                // `v2/decimal_obj.rs`, not `Arc<T>` allocations). On
679                // refcount=0, the carrier's `HeapElement::release_elem`
680                // implementation deallocates the struct.
681                NativeKind::StringV2 => {
682                    use crate::v2::heap_element::HeapElement;
683                    crate::v2::string_obj::StringObj::release_elem(
684                        bits as *const crate::v2::string_obj::StringObj,
685                    );
686                }
687                NativeKind::DecimalV2 => {
688                    use crate::v2::heap_element::HeapElement;
689                    crate::v2::decimal_obj::DecimalObj::release_elem(
690                        bits as *const crate::v2::decimal_obj::DecimalObj,
691                    );
692                }
693                NativeKind::Ptr(hk) => match hk {
694                    HeapKind::String => {
695                        Arc::decrement_strong_count(bits as *const String);
696                    }
697                    // V3-S5 ckpt-5-prime (2026-05-15): `HeapKind::TypedArray`
698                    // dispatch arm RETIRED per W12 audit §3.6 + handover §0
699                    // 4-table lockstep rule. Mirror of `heap_value.rs` retire
700                    // arm above. Ordinal 8 stays vacated; no live slot bits
701                    // carry this kind post-V3-S5 ckpt-4. Refusal #1 binding.
702                    HeapKind::TypedArray => {
703                        unreachable!(
704                            "HeapKind::TypedArray ordinal 8 is vacated per W12 audit §3.6 \
705                             (KindedSlot::drop_with_kind); no live slot bits carry this kind \
706                             post-V3-S5 ckpt-4 (v2-raw *mut TypedArray<T> carriers per ADR-006 \
707                             §2.7.24 Q25.A SUPERSEDED)"
708                        );
709                    }
710                    // Wave 2 Agent D4 ckpt-2 (ADR-006 §2.3 / §2.7.5
711                    // amendment, 2026-05-14): TypedObject release via
712                    // `HeapElement::release_elem` + carrier-side `_drop`
713                    // (per Agent D1's `impl HeapElement for
714                    // TypedObjectStorage` — calls `v2_release` against the
715                    // HeapHeader at offset 0; on refcount=0 the
716                    // carrier-side `_drop` runs the per-field heap-mask
717                    // walk and deallocates the `repr(C)` struct). Mirror
718                    // of the §2.7.5 StringV2 / DecimalV2 release arms
719                    // above.
720                    HeapKind::TypedObject => {
721                        use crate::v2::heap_element::HeapElement;
722                        TypedObjectStorage::release_elem(
723                            bits as *const TypedObjectStorage,
724                        );
725                    }
726                    HeapKind::HashMap => {
727                        // Wave 2 Round 3b C2-joint ckpt-2 (2026-05-14):
728                        // bits are `Arc::into_raw(Arc<HashMapKindedRef>)`;
729                        // release dispatches outer Arc decrement → enum
730                        // Drop chains to per-V `Arc<HashMapData<V>>` release.
731                        Arc::decrement_strong_count(
732                            bits as *const crate::heap_value::HashMapKindedRef,
733                        );
734                    }
735                    // Wave 13 W13-hashset-rebuild (ADR-006 §2.7.15 / Q16,
736                    // 2026-05-10): mirror of the HashMap arm. Retires
737                    // one `Arc<HashSetData>` strong-count share.
738                    HeapKind::HashSet => {
739                        Arc::decrement_strong_count(bits as *const HashSetData);
740                    }
741                    // Wave 15 W15-deque (ADR-006 §2.7.19 / Q20,
742                    // 2026-05-10): mirror of the HashSet arm. Retires
743                    // one `Arc<DequeData>` strong-count share.
744                    HeapKind::Deque => {
745                        Arc::decrement_strong_count(bits as *const DequeData);
746                    }
747                    // Wave 15 W15-channel-rebuild (ADR-006 §2.7.20 / Q21,
748                    // 2026-05-10): mirror of the HashSet arm. Slot bits
749                    // are `Arc::into_raw(Arc<ChannelData>) as u64`.
750                    // Retires one `Arc<ChannelData>` strong-count share
751                    // — at refcount=0 the inner `ChannelData` Drop runs
752                    // (default-derived) which retires the queued
753                    // `KindedSlot` payloads via their own Drop.
754                    HeapKind::Channel => {
755                        Arc::decrement_strong_count(bits as *const ChannelData);
756                    }
757                    // W17-trait-object-storage (ADR-006 §2.7.24 / Q25.C,
758                    // 2026-05-11): TraitObject mirrors the typed-Arc
759                    // dispatch shape. Slot bits are
760                    // `Arc::into_raw(Arc<TraitObjectStorage>) as u64`.
761                    // Retires one strong-count share — at refcount=0
762                    // the inner `TraitObjectStorage::Drop` runs,
763                    // releasing its inner `Arc<TypedObjectStorage>`
764                    // value half + `Arc<VTable>` vtable half via
765                    // auto-derived `Drop`.
766                    // Wave 2 Agent D4 ckpt-2 (ADR-006 §2.7.24 / Q25.C.5 +
767                    // E close 2026-05-14): TraitObject release via
768                    // `HeapElement::release_elem` + carrier-side `_drop`
769                    // (per Agent E's `impl HeapElement for
770                    // TraitObjectStorage`). Mirror of the TypedObject arm
771                    // above.
772                    HeapKind::TraitObject => {
773                        use crate::v2::heap_element::HeapElement;
774                        TraitObjectStorage::release_elem(
775                            bits as *const TraitObjectStorage,
776                        );
777                    }
778                    // W17-concurrency (ADR-006 §2.7.25, 2026-05-11):
779                    // Mutex / Atomic / Lazy mirror the Channel arm.
780                    // Slot bits are `Arc::into_raw(Arc<MutexData>) /
781                    // Arc<AtomicData> / Arc<LazyData>) as u64`. Retires
782                    // one strong-count share — at refcount=0 the inner
783                    // Drop runs and (for Mutex/Lazy) retires the
784                    // protected `KindedSlot` payload via its own Drop.
785                    HeapKind::Mutex => {
786                        Arc::decrement_strong_count(bits as *const MutexData);
787                    }
788                    HeapKind::Atomic => {
789                        Arc::decrement_strong_count(bits as *const AtomicData);
790                    }
791                    HeapKind::Lazy => {
792                        Arc::decrement_strong_count(bits as *const LazyData);
793                    }
794                    HeapKind::Decimal => {
795                        Arc::decrement_strong_count(bits as *const rust_decimal::Decimal);
796                    }
797                    HeapKind::BigInt => {
798                        Arc::decrement_strong_count(bits as *const i64);
799                    }
800                    HeapKind::DataTable => {
801                        Arc::decrement_strong_count(
802                            bits as *const crate::datatable::DataTable,
803                        );
804                    }
805                    HeapKind::IoHandle => {
806                        Arc::decrement_strong_count(bits as *const IoHandleData);
807                    }
808                    HeapKind::NativeView => {
809                        Arc::decrement_strong_count(bits as *const NativeViewData);
810                    }
811                    HeapKind::Content => {
812                        Arc::decrement_strong_count(
813                            bits as *const crate::content::ContentNode,
814                        );
815                    }
816                    HeapKind::Instant => {
817                        Arc::decrement_strong_count(bits as *const std::time::Instant);
818                    }
819                    HeapKind::Temporal => {
820                        Arc::decrement_strong_count(bits as *const TemporalData);
821                    }
822                    HeapKind::TableView => {
823                        Arc::decrement_strong_count(bits as *const TableViewData);
824                    }
825                    HeapKind::TaskGroup => {
826                        Arc::decrement_strong_count(bits as *const TaskGroupData);
827                    }
828                    // Wave-γ G-heap-filter-expr (ADR-006 §2.3 / §2.7.6 / Q8
829                    // amendment): FilterExpr-kinded `KindedSlot`s own one
830                    // `Arc::into_raw(Arc<FilterNode>)` strong-count share.
831                    // The pre-amendment `HeapKind::NativeView` mislabel
832                    // would have dispatched the share as
833                    // `Arc<NativeViewData>` — wrong-type retire.
834                    HeapKind::FilterExpr => {
835                        Arc::decrement_strong_count(bits as *const FilterNode);
836                    }
837                    // Wave 8 W8-T26 (ADR-006 §2.7.13 / Q14, 2026-05-10):
838                    // mirror of `vm_impl/stack.rs:drop_with_kind` Reference
839                    // arm. Slot bits are `Arc::into_raw(Arc<RefTarget>)`
840                    // directly per §2.7.13's pure-discriminator-style
841                    // dispatch (NOT a `Box<HeapValue>` wrap); retire one
842                    // `Arc<RefTarget>` strong-count share. At refcount=0
843                    // the inner `RefTarget` Drop releases its `receiver`
844                    // typed-Arc share for `TypedField` / `TypedIndex`
845                    // variants — `Local` / `ModuleBinding` variants hold
846                    // no Arc.
847                    HeapKind::Reference => {
848                        Arc::decrement_strong_count(bits as *const RefTarget);
849                    }
850                    // W13-iterator-state (ADR-006 §2.7.16 / Q17,
851                    // 2026-05-10): mirror of `vm_impl/stack.rs::
852                    // drop_with_kind` Iterator arm. Slot bits are
853                    // `Arc::into_raw(Arc<IteratorState>)` directly
854                    // (mirror of FilterExpr / Reference's typed-Arc
855                    // dispatch — NOT a `Box<HeapValue>` wrap); retire
856                    // one `Arc<IteratorState>` strong-count share.
857                    HeapKind::Iterator => {
858                        Arc::decrement_strong_count(bits as *const IteratorState);
859                    }
860                    // Wave 15 W15-priority-queue (ADR-006 §2.7.18 / Q19,
861                    // 2026-05-10): mirror of the HashSet arm. Retires
862                    // one `Arc<PriorityQueueData>` strong-count share —
863                    // PriorityQueue is a HashSet sibling per §2.7.18,
864                    // full-`HeapValue` arm.
865                    HeapKind::PriorityQueue => {
866                        Arc::decrement_strong_count(bits as *const PriorityQueueData);
867                    }
868                    // W15-range (ADR-006 §2.7.23 / Q24, 2026-05-10):
869                    // mirror of `vm_impl/stack.rs::drop_with_kind`
870                    // Range arm. Slot bits are
871                    // `Arc::into_raw(Arc<RangeData>)` directly (typed-Arc
872                    // shape, mirror of HashMap / HashSet / Iterator);
873                    // retire one `Arc<RangeData>` strong-count share.
874                    // RangeData is `Copy`-shaped (four scalar fields,
875                    // no inner Arcs) so refcount=0 just deallocates the
876                    // small heap block.
877                    HeapKind::Range => {
878                        Arc::decrement_strong_count(bits as *const RangeData);
879                    }
880                    // Wave 14 W14-variant-codegen (ADR-006 §2.7.17 / Q18,
881                    // 2026-05-10): mirror of the Iterator arm. Slot
882                    // bits are `Arc::into_raw(Arc<ResultData>)`; retire
883                    // one `Arc<ResultData>` strong-count share. At
884                    // refcount=0 `ResultData::Drop` (auto-derived from
885                    // its embedded `KindedSlot` payload) retires the
886                    // inner-value share recursively.
887                    HeapKind::Result => {
888                        Arc::decrement_strong_count(bits as *const ResultData);
889                    }
890                    HeapKind::Option => {
891                        Arc::decrement_strong_count(bits as *const OptionData);
892                    }
893                    // Char: inline-scalar payload (codepoint bits, not an
894                    // `Arc<T>`). Drop is a no-op; non-zero bits are valid
895                    // (e.g. `from_char('a')` stores 97).
896                    HeapKind::Char => {
897                        // No-op: inline-scalar payload.
898                    }
899                    // Round 2.5b W7-closure-retain-parallel (ADR-006
900                    // §2.7.11 / Q12, 2026-05-09 — lockstep with vm-tier
901                    // Round 2.5 close `5fa4b19`): a
902                    // `NativeKind::Ptr(HeapKind::Closure)` slot carries
903                    // `Arc::into_raw(Arc<HeapValue>) as u64` pointing to
904                    // a `HeapValue::ClosureRaw(OwnedClosureBlock)` arm.
905                    // The share carrier at the slot tier is the outer
906                    // `Arc<HeapValue>`, not the inner `OwnedClosureBlock`'s
907                    // typed-closure-header refcount (which
908                    // `OwnedClosureBlock` manages internally on its own
909                    // `clone()` / `drop()`). Round 2 close (`06cdfce`)
910                    // committed to this slot-bits shape via
911                    // `callee.slot.as_heap_value()` →
912                    // `HeapValue::ClosureRaw(block)` in
913                    // `call_value_immediate_nb`. The §2.7.11 dispatch
914                    // shell pops closure-bearing `KindedSlot` carriers
915                    // whose `Drop` arrives here on every consumed call
916                    // arg and on the callee itself. Same dispatch
917                    // shape as the `HeapKind::FilterExpr` §2.7.9
918                    // amendment (one variant, one matching `Arc<T>`
919                    // retain/release at the slot tier).
920                    HeapKind::Closure => {
921                        Arc::decrement_strong_count(bits as *const HeapValue);
922                    }
923                    // `Ptr(HeapKind::Future)` carries the future-id u64
924                    // directly in `bits` (inline scalar — no heap state,
925                    // no `Arc<T>` payload). See `async_ops/mod.rs`
926                    // §"Wave 6.5 / E-async migration" docstring and
927                    // `printing.rs` `HeapKind::Future` arm. Same shape
928                    // as `HeapKind::Char`.
929                    HeapKind::Future => {
930                        // No-op: future-id inline scalar.
931                    }
932                    // W17-comptime-vm-dispatch (ADR-006 §2.7.26, 2026-05-12):
933                    // `Ptr(HeapKind::ModuleFn)` carries the module-fn-id
934                    // u64 directly in `bits` (inline scalar — no heap
935                    // state, no `Arc<T>` payload). Same shape as
936                    // `HeapKind::Future` / `HeapKind::Char`.
937                    HeapKind::ModuleFn => {
938                        // No-op: module-fn-id inline scalar.
939                    }
940                    // Wave 8 W8-T25 (ADR-006 §2.7.12 / Q13 amendment,
941                    // 2026-05-10): `SharedCell`-kinded `KindedSlot`s
942                    // own one `Arc::into_raw(Arc<SharedCell>)` strong-
943                    // count share — the runtime-tier carrier shape for
944                    // an `Arc<SharedCell>` cell-pointer that flows
945                    // through dispatch-slice / module-binding /
946                    // exception-payload carriers. Retires one
947                    // `Arc<SharedCell>` strong-count share. Same dispatch
948                    // shape as the `HeapKind::FilterExpr` §2.7.9
949                    // amendment.
950                    HeapKind::SharedCell => {
951                        Arc::decrement_strong_count(
952                            bits as *const crate::v2::closure_layout::SharedCell,
953                        );
954                    }
955                    // ADR-006 §2.7.22 amendment (Round 18 S3, 2026-05-13):
956                    // Matrix / MatrixSlice slots own one typed-Arc
957                    // strong-count share. Slot bits are
958                    // `Arc::into_raw(Arc<MatrixData>) as u64` /
959                    // `Arc::into_raw(Arc<MatrixSliceData>) as u64`. Retire
960                    // one matching strong-count share. Typed-Arc
961                    // pure-discriminator dispatch (mirror of §2.7.9
962                    // FilterExpr); `as_heap_value()` is unsound on
963                    // Matrix/MatrixSlice-labeled bits.
964                    HeapKind::Matrix => {
965                        Arc::decrement_strong_count(bits as *const MatrixData);
966                    }
967                    HeapKind::MatrixSlice => {
968                        Arc::decrement_strong_count(bits as *const MatrixSliceData);
969                    }
970                    // `HeapKind::NativeScalar` has no kinded `Arc<T>`
971                    // carrier yet — the redesign is the phase-2c
972                    // surface tracked in ADR-006 §2.7.4. The
973                    // `v2_stack_tests.rs` round-trip tests for
974                    // NativeScalar are `todo!()` for the same reason.
975                    // When the kinded NativeScalar carrier lands, this
976                    // arm wires its retain/release per the chosen
977                    // share carrier (per the playbook's
978                    // surface-and-stop discipline — no Bool-default
979                    // fallback, no construction-side fabrication).
980                    // Until then, a non-zero pointer with this kind is
981                    // a construction-side bug.
982                    HeapKind::NativeScalar => {
983                        debug_assert!(
984                            false,
985                            "KindedSlot::drop: NativeScalar kinded carrier pending \
986                             phase-2c kinded redesign (ADR-006 §2.7.4)"
987                        );
988                    }
989                },
990                // Inline-scalar kinds: nothing to decrement. Bits are raw
991                // value, not a pointer.
992                NativeKind::Float64
993                | NativeKind::NullableFloat64
994                | NativeKind::Int8
995                | NativeKind::NullableInt8
996                | NativeKind::UInt8
997                | NativeKind::NullableUInt8
998                | NativeKind::Int16
999                | NativeKind::NullableInt16
1000                | NativeKind::UInt16
1001                | NativeKind::NullableUInt16
1002                | NativeKind::Int32
1003                | NativeKind::NullableInt32
1004                | NativeKind::UInt32
1005                | NativeKind::NullableUInt32
1006                | NativeKind::Int64
1007                | NativeKind::NullableInt64
1008                | NativeKind::UInt64
1009                | NativeKind::NullableUInt64
1010                | NativeKind::IntSize
1011                | NativeKind::NullableIntSize
1012                | NativeKind::UIntSize
1013                | NativeKind::NullableUIntSize
1014                | NativeKind::Bool
1015                // Round 19 S1.5 W12-nativekind-scalar-additions
1016                // (2026-05-14): Float32 + Char are inline 4-byte scalars
1017                // per ADR-006 §2.7.5 amendment. No `Arc<T>` payload, no
1018                // refcount work. Slot bits are the raw f32 bit pattern
1019                // / `c as u32` zero-extended into the low 32 bits.
1020                | NativeKind::Float32
1021                | NativeKind::Char => {}
1022            }
1023        }
1024    }
1025}
1026
1027/// Clone dispatches on `kind` to bump the matching `Arc<T>` strong-count.
1028impl Clone for KindedSlot {
1029    fn clone(&self) -> Self {
1030        let bits = self.slot.raw();
1031        if bits == 0 {
1032            return Self {
1033                slot: self.slot,
1034                kind: self.kind,
1035            };
1036        }
1037        // SAFETY: same construction-side contract as Drop. We bump exactly
1038        // one strong-count share and let Rust copy the slot bits.
1039        unsafe {
1040            match self.kind {
1041                NativeKind::String => {
1042                    Arc::increment_strong_count(bits as *const String);
1043                }
1044                // Wave 2 Agent B (ADR-006 §2.7.5 amendment, 2026-05-14):
1045                // StringV2 / DecimalV2 retain via `v2_retain` against the
1046                // HeapHeader at offset 0 of the carrier — mirror of the
1047                // Drop arms above.
1048                NativeKind::StringV2 => {
1049                    let hdr =
1050                        &(*(bits as *const crate::v2::string_obj::StringObj)).header;
1051                    crate::v2::refcount::v2_retain(hdr);
1052                }
1053                NativeKind::DecimalV2 => {
1054                    let hdr =
1055                        &(*(bits as *const crate::v2::decimal_obj::DecimalObj)).header;
1056                    crate::v2::refcount::v2_retain(hdr);
1057                }
1058                NativeKind::Ptr(hk) => match hk {
1059                    HeapKind::String => {
1060                        Arc::increment_strong_count(bits as *const String);
1061                    }
1062                    // V3-S5 ckpt-5-prime (2026-05-15): `HeapKind::TypedArray`
1063                    // dispatch arm RETIRED per W12 audit §3.6 + handover §0
1064                    // 4-table lockstep rule. Mirror of the drop_with_kind arm.
1065                    // Ordinal 8 vacated; no live slot bits carry this kind
1066                    // post-V3-S5 ckpt-4. Refusal #1 binding.
1067                    HeapKind::TypedArray => {
1068                        unreachable!(
1069                            "HeapKind::TypedArray ordinal 8 is vacated per W12 audit §3.6 \
1070                             (KindedSlot::clone_with_kind); no live slot bits carry this kind \
1071                             post-V3-S5 ckpt-4 (v2-raw *mut TypedArray<T> carriers per ADR-006 \
1072                             §2.7.24 Q25.A SUPERSEDED)"
1073                        );
1074                    }
1075                    // Wave 2 Agent D4 ckpt-2 (ADR-006 §2.3 / §2.7.5
1076                    // amendment, 2026-05-14): TypedObject retain via
1077                    // `v2_retain` against the HeapHeader at offset 0 of
1078                    // the v2-raw carrier. Mirror of the §2.7.5 StringV2
1079                    // / DecimalV2 retain arms above (Agent B precedent).
1080                    HeapKind::TypedObject => {
1081                        let hdr =
1082                            &(*(bits as *const TypedObjectStorage)).header;
1083                        crate::v2::refcount::v2_retain(hdr);
1084                    }
1085                    HeapKind::HashMap => {
1086                        // Wave 2 Round 3b C2-joint ckpt-2 (2026-05-14):
1087                        // bits are `Arc::into_raw(Arc<HashMapKindedRef>)`;
1088                        // retain dispatches outer Arc increment (the per-V
1089                        // inner `Arc<HashMapData<V>>` is preserved by-share
1090                        // via the enum's structural sharing).
1091                        Arc::increment_strong_count(
1092                            bits as *const crate::heap_value::HashMapKindedRef,
1093                        );
1094                    }
1095                    // Wave 13 W13-hashset-rebuild (ADR-006 §2.7.15 / Q16,
1096                    // 2026-05-10): mirror of the HashMap arm. Bumps one
1097                    // `Arc<HashSetData>` strong-count share.
1098                    HeapKind::HashSet => {
1099                        Arc::increment_strong_count(bits as *const HashSetData);
1100                    }
1101                    // Wave 15 W15-deque (ADR-006 §2.7.19 / Q20,
1102                    // 2026-05-10): mirror of the HashSet arm. Bumps
1103                    // one `Arc<DequeData>` strong-count share.
1104                    HeapKind::Deque => {
1105                        Arc::increment_strong_count(bits as *const DequeData);
1106                    }
1107                    // Wave 15 W15-channel-rebuild (ADR-006 §2.7.20 / Q21,
1108                    // 2026-05-10): mirror of the HashSet arm above. Bumps
1109                    // one `Arc<ChannelData>` strong-count share — the
1110                    // outer Arc clone hands out a fresh endpoint of the
1111                    // same channel (interior `Mutex<ChannelInner>` is
1112                    // shared, NOT cloned).
1113                    HeapKind::Channel => {
1114                        Arc::increment_strong_count(bits as *const ChannelData);
1115                    }
1116                    // W17-trait-object-storage (ADR-006 §2.7.24 / Q25.C,
1117                    // 2026-05-11): TraitObject mirrors the typed-Arc
1118                    // dispatch shape. Bumps one strong-count share on
1119                    // the outer `Arc<TraitObjectStorage>` — inner
1120                    // `Arc<TypedObjectStorage>` value half and
1121                    // `Arc<VTable>` vtable half stay shared with the
1122                    // source carrier. `Arc::ptr_eq` on the vtable
1123                    // preserves the §Q25.C.2 `Self`-arg identity
1124                    // contract across the clone.
1125                    // Wave 2 Agent D4 ckpt-2 (ADR-006 §2.7.24 / Q25.C.5 +
1126                    // E close 2026-05-14): TraitObject retain via
1127                    // `v2_retain` against the HeapHeader at offset 0 of
1128                    // the v2-raw carrier. Mirror of the TypedObject arm
1129                    // above.
1130                    HeapKind::TraitObject => {
1131                        let hdr =
1132                            &(*(bits as *const TraitObjectStorage)).header;
1133                        crate::v2::refcount::v2_retain(hdr);
1134                    }
1135                    // W17-concurrency (ADR-006 §2.7.25, 2026-05-11):
1136                    // Mutex / Atomic / Lazy mirror the Channel arm.
1137                    // Bumps one strong-count share on the shared inner
1138                    // Arc — the outer Arc clone hands out a fresh
1139                    // endpoint of the same protected cell (Mutex/Lazy)
1140                    // or shares observation of the same atomic
1141                    // (Atomic). Interior state is NOT cloned.
1142                    HeapKind::Mutex => {
1143                        Arc::increment_strong_count(bits as *const MutexData);
1144                    }
1145                    HeapKind::Atomic => {
1146                        Arc::increment_strong_count(bits as *const AtomicData);
1147                    }
1148                    HeapKind::Lazy => {
1149                        Arc::increment_strong_count(bits as *const LazyData);
1150                    }
1151                    HeapKind::Decimal => {
1152                        Arc::increment_strong_count(bits as *const rust_decimal::Decimal);
1153                    }
1154                    HeapKind::BigInt => {
1155                        Arc::increment_strong_count(bits as *const i64);
1156                    }
1157                    HeapKind::DataTable => {
1158                        Arc::increment_strong_count(
1159                            bits as *const crate::datatable::DataTable,
1160                        );
1161                    }
1162                    HeapKind::IoHandle => {
1163                        Arc::increment_strong_count(bits as *const IoHandleData);
1164                    }
1165                    HeapKind::NativeView => {
1166                        Arc::increment_strong_count(bits as *const NativeViewData);
1167                    }
1168                    HeapKind::Content => {
1169                        Arc::increment_strong_count(
1170                            bits as *const crate::content::ContentNode,
1171                        );
1172                    }
1173                    HeapKind::Instant => {
1174                        Arc::increment_strong_count(bits as *const std::time::Instant);
1175                    }
1176                    HeapKind::Temporal => {
1177                        Arc::increment_strong_count(bits as *const TemporalData);
1178                    }
1179                    HeapKind::TableView => {
1180                        Arc::increment_strong_count(bits as *const TableViewData);
1181                    }
1182                    HeapKind::TaskGroup => {
1183                        Arc::increment_strong_count(bits as *const TaskGroupData);
1184                    }
1185                    // Wave-γ G-heap-filter-expr (ADR-006 §2.3 / §2.7.6 / Q8
1186                    // amendment): FilterExpr-kinded clone bumps the
1187                    // `Arc<FilterNode>` strong-count exactly once. Mirrors
1188                    // the Drop arm above.
1189                    HeapKind::FilterExpr => {
1190                        Arc::increment_strong_count(bits as *const FilterNode);
1191                    }
1192                    // Wave 8 W8-T26 (ADR-006 §2.7.13 / Q14, 2026-05-10):
1193                    // mirror of the Drop Reference arm above. Bumps one
1194                    // `Arc<RefTarget>` strong-count share — slot bits are
1195                    // `Arc::into_raw(Arc<RefTarget>)` directly per
1196                    // §2.7.13's pure-discriminator-style dispatch.
1197                    HeapKind::Reference => {
1198                        Arc::increment_strong_count(bits as *const RefTarget);
1199                    }
1200                    // W13-iterator-state (ADR-006 §2.7.16 / Q17,
1201                    // 2026-05-10): mirror of the Drop Iterator arm
1202                    // above. Bumps one `Arc<IteratorState>`
1203                    // strong-count share — slot bits are
1204                    // `Arc::into_raw(Arc<IteratorState>)` directly per
1205                    // §2.7.16's typed-Arc dispatch.
1206                    HeapKind::Iterator => {
1207                        Arc::increment_strong_count(bits as *const IteratorState);
1208                    }
1209                    // Wave 15 W15-priority-queue (ADR-006 §2.7.18 / Q19,
1210                    // 2026-05-10): mirror of the HashSet arm. Bumps one
1211                    // `Arc<PriorityQueueData>` strong-count share —
1212                    // PriorityQueue is a HashSet sibling per §2.7.18,
1213                    // full-`HeapValue` arm.
1214                    HeapKind::PriorityQueue => {
1215                        Arc::increment_strong_count(bits as *const PriorityQueueData);
1216                    }
1217                    // W15-range (ADR-006 §2.7.23 / Q24, 2026-05-10):
1218                    // mirror of the Drop Range arm above. Bumps one
1219                    // `Arc<RangeData>` strong-count share — slot bits
1220                    // are `Arc::into_raw(Arc<RangeData>)` directly per
1221                    // §2.7.23's typed-Arc dispatch (mirror of HashMap /
1222                    // HashSet / Iterator).
1223                    HeapKind::Range => {
1224                        Arc::increment_strong_count(bits as *const RangeData);
1225                    }
1226                    // Wave 14 W14-variant-codegen (ADR-006 §2.7.17 / Q18,
1227                    // 2026-05-10): mirror of the Drop arm above. Bumps
1228                    // one `Arc<ResultData>` / `Arc<OptionData>`
1229                    // strong-count share.
1230                    HeapKind::Result => {
1231                        Arc::increment_strong_count(bits as *const ResultData);
1232                    }
1233                    HeapKind::Option => {
1234                        Arc::increment_strong_count(bits as *const OptionData);
1235                    }
1236                    // Char: inline-scalar payload (codepoint bits). Clone
1237                    // is a no-op (Rust copies the slot bits below).
1238                    HeapKind::Char => {
1239                        // No-op: inline-scalar payload.
1240                    }
1241                    // Round 2.5b W7-closure-retain-parallel (ADR-006
1242                    // §2.7.11 / Q12, 2026-05-09 — lockstep with vm-tier
1243                    // Round 2.5 close `5fa4b19`): mirror of the Drop
1244                    // arm above. Bumps one `Arc<HeapValue>`
1245                    // strong-count share — the slot bits are
1246                    // `Arc::into_raw(Arc<HeapValue>)` pointing to a
1247                    // `HeapValue::ClosureRaw(OwnedClosureBlock)` arm.
1248                    // The §2.7.11 dispatch shell duplicates closure-
1249                    // bearing `KindedSlot` carriers (e.g. when a
1250                    // closure value is shared into multiple call
1251                    // sites); each clone owes one matching strong-
1252                    // count bump.
1253                    HeapKind::Closure => {
1254                        Arc::increment_strong_count(bits as *const HeapValue);
1255                    }
1256                    // `Ptr(HeapKind::Future)` carries the future-id u64
1257                    // directly in `bits` — Rust copies the slot bits
1258                    // below; no refcount work. Mirror of the Drop arm.
1259                    HeapKind::Future => {
1260                        // No-op: future-id inline scalar.
1261                    }
1262                    // W17-comptime-vm-dispatch (ADR-006 §2.7.26, 2026-05-12):
1263                    // mirror of the Drop arm — module-fn-id is an
1264                    // inline scalar payload; Rust copies the slot bits
1265                    // below; no refcount work.
1266                    HeapKind::ModuleFn => {
1267                        // No-op: module-fn-id inline scalar.
1268                    }
1269                    // Wave 8 W8-T25 (ADR-006 §2.7.12 / Q13 amendment,
1270                    // 2026-05-10): mirror of the Drop arm above. Bumps
1271                    // one `Arc<SharedCell>` strong-count share — the
1272                    // slot bits are `Arc::into_raw(Arc<SharedCell>)`
1273                    // pointing to a closure-capture / module-binding /
1274                    // local-slot SharedCell. Carriers that duplicate
1275                    // `KindedSlot` (e.g. `read_owned_kinded` on a stack
1276                    // slot whose kind is SharedCell) owe one matching
1277                    // strong-count bump.
1278                    HeapKind::SharedCell => {
1279                        Arc::increment_strong_count(
1280                            bits as *const crate::v2::closure_layout::SharedCell,
1281                        );
1282                    }
1283                    // ADR-006 §2.7.22 amendment (Round 18 S3, 2026-05-13):
1284                    // mirror of the Drop arm above. Bumps one
1285                    // `Arc<MatrixData>` / `Arc<MatrixSliceData>`
1286                    // strong-count share. Typed-Arc pure-discriminator
1287                    // dispatch.
1288                    HeapKind::Matrix => {
1289                        Arc::increment_strong_count(bits as *const MatrixData);
1290                    }
1291                    HeapKind::MatrixSlice => {
1292                        Arc::increment_strong_count(bits as *const MatrixSliceData);
1293                    }
1294                    // `HeapKind::NativeScalar` kinded carrier pending
1295                    // phase-2c kinded redesign (ADR-006 §2.7.4). When
1296                    // it lands, this arm wires its retain per the
1297                    // chosen share carrier. Until then, a non-zero
1298                    // pointer with this kind is a construction-side
1299                    // bug — no Bool-default fallback (forbidden #9).
1300                    HeapKind::NativeScalar => {
1301                        debug_assert!(
1302                            false,
1303                            "KindedSlot::clone: NativeScalar kinded carrier pending \
1304                             phase-2c kinded redesign (ADR-006 §2.7.4)"
1305                        );
1306                    }
1307                },
1308                // Inline scalars: nothing to bump.
1309                NativeKind::Float64
1310                | NativeKind::NullableFloat64
1311                | NativeKind::Int8
1312                | NativeKind::NullableInt8
1313                | NativeKind::UInt8
1314                | NativeKind::NullableUInt8
1315                | NativeKind::Int16
1316                | NativeKind::NullableInt16
1317                | NativeKind::UInt16
1318                | NativeKind::NullableUInt16
1319                | NativeKind::Int32
1320                | NativeKind::NullableInt32
1321                | NativeKind::UInt32
1322                | NativeKind::NullableUInt32
1323                | NativeKind::Int64
1324                | NativeKind::NullableInt64
1325                | NativeKind::UInt64
1326                | NativeKind::NullableUInt64
1327                | NativeKind::IntSize
1328                | NativeKind::NullableIntSize
1329                | NativeKind::UIntSize
1330                | NativeKind::NullableUIntSize
1331                | NativeKind::Bool
1332                // Round 19 S1.5 W12-nativekind-scalar-additions
1333                // (2026-05-14): Float32 + Char are inline 4-byte scalars
1334                // per ADR-006 §2.7.5 amendment. No `Arc<T>` payload, no
1335                // refcount work. Slot bits are the raw f32 bit pattern
1336                // / `c as u32` zero-extended into the low 32 bits.
1337                | NativeKind::Float32
1338                | NativeKind::Char => {}
1339            }
1340        }
1341        Self {
1342            slot: self.slot,
1343            kind: self.kind,
1344        }
1345    }
1346}
1347
1348#[cfg(test)]
1349mod tests {
1350    use super::*;
1351
1352    /// ADR-006 §2.7: dropping a `String`-kind `KindedSlot` retires the
1353    /// final strong-count share, deallocating the inner `Arc<String>`.
1354    #[test]
1355    fn drop_string_kind_retires_arc() {
1356        let arc = Arc::new("hello".to_string());
1357        let weak = Arc::downgrade(&arc);
1358        let slot = KindedSlot::from_string_arc(arc);
1359        assert_eq!(weak.strong_count(), 1, "slot owns the only strong share");
1360        drop(slot);
1361        assert_eq!(
1362            weak.strong_count(),
1363            0,
1364            "Drop dispatched and decremented refcount"
1365        );
1366    }
1367
1368    /// ADR-006 §2.7: cloning a `KindedSlot` bumps the underlying refcount;
1369    /// dropping both clones retires it cleanly.
1370    #[test]
1371    fn clone_then_double_drop_balances_refcount() {
1372        let storage = TypedObjectStorage::new(
1373            0,
1374            Vec::<ValueSlot>::new().into_boxed_slice(),
1375            0,
1376            Arc::from(Vec::<NativeKind>::new().into_boxed_slice()),
1377        );
1378        let arc = Arc::new(storage);
1379        let weak = Arc::downgrade(&arc);
1380        let slot1 = KindedSlot::from_typed_object(arc);
1381        assert_eq!(weak.strong_count(), 1);
1382        let slot2 = slot1.clone();
1383        assert_eq!(weak.strong_count(), 2, "Clone bumped refcount");
1384        drop(slot1);
1385        assert_eq!(weak.strong_count(), 1, "first Drop retired one share");
1386        drop(slot2);
1387        assert_eq!(weak.strong_count(), 0, "second Drop retired the last");
1388    }
1389
1390    /// `Vec<KindedSlot>` push + pop + clone must preserve refcount
1391    /// discipline. Without explicit `Drop`/`Clone`, this would alias-copy
1392    /// the heap pointer (WB2.4 / WB2.5 bug class).
1393    #[test]
1394    fn vec_push_pop_clone_balanced() {
1395        let arc = Arc::new("vec test".to_string());
1396        let weak = Arc::downgrade(&arc);
1397        let mut v: Vec<KindedSlot> = Vec::new();
1398        v.push(KindedSlot::from_string_arc(arc));
1399        assert_eq!(weak.strong_count(), 1);
1400        // Clone the Vec — every element clones independently.
1401        let v2 = v.clone();
1402        assert_eq!(weak.strong_count(), 2);
1403        // Pop drops the popped element when it goes out of scope.
1404        {
1405            let _popped = v.pop().expect("vec has one element");
1406            // _popped is alive here — refcount stays 2.
1407            assert_eq!(weak.strong_count(), 2);
1408        }
1409        // After the block, _popped dropped → refcount → 1.
1410        assert_eq!(weak.strong_count(), 1);
1411        drop(v2);
1412        assert_eq!(weak.strong_count(), 0);
1413    }
1414
1415    /// Inline-scalar kinds (Int64, Bool, Float64) have no refcount
1416    /// payload; Drop and Clone are no-ops on the bits.
1417    #[test]
1418    fn inline_scalars_no_refcount() {
1419        let s1 = KindedSlot::from_int(42);
1420        let s2 = s1.clone();
1421        assert_eq!(s1.slot.as_i64(), 42);
1422        assert_eq!(s2.slot.as_i64(), 42);
1423        let b = KindedSlot::from_bool(true);
1424        assert!(b.slot.as_bool());
1425        let n = KindedSlot::from_number(3.14);
1426        assert_eq!(n.slot.as_f64(), 3.14);
1427        // No leak / double-free; would fail under miri otherwise.
1428    }
1429
1430    /// `KindedSlot::none()` is the conventional null carrier — Drop is a
1431    /// no-op (zero bits, Bool kind).
1432    #[test]
1433    fn none_drop_safe() {
1434        let n = KindedSlot::none();
1435        assert_eq!(n.slot.raw(), 0);
1436        drop(n);
1437    }
1438
1439    /// Wave 2 Agent D1 (2026-05-14): the new
1440    /// `KindedSlot::from_typed_object_raw` constructor stores a
1441    /// `*const TypedObjectStorage` pointer (v2-raw carrier; refcount on the
1442    /// on-header). The slot carries the `NativeKind::Ptr(HeapKind::TypedObject)`
1443    /// kind; bits are the raw pointer value. This test exercises the
1444    /// constructor + slot-bit round-trip; Drop semantics for the raw-pointer
1445    /// path are exercised by the `heap_value` module's
1446    /// `heap_element_release_elem_*` tests since `KindedSlot::Drop` still
1447    /// dispatches Arc-style (the Wave-2-pre-D2 transitional bit-shape).
1448    #[test]
1449    fn from_typed_object_raw_constructor_kind_and_bits() {
1450        let kinds: Arc<[NativeKind]> = Arc::from(vec![NativeKind::Int64]);
1451        unsafe {
1452            let ptr = TypedObjectStorage::_new(
1453                99,
1454                vec![ValueSlot::from_int(0)].into_boxed_slice(),
1455                0,
1456                kinds,
1457            );
1458            // Construct via the v2-raw constructor; assert the kind label
1459            // and slot bits.
1460            let slot = KindedSlot::from_typed_object_raw(ptr);
1461            assert_eq!(slot.kind, NativeKind::Ptr(HeapKind::TypedObject));
1462            assert_eq!(slot.slot.raw(), ptr as u64);
1463            // The raw-pointer carrier owns the refcount independently; we
1464            // leak the slot here (don't drop it through the Arc-style
1465            // KindedSlot::Drop arms) and clean up via `_drop`. D2 wires the
1466            // dispatch arms to v2_release; pre-D2 this constructor's slot
1467            // bits MUST NOT flow into Arc-style dispatch (Drop or
1468            // clone_with_kind on these bits would call
1469            // Arc::decrement_strong_count on a non-Arc pointer →
1470            // segfault / heap corruption). Test exits via mem::forget +
1471            // explicit _drop.
1472            std::mem::forget(slot);
1473            TypedObjectStorage::_drop(ptr);
1474        }
1475    }
1476
1477    // ── §2.7.6 / Q8 scalar accessor coverage ──────────────────────────────
1478    //
1479    // One test per scalar accessor: same-kind returns Some, different-kind
1480    // returns None. These tests pin the `KindedSlot` carrier API bound:
1481    // accessors discriminate on `self.kind` and never decode bits when the
1482    // kind is wrong.
1483
1484    #[test]
1485    fn kinded_slot_as_i64_int_returns_some_value() {
1486        let s = KindedSlot::from_int(42);
1487        assert_eq!(s.as_i64(), Some(42));
1488    }
1489
1490    #[test]
1491    fn kinded_slot_as_i64_float_returns_none() {
1492        let s = KindedSlot::from_number(3.14);
1493        assert_eq!(s.as_i64(), None);
1494    }
1495
1496    #[test]
1497    fn kinded_slot_as_f64_float_returns_some_value() {
1498        let s = KindedSlot::from_number(3.14);
1499        assert_eq!(s.as_f64(), Some(3.14));
1500    }
1501
1502    #[test]
1503    fn kinded_slot_as_f64_int_returns_none() {
1504        let s = KindedSlot::from_int(42);
1505        assert_eq!(s.as_f64(), None);
1506    }
1507
1508    #[test]
1509    fn kinded_slot_as_bool_bool_returns_some_value() {
1510        let t = KindedSlot::from_bool(true);
1511        let f = KindedSlot::from_bool(false);
1512        assert_eq!(t.as_bool(), Some(true));
1513        assert_eq!(f.as_bool(), Some(false));
1514    }
1515
1516    #[test]
1517    fn kinded_slot_as_bool_int_returns_none() {
1518        let s = KindedSlot::from_int(1);
1519        assert_eq!(s.as_bool(), None);
1520    }
1521
1522    #[test]
1523    fn kinded_slot_as_char_char_returns_some_value() {
1524        let s = KindedSlot::from_char('A');
1525        assert_eq!(s.as_char(), Some('A'));
1526        // Unicode round-trip.
1527        let s2 = KindedSlot::from_char('λ');
1528        assert_eq!(s2.as_char(), Some('λ'));
1529    }
1530
1531    #[test]
1532    fn kinded_slot_as_char_int_returns_none() {
1533        let s = KindedSlot::from_int(65);
1534        assert_eq!(s.as_char(), None);
1535    }
1536
1537    #[test]
1538    fn kinded_slot_as_char_drop_safe() {
1539        // `from_char` stores codepoint bits inline; Drop must NOT try to
1540        // free them as if they were an `Arc<T>` pointer. Failure mode is
1541        // a debug-assert under the previous Char arm, or a free of an
1542        // invalid pointer in release.
1543        let s = KindedSlot::from_char('Z');
1544        drop(s);
1545    }
1546
1547    #[test]
1548    fn kinded_slot_as_str_string_returns_some_value() {
1549        let s = KindedSlot::from_string_arc(Arc::new("hello".to_string()));
1550        assert_eq!(s.as_str(), Some("hello"));
1551    }
1552
1553    #[test]
1554    fn kinded_slot_as_str_int_returns_none() {
1555        let s = KindedSlot::from_int(42);
1556        assert_eq!(s.as_str(), None);
1557    }
1558
1559    #[test]
1560    fn kinded_slot_from_string_borrows_back() {
1561        // `from_string(&str)` allocates an Arc<String> and stores its
1562        // pointer; `as_str()` should round-trip the contents.
1563        let s = KindedSlot::from_string("round trip");
1564        assert_eq!(s.as_str(), Some("round trip"));
1565    }
1566
1567    // ── §2.7.6 / Q8 from_temporal / from_instant constructor pair ────────
1568    //
1569    // W17-from-temporal-instant-constructors (Wave 3, 2026-05-12). These
1570    // pin the bounded-carrier-API rule: one constructor per `NativeKind`
1571    // heap variant, no parallel discrimination. Both constructors share
1572    // the `Arc::into_raw` typed-Arc shape with the existing Drop / Clone
1573    // arms for `HeapKind::Temporal` / `HeapKind::Instant`.
1574
1575    #[test]
1576    fn kinded_slot_from_temporal_sets_kind_and_retires_arc() {
1577        use crate::heap_value::TemporalData;
1578        let arc = Arc::new(TemporalData::TimeSpan(chrono::Duration::seconds(7)));
1579        let weak = Arc::downgrade(&arc);
1580        let slot = KindedSlot::from_temporal(arc);
1581        assert_eq!(slot.kind(), NativeKind::Ptr(HeapKind::Temporal));
1582        assert_eq!(weak.strong_count(), 1, "slot owns the only strong share");
1583        drop(slot);
1584        assert_eq!(
1585            weak.strong_count(),
1586            0,
1587            "Drop dispatched HeapKind::Temporal arm and retired refcount"
1588        );
1589    }
1590
1591    #[test]
1592    fn kinded_slot_from_temporal_clone_then_double_drop_balances() {
1593        use crate::heap_value::TemporalData;
1594        let arc = Arc::new(TemporalData::TimeSpan(chrono::Duration::milliseconds(500)));
1595        let weak = Arc::downgrade(&arc);
1596        let slot1 = KindedSlot::from_temporal(arc);
1597        assert_eq!(weak.strong_count(), 1);
1598        let slot2 = slot1.clone();
1599        assert_eq!(weak.strong_count(), 2, "Clone bumped refcount");
1600        drop(slot1);
1601        assert_eq!(weak.strong_count(), 1, "first Drop retired one share");
1602        drop(slot2);
1603        assert_eq!(weak.strong_count(), 0, "second Drop retired the last");
1604    }
1605
1606    #[test]
1607    fn kinded_slot_from_instant_sets_kind_and_retires_arc() {
1608        let arc = Arc::new(std::time::Instant::now());
1609        let weak = Arc::downgrade(&arc);
1610        let slot = KindedSlot::from_instant(arc);
1611        assert_eq!(slot.kind(), NativeKind::Ptr(HeapKind::Instant));
1612        assert_eq!(weak.strong_count(), 1, "slot owns the only strong share");
1613        drop(slot);
1614        assert_eq!(
1615            weak.strong_count(),
1616            0,
1617            "Drop dispatched HeapKind::Instant arm and retired refcount"
1618        );
1619    }
1620
1621    #[test]
1622    fn kinded_slot_from_instant_clone_then_double_drop_balances() {
1623        let arc = Arc::new(std::time::Instant::now());
1624        let weak = Arc::downgrade(&arc);
1625        let slot1 = KindedSlot::from_instant(arc);
1626        assert_eq!(weak.strong_count(), 1);
1627        let slot2 = slot1.clone();
1628        assert_eq!(weak.strong_count(), 2, "Clone bumped refcount");
1629        drop(slot1);
1630        assert_eq!(weak.strong_count(), 1, "first Drop retired one share");
1631        drop(slot2);
1632        assert_eq!(weak.strong_count(), 0, "second Drop retired the last");
1633    }
1634}