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}