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::Content)`-kind slot. Stores the
365 /// `Arc<ContentNode>` directly per ADR-006 §2.3 (W18.6 R8 W3
366 /// 2026-05-24 — supervisor D3+D4 Display.display() → content +
367 /// ContentNode user-facing type exposure). Slot bits are
368 /// `Arc::into_raw(Arc<ContentNode>) as u64`; the Drop / Clone arms
369 /// for `HeapKind::Content` already dispatch the matching strong-
370 /// count retain/release. Used by the `Content.text(...)` /
371 /// `Content.code(...)` / etc. user-facing constructor builtins
372 /// (`ContentTextCtor` family) and by user-defined `Display` impls
373 /// whose `method display() -> content` return value is pushed
374 /// through this carrier.
375 #[inline]
376 pub fn from_content(c: Arc<crate::content::ContentNode>) -> Self {
377 Self::new(
378 ValueSlot::from_content(c),
379 NativeKind::Ptr(HeapKind::Content),
380 )
381 }
382
383 /// Convenience: a `Ptr(HeapKind::Result)`-kind slot. ADR-006 §2.7.17 /
384 /// Q18 amendment (Wave 14 W14-variant-codegen). Mirror of
385 /// `from_iterator` typed-Arc dispatch shape.
386 #[inline]
387 pub fn from_result(r: Arc<ResultData>) -> Self {
388 Self::new(
389 ValueSlot::from_result(r),
390 NativeKind::Ptr(HeapKind::Result),
391 )
392 }
393
394 /// Convenience: a `Ptr(HeapKind::Option)`-kind slot. ADR-006 §2.7.17 /
395 /// Q18 amendment (Wave 14 W14-variant-codegen).
396 #[inline]
397 pub fn from_option(o: Arc<OptionData>) -> Self {
398 Self::new(
399 ValueSlot::from_option(o),
400 NativeKind::Ptr(HeapKind::Option),
401 )
402 }
403
404 /// Convenience: a `Ptr(HeapKind::Decimal)`-kind slot.
405 #[inline]
406 pub fn from_decimal(d: Arc<rust_decimal::Decimal>) -> Self {
407 Self::new(
408 ValueSlot::from_decimal(d),
409 NativeKind::Ptr(HeapKind::Decimal),
410 )
411 }
412
413 /// Convenience: a `StringV2`-kind slot from a v2-raw `*const StringObj`
414 /// pointer. ADR-006 §2.7.5 amendment (Wave 2 Agent B W12-StringV2-
415 /// DecimalV2-NativeKind-additions, 2026-05-14): paired with
416 /// `NativeKind::StringV2` per the audit §H.4 H-c decision. Slot bits
417 /// = `ptr as u64`; the `KindedSlot` owns one refcount share on the
418 /// underlying `StringObj` — Drop dispatches the matching `v2_release`
419 /// against the `HeapHeader` at offset 0.
420 ///
421 /// Caller's construction-side contract: `ptr` MUST point to a live
422 /// `StringObj` whose refcount has been incremented (typically via
423 /// `v2_retain` at the producing `op_typed_array_get` site) to claim
424 /// the share this `KindedSlot` now owns. Mirror of `from_string_arc`'s
425 /// "slot owns one strong share" contract — but the underlying retain
426 /// is `v2_retain` against the `repr(C)` HeapHeader, not
427 /// `Arc::increment_strong_count` against an `Arc<String>`.
428 #[inline]
429 pub fn from_string_v2_ptr(ptr: *const crate::v2::string_obj::StringObj) -> Self {
430 Self::new(ValueSlot::from_string_v2_ptr(ptr), NativeKind::StringV2)
431 }
432
433 /// Convenience: a `DecimalV2`-kind slot from a v2-raw `*const DecimalObj`
434 /// pointer. ADR-006 §2.7.5 amendment (Wave 2 Agent B W12-StringV2-
435 /// DecimalV2-NativeKind-additions, 2026-05-14): mirror of
436 /// `from_string_v2_ptr` for the `DecimalObj` sibling. Same construction-
437 /// side contract.
438 #[inline]
439 pub fn from_decimal_v2_ptr(ptr: *const crate::v2::decimal_obj::DecimalObj) -> Self {
440 Self::new(ValueSlot::from_decimal_v2_ptr(ptr), NativeKind::DecimalV2)
441 }
442
443 /// Convenience: a `Ptr(HeapKind::BigInt)`-kind slot.
444 #[inline]
445 pub fn from_bigint(b: Arc<i64>) -> Self {
446 Self::new(ValueSlot::from_bigint(b), NativeKind::Ptr(HeapKind::BigInt))
447 }
448
449 /// Convenience: a `Ptr(HeapKind::IoHandle)`-kind slot from an
450 /// `Arc<IoHandleData>`. R8 W6 G.1 W17-marshal-return-arms close
451 /// (2026-05-24): mirrors the existing 14 typed-Arc constructor
452 /// precedents (ADR-005 §1 single-discriminator + ADR-006 §2.7.6 / Q8
453 /// bounded carrier-API). Slot bits = `Arc::into_raw(h) as u64` via
454 /// `ValueSlot::from_io_handle`; the Drop / Clone arms for
455 /// `HeapKind::IoHandle` (`kinded_slot.rs` ~line 868 / ~line 1227)
456 /// already dispatch the matching `Arc::increment/decrement_strong_
457 /// count::<IoHandleData>` retain/release. Used by
458 /// `project_typed_return` (`vm_impl/modules.rs`) to land the
459 /// `ConcreteReturn::IoHandle(Arc<IoHandleData>)` arm from
460 /// `transport::tcp()` / `io::tcp_listen()` / `file::open()` etc.
461 /// without producer/consumer divergence (pre-fix VM surfaced
462 /// `project_typed_return: W17-marshal-return-arms` while JIT
463 /// returned ec=0 garbage).
464 #[inline]
465 pub fn from_io_handle(h: Arc<IoHandleData>) -> Self {
466 Self::new(
467 ValueSlot::from_io_handle(h),
468 NativeKind::Ptr(HeapKind::IoHandle),
469 )
470 }
471
472 /// Convenience: a `Ptr(HeapKind::DataTable)`-kind slot from an
473 /// `Arc<DataTable>`. R8 W6 G.1 W17-marshal-return-arms close
474 /// (2026-05-24): sibling of `from_io_handle` — same family of
475 /// `ConcreteReturn` discriminant gap (disc 15 vs disc 16). Slot
476 /// bits = `Arc::into_raw(d) as u64` via `ValueSlot::from_data_table`;
477 /// the Drop / Clone arms for `HeapKind::DataTable` (`kinded_slot.rs`
478 /// ~line 863 / ~line 1222) already dispatch the matching
479 /// `Arc::increment/decrement_strong_count::<DataTable>`
480 /// retain/release. Used by `project_typed_return` to land the
481 /// `ConcreteReturn::DataTable(Arc<DataTable>)` arm from
482 /// `arrow_module` / wire-conversion factory returns.
483 #[inline]
484 pub fn from_data_table(d: Arc<crate::datatable::DataTable>) -> Self {
485 Self::new(
486 ValueSlot::from_data_table(d),
487 NativeKind::Ptr(HeapKind::DataTable),
488 )
489 }
490
491 /// Convenience: a `Char`-kind slot. ADR-006 §2.7.5 amendment (Round
492 /// 19 S1.5 W12-nativekind-scalar-additions, 2026-05-14): Char joins
493 /// the scalar bucket — `char` is `Copy + 4-byte` (UTF-32 codepoint),
494 /// no Arc payload. Slot bits store `c as u32` zero-extended into
495 /// the low 32 bits of the 8-byte slot.
496 ///
497 /// Pre-amendment (Round 18 and earlier) this constructor returned
498 /// a slot with kind `NativeKind::Ptr(HeapKind::Char)` — the inline-
499 /// codepoint payload tagged through `HeapKind` for dispatch
500 /// uniformity. The post-amendment shape is a pure scalar variant
501 /// (`NativeKind::Char`), aligning Char with the other 4-byte
502 /// scalars (`Int32` / `UInt32` / `Float32`) per §Q8 carrier-API
503 /// bound (one constructor per scalar variant). The
504 /// `NativeKind::Ptr(HeapKind::Char)` label still exists in source
505 /// (direct `push_kinded(c as u64, NativeKind::Ptr(HeapKind::Char))`
506 /// call-sites have NOT been migrated in this dispatch — a future
507 /// cluster-1 hardening sub-cluster retires that parallel label).
508 /// Drop is a no-op (inline scalar; the `NativeKind::Char` arm in
509 /// `Drop` is part of the inline-scalar group).
510 #[inline]
511 pub fn from_char(c: char) -> Self {
512 Self::new(ValueSlot::from_char(c), NativeKind::Char)
513 }
514
515 /// Convenience: a `Ptr(HeapKind::Matrix)`-kind slot. ADR-006 §2.7.22
516 /// amendment (Round 18 S3 W12-matrix-floatslice-heapkind-exit,
517 /// 2026-05-13). Slot bits are `Arc::into_raw(Arc<MatrixData>) as u64`;
518 /// recovery goes through the canonical reconstruct-clone-restore
519 /// pattern (`Arc::<MatrixData>::from_raw(bits)` → clone → `into_raw`)
520 /// to bump the inner share while preserving the carrier's owned
521 /// outer share. Mirror of `from_iterator` / `from_range` typed-Arc
522 /// dispatch shape — `as_heap_value()` on a Matrix-labeled slot is
523 /// unsound (the slot bits are an `Arc<MatrixData>` pointer, not a
524 /// `*const HeapValue`). The Drop / Clone arms for `HeapKind::Matrix`
525 /// dispatch the matching `Arc::increment/decrement_strong_count
526 /// ::<MatrixData>` retain/release.
527 #[inline]
528 pub fn from_matrix(m: Arc<MatrixData>) -> Self {
529 let bits = Arc::into_raw(m) as u64;
530 Self::new(ValueSlot::from_raw(bits), NativeKind::Ptr(HeapKind::Matrix))
531 }
532
533 /// Convenience: a `Ptr(HeapKind::MatrixSlice)`-kind slot. ADR-006
534 /// §2.7.22 amendment (Round 18 S3 W12-matrix-floatslice-heapkind-exit,
535 /// 2026-05-13). Slot bits are
536 /// `Arc::into_raw(Arc<MatrixSliceData>) as u64`. Same typed-Arc
537 /// pure-discriminator dispatch shape as `from_matrix`. The inner
538 /// `MatrixSliceData { parent, offset, len }` retains a separate
539 /// strong-count share on its parent matrix; cloning the outer share
540 /// does NOT bump the parent — that bump happens at
541 /// `MatrixSliceData::clone` (auto-derived) when the inner struct is
542 /// duplicated under `Arc::make_mut`.
543 #[inline]
544 pub fn from_matrix_slice(s: Arc<MatrixSliceData>) -> Self {
545 let bits = Arc::into_raw(s) as u64;
546 Self::new(
547 ValueSlot::from_raw(bits),
548 NativeKind::Ptr(HeapKind::MatrixSlice),
549 )
550 }
551
552 /// Convenience: a `Ptr(HeapKind::ModuleFn)`-kind slot. Stores the
553 /// `module_fn_id` as raw `u64` slot bits directly (inline-scalar
554 /// payload — no `Arc<T>`, no heap state). Same shape as
555 /// `from_char` / `from_future` per ADR-006 §2.7.26
556 /// (W17-comptime-vm-dispatch).
557 ///
558 /// Construction-side contract: `id` must index a registered entry
559 /// in `VirtualMachine.module_fn_table`. The dispatch shell at
560 /// `executor/call_convention.rs::call_value_immediate_nb` consumes
561 /// the kind label to route the slot's bits to
562 /// `invoke_module_fn_id_stub` at `CallValue` time.
563 #[inline]
564 pub fn from_module_fn_id(id: u64) -> Self {
565 Self::new(
566 ValueSlot::from_raw(id),
567 NativeKind::Ptr(HeapKind::ModuleFn),
568 )
569 }
570
571 /// Convenience: a `String`-kind slot from a `&str`. Allocates a fresh
572 /// `Arc<String>`. Use `from_string_arc` when you already have the
573 /// `Arc<String>` in hand and want to avoid a clone.
574 #[inline]
575 pub fn from_string(s: &str) -> Self {
576 Self::from_string_arc(Arc::new(s.to_string()))
577 }
578
579 /// A null/none-value `KindedSlot`.
580 ///
581 /// R5b-2-bool-null-sentinel-cluster (ADR-006 §2.7 + §2.7.5 +
582 /// §2.7.7/Q9, 2026-05-19): pre-disposition this returned
583 /// `(ValueSlot::none(), NativeKind::Bool)` as the §2.7 sentinel,
584 /// which collided with legitimate `false` bool slots (both encoded
585 /// as bits=0). The collision caused VM-only divergence per
586 /// W14.2-G6 SURFACE-G6-BOOL-NULL + SURFACE-G6-LET-ONLY-BODY +
587 /// SURFACE-G6-NONE-OUTPUT-ADAPTER. Post-disposition: kind IS the
588 /// discriminator per §2.7.7/Q9 — return `NativeKind::Null`. Drop
589 /// remains a no-op (Null is a non-parametric absence-of-value
590 /// sentinel with no Arc<T> payload).
591 #[inline]
592 pub fn none() -> Self {
593 Self::new(ValueSlot::none(), NativeKind::Null)
594 }
595
596 /// Read the inner slot.
597 #[inline]
598 pub fn slot(&self) -> ValueSlot {
599 self.slot
600 }
601
602 /// Read the kind.
603 #[inline]
604 pub fn kind(&self) -> NativeKind {
605 self.kind
606 }
607
608 /// Raw slot bits. Provided for sites that need to peek at the storage
609 /// shape (e.g. wire serialization). Prefer typed accessors.
610 #[inline]
611 pub fn raw(&self) -> u64 {
612 self.slot.raw()
613 }
614
615 // ── Scalar accessors (ADR-006 §2.7.6 / Q8) ────────────────────────────
616 //
617 // One accessor per `NativeKind` *scalar* variant. Each kind-dispatches
618 // on `self.kind` and returns `Some(payload)` only when the kind matches
619 // exactly. Heap variants do NOT get per-variant accessors here; bodies
620 // dispatching on a heap-typed `KindedSlot` use
621 // `kinded_slot.slot.as_heap_value() -> &HeapValue` and pattern-match,
622 // preserving ADR-005 §1's single-discriminator discipline.
623
624 /// Read as `i64` if `self.kind == NativeKind::Int64`, else `None`.
625 #[inline]
626 pub fn as_i64(&self) -> Option<i64> {
627 match self.kind {
628 NativeKind::Int64 => Some(self.slot.as_i64()),
629 _ => None,
630 }
631 }
632
633 /// Read as `f64` if `self.kind == NativeKind::Float64`, else `None`.
634 #[inline]
635 pub fn as_f64(&self) -> Option<f64> {
636 match self.kind {
637 NativeKind::Float64 => Some(self.slot.as_f64()),
638 _ => None,
639 }
640 }
641
642 /// Read as `bool` if `self.kind == NativeKind::Bool`, else `None`.
643 #[inline]
644 pub fn as_bool(&self) -> Option<bool> {
645 match self.kind {
646 NativeKind::Bool => Some(self.slot.as_bool()),
647 _ => None,
648 }
649 }
650
651 /// Read as `char` if `self.kind == NativeKind::Char`, else `None`.
652 /// ADR-006 §2.7.5 amendment (Round 19 S1.5, 2026-05-14): the §Q8
653 /// carrier-API bound binds `as_char` to the new scalar
654 /// `NativeKind::Char` variant. The pre-amendment
655 /// `NativeKind::Ptr(HeapKind::Char)` carrier label is ALSO recognized
656 /// for cross-tier-compatibility — the label still exists in source
657 /// (direct `push_kinded(c as u64, NativeKind::Ptr(HeapKind::Char))`
658 /// call-sites have NOT been migrated in this dispatch); recognizing
659 /// both labels avoids producer/consumer mismatch when those call-
660 /// sites flow values through code paths that materialize as
661 /// `KindedSlot` before consuming `as_char`. Both labels store
662 /// codepoint bits zero-extended in the low 32 bits of the slot, so
663 /// the read is identical in either kind.
664 #[inline]
665 pub fn as_char(&self) -> Option<char> {
666 match self.kind {
667 NativeKind::Char | NativeKind::Ptr(HeapKind::Char) => self.slot.as_char(),
668 _ => None,
669 }
670 }
671
672 /// Read as `f32` if `self.kind == NativeKind::Float32`, else `None`.
673 /// ADR-006 §2.7.5 amendment (Round 19 S1.5, 2026-05-14). Slot bits
674 /// store the IEEE-754 single-precision pattern zero-extended into
675 /// the low 32 bits; `f32::from_bits` reinterprets the low 32 bits.
676 #[inline]
677 pub fn as_f32(&self) -> Option<f32> {
678 match self.kind {
679 NativeKind::Float32 => Some(f32::from_bits(self.slot.raw() as u32)),
680 _ => None,
681 }
682 }
683
684 /// Read as `&str` if `self.kind == NativeKind::String` (legacy Arc<String>
685 /// carrier) or `NativeKind::StringV2` (v2-raw `*const StringObj` carrier),
686 /// else `None`. Both carriers expose the same UTF-8 bytes; the accessor
687 /// dispatches on kind and borrows the inner `&str` for the lifetime of
688 /// `&self`. The carrier owns one strong-count share (Arc<String>) /
689 /// HeapHeader refcount (StringObj), so the inner string is alive while
690 /// `&self` lives.
691 ///
692 /// D-β string-join receiver-kind fix (v0.3 KC #6(d), 2026-05-22):
693 /// the pre-fix arm gated only on `NativeKind::String`. Elements read
694 /// from a v2-raw `TypedArray<*const StringObj>` (i.e. an `Array<string>`
695 /// literal lowered through `NewTypedArrayString` + `TypedArrayPushString`)
696 /// flow through `TypedArrayGetString` (`v2_handlers/array.rs:702`) which
697 /// pushes `NativeKind::StringV2`; without the StringV2 arm here, every
698 /// universal-method dispatch on a v2-raw string receiver (e.g.
699 /// `arr[i].toString()` inside `Vec<T>.join`'s body) surfaced
700 /// "TypeError: expected string receiver, got non-string kind". Per
701 /// ADR-006 §2.7.5 amendment Wave 2 Agent B both carrier labels store
702 /// equivalent UTF-8 bytes; the per-carrier read is the producer-site
703 /// proof, not fabrication.
704 #[inline]
705 pub fn as_str(&self) -> Option<&str> {
706 match self.kind {
707 NativeKind::String => {
708 let bits = self.slot.raw();
709 if bits == 0 {
710 return None;
711 }
712 // SAFETY: per the construction-side contract, `NativeKind::String`
713 // means the slot bits are `Arc::into_raw::<String>` and this
714 // `KindedSlot` owns one strong-count share (so the inner
715 // `String` is alive). The returned `&str` borrows from
716 // `&self`; lifetime is bounded by the slot's ownership.
717 let s: &String = unsafe { &*(bits as *const String) };
718 Some(s.as_str())
719 }
720 NativeKind::StringV2 => {
721 let bits = self.slot.raw();
722 if bits == 0 {
723 return None;
724 }
725 // SAFETY: per ADR-006 §2.7.5 amendment Wave 2 Agent B, when
726 // `kind == NativeKind::StringV2` the slot bits are
727 // `ptr as u64` where `ptr: *const StringObj` (the v2-raw
728 // `HeapHeader`-prefixed UTF-8 carrier per
729 // `crates/shape-value/src/v2/string_obj.rs`). The carrier
730 // owns one HeapHeader refcount share for the duration of
731 // `&self`; `StringObj::as_str(ptr)` borrows the inner UTF-8
732 // bytes — lifetime is bounded by the slot's ownership.
733 let ptr = bits as *const crate::v2::string_obj::StringObj;
734 Some(unsafe {
735 crate::v2::string_obj::StringObj::as_str(ptr)
736 })
737 }
738 _ => None,
739 }
740 }
741}
742
743impl std::fmt::Debug for KindedSlot {
744 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
745 f.debug_struct("KindedSlot")
746 .field("slot", &self.slot)
747 .field("kind", &self.kind)
748 .finish()
749 }
750}
751
752impl Default for KindedSlot {
753 fn default() -> Self {
754 Self::none()
755 }
756}
757
758/// Drop dispatches on `kind` to retire the matching `Arc<T>` strong-count
759/// share. Mirrors `TypedObjectStorage::Drop` in `heap_value.rs:761`.
760impl Drop for KindedSlot {
761 fn drop(&mut self) {
762 let bits = self.slot.raw();
763 if bits == 0 {
764 return;
765 }
766 // SAFETY: per the construction-side contract on every `KindedSlot`
767 // constructor, when `kind` selects a heap arm the slot bits are
768 // the result of `Arc::into_raw::<T>` for the matching `T`. Drop
769 // retires exactly one strong-count share.
770 unsafe {
771 match self.kind {
772 NativeKind::String => {
773 Arc::decrement_strong_count(bits as *const String);
774 }
775 // Wave 2 Agent B (ADR-006 §2.7.5 amendment, 2026-05-14):
776 // StringV2 / DecimalV2 are v2-raw heap-pointer carriers per
777 // the §H.4 H-c decision. Slot bits are `ptr as u64` where
778 // `ptr: *const StringObj` / `*const DecimalObj`. Refcount
779 // discipline goes through `v2_release` against the
780 // `HeapHeader` at offset 0 of the carrier (NOT
781 // `Arc::decrement_strong_count` — these are manually-
782 // allocated `repr(C)` carriers per `v2/string_obj.rs` /
783 // `v2/decimal_obj.rs`, not `Arc<T>` allocations). On
784 // refcount=0, the carrier's `HeapElement::release_elem`
785 // implementation deallocates the struct.
786 NativeKind::StringV2 => {
787 use crate::v2::heap_element::HeapElement;
788 crate::v2::string_obj::StringObj::release_elem(
789 bits as *const crate::v2::string_obj::StringObj,
790 );
791 }
792 NativeKind::DecimalV2 => {
793 use crate::v2::heap_element::HeapElement;
794 crate::v2::decimal_obj::DecimalObj::release_elem(
795 bits as *const crate::v2::decimal_obj::DecimalObj,
796 );
797 }
798 NativeKind::Ptr(hk) => match hk {
799 HeapKind::String => {
800 Arc::decrement_strong_count(bits as *const String);
801 }
802 // r5c-2-β-δ-(α) (2026-05-20): `HeapKind::TypedArray`
803 // dispatch arm RE-INSTATED — the V3-S5 ckpt-5-prime
804 // "no live slot bits carry this kind" claim was
805 // empirically false (`Array<T>` struct fields + closure
806 // captures carry it). The carrier is the v2-raw
807 // `*mut TypedArray<T>` flat struct; release retires one
808 // refcount share and, on the last share, frees via the
809 // stamped-element-type `drop_array` / `drop_array_heap`.
810 // Mirror of the `TypedObject` release arm below
811 // (4-table lockstep, ADR-006 §2.3 / §2.7.7).
812 HeapKind::TypedArray => {
813 crate::v2::typed_array::release_v2_typed_array(bits as *mut u8);
814 }
815 // Wave 2 Agent D4 ckpt-2 (ADR-006 §2.3 / §2.7.5
816 // amendment, 2026-05-14): TypedObject release via
817 // `HeapElement::release_elem` + carrier-side `_drop`
818 // (per Agent D1's `impl HeapElement for
819 // TypedObjectStorage` — calls `v2_release` against the
820 // HeapHeader at offset 0; on refcount=0 the
821 // carrier-side `_drop` runs the per-field heap-mask
822 // walk and deallocates the `repr(C)` struct). Mirror
823 // of the §2.7.5 StringV2 / DecimalV2 release arms
824 // above.
825 HeapKind::TypedObject => {
826 use crate::v2::heap_element::HeapElement;
827 TypedObjectStorage::release_elem(
828 bits as *const TypedObjectStorage,
829 );
830 }
831 HeapKind::HashMap => {
832 // Wave 2 Round 3b C2-joint ckpt-2 (2026-05-14):
833 // bits are `Arc::into_raw(Arc<HashMapKindedRef>)`;
834 // release dispatches outer Arc decrement → enum
835 // Drop chains to per-V `Arc<HashMapData<V>>` release.
836 Arc::decrement_strong_count(
837 bits as *const crate::heap_value::HashMapKindedRef,
838 );
839 }
840 // Wave 13 W13-hashset-rebuild (ADR-006 §2.7.15 / Q16,
841 // 2026-05-10): mirror of the HashMap arm. Retires
842 // one `Arc<HashSetData>` strong-count share.
843 HeapKind::HashSet => {
844 Arc::decrement_strong_count(bits as *const HashSetData);
845 }
846 // Wave 15 W15-deque (ADR-006 §2.7.19 / Q20,
847 // 2026-05-10): mirror of the HashSet arm. Retires
848 // one `Arc<DequeData>` strong-count share.
849 HeapKind::Deque => {
850 Arc::decrement_strong_count(bits as *const DequeData);
851 }
852 // Wave 15 W15-channel-rebuild (ADR-006 §2.7.20 / Q21,
853 // 2026-05-10): mirror of the HashSet arm. Slot bits
854 // are `Arc::into_raw(Arc<ChannelData>) as u64`.
855 // Retires one `Arc<ChannelData>` strong-count share
856 // — at refcount=0 the inner `ChannelData` Drop runs
857 // (default-derived) which retires the queued
858 // `KindedSlot` payloads via their own Drop.
859 HeapKind::Channel => {
860 Arc::decrement_strong_count(bits as *const ChannelData);
861 }
862 // W17-trait-object-storage (ADR-006 §2.7.24 / Q25.C,
863 // 2026-05-11): TraitObject mirrors the typed-Arc
864 // dispatch shape. Slot bits are
865 // `Arc::into_raw(Arc<TraitObjectStorage>) as u64`.
866 // Retires one strong-count share — at refcount=0
867 // the inner `TraitObjectStorage::Drop` runs,
868 // releasing its inner `Arc<TypedObjectStorage>`
869 // value half + `Arc<VTable>` vtable half via
870 // auto-derived `Drop`.
871 // Wave 2 Agent D4 ckpt-2 (ADR-006 §2.7.24 / Q25.C.5 +
872 // E close 2026-05-14): TraitObject release via
873 // `HeapElement::release_elem` + carrier-side `_drop`
874 // (per Agent E's `impl HeapElement for
875 // TraitObjectStorage`). Mirror of the TypedObject arm
876 // above.
877 HeapKind::TraitObject => {
878 use crate::v2::heap_element::HeapElement;
879 TraitObjectStorage::release_elem(
880 bits as *const TraitObjectStorage,
881 );
882 }
883 // W17-concurrency (ADR-006 §2.7.25, 2026-05-11):
884 // Mutex / Atomic / Lazy mirror the Channel arm.
885 // Slot bits are `Arc::into_raw(Arc<MutexData>) /
886 // Arc<AtomicData> / Arc<LazyData>) as u64`. Retires
887 // one strong-count share — at refcount=0 the inner
888 // Drop runs and (for Mutex/Lazy) retires the
889 // protected `KindedSlot` payload via its own Drop.
890 HeapKind::Mutex => {
891 Arc::decrement_strong_count(bits as *const MutexData);
892 }
893 HeapKind::Atomic => {
894 Arc::decrement_strong_count(bits as *const AtomicData);
895 }
896 HeapKind::Lazy => {
897 Arc::decrement_strong_count(bits as *const LazyData);
898 }
899 HeapKind::Decimal => {
900 Arc::decrement_strong_count(bits as *const rust_decimal::Decimal);
901 }
902 HeapKind::BigInt => {
903 Arc::decrement_strong_count(bits as *const i64);
904 }
905 HeapKind::DataTable => {
906 Arc::decrement_strong_count(
907 bits as *const crate::datatable::DataTable,
908 );
909 }
910 HeapKind::IoHandle => {
911 Arc::decrement_strong_count(bits as *const IoHandleData);
912 }
913 HeapKind::NativeView => {
914 Arc::decrement_strong_count(bits as *const NativeViewData);
915 }
916 HeapKind::Content => {
917 Arc::decrement_strong_count(
918 bits as *const crate::content::ContentNode,
919 );
920 }
921 HeapKind::Instant => {
922 Arc::decrement_strong_count(bits as *const std::time::Instant);
923 }
924 HeapKind::Temporal => {
925 Arc::decrement_strong_count(bits as *const TemporalData);
926 }
927 HeapKind::TableView => {
928 Arc::decrement_strong_count(bits as *const TableViewData);
929 }
930 HeapKind::TaskGroup => {
931 Arc::decrement_strong_count(bits as *const TaskGroupData);
932 }
933 // Wave-γ G-heap-filter-expr (ADR-006 §2.3 / §2.7.6 / Q8
934 // amendment): FilterExpr-kinded `KindedSlot`s own one
935 // `Arc::into_raw(Arc<FilterNode>)` strong-count share.
936 // The pre-amendment `HeapKind::NativeView` mislabel
937 // would have dispatched the share as
938 // `Arc<NativeViewData>` — wrong-type retire.
939 HeapKind::FilterExpr => {
940 Arc::decrement_strong_count(bits as *const FilterNode);
941 }
942 // Wave 8 W8-T26 (ADR-006 §2.7.13 / Q14, 2026-05-10):
943 // mirror of `vm_impl/stack.rs:drop_with_kind` Reference
944 // arm. Slot bits are `Arc::into_raw(Arc<RefTarget>)`
945 // directly per §2.7.13's pure-discriminator-style
946 // dispatch (NOT a `Box<HeapValue>` wrap); retire one
947 // `Arc<RefTarget>` strong-count share. At refcount=0
948 // the inner `RefTarget` Drop releases its `receiver`
949 // typed-Arc share for `TypedField` / `TypedIndex`
950 // variants — `Local` / `ModuleBinding` variants hold
951 // no Arc.
952 HeapKind::Reference => {
953 Arc::decrement_strong_count(bits as *const RefTarget);
954 }
955 // W13-iterator-state (ADR-006 §2.7.16 / Q17,
956 // 2026-05-10): mirror of `vm_impl/stack.rs::
957 // drop_with_kind` Iterator arm. Slot bits are
958 // `Arc::into_raw(Arc<IteratorState>)` directly
959 // (mirror of FilterExpr / Reference's typed-Arc
960 // dispatch — NOT a `Box<HeapValue>` wrap); retire
961 // one `Arc<IteratorState>` strong-count share.
962 HeapKind::Iterator => {
963 Arc::decrement_strong_count(bits as *const IteratorState);
964 }
965 // Wave 15 W15-priority-queue (ADR-006 §2.7.18 / Q19,
966 // 2026-05-10): mirror of the HashSet arm. Retires
967 // one `Arc<PriorityQueueData>` strong-count share —
968 // PriorityQueue is a HashSet sibling per §2.7.18,
969 // full-`HeapValue` arm.
970 HeapKind::PriorityQueue => {
971 Arc::decrement_strong_count(bits as *const PriorityQueueData);
972 }
973 // W15-range (ADR-006 §2.7.23 / Q24, 2026-05-10):
974 // mirror of `vm_impl/stack.rs::drop_with_kind`
975 // Range arm. Slot bits are
976 // `Arc::into_raw(Arc<RangeData>)` directly (typed-Arc
977 // shape, mirror of HashMap / HashSet / Iterator);
978 // retire one `Arc<RangeData>` strong-count share.
979 // RangeData is `Copy`-shaped (four scalar fields,
980 // no inner Arcs) so refcount=0 just deallocates the
981 // small heap block.
982 HeapKind::Range => {
983 Arc::decrement_strong_count(bits as *const RangeData);
984 }
985 // Wave 14 W14-variant-codegen (ADR-006 §2.7.17 / Q18,
986 // 2026-05-10): mirror of the Iterator arm. Slot
987 // bits are `Arc::into_raw(Arc<ResultData>)`; retire
988 // one `Arc<ResultData>` strong-count share. At
989 // refcount=0 `ResultData::Drop` (auto-derived from
990 // its embedded `KindedSlot` payload) retires the
991 // inner-value share recursively.
992 HeapKind::Result => {
993 Arc::decrement_strong_count(bits as *const ResultData);
994 }
995 HeapKind::Option => {
996 Arc::decrement_strong_count(bits as *const OptionData);
997 }
998 // Char: inline-scalar payload (codepoint bits, not an
999 // `Arc<T>`). Drop is a no-op; non-zero bits are valid
1000 // (e.g. `from_char('a')` stores 97).
1001 HeapKind::Char => {
1002 // No-op: inline-scalar payload.
1003 }
1004 // Round 2.5b W7-closure-retain-parallel (ADR-006
1005 // §2.7.11 / Q12, 2026-05-09 — lockstep with vm-tier
1006 // Round 2.5 close `5fa4b19`): a
1007 // `NativeKind::Ptr(HeapKind::Closure)` slot carries
1008 // `Arc::into_raw(Arc<HeapValue>) as u64` pointing to
1009 // a `HeapValue::ClosureRaw(OwnedClosureBlock)` arm.
1010 // The share carrier at the slot tier is the outer
1011 // `Arc<HeapValue>`, not the inner `OwnedClosureBlock`'s
1012 // typed-closure-header refcount (which
1013 // `OwnedClosureBlock` manages internally on its own
1014 // `clone()` / `drop()`). Round 2 close (`06cdfce`)
1015 // committed to this slot-bits shape via
1016 // `callee.slot.as_heap_value()` →
1017 // `HeapValue::ClosureRaw(block)` in
1018 // `call_value_immediate_nb`. The §2.7.11 dispatch
1019 // shell pops closure-bearing `KindedSlot` carriers
1020 // whose `Drop` arrives here on every consumed call
1021 // arg and on the callee itself. Same dispatch
1022 // shape as the `HeapKind::FilterExpr` §2.7.9
1023 // amendment (one variant, one matching `Arc<T>`
1024 // retain/release at the slot tier).
1025 HeapKind::Closure => {
1026 Arc::decrement_strong_count(bits as *const HeapValue);
1027 }
1028 // `Ptr(HeapKind::Future)` carries the future-id u64
1029 // directly in `bits` (inline scalar — no heap state,
1030 // no `Arc<T>` payload). See `async_ops/mod.rs`
1031 // §"Wave 6.5 / E-async migration" docstring and
1032 // `printing.rs` `HeapKind::Future` arm. Same shape
1033 // as `HeapKind::Char`.
1034 HeapKind::Future => {
1035 // No-op: future-id inline scalar.
1036 }
1037 // W17-comptime-vm-dispatch (ADR-006 §2.7.26, 2026-05-12):
1038 // `Ptr(HeapKind::ModuleFn)` carries the module-fn-id
1039 // u64 directly in `bits` (inline scalar — no heap
1040 // state, no `Arc<T>` payload). Same shape as
1041 // `HeapKind::Future` / `HeapKind::Char`.
1042 HeapKind::ModuleFn => {
1043 // No-op: module-fn-id inline scalar.
1044 }
1045 // Wave 8 W8-T25 (ADR-006 §2.7.12 / Q13 amendment,
1046 // 2026-05-10): `SharedCell`-kinded `KindedSlot`s
1047 // own one `Arc::into_raw(Arc<SharedCell>)` strong-
1048 // count share — the runtime-tier carrier shape for
1049 // an `Arc<SharedCell>` cell-pointer that flows
1050 // through dispatch-slice / module-binding /
1051 // exception-payload carriers. Retires one
1052 // `Arc<SharedCell>` strong-count share. Same dispatch
1053 // shape as the `HeapKind::FilterExpr` §2.7.9
1054 // amendment.
1055 HeapKind::SharedCell => {
1056 Arc::decrement_strong_count(
1057 bits as *const crate::v2::closure_layout::SharedCell,
1058 );
1059 }
1060 // ADR-006 §2.7.22 amendment (Round 18 S3, 2026-05-13):
1061 // Matrix / MatrixSlice slots own one typed-Arc
1062 // strong-count share. Slot bits are
1063 // `Arc::into_raw(Arc<MatrixData>) as u64` /
1064 // `Arc::into_raw(Arc<MatrixSliceData>) as u64`. Retire
1065 // one matching strong-count share. Typed-Arc
1066 // pure-discriminator dispatch (mirror of §2.7.9
1067 // FilterExpr); `as_heap_value()` is unsound on
1068 // Matrix/MatrixSlice-labeled bits.
1069 HeapKind::Matrix => {
1070 Arc::decrement_strong_count(bits as *const MatrixData);
1071 }
1072 HeapKind::MatrixSlice => {
1073 Arc::decrement_strong_count(bits as *const MatrixSliceData);
1074 }
1075 // `HeapKind::NativeScalar` has no kinded `Arc<T>`
1076 // carrier yet — the redesign is the phase-2c
1077 // surface tracked in ADR-006 §2.7.4. The
1078 // `v2_stack_tests.rs` round-trip tests for
1079 // NativeScalar are `todo!()` for the same reason.
1080 // When the kinded NativeScalar carrier lands, this
1081 // arm wires its retain/release per the chosen
1082 // share carrier (per the playbook's
1083 // surface-and-stop discipline — no Bool-default
1084 // fallback, no construction-side fabrication).
1085 // Until then, a non-zero pointer with this kind is
1086 // a construction-side bug.
1087 HeapKind::NativeScalar => {
1088 debug_assert!(
1089 false,
1090 "KindedSlot::drop: NativeScalar kinded carrier pending \
1091 phase-2c kinded redesign (ADR-006 §2.7.4)"
1092 );
1093 }
1094 },
1095 // Inline-scalar kinds: nothing to decrement. Bits are raw
1096 // value, not a pointer.
1097 NativeKind::Float64
1098 | NativeKind::NullableFloat64
1099 | NativeKind::Int8
1100 | NativeKind::NullableInt8
1101 | NativeKind::UInt8
1102 | NativeKind::NullableUInt8
1103 | NativeKind::Int16
1104 | NativeKind::NullableInt16
1105 | NativeKind::UInt16
1106 | NativeKind::NullableUInt16
1107 | NativeKind::Int32
1108 | NativeKind::NullableInt32
1109 | NativeKind::UInt32
1110 | NativeKind::NullableUInt32
1111 | NativeKind::Int64
1112 | NativeKind::NullableInt64
1113 | NativeKind::UInt64
1114 | NativeKind::NullableUInt64
1115 | NativeKind::IntSize
1116 | NativeKind::NullableIntSize
1117 | NativeKind::UIntSize
1118 | NativeKind::NullableUIntSize
1119 | NativeKind::Bool
1120 // Round 19 S1.5 W12-nativekind-scalar-additions
1121 // (2026-05-14): Float32 + Char are inline 4-byte scalars
1122 // per ADR-006 §2.7.5 amendment. No `Arc<T>` payload, no
1123 // refcount work. Slot bits are the raw f32 bit pattern
1124 // / `c as u32` zero-extended into the low 32 bits.
1125 | NativeKind::Float32
1126 | NativeKind::Char
1127 // R5b-2-bool-null-sentinel-cluster (ADR-006 §2.7 +
1128 // §2.7.5 + §2.7.7/Q9, 2026-05-19): `NativeKind::Null`
1129 // is a non-parametric absence-of-value sentinel — no
1130 // Arc<T> payload, no refcount work.
1131 | NativeKind::Null => {}
1132 }
1133 }
1134 }
1135}
1136
1137/// Clone dispatches on `kind` to bump the matching `Arc<T>` strong-count.
1138impl Clone for KindedSlot {
1139 fn clone(&self) -> Self {
1140 let bits = self.slot.raw();
1141 if bits == 0 {
1142 return Self {
1143 slot: self.slot,
1144 kind: self.kind,
1145 };
1146 }
1147 // SAFETY: same construction-side contract as Drop. We bump exactly
1148 // one strong-count share and let Rust copy the slot bits.
1149 unsafe {
1150 match self.kind {
1151 NativeKind::String => {
1152 Arc::increment_strong_count(bits as *const String);
1153 }
1154 // Wave 2 Agent B (ADR-006 §2.7.5 amendment, 2026-05-14):
1155 // StringV2 / DecimalV2 retain via `v2_retain` against the
1156 // HeapHeader at offset 0 of the carrier — mirror of the
1157 // Drop arms above.
1158 NativeKind::StringV2 => {
1159 let hdr =
1160 &(*(bits as *const crate::v2::string_obj::StringObj)).header;
1161 crate::v2::refcount::v2_retain(hdr);
1162 }
1163 NativeKind::DecimalV2 => {
1164 let hdr =
1165 &(*(bits as *const crate::v2::decimal_obj::DecimalObj)).header;
1166 crate::v2::refcount::v2_retain(hdr);
1167 }
1168 NativeKind::Ptr(hk) => match hk {
1169 HeapKind::String => {
1170 Arc::increment_strong_count(bits as *const String);
1171 }
1172 // r5c-2-β-δ-(α) (2026-05-20): `HeapKind::TypedArray`
1173 // dispatch arm RE-INSTATED — mirror of the
1174 // `drop_with_kind` arm above. Retain bumps the v2-raw
1175 // `TypedArray<T>` on-header refcount via `v2_retain`
1176 // (element type irrelevant for a retain). Mirror of the
1177 // `TypedObject` retain arm below (4-table lockstep,
1178 // ADR-006 §2.3 / §2.7.7).
1179 HeapKind::TypedArray => {
1180 crate::v2::typed_array::retain_v2_typed_array(bits as *mut u8);
1181 }
1182 // Wave 2 Agent D4 ckpt-2 (ADR-006 §2.3 / §2.7.5
1183 // amendment, 2026-05-14): TypedObject retain via
1184 // `v2_retain` against the HeapHeader at offset 0 of
1185 // the v2-raw carrier. Mirror of the §2.7.5 StringV2
1186 // / DecimalV2 retain arms above (Agent B precedent).
1187 HeapKind::TypedObject => {
1188 let hdr =
1189 &(*(bits as *const TypedObjectStorage)).header;
1190 crate::v2::refcount::v2_retain(hdr);
1191 }
1192 HeapKind::HashMap => {
1193 // Wave 2 Round 3b C2-joint ckpt-2 (2026-05-14):
1194 // bits are `Arc::into_raw(Arc<HashMapKindedRef>)`;
1195 // retain dispatches outer Arc increment (the per-V
1196 // inner `Arc<HashMapData<V>>` is preserved by-share
1197 // via the enum's structural sharing).
1198 Arc::increment_strong_count(
1199 bits as *const crate::heap_value::HashMapKindedRef,
1200 );
1201 }
1202 // Wave 13 W13-hashset-rebuild (ADR-006 §2.7.15 / Q16,
1203 // 2026-05-10): mirror of the HashMap arm. Bumps one
1204 // `Arc<HashSetData>` strong-count share.
1205 HeapKind::HashSet => {
1206 Arc::increment_strong_count(bits as *const HashSetData);
1207 }
1208 // Wave 15 W15-deque (ADR-006 §2.7.19 / Q20,
1209 // 2026-05-10): mirror of the HashSet arm. Bumps
1210 // one `Arc<DequeData>` strong-count share.
1211 HeapKind::Deque => {
1212 Arc::increment_strong_count(bits as *const DequeData);
1213 }
1214 // Wave 15 W15-channel-rebuild (ADR-006 §2.7.20 / Q21,
1215 // 2026-05-10): mirror of the HashSet arm above. Bumps
1216 // one `Arc<ChannelData>` strong-count share — the
1217 // outer Arc clone hands out a fresh endpoint of the
1218 // same channel (interior `Mutex<ChannelInner>` is
1219 // shared, NOT cloned).
1220 HeapKind::Channel => {
1221 Arc::increment_strong_count(bits as *const ChannelData);
1222 }
1223 // W17-trait-object-storage (ADR-006 §2.7.24 / Q25.C,
1224 // 2026-05-11): TraitObject mirrors the typed-Arc
1225 // dispatch shape. Bumps one strong-count share on
1226 // the outer `Arc<TraitObjectStorage>` — inner
1227 // `Arc<TypedObjectStorage>` value half and
1228 // `Arc<VTable>` vtable half stay shared with the
1229 // source carrier. `Arc::ptr_eq` on the vtable
1230 // preserves the §Q25.C.2 `Self`-arg identity
1231 // contract across the clone.
1232 // Wave 2 Agent D4 ckpt-2 (ADR-006 §2.7.24 / Q25.C.5 +
1233 // E close 2026-05-14): TraitObject retain via
1234 // `v2_retain` against the HeapHeader at offset 0 of
1235 // the v2-raw carrier. Mirror of the TypedObject arm
1236 // above.
1237 HeapKind::TraitObject => {
1238 let hdr =
1239 &(*(bits as *const TraitObjectStorage)).header;
1240 crate::v2::refcount::v2_retain(hdr);
1241 }
1242 // W17-concurrency (ADR-006 §2.7.25, 2026-05-11):
1243 // Mutex / Atomic / Lazy mirror the Channel arm.
1244 // Bumps one strong-count share on the shared inner
1245 // Arc — the outer Arc clone hands out a fresh
1246 // endpoint of the same protected cell (Mutex/Lazy)
1247 // or shares observation of the same atomic
1248 // (Atomic). Interior state is NOT cloned.
1249 HeapKind::Mutex => {
1250 Arc::increment_strong_count(bits as *const MutexData);
1251 }
1252 HeapKind::Atomic => {
1253 Arc::increment_strong_count(bits as *const AtomicData);
1254 }
1255 HeapKind::Lazy => {
1256 Arc::increment_strong_count(bits as *const LazyData);
1257 }
1258 HeapKind::Decimal => {
1259 Arc::increment_strong_count(bits as *const rust_decimal::Decimal);
1260 }
1261 HeapKind::BigInt => {
1262 Arc::increment_strong_count(bits as *const i64);
1263 }
1264 HeapKind::DataTable => {
1265 Arc::increment_strong_count(
1266 bits as *const crate::datatable::DataTable,
1267 );
1268 }
1269 HeapKind::IoHandle => {
1270 Arc::increment_strong_count(bits as *const IoHandleData);
1271 }
1272 HeapKind::NativeView => {
1273 Arc::increment_strong_count(bits as *const NativeViewData);
1274 }
1275 HeapKind::Content => {
1276 Arc::increment_strong_count(
1277 bits as *const crate::content::ContentNode,
1278 );
1279 }
1280 HeapKind::Instant => {
1281 Arc::increment_strong_count(bits as *const std::time::Instant);
1282 }
1283 HeapKind::Temporal => {
1284 Arc::increment_strong_count(bits as *const TemporalData);
1285 }
1286 HeapKind::TableView => {
1287 Arc::increment_strong_count(bits as *const TableViewData);
1288 }
1289 HeapKind::TaskGroup => {
1290 Arc::increment_strong_count(bits as *const TaskGroupData);
1291 }
1292 // Wave-γ G-heap-filter-expr (ADR-006 §2.3 / §2.7.6 / Q8
1293 // amendment): FilterExpr-kinded clone bumps the
1294 // `Arc<FilterNode>` strong-count exactly once. Mirrors
1295 // the Drop arm above.
1296 HeapKind::FilterExpr => {
1297 Arc::increment_strong_count(bits as *const FilterNode);
1298 }
1299 // Wave 8 W8-T26 (ADR-006 §2.7.13 / Q14, 2026-05-10):
1300 // mirror of the Drop Reference arm above. Bumps one
1301 // `Arc<RefTarget>` strong-count share — slot bits are
1302 // `Arc::into_raw(Arc<RefTarget>)` directly per
1303 // §2.7.13's pure-discriminator-style dispatch.
1304 HeapKind::Reference => {
1305 Arc::increment_strong_count(bits as *const RefTarget);
1306 }
1307 // W13-iterator-state (ADR-006 §2.7.16 / Q17,
1308 // 2026-05-10): mirror of the Drop Iterator arm
1309 // above. Bumps one `Arc<IteratorState>`
1310 // strong-count share — slot bits are
1311 // `Arc::into_raw(Arc<IteratorState>)` directly per
1312 // §2.7.16's typed-Arc dispatch.
1313 HeapKind::Iterator => {
1314 Arc::increment_strong_count(bits as *const IteratorState);
1315 }
1316 // Wave 15 W15-priority-queue (ADR-006 §2.7.18 / Q19,
1317 // 2026-05-10): mirror of the HashSet arm. Bumps one
1318 // `Arc<PriorityQueueData>` strong-count share —
1319 // PriorityQueue is a HashSet sibling per §2.7.18,
1320 // full-`HeapValue` arm.
1321 HeapKind::PriorityQueue => {
1322 Arc::increment_strong_count(bits as *const PriorityQueueData);
1323 }
1324 // W15-range (ADR-006 §2.7.23 / Q24, 2026-05-10):
1325 // mirror of the Drop Range arm above. Bumps one
1326 // `Arc<RangeData>` strong-count share — slot bits
1327 // are `Arc::into_raw(Arc<RangeData>)` directly per
1328 // §2.7.23's typed-Arc dispatch (mirror of HashMap /
1329 // HashSet / Iterator).
1330 HeapKind::Range => {
1331 Arc::increment_strong_count(bits as *const RangeData);
1332 }
1333 // Wave 14 W14-variant-codegen (ADR-006 §2.7.17 / Q18,
1334 // 2026-05-10): mirror of the Drop arm above. Bumps
1335 // one `Arc<ResultData>` / `Arc<OptionData>`
1336 // strong-count share.
1337 HeapKind::Result => {
1338 Arc::increment_strong_count(bits as *const ResultData);
1339 }
1340 HeapKind::Option => {
1341 Arc::increment_strong_count(bits as *const OptionData);
1342 }
1343 // Char: inline-scalar payload (codepoint bits). Clone
1344 // is a no-op (Rust copies the slot bits below).
1345 HeapKind::Char => {
1346 // No-op: inline-scalar payload.
1347 }
1348 // Round 2.5b W7-closure-retain-parallel (ADR-006
1349 // §2.7.11 / Q12, 2026-05-09 — lockstep with vm-tier
1350 // Round 2.5 close `5fa4b19`): mirror of the Drop
1351 // arm above. Bumps one `Arc<HeapValue>`
1352 // strong-count share — the slot bits are
1353 // `Arc::into_raw(Arc<HeapValue>)` pointing to a
1354 // `HeapValue::ClosureRaw(OwnedClosureBlock)` arm.
1355 // The §2.7.11 dispatch shell duplicates closure-
1356 // bearing `KindedSlot` carriers (e.g. when a
1357 // closure value is shared into multiple call
1358 // sites); each clone owes one matching strong-
1359 // count bump.
1360 HeapKind::Closure => {
1361 Arc::increment_strong_count(bits as *const HeapValue);
1362 }
1363 // `Ptr(HeapKind::Future)` carries the future-id u64
1364 // directly in `bits` — Rust copies the slot bits
1365 // below; no refcount work. Mirror of the Drop arm.
1366 HeapKind::Future => {
1367 // No-op: future-id inline scalar.
1368 }
1369 // W17-comptime-vm-dispatch (ADR-006 §2.7.26, 2026-05-12):
1370 // mirror of the Drop arm — module-fn-id is an
1371 // inline scalar payload; Rust copies the slot bits
1372 // below; no refcount work.
1373 HeapKind::ModuleFn => {
1374 // No-op: module-fn-id inline scalar.
1375 }
1376 // Wave 8 W8-T25 (ADR-006 §2.7.12 / Q13 amendment,
1377 // 2026-05-10): mirror of the Drop arm above. Bumps
1378 // one `Arc<SharedCell>` strong-count share — the
1379 // slot bits are `Arc::into_raw(Arc<SharedCell>)`
1380 // pointing to a closure-capture / module-binding /
1381 // local-slot SharedCell. Carriers that duplicate
1382 // `KindedSlot` (e.g. `read_owned_kinded` on a stack
1383 // slot whose kind is SharedCell) owe one matching
1384 // strong-count bump.
1385 HeapKind::SharedCell => {
1386 Arc::increment_strong_count(
1387 bits as *const crate::v2::closure_layout::SharedCell,
1388 );
1389 }
1390 // ADR-006 §2.7.22 amendment (Round 18 S3, 2026-05-13):
1391 // mirror of the Drop arm above. Bumps one
1392 // `Arc<MatrixData>` / `Arc<MatrixSliceData>`
1393 // strong-count share. Typed-Arc pure-discriminator
1394 // dispatch.
1395 HeapKind::Matrix => {
1396 Arc::increment_strong_count(bits as *const MatrixData);
1397 }
1398 HeapKind::MatrixSlice => {
1399 Arc::increment_strong_count(bits as *const MatrixSliceData);
1400 }
1401 // `HeapKind::NativeScalar` kinded carrier pending
1402 // phase-2c kinded redesign (ADR-006 §2.7.4). When
1403 // it lands, this arm wires its retain per the
1404 // chosen share carrier. Until then, a non-zero
1405 // pointer with this kind is a construction-side
1406 // bug — no Bool-default fallback (forbidden #9).
1407 HeapKind::NativeScalar => {
1408 debug_assert!(
1409 false,
1410 "KindedSlot::clone: NativeScalar kinded carrier pending \
1411 phase-2c kinded redesign (ADR-006 §2.7.4)"
1412 );
1413 }
1414 },
1415 // Inline scalars: nothing to bump.
1416 NativeKind::Float64
1417 | NativeKind::NullableFloat64
1418 | NativeKind::Int8
1419 | NativeKind::NullableInt8
1420 | NativeKind::UInt8
1421 | NativeKind::NullableUInt8
1422 | NativeKind::Int16
1423 | NativeKind::NullableInt16
1424 | NativeKind::UInt16
1425 | NativeKind::NullableUInt16
1426 | NativeKind::Int32
1427 | NativeKind::NullableInt32
1428 | NativeKind::UInt32
1429 | NativeKind::NullableUInt32
1430 | NativeKind::Int64
1431 | NativeKind::NullableInt64
1432 | NativeKind::UInt64
1433 | NativeKind::NullableUInt64
1434 | NativeKind::IntSize
1435 | NativeKind::NullableIntSize
1436 | NativeKind::UIntSize
1437 | NativeKind::NullableUIntSize
1438 | NativeKind::Bool
1439 // Round 19 S1.5 W12-nativekind-scalar-additions
1440 // (2026-05-14): Float32 + Char are inline 4-byte scalars
1441 // per ADR-006 §2.7.5 amendment. No `Arc<T>` payload, no
1442 // refcount work. Slot bits are the raw f32 bit pattern
1443 // / `c as u32` zero-extended into the low 32 bits.
1444 | NativeKind::Float32
1445 | NativeKind::Char
1446 // R5b-2-bool-null-sentinel-cluster (ADR-006 §2.7 +
1447 // §2.7.5 + §2.7.7/Q9, 2026-05-19): `NativeKind::Null`
1448 // is a non-parametric absence-of-value sentinel — no
1449 // Arc<T> payload, no refcount work.
1450 | NativeKind::Null => {}
1451 }
1452 }
1453 Self {
1454 slot: self.slot,
1455 kind: self.kind,
1456 }
1457 }
1458}
1459
1460#[cfg(test)]
1461mod tests {
1462 use super::*;
1463
1464 /// ADR-006 §2.7: dropping a `String`-kind `KindedSlot` retires the
1465 /// final strong-count share, deallocating the inner `Arc<String>`.
1466 #[test]
1467 fn drop_string_kind_retires_arc() {
1468 let arc = Arc::new("hello".to_string());
1469 let weak = Arc::downgrade(&arc);
1470 let slot = KindedSlot::from_string_arc(arc);
1471 assert_eq!(weak.strong_count(), 1, "slot owns the only strong share");
1472 drop(slot);
1473 assert_eq!(
1474 weak.strong_count(),
1475 0,
1476 "Drop dispatched and decremented refcount"
1477 );
1478 }
1479
1480 /// ADR-006 §2.7: cloning a `KindedSlot` bumps the underlying refcount;
1481 /// dropping both clones retires it cleanly.
1482 ///
1483 /// W5 v0.3 fix (2026-05-17): migrated to the v2-raw `_new` carrier per
1484 /// the same rationale as `executor/objects/property_access.rs::
1485 /// length_typed_object_empty`. The post-Wave-2-D1 `KindedSlot::Clone`
1486 /// / `Drop` dispatch for `Ptr(HeapKind::TypedObject)` uses
1487 /// `v2_retain` / `release_elem` (HeapElement trait) against the
1488 /// embedded `HeapHeader`, NOT `Arc::increment/decrement_strong_count`.
1489 /// Pushing Arc-allocated bits through the v2-raw dispatch attempts
1490 /// `std::alloc::dealloc` with `Layout::new::<TypedObjectStorage>` on
1491 /// an `Arc::into_raw`'d pointer at refcount=0 — a wrong-allocator-pair
1492 /// free that surfaces as `free(): invalid size` SIGABRT.
1493 ///
1494 /// The test now observes the v2-raw refcount through
1495 /// `(*ptr).header.refcount` directly — the canonical way to inspect
1496 /// a `_new`-allocated TypedObject's share count. The Arc-based
1497 /// `weak.strong_count()` probe is incompatible with v2-raw memory
1498 /// (no `ArcInner` header) so the migration replaces it.
1499 #[test]
1500 fn clone_then_double_drop_balances_refcount() {
1501 use std::sync::atomic::Ordering;
1502 // SAFETY block: the entire test sequences raw-pointer
1503 // allocations, refcount inspections, and drop dispatch through
1504 // the v2-raw HeapElement trait. The pointer is allocated via
1505 // `_new` (refcount=1), borrowed transiently for refcount reads,
1506 // and finally retired by the second Drop reaching refcount=0
1507 // (which runs `_drop` to dealloc).
1508 unsafe {
1509 let ptr = TypedObjectStorage::_new(
1510 0,
1511 Vec::<ValueSlot>::new().into_boxed_slice(),
1512 0,
1513 Arc::from(Vec::<NativeKind>::new().into_boxed_slice()),
1514 );
1515 let header_refcount =
1516 |p: *const TypedObjectStorage| (*p).header.refcount.load(Ordering::SeqCst);
1517
1518 assert_eq!(header_refcount(ptr), 1, "_new starts at refcount 1");
1519 let slot1 = KindedSlot::from_typed_object_raw(ptr);
1520 assert_eq!(header_refcount(ptr), 1, "from_typed_object_raw transfers existing share");
1521 let slot2 = slot1.clone();
1522 assert_eq!(header_refcount(ptr), 2, "Clone bumped refcount");
1523 drop(slot1);
1524 assert_eq!(header_refcount(ptr), 1, "first Drop retired one share");
1525 // slot2's Drop retires the final share and runs _drop; the
1526 // pointer is dangling after this line (no further reads).
1527 drop(slot2);
1528 }
1529 }
1530
1531 /// `Vec<KindedSlot>` push + pop + clone must preserve refcount
1532 /// discipline. Without explicit `Drop`/`Clone`, this would alias-copy
1533 /// the heap pointer (WB2.4 / WB2.5 bug class).
1534 #[test]
1535 fn vec_push_pop_clone_balanced() {
1536 let arc = Arc::new("vec test".to_string());
1537 let weak = Arc::downgrade(&arc);
1538 let mut v: Vec<KindedSlot> = Vec::new();
1539 v.push(KindedSlot::from_string_arc(arc));
1540 assert_eq!(weak.strong_count(), 1);
1541 // Clone the Vec — every element clones independently.
1542 let v2 = v.clone();
1543 assert_eq!(weak.strong_count(), 2);
1544 // Pop drops the popped element when it goes out of scope.
1545 {
1546 let _popped = v.pop().expect("vec has one element");
1547 // _popped is alive here — refcount stays 2.
1548 assert_eq!(weak.strong_count(), 2);
1549 }
1550 // After the block, _popped dropped → refcount → 1.
1551 assert_eq!(weak.strong_count(), 1);
1552 drop(v2);
1553 assert_eq!(weak.strong_count(), 0);
1554 }
1555
1556 /// Inline-scalar kinds (Int64, Bool, Float64) have no refcount
1557 /// payload; Drop and Clone are no-ops on the bits.
1558 #[test]
1559 fn inline_scalars_no_refcount() {
1560 let s1 = KindedSlot::from_int(42);
1561 let s2 = s1.clone();
1562 assert_eq!(s1.slot.as_i64(), 42);
1563 assert_eq!(s2.slot.as_i64(), 42);
1564 let b = KindedSlot::from_bool(true);
1565 assert!(b.slot.as_bool());
1566 let n = KindedSlot::from_number(3.14);
1567 assert_eq!(n.slot.as_f64(), 3.14);
1568 // No leak / double-free; would fail under miri otherwise.
1569 }
1570
1571 /// `KindedSlot::none()` is the conventional null carrier — Drop is a
1572 /// no-op (zero bits, Bool kind).
1573 #[test]
1574 fn none_drop_safe() {
1575 let n = KindedSlot::none();
1576 assert_eq!(n.slot.raw(), 0);
1577 drop(n);
1578 }
1579
1580 /// Wave 2 Agent D1 (2026-05-14): the new
1581 /// `KindedSlot::from_typed_object_raw` constructor stores a
1582 /// `*const TypedObjectStorage` pointer (v2-raw carrier; refcount on the
1583 /// on-header). The slot carries the `NativeKind::Ptr(HeapKind::TypedObject)`
1584 /// kind; bits are the raw pointer value. This test exercises the
1585 /// constructor + slot-bit round-trip; Drop semantics for the raw-pointer
1586 /// path are exercised by the `heap_value` module's
1587 /// `heap_element_release_elem_*` tests since `KindedSlot::Drop` still
1588 /// dispatches Arc-style (the Wave-2-pre-D2 transitional bit-shape).
1589 #[test]
1590 fn from_typed_object_raw_constructor_kind_and_bits() {
1591 let kinds: Arc<[NativeKind]> = Arc::from(vec![NativeKind::Int64]);
1592 unsafe {
1593 let ptr = TypedObjectStorage::_new(
1594 99,
1595 vec![ValueSlot::from_int(0)].into_boxed_slice(),
1596 0,
1597 kinds,
1598 );
1599 // Construct via the v2-raw constructor; assert the kind label
1600 // and slot bits.
1601 let slot = KindedSlot::from_typed_object_raw(ptr);
1602 assert_eq!(slot.kind, NativeKind::Ptr(HeapKind::TypedObject));
1603 assert_eq!(slot.slot.raw(), ptr as u64);
1604 // The raw-pointer carrier owns the refcount independently; we
1605 // leak the slot here (don't drop it through the Arc-style
1606 // KindedSlot::Drop arms) and clean up via `_drop`. D2 wires the
1607 // dispatch arms to v2_release; pre-D2 this constructor's slot
1608 // bits MUST NOT flow into Arc-style dispatch (Drop or
1609 // clone_with_kind on these bits would call
1610 // Arc::decrement_strong_count on a non-Arc pointer →
1611 // segfault / heap corruption). Test exits via mem::forget +
1612 // explicit _drop.
1613 std::mem::forget(slot);
1614 TypedObjectStorage::_drop(ptr);
1615 }
1616 }
1617
1618 // ── §2.7.6 / Q8 scalar accessor coverage ──────────────────────────────
1619 //
1620 // One test per scalar accessor: same-kind returns Some, different-kind
1621 // returns None. These tests pin the `KindedSlot` carrier API bound:
1622 // accessors discriminate on `self.kind` and never decode bits when the
1623 // kind is wrong.
1624
1625 #[test]
1626 fn kinded_slot_as_i64_int_returns_some_value() {
1627 let s = KindedSlot::from_int(42);
1628 assert_eq!(s.as_i64(), Some(42));
1629 }
1630
1631 #[test]
1632 fn kinded_slot_as_i64_float_returns_none() {
1633 let s = KindedSlot::from_number(3.14);
1634 assert_eq!(s.as_i64(), None);
1635 }
1636
1637 #[test]
1638 fn kinded_slot_as_f64_float_returns_some_value() {
1639 let s = KindedSlot::from_number(3.14);
1640 assert_eq!(s.as_f64(), Some(3.14));
1641 }
1642
1643 #[test]
1644 fn kinded_slot_as_f64_int_returns_none() {
1645 let s = KindedSlot::from_int(42);
1646 assert_eq!(s.as_f64(), None);
1647 }
1648
1649 #[test]
1650 fn kinded_slot_as_bool_bool_returns_some_value() {
1651 let t = KindedSlot::from_bool(true);
1652 let f = KindedSlot::from_bool(false);
1653 assert_eq!(t.as_bool(), Some(true));
1654 assert_eq!(f.as_bool(), Some(false));
1655 }
1656
1657 #[test]
1658 fn kinded_slot_as_bool_int_returns_none() {
1659 let s = KindedSlot::from_int(1);
1660 assert_eq!(s.as_bool(), None);
1661 }
1662
1663 #[test]
1664 fn kinded_slot_as_char_char_returns_some_value() {
1665 let s = KindedSlot::from_char('A');
1666 assert_eq!(s.as_char(), Some('A'));
1667 // Unicode round-trip.
1668 let s2 = KindedSlot::from_char('λ');
1669 assert_eq!(s2.as_char(), Some('λ'));
1670 }
1671
1672 #[test]
1673 fn kinded_slot_as_char_int_returns_none() {
1674 let s = KindedSlot::from_int(65);
1675 assert_eq!(s.as_char(), None);
1676 }
1677
1678 #[test]
1679 fn kinded_slot_as_char_drop_safe() {
1680 // `from_char` stores codepoint bits inline; Drop must NOT try to
1681 // free them as if they were an `Arc<T>` pointer. Failure mode is
1682 // a debug-assert under the previous Char arm, or a free of an
1683 // invalid pointer in release.
1684 let s = KindedSlot::from_char('Z');
1685 drop(s);
1686 }
1687
1688 #[test]
1689 fn kinded_slot_as_str_string_returns_some_value() {
1690 let s = KindedSlot::from_string_arc(Arc::new("hello".to_string()));
1691 assert_eq!(s.as_str(), Some("hello"));
1692 }
1693
1694 #[test]
1695 fn kinded_slot_as_str_int_returns_none() {
1696 let s = KindedSlot::from_int(42);
1697 assert_eq!(s.as_str(), None);
1698 }
1699
1700 #[test]
1701 fn kinded_slot_from_string_borrows_back() {
1702 // `from_string(&str)` allocates an Arc<String> and stores its
1703 // pointer; `as_str()` should round-trip the contents.
1704 let s = KindedSlot::from_string("round trip");
1705 assert_eq!(s.as_str(), Some("round trip"));
1706 }
1707
1708 // ── §2.7.6 / Q8 from_temporal / from_instant constructor pair ────────
1709 //
1710 // W17-from-temporal-instant-constructors (Wave 3, 2026-05-12). These
1711 // pin the bounded-carrier-API rule: one constructor per `NativeKind`
1712 // heap variant, no parallel discrimination. Both constructors share
1713 // the `Arc::into_raw` typed-Arc shape with the existing Drop / Clone
1714 // arms for `HeapKind::Temporal` / `HeapKind::Instant`.
1715
1716 #[test]
1717 fn kinded_slot_from_temporal_sets_kind_and_retires_arc() {
1718 use crate::heap_value::TemporalData;
1719 let arc = Arc::new(TemporalData::TimeSpan(chrono::Duration::seconds(7)));
1720 let weak = Arc::downgrade(&arc);
1721 let slot = KindedSlot::from_temporal(arc);
1722 assert_eq!(slot.kind(), NativeKind::Ptr(HeapKind::Temporal));
1723 assert_eq!(weak.strong_count(), 1, "slot owns the only strong share");
1724 drop(slot);
1725 assert_eq!(
1726 weak.strong_count(),
1727 0,
1728 "Drop dispatched HeapKind::Temporal arm and retired refcount"
1729 );
1730 }
1731
1732 #[test]
1733 fn kinded_slot_from_temporal_clone_then_double_drop_balances() {
1734 use crate::heap_value::TemporalData;
1735 let arc = Arc::new(TemporalData::TimeSpan(chrono::Duration::milliseconds(500)));
1736 let weak = Arc::downgrade(&arc);
1737 let slot1 = KindedSlot::from_temporal(arc);
1738 assert_eq!(weak.strong_count(), 1);
1739 let slot2 = slot1.clone();
1740 assert_eq!(weak.strong_count(), 2, "Clone bumped refcount");
1741 drop(slot1);
1742 assert_eq!(weak.strong_count(), 1, "first Drop retired one share");
1743 drop(slot2);
1744 assert_eq!(weak.strong_count(), 0, "second Drop retired the last");
1745 }
1746
1747 #[test]
1748 fn kinded_slot_from_instant_sets_kind_and_retires_arc() {
1749 let arc = Arc::new(std::time::Instant::now());
1750 let weak = Arc::downgrade(&arc);
1751 let slot = KindedSlot::from_instant(arc);
1752 assert_eq!(slot.kind(), NativeKind::Ptr(HeapKind::Instant));
1753 assert_eq!(weak.strong_count(), 1, "slot owns the only strong share");
1754 drop(slot);
1755 assert_eq!(
1756 weak.strong_count(),
1757 0,
1758 "Drop dispatched HeapKind::Instant arm and retired refcount"
1759 );
1760 }
1761
1762 #[test]
1763 fn kinded_slot_from_instant_clone_then_double_drop_balances() {
1764 let arc = Arc::new(std::time::Instant::now());
1765 let weak = Arc::downgrade(&arc);
1766 let slot1 = KindedSlot::from_instant(arc);
1767 assert_eq!(weak.strong_count(), 1);
1768 let slot2 = slot1.clone();
1769 assert_eq!(weak.strong_count(), 2, "Clone bumped refcount");
1770 drop(slot1);
1771 assert_eq!(weak.strong_count(), 1, "first Drop retired one share");
1772 drop(slot2);
1773 assert_eq!(weak.strong_count(), 0, "second Drop retired the last");
1774 }
1775}