Skip to main content

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}