shape_value/iterator_state.rs
1//! Iterator-state carrier — the kinded redesign of the deleted
2//! `heap_value::IteratorState` / `IteratorTransform` `ValueWord`-shaped enums.
3//!
4//! ADR-006 §2.7.16 / Q17 (W13-iterator-state, 2026-05-10). Lazy iterator
5//! pipelines are represented as a plain typed `Arc<IteratorState>` whose
6//! payload is (a) a typed `IteratorSource` over an existing `Arc<T>`-backed
7//! collection and (b) an ordered list of typed `IteratorTransform` stages.
8//! Each transform that takes a callback stores the closure carrier as
9//! `Arc<HeapValue>` directly per ADR-006 §2.7.11 / Q12 — the value-call ABI
10//! is kind-aware via `KindedSlot` at the dispatch boundary, so a stored
11//! closure flows back into `vm.call_value_immediate_nb` as a fresh
12//! `KindedSlot { kind: Ptr(HeapKind::Closure), .. }` carrier whose share is
13//! bumped from the stored `Arc<HeapValue>`.
14//!
15//! Slot bits for an `Iterator`-labeled slot are
16//! `Arc::into_raw(Arc<IteratorState>) as u64` (mirror of §2.7.9 FilterExpr
17//! / §2.7.13 Reference — NOT a `Box::into_raw(Box<HeapValue>)` wrap).
18//! `clone_with_kind` / `drop_with_kind` retain/release `Arc<IteratorState>`
19//! directly via the `HeapKind::Iterator` dispatch arm. `slot.as_heap_value()`
20//! IS valid on Iterator-labeled bits — unlike FilterExpr/Reference,
21//! `HeapValue::Iterator(Arc<IteratorState>)` participates in the §2.3
22//! typed-Arc payload pattern, and the dispatch shell may recover the
23//! state via the canonical `slot.as_heap_value()` → `HeapValue::Iterator`
24//! match (the iterator method handlers use this path).
25//!
26//! No new dispatch surface is introduced — `clone_with_kind` /
27//! `drop_with_kind` / `KindedSlot::clone` / `KindedSlot::drop` /
28//! `TypedObjectStorage::drop` / `SharedCell::drop` each grow one new arm
29//! (the same shape as the §2.7.9 FilterExpr / §2.7.12 SharedCell /
30//! §2.7.13 Reference precedents).
31
32// V3-S5 ckpt-4 (2026-05-15): `TypedArrayData` import deleted — the enum
33// was retired at V3-S5 ckpt-1 per W12-typed-array-data-deletion-audit §3.5
34// + ADR-006 §2.7.24 Q25.A SUPERSEDED. `IteratorSource::Array(Arc<
35// TypedArrayData>)` variant deleted in lockstep; iterator pipelines over
36// typed-array receivers cascade-break for v2-raw `TypedArray<T>` rebuild
37// in a downstream wave (the `IteratorSource::Array` carrier needs a
38// per-element-kind redesign — the typed-Arc payload Q25.A SUPERSEDED
39// pattern produces `Arc<TypedArray<f64>>` / `Arc<TypedArray<i64>>` /
40// etc., not a single `Arc<T>` enum carrier).
41use crate::heap_value::{HashMapKindedRef, HeapValue};
42use std::sync::Arc;
43
44/// Source backing a lazy iterator pipeline. Each variant holds a typed
45/// `Arc<T>` over an existing collection so iteration shares the receiver's
46/// storage without a deep copy.
47///
48/// `Range` carries inline `i64` bounds + step (no `Arc` payload); the
49/// post-§2.7.4 Range-value carrier rebuild is tracked separately, so for
50/// now Range sources are constructed by the iterator factory's own
51/// receiver-decode path (Range receivers themselves remain a phase-2c
52/// surface; the field is provided in `IteratorSource` so the carrier is
53/// future-proof against the §2.3 / Q8 cardinality constraints).
54#[derive(Debug, Clone)]
55pub enum IteratorSource {
56 // V3-S5 ckpt-4 (2026-05-15): `Array(Arc<TypedArrayData>)` variant
57 // DELETED. The `TypedArrayData` enum + wrapper layer
58 // (`TypedBuffer<T>`/`AlignedTypedBuffer`) are retired wholesale at
59 // ckpt-1..ckpt-4 per W12-typed-array-data-deletion-audit §3.5 + §B
60 // + ADR-006 §2.7.24 Q25.A SUPERSEDED. Iterator pipelines over typed-
61 // array receivers cascade-break here; the v2-raw `TypedArray<T>`
62 // rebuild produces per-element-kind `Arc<TypedArray<f64>>` /
63 // `Arc<TypedArray<i64>>` payloads (not a single-Arc enum), so the
64 // replacement is per-element-kind variants whose design is
65 // downstream-wave territory. Refusal #1 binding.
66
67 /// Iteration over a string receiver (per-codepoint).
68 String(Arc<String>),
69
70 /// Iteration over a numeric range. `start` is inclusive, `end` is
71 /// exclusive (matching `0..n` Rust-shape range semantics); `step`
72 /// is the per-iteration increment (always positive, defaulting to 1
73 /// for `start..end` ranges).
74 Range { start: i64, end: i64, step: i64 },
75
76 /// Iteration over a HashMap receiver. Per-entry yields are
77 /// 2-element `[key, value]` inner arrays, mirroring the
78 /// `HashMap.entries()` shape.
79 ///
80 /// **Wave 2 Round 3b C2-joint ckpt-2 (2026-05-14):** payload flipped
81 /// from `Arc<HashMapData>` (non-generic) to `HashMapKindedRef` per
82 /// ADR-006 §2.7.24 Q25.B SUPERSEDED. Per-entry yields dispatch per-V
83 /// at iteration time via the inner `HashMapKindedRef` arm.
84 HashMap(HashMapKindedRef),
85}
86
87impl IteratorSource {
88 /// Element count of the source — the upper bound on the cursor before
89 /// any take/skip/filter is applied. For Range, computed from the
90 /// (start, end, step) triple; for collections, the receiver length.
91 #[inline]
92 pub fn len(&self) -> usize {
93 match self {
94 // V3-S5 ckpt-4: `IteratorSource::Array(...)` arm deleted in
95 // lockstep with the variant.
96 IteratorSource::String(s) => s.chars().count(),
97 IteratorSource::Range { start, end, step } => {
98 if *step <= 0 || *end <= *start {
99 return 0;
100 }
101 let span = (*end - *start) as u64;
102 let step = *step as u64;
103 ((span + step - 1) / step) as usize
104 }
105 IteratorSource::HashMap(m) => m.len(),
106 }
107 }
108}
109
110// V3-S5 ckpt-4 (2026-05-15): the module-local `typed_array_len` helper is
111// DELETED in lockstep with the `IteratorSource::Array` variant + the
112// `TypedArrayData` enum (ckpt-1) + `TypedBuffer<T>` wrapper layer
113// (ckpt-4). W12-typed-array-data-deletion-audit §3.5 + ADR-006 §2.7.24
114// Q25.A SUPERSEDED. Replacement (downstream wave): per-element-kind
115// `Arc<TypedArray<T>>` source variants whose len() reads the v2-raw
116// flat-struct `len` field directly.
117
118/// Lazy transform stage in an iterator pipeline. Each closure-bearing
119/// variant stores the callback as `Arc<HeapValue>` per ADR-006 §2.3 /
120/// §2.7.11 — the same share carrier the `op_call_value` /
121/// `call_value_immediate_nb` path consumes (the slot bits at the §2.7.7
122/// stack tier are `Arc::into_raw(Arc<HeapValue>)` pointing to a
123/// `HeapValue::ClosureRaw(OwnedClosureBlock)` arm; the iterator-state
124/// stash here keeps an extra share alive for the iterator's lifetime).
125#[derive(Debug, Clone)]
126pub enum IteratorTransform {
127 /// `iter.map(closure)` — replace each element with `closure(element)`.
128 Map(Arc<HeapValue>),
129
130 /// `iter.filter(predicate)` — drop elements where `predicate(element)`
131 /// returns `false`.
132 Filter(Arc<HeapValue>),
133
134 /// `iter.take(n)` — limit the output to the first `n` elements.
135 Take(usize),
136
137 /// `iter.skip(n)` — drop the first `n` elements before yielding.
138 Skip(usize),
139
140 /// `iter.flatMap(closure)` — replace each element with the array
141 /// returned by `closure(element)` and concatenate the results.
142 FlatMap(Arc<HeapValue>),
143
144 /// `iter.enumerate()` — replace each element `e` with the 2-element
145 /// inner array `[index, e]`.
146 Enumerate,
147
148 /// `iter.chain(other)` — append `other`'s elements after `self`'s
149 /// elements. The other iterator is materialized at terminal-evaluation
150 /// time, sharing its source/transforms with no deep copy.
151 Chain(Arc<IteratorState>),
152}
153
154/// Lazy iterator carrier. Stored on the heap as
155/// `Arc<IteratorState>`; the runtime slot label is
156/// `NativeKind::Ptr(HeapKind::Iterator)`.
157///
158/// `cursor` is preserved across clones (a cloned iterator continues from
159/// the parent's position); transforms append new stages without consuming
160/// the source. Terminal operations (`collect`, `forEach`, `reduce`, etc.)
161/// walk the (source, transforms, cursor) triple and emit results, leaving
162/// the input state immutable so that `let it = arr.iter().map(f); it.collect()`
163/// is observably the same as `arr.iter().map(f).collect()`.
164#[derive(Debug)]
165pub struct IteratorState {
166 pub source: IteratorSource,
167 pub transforms: Vec<IteratorTransform>,
168 pub cursor: usize,
169}
170
171impl IteratorState {
172 /// Construct a fresh iterator over `source` with no transforms.
173 #[inline]
174 pub fn new(source: IteratorSource) -> Self {
175 Self {
176 source,
177 transforms: Vec::new(),
178 cursor: 0,
179 }
180 }
181
182 /// Append a transform stage, returning a new `IteratorState`. The
183 /// receiver's source and existing transforms are cloned (each is a
184 /// typed-Arc bump — no deep copy of the underlying buffers).
185 #[inline]
186 pub fn with_transform(&self, t: IteratorTransform) -> Self {
187 let mut transforms = self.transforms.clone();
188 transforms.push(t);
189 Self {
190 source: self.source.clone(),
191 transforms,
192 cursor: self.cursor,
193 }
194 }
195}
196
197impl Clone for IteratorState {
198 /// Per-field clone — `IteratorSource` and `IteratorTransform` are
199 /// already `Clone` (they hold typed `Arc<T>` payloads whose `Clone`
200 /// is a single atomic refcount bump).
201 fn clone(&self) -> Self {
202 Self {
203 source: self.source.clone(),
204 transforms: self.transforms.clone(),
205 cursor: self.cursor,
206 }
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 //! V3-S5 ckpt-4 (2026-05-15): tests over `IteratorSource::Array(Arc<
213 //! TypedArrayData>)` DELETED in lockstep with the variant + the
214 //! `TypedArrayData` enum (ckpt-1) + `TypedBuffer<T>` wrapper layer
215 //! (ckpt-4). W12-typed-array-data-deletion-audit §3.5/§B + ADR-006
216 //! §2.7.24 Q25.A SUPERSEDED. The String / Range / HashMap source
217 //! tests below are preserved — they don't touch the deleted carrier.
218
219 use super::*;
220 use std::sync::Arc;
221
222 #[test]
223 fn iterator_source_string_len_codepoints() {
224 let s = Arc::new("abcλ".to_string());
225 let src = IteratorSource::String(s);
226 assert_eq!(src.len(), 4); // codepoints, not bytes
227 }
228
229 #[test]
230 fn iterator_source_range_len() {
231 let src = IteratorSource::Range { start: 0, end: 10, step: 1 };
232 assert_eq!(src.len(), 10);
233 let src2 = IteratorSource::Range { start: 0, end: 10, step: 3 };
234 assert_eq!(src2.len(), 4); // 0, 3, 6, 9
235 }
236
237 #[test]
238 fn iterator_source_range_empty_on_zero_step() {
239 let src = IteratorSource::Range { start: 0, end: 10, step: 0 };
240 assert_eq!(src.len(), 0);
241 }
242}