seq_runtime/
stack.rs

1use crate::pool;
2use crate::value::Value;
3
4/// StackNode: Implementation detail of the stack
5///
6/// This is a linked list node that contains a Value.
7/// The key insight: StackNode is separate from Value.
8/// Users think about Values, not StackNodes.
9pub struct StackNode {
10    /// The actual value stored in this node
11    pub value: Value,
12
13    /// Pointer to the next node in the stack (or null if this is the bottom)
14    /// This pointer is ONLY for stack structure, never for variant field linking
15    pub next: *mut StackNode,
16}
17
18/// Stack: A pointer to the top of the stack
19///
20/// null represents an empty stack
21pub type Stack = *mut StackNode;
22
23/// Push a value onto the stack
24///
25/// Takes ownership of the value and creates a new StackNode from the pool.
26/// Returns a pointer to the new top of the stack.
27///
28/// # Safety
29/// Stack pointer must be valid (or null for empty stack)
30///
31/// # Performance
32/// Uses thread-local pool for ~10x speedup over Box::new()
33pub unsafe fn push(stack: Stack, value: Value) -> Stack {
34    pool::pool_allocate(value, stack)
35}
36
37/// Pop a value from the stack
38///
39/// Returns the rest of the stack and the popped value.
40/// Returns the StackNode to the pool for reuse.
41///
42/// # Safety
43/// Stack must not be null (use is_empty to check first)
44///
45/// # Performance
46/// Returns node to thread-local pool for reuse (~10x faster than free)
47pub unsafe fn pop(stack: Stack) -> (Stack, Value) {
48    assert!(!stack.is_null(), "pop: stack is empty");
49
50    unsafe {
51        let rest = (*stack).next;
52        // CRITICAL: Replace value with dummy before returning node to pool
53        // This prevents double-drop when pool reuses the node
54        // The dummy value (Int(0)) will be overwritten when node is reused
55        let value = std::mem::replace(&mut (*stack).value, Value::Int(0));
56
57        // Return node to pool for reuse
58        pool::pool_free(stack);
59
60        (rest, value)
61    }
62}
63
64/// Pop two values from the stack (for binary operations)
65///
66/// Returns the rest of the stack and both values (first popped, second popped).
67/// This is a common pattern for binary operations like add, subtract, etc.
68///
69/// # Safety
70/// Stack must have at least two values
71///
72/// # Returns
73/// (remaining_stack, second_value, first_value) - note: second is "a", first is "b"
74/// for operations like a - b where b is on top
75pub unsafe fn pop_two(stack: Stack, op_name: &str) -> (Stack, Value, Value) {
76    assert!(!stack.is_null(), "{}: stack is empty", op_name);
77    let (rest, b) = unsafe { pop(stack) };
78    assert!(!rest.is_null(), "{}: need two values", op_name);
79    let (rest, a) = unsafe { pop(rest) };
80    (rest, a, b)
81}
82
83/// Check if the stack is empty
84pub fn is_empty(stack: Stack) -> bool {
85    stack.is_null()
86}
87
88/// Peek at the top value without removing it
89///
90/// # Safety
91/// Stack must not be null
92/// Caller must ensure the returned reference is used within a valid lifetime
93pub unsafe fn peek<'a>(stack: Stack) -> &'a Value {
94    assert!(!stack.is_null(), "peek: stack is empty");
95    unsafe { &(*stack).value }
96}
97
98/// Duplicate the top value on the stack: ( a -- a a )
99///
100/// # Safety
101/// Stack must not be null
102#[unsafe(no_mangle)]
103pub unsafe extern "C" fn patch_seq_dup(stack: Stack) -> Stack {
104    assert!(!stack.is_null(), "dup: stack is empty");
105    // SAFETY NOTE: In Rust 2024 edition, even within `unsafe fn`, the body is not
106    // automatically an unsafe context. Explicit `unsafe {}` blocks are required for
107    // all unsafe operations (dereferencing raw pointers, calling unsafe functions).
108    // This is intentional and follows best practices for clarity about what's unsafe.
109    let value = unsafe { (*stack).value.clone() };
110    unsafe { push(stack, value) }
111}
112
113/// Drop the top value from the stack: ( a -- )
114///
115/// # Safety
116/// Stack must not be null
117pub unsafe fn drop(stack: Stack) -> Stack {
118    assert!(!stack.is_null(), "drop: stack is empty");
119    let (rest, _) = unsafe { pop(stack) };
120    rest
121}
122
123/// Alias for drop to avoid LLVM keyword conflicts
124///
125/// LLVM uses "drop" as a reserved word, so codegen calls this as drop_op
126///
127/// # Safety
128/// Stack must not be null
129#[unsafe(no_mangle)]
130pub unsafe extern "C" fn patch_seq_drop_op(stack: Stack) -> Stack {
131    unsafe { drop(stack) }
132}
133
134/// Push an arbitrary Value onto the stack (for LLVM codegen)
135///
136/// This is used by closure functions to push captured values.
137///
138/// # Safety
139/// Value must be a valid Value
140// Allow improper_ctypes_definitions: Called from LLVM IR (not C), both sides understand layout
141#[allow(improper_ctypes_definitions)]
142#[unsafe(no_mangle)]
143pub unsafe extern "C" fn patch_seq_push_value(stack: Stack, value: Value) -> Stack {
144    unsafe { push(stack, value) }
145}
146
147/// Swap the top two values: ( a b -- b a )
148///
149/// # Safety
150/// Stack must have at least two values
151#[unsafe(no_mangle)]
152pub unsafe extern "C" fn patch_seq_swap(stack: Stack) -> Stack {
153    assert!(!stack.is_null(), "swap: stack is empty");
154    let (rest, b) = unsafe { pop(stack) };
155    assert!(!rest.is_null(), "swap: stack has only one value");
156    let (rest, a) = unsafe { pop(rest) };
157    let stack = unsafe { push(rest, b) };
158    unsafe { push(stack, a) }
159}
160
161/// Copy the second value to the top: ( a b -- a b a )
162///
163/// # Safety
164/// Stack must have at least two values
165#[unsafe(no_mangle)]
166pub unsafe extern "C" fn patch_seq_over(stack: Stack) -> Stack {
167    assert!(!stack.is_null(), "over: stack is empty");
168    let (rest, b) = unsafe { pop(stack) };
169    assert!(!rest.is_null(), "over: stack has only one value");
170    let a = unsafe { (*rest).value.clone() };
171    let stack = unsafe { push(rest, b) };
172    unsafe { push(stack, a) }
173}
174
175/// Rotate the top three values: ( a b c -- b c a )
176///
177/// # Safety
178/// Stack must have at least three values
179#[unsafe(no_mangle)]
180pub unsafe extern "C" fn patch_seq_rot(stack: Stack) -> Stack {
181    assert!(!stack.is_null(), "rot: stack is empty");
182    let (rest, c) = unsafe { pop(stack) };
183    assert!(!rest.is_null(), "rot: stack has only one value");
184    let (rest, b) = unsafe { pop(rest) };
185    assert!(!rest.is_null(), "rot: stack has only two values");
186    let (rest, a) = unsafe { pop(rest) };
187    let stack = unsafe { push(rest, b) };
188    let stack = unsafe { push(stack, c) };
189    unsafe { push(stack, a) }
190}
191
192/// Remove the second value: ( a b -- b )
193///
194/// # Safety
195/// Stack must have at least two values
196#[unsafe(no_mangle)]
197pub unsafe extern "C" fn patch_seq_nip(stack: Stack) -> Stack {
198    assert!(!stack.is_null(), "nip: stack is empty");
199    let (rest, b) = unsafe { pop(stack) };
200    assert!(!rest.is_null(), "nip: stack has only one value");
201    let (rest, _a) = unsafe { pop(rest) };
202    unsafe { push(rest, b) }
203}
204
205/// Copy top value below second value: ( a b -- b a b )
206///
207/// # Safety
208/// Stack must have at least two values
209#[unsafe(no_mangle)]
210pub unsafe extern "C" fn patch_seq_tuck(stack: Stack) -> Stack {
211    assert!(!stack.is_null(), "tuck: stack is empty");
212    let (rest, b) = unsafe { pop(stack) };
213    assert!(!rest.is_null(), "tuck: stack has only one value");
214    let (rest, a) = unsafe { pop(rest) };
215    let stack = unsafe { push(rest, b.clone()) };
216    let stack = unsafe { push(stack, a) };
217    unsafe { push(stack, b) }
218}
219
220/// Pick: Copy the nth value to the top (0-indexed from top)
221/// ( ... xn ... x1 x0 n -- ... xn ... x1 x0 xn )
222///
223/// Examples:
224/// - pick(0) is equivalent to dup
225/// - pick(1) is equivalent to over
226/// - pick(2) copies the third value to the top
227///
228/// # Safety
229/// Stack must have at least n+1 values
230pub unsafe fn pick(stack: Stack, n: usize) -> Stack {
231    assert!(!stack.is_null(), "pick: stack is empty");
232
233    // Walk down n nodes to find the target value
234    let mut current = stack;
235    for i in 0..n {
236        assert!(
237            !current.is_null(),
238            "pick: stack has only {} values, need at least {}",
239            i + 1,
240            n + 1
241        );
242        current = unsafe { (*current).next };
243    }
244
245    assert!(
246        !current.is_null(),
247        "pick: stack has only {} values, need at least {}",
248        n,
249        n + 1
250    );
251
252    // Clone the value at position n
253    let value = unsafe { (*current).value.clone() };
254
255    // Push it on top of the stack
256    unsafe { push(stack, value) }
257}
258
259/// Pick operation exposed to compiler
260///
261/// Copies a value from depth n to the top of the stack.
262///
263/// Stack effect: ( ..stack n -- ..stack value )
264/// where n is how deep to look (0 = top, 1 = second, etc.)
265///
266/// # Examples
267///
268/// ```cem
269/// 10 20 30 0 pick   # Stack: 10 20 30 30  (pick(0) = dup)
270/// 10 20 30 1 pick   # Stack: 10 20 30 20  (pick(1) = over)
271/// 10 20 30 2 pick   # Stack: 10 20 30 10  (pick(2) = third)
272/// ```
273///
274/// # Common Equivalences
275///
276/// - `pick(0)` is equivalent to `dup`  - copy top value
277/// - `pick(1)` is equivalent to `over` - copy second value
278/// - `pick(2)` copies third value (sometimes called "third")
279/// - `pick(n)` copies value at depth n
280///
281/// # Use Cases
282///
283/// Useful for building stack utilities like:
284/// - `third`: `2 pick` - access third item
285/// - `3dup`: `2 pick 2 pick 2 pick` - duplicate top three items
286/// - Complex stack manipulations without excessive rot/swap
287///
288/// # Safety
289/// Stack must have at least one value (the Int n), and at least n+1 values total
290#[unsafe(no_mangle)]
291pub unsafe extern "C" fn patch_seq_pick_op(stack: Stack) -> Stack {
292    use crate::value::Value;
293
294    assert!(!stack.is_null(), "pick: stack is empty");
295
296    // Peek at the depth value without popping (for better error messages)
297    let depth_value = unsafe { &(*stack).value };
298    let n = match depth_value {
299        Value::Int(i) => {
300            if *i < 0 {
301                panic!("pick: depth must be non-negative, got {}", i);
302            }
303            *i as usize
304        }
305        _ => panic!(
306            "pick: expected Int depth on top of stack, got {:?}",
307            depth_value
308        ),
309    };
310
311    // Count stack depth (excluding the depth parameter itself)
312    let mut count = 0;
313    let mut current = unsafe { (*stack).next };
314    while !current.is_null() {
315        count += 1;
316        current = unsafe { (*current).next };
317    }
318
319    // Validate we have enough values
320    // Need: n+1 values total (depth parameter + n values below it)
321    // We have: 1 (depth param) + count
322    // So we need: count >= n + 1
323    if count < n + 1 {
324        panic!(
325            "pick: cannot access depth {} - stack has only {} value{} (need at least {})",
326            n,
327            count,
328            if count == 1 { "" } else { "s" },
329            n + 1
330        );
331    }
332
333    // Now pop the depth value and call internal pick
334    let (stack, _) = unsafe { pop(stack) };
335    unsafe { pick(stack, n) }
336}
337
338/// Roll: Rotate n+1 items, bringing the item at depth n to the top
339/// ( x_n x_(n-1) ... x_1 x_0 n -- x_(n-1) ... x_1 x_0 x_n )
340///
341/// Examples:
342/// - roll(0) is a no-op (rotate 1 item)
343/// - roll(1) is equivalent to swap (rotate 2 items)
344/// - roll(2) is equivalent to rot (rotate 3 items)
345/// - roll(3) rotates 4 items: ( a b c d 3 -- b c d a )
346///
347/// This is the standard Forth ROLL word.
348///
349/// # Safety
350/// Stack must have the depth parameter (Int) on top, and at least n+1 values below it
351#[unsafe(no_mangle)]
352pub unsafe extern "C" fn patch_seq_roll(stack: Stack) -> Stack {
353    use crate::value::Value;
354
355    assert!(!stack.is_null(), "roll: stack is empty");
356
357    // Get the depth parameter
358    let depth_value = unsafe { &(*stack).value };
359    let n = match depth_value {
360        Value::Int(i) => {
361            if *i < 0 {
362                panic!("roll: depth must be non-negative, got {}", i);
363            }
364            *i as usize
365        }
366        _ => panic!(
367            "roll: expected Int depth on top of stack, got {:?}",
368            depth_value
369        ),
370    };
371
372    // Pop the depth parameter
373    let (stack, _) = unsafe { pop(stack) };
374
375    // Special cases for efficiency
376    if n == 0 {
377        // roll(0) is a no-op
378        return stack;
379    }
380    if n == 1 {
381        // roll(1) is swap
382        return unsafe { patch_seq_swap(stack) };
383    }
384    if n == 2 {
385        // roll(2) is rot
386        return unsafe { patch_seq_rot(stack) };
387    }
388
389    // General case: rotate n+1 items
390    // We need to pop n+1 items, then push them back in rotated order
391
392    // Validate stack depth
393    let mut count = 0;
394    let mut current = stack;
395    while !current.is_null() && count <= n {
396        count += 1;
397        current = unsafe { (*current).next };
398    }
399
400    if count < n + 1 {
401        panic!(
402            "roll: cannot rotate {} items - stack has only {} value{}",
403            n + 1,
404            count,
405            if count == 1 { "" } else { "s" }
406        );
407    }
408
409    // Pop n+1 items into a vector
410    let mut items = Vec::with_capacity(n + 1);
411    let mut current_stack = stack;
412    for _ in 0..=n {
413        let (rest, val) = unsafe { pop(current_stack) };
414        items.push(val);
415        current_stack = rest;
416    }
417
418    // items[0] = top, items[1] = second, ..., items[n] = bottom of rotated section
419    // We want: items[n] goes to top, items[0..n] shift down
420    // So push order is: items[n-1], items[n-2], ..., items[0], items[n]
421
422    // Take item[n] first using swap_remove (it goes on top at the end)
423    // swap_remove(n) moves the last element to position n, but n IS the last position,
424    // so this just removes and returns items[n]
425    let top_item = items.swap_remove(n);
426
427    // Push items in reverse order (items[n-1] down to items[0])
428    // Drain in reverse to consume the vector without cloning
429    for val in items.into_iter().rev() {
430        current_stack = unsafe { push(current_stack, val) };
431    }
432    // Push the saved item[n] on top (this was at depth n, now at top)
433    current_stack = unsafe { push(current_stack, top_item) };
434
435    current_stack
436}
437
438/// Clone a stack - creates a deep copy of all values
439///
440/// This is used by spawn to pass a copy of the parent's stack to the child.
441/// Returns a new stack with cloned values, or null if input is null.
442///
443/// NOTE: This uses Box allocation instead of pool allocation because the
444/// cloned stack will be used by a different strand (different thread context).
445/// Thread-local pools are not shared between strands.
446///
447/// # Safety
448/// Stack pointer must be valid (or null for empty stack)
449pub unsafe fn clone_stack(stack: Stack) -> Stack {
450    if stack.is_null() {
451        return std::ptr::null_mut();
452    }
453
454    // Collect all values (bottom to top for correct push order)
455    let mut values = Vec::new();
456    let mut current = stack;
457    while !current.is_null() {
458        values.push(unsafe { (*current).value.clone() });
459        current = unsafe { (*current).next };
460    }
461
462    // Build new stack using Box allocation (not pool) for cross-strand safety
463    // We can't use the thread-local pool because this stack will be used by another strand
464    let mut new_stack: Stack = std::ptr::null_mut();
465    for value in values.into_iter().rev() {
466        let node = Box::new(StackNode {
467            value,
468            next: new_stack,
469        });
470        new_stack = Box::into_raw(node);
471    }
472
473    new_stack
474}
475
476// Public re-exports with short names for internal use
477pub use patch_seq_drop_op as drop_op;
478pub use patch_seq_dup as dup;
479pub use patch_seq_nip as nip;
480pub use patch_seq_over as over;
481pub use patch_seq_pick_op as pick_op;
482pub use patch_seq_push_value as push_value;
483pub use patch_seq_roll as roll;
484pub use patch_seq_rot as rot;
485pub use patch_seq_swap as swap;
486pub use patch_seq_tuck as tuck;
487
488#[cfg(test)]
489mod tests {
490    use super::*;
491
492    /// Test helper: Create a stack with integer values
493    fn make_stack(values: &[i64]) -> Stack {
494        unsafe {
495            let mut stack = std::ptr::null_mut();
496            for &val in values {
497                stack = push(stack, Value::Int(val));
498            }
499            stack
500        }
501    }
502
503    /// Test helper: Pop all values from stack and return as Vec
504    fn drain_stack(mut stack: Stack) -> Vec<Value> {
505        unsafe {
506            let mut values = Vec::new();
507            while !is_empty(stack) {
508                let (rest, val) = pop(stack);
509                stack = rest;
510                values.push(val);
511            }
512            values
513        }
514    }
515
516    /// Test helper: Assert stack contains expected integer values (top to bottom)
517    fn assert_stack_ints(stack: Stack, expected: &[i64]) {
518        let values = drain_stack(stack);
519        let ints: Vec<i64> = values
520            .into_iter()
521            .map(|v| match v {
522                Value::Int(n) => n,
523                _ => panic!("Expected Int, got {:?}", v),
524            })
525            .collect();
526        assert_eq!(ints, expected);
527    }
528
529    #[test]
530    fn test_push_pop() {
531        unsafe {
532            let stack = std::ptr::null_mut();
533            assert!(is_empty(stack));
534
535            let stack = push(stack, Value::Int(42));
536            assert!(!is_empty(stack));
537
538            let (stack, value) = pop(stack);
539            assert_eq!(value, Value::Int(42));
540            assert!(is_empty(stack));
541        }
542    }
543
544    #[test]
545    fn test_multiple_values() {
546        unsafe {
547            let mut stack = std::ptr::null_mut();
548
549            stack = push(stack, Value::Int(1));
550            stack = push(stack, Value::Int(2));
551            stack = push(stack, Value::Int(3));
552
553            let (stack, val) = pop(stack);
554            assert_eq!(val, Value::Int(3));
555
556            let (stack, val) = pop(stack);
557            assert_eq!(val, Value::Int(2));
558
559            let (stack, val) = pop(stack);
560            assert_eq!(val, Value::Int(1));
561
562            assert!(is_empty(stack));
563        }
564    }
565
566    #[test]
567    fn test_peek() {
568        unsafe {
569            let stack = push(std::ptr::null_mut(), Value::Int(42));
570            let peeked = peek(stack);
571            assert_eq!(*peeked, Value::Int(42));
572
573            // Value still there
574            let (stack, value) = pop(stack);
575            assert_eq!(value, Value::Int(42));
576            assert!(is_empty(stack));
577        }
578    }
579
580    #[test]
581    fn test_dup() {
582        unsafe {
583            let stack = push(std::ptr::null_mut(), Value::Int(42));
584            let stack = dup(stack);
585
586            // Should have two copies of 42
587            let (stack, val1) = pop(stack);
588            assert_eq!(val1, Value::Int(42));
589
590            let (stack, val2) = pop(stack);
591            assert_eq!(val2, Value::Int(42));
592
593            assert!(is_empty(stack));
594        }
595    }
596
597    #[test]
598    fn test_drop() {
599        unsafe {
600            let mut stack = std::ptr::null_mut();
601            stack = push(stack, Value::Int(1));
602            stack = push(stack, Value::Int(2));
603            stack = push(stack, Value::Int(3));
604
605            // Drop top value (3)
606            stack = drop(stack);
607
608            // Should have 2 on top now
609            let (stack, val) = pop(stack);
610            assert_eq!(val, Value::Int(2));
611
612            let (stack, val) = pop(stack);
613            assert_eq!(val, Value::Int(1));
614
615            assert!(is_empty(stack));
616        }
617    }
618
619    #[test]
620    fn test_swap() {
621        unsafe {
622            let mut stack = std::ptr::null_mut();
623            stack = push(stack, Value::Int(1));
624            stack = push(stack, Value::Int(2));
625
626            // Swap: 1 2 -> 2 1
627            stack = swap(stack);
628
629            let (stack, val) = pop(stack);
630            assert_eq!(val, Value::Int(1));
631
632            let (stack, val) = pop(stack);
633            assert_eq!(val, Value::Int(2));
634
635            assert!(is_empty(stack));
636        }
637    }
638
639    #[test]
640    fn test_composition() {
641        // Test: compose swap + drop + dup to verify operations work together
642        // Trace:
643        // Start:  [1]
644        // push 2: [2, 1]
645        // push 3: [3, 2, 1]
646        // swap:   [2, 3, 1]  (swap top two)
647        // drop:   [3, 1]     (remove top)
648        // dup:    [3, 3, 1]  (duplicate top)
649        unsafe {
650            let mut stack = make_stack(&[1, 2, 3]);
651
652            stack = swap(stack);
653            stack = drop(stack);
654            stack = dup(stack);
655
656            assert_stack_ints(stack, &[3, 3, 1]);
657        }
658    }
659
660    #[test]
661    fn test_over() {
662        // over: ( a b -- a b a )
663        unsafe {
664            let mut stack = std::ptr::null_mut();
665            stack = push(stack, Value::Int(1));
666            stack = push(stack, Value::Int(2));
667
668            stack = over(stack); // [1, 2, 1]
669
670            let (stack, val) = pop(stack);
671            assert_eq!(val, Value::Int(1));
672
673            let (stack, val) = pop(stack);
674            assert_eq!(val, Value::Int(2));
675
676            let (stack, val) = pop(stack);
677            assert_eq!(val, Value::Int(1));
678
679            assert!(is_empty(stack));
680        }
681    }
682
683    #[test]
684    fn test_rot() {
685        // rot: ( a b c -- b c a )
686        unsafe {
687            let mut stack = std::ptr::null_mut();
688            stack = push(stack, Value::Int(1));
689            stack = push(stack, Value::Int(2));
690            stack = push(stack, Value::Int(3));
691
692            stack = rot(stack); // [1, 3, 2]
693
694            let (stack, val) = pop(stack);
695            assert_eq!(val, Value::Int(1));
696
697            let (stack, val) = pop(stack);
698            assert_eq!(val, Value::Int(3));
699
700            let (stack, val) = pop(stack);
701            assert_eq!(val, Value::Int(2));
702
703            assert!(is_empty(stack));
704        }
705    }
706
707    #[test]
708    fn test_nip() {
709        // nip: ( a b -- b )
710        unsafe {
711            let mut stack = std::ptr::null_mut();
712            stack = push(stack, Value::Int(1));
713            stack = push(stack, Value::Int(2));
714
715            stack = nip(stack); // [2]
716
717            let (stack, val) = pop(stack);
718            assert_eq!(val, Value::Int(2));
719
720            assert!(is_empty(stack));
721        }
722    }
723
724    #[test]
725    fn test_tuck() {
726        // tuck: ( a b -- b a b )
727        unsafe {
728            let mut stack = std::ptr::null_mut();
729            stack = push(stack, Value::Int(1));
730            stack = push(stack, Value::Int(2));
731
732            stack = tuck(stack); // [2, 1, 2]
733
734            let (stack, val) = pop(stack);
735            assert_eq!(val, Value::Int(2));
736
737            let (stack, val) = pop(stack);
738            assert_eq!(val, Value::Int(1));
739
740            let (stack, val) = pop(stack);
741            assert_eq!(val, Value::Int(2));
742
743            assert!(is_empty(stack));
744        }
745    }
746
747    #[test]
748    fn test_critical_shuffle_pattern() {
749        // This is THE CRITICAL TEST that failed in cem2!
750        // In cem2, this shuffle pattern caused variant field corruption because
751        // StackCell.next pointers were used for BOTH stack linking AND variant fields.
752        // When the stack was shuffled, next pointers became stale, corrupting variants.
753        //
754        // In Seq, this CANNOT happen because:
755        // - StackNode.next is ONLY for stack structure
756        // - Variant fields are stored in Box<[Value]> arrays
757        // - Values are independent of stack position
758        //
759        // Shuffle pattern: rot swap rot rot swap
760        // This was extracted from the list-reverse-helper function
761
762        unsafe {
763            let mut stack = make_stack(&[1, 2, 3, 4, 5]);
764
765            // Initial state: [5, 4, 3, 2, 1] (top to bottom)
766            //
767            // Apply the critical shuffle pattern:
768            stack = rot(stack);
769            // rot: ( a b c -- b c a )
770            // Before: [5, 4, 3, 2, 1]
771            //          ^  ^  ^
772            // After:  [3, 5, 4, 2, 1]
773
774            stack = swap(stack);
775            // swap: ( a b -- b a )
776            // Before: [3, 5, 4, 2, 1]
777            //          ^  ^
778            // After:  [5, 3, 4, 2, 1]
779
780            stack = rot(stack);
781            // rot: ( a b c -- b c a )
782            // Before: [5, 3, 4, 2, 1]
783            //          ^  ^  ^
784            // After:  [4, 5, 3, 2, 1]
785
786            stack = rot(stack);
787            // rot: ( a b c -- b c a )
788            // Before: [4, 5, 3, 2, 1]
789            //          ^  ^  ^
790            // After:  [3, 4, 5, 2, 1]
791
792            stack = swap(stack);
793            // swap: ( a b -- b a )
794            // Before: [3, 4, 5, 2, 1]
795            //          ^  ^
796            // After:  [4, 3, 5, 2, 1]
797
798            // Final state: [4, 3, 5, 2, 1] (top to bottom)
799            // Verify every value is intact - no corruption
800            assert_stack_ints(stack, &[4, 3, 5, 2, 1]);
801        }
802    }
803
804    #[test]
805    fn test_pick_0_is_dup() {
806        // pick(0) should be equivalent to dup
807        unsafe {
808            let mut stack = std::ptr::null_mut();
809            stack = push(stack, Value::Int(42));
810
811            stack = pick(stack, 0);
812
813            let (stack, val) = pop(stack);
814            assert_eq!(val, Value::Int(42));
815
816            let (stack, val) = pop(stack);
817            assert_eq!(val, Value::Int(42));
818
819            assert!(is_empty(stack));
820        }
821    }
822
823    #[test]
824    fn test_pick_1_is_over() {
825        // pick(1) should be equivalent to over
826        unsafe {
827            let mut stack = std::ptr::null_mut();
828            stack = push(stack, Value::Int(1));
829            stack = push(stack, Value::Int(2));
830
831            stack = pick(stack, 1);
832
833            let (stack, val) = pop(stack);
834            assert_eq!(val, Value::Int(1));
835
836            let (stack, val) = pop(stack);
837            assert_eq!(val, Value::Int(2));
838
839            let (stack, val) = pop(stack);
840            assert_eq!(val, Value::Int(1));
841
842            assert!(is_empty(stack));
843        }
844    }
845
846    #[test]
847    fn test_pick_deep() {
848        // Test picking from deeper in the stack
849        unsafe {
850            let mut stack = std::ptr::null_mut();
851            stack = push(stack, Value::Int(1));
852            stack = push(stack, Value::Int(2));
853            stack = push(stack, Value::Int(3));
854            stack = push(stack, Value::Int(4));
855            stack = push(stack, Value::Int(5));
856
857            // pick(3) should copy the 4th value (2) to the top
858            // Stack: [5, 4, 3, 2, 1]
859            //         0  1  2  3  <- indices
860            stack = pick(stack, 3); // [2, 5, 4, 3, 2, 1]
861
862            let (stack, val) = pop(stack);
863            assert_eq!(val, Value::Int(2));
864
865            let (stack, val) = pop(stack);
866            assert_eq!(val, Value::Int(5));
867
868            let (stack, val) = pop(stack);
869            assert_eq!(val, Value::Int(4));
870
871            let (stack, val) = pop(stack);
872            assert_eq!(val, Value::Int(3));
873
874            let (stack, val) = pop(stack);
875            assert_eq!(val, Value::Int(2));
876
877            let (stack, val) = pop(stack);
878            assert_eq!(val, Value::Int(1));
879
880            assert!(is_empty(stack));
881        }
882    }
883
884    #[test]
885    fn test_multifield_variant_survives_shuffle() {
886        // THE TEST THAT WOULD HAVE FAILED IN CEM2!
887        // Create a multi-field variant (simulating Cons(head, tail)),
888        // apply the critical shuffle pattern, and verify variant is intact
889        use crate::value::VariantData;
890
891        unsafe {
892            // Create a Cons-like variant: Cons(42, Nil)
893            // Tag 0 = Nil, Tag 1 = Cons
894            let nil = Value::Variant(Box::new(VariantData::new(0, vec![])));
895            let cons = Value::Variant(Box::new(VariantData::new(
896                1,
897                vec![Value::Int(42), nil.clone()],
898            )));
899
900            // Put the variant on the stack with some other values
901            let mut stack = std::ptr::null_mut();
902            stack = push(stack, Value::Int(100)); // Extra value
903            stack = push(stack, Value::Int(200)); // Extra value
904            stack = push(stack, cons.clone()); // Our variant
905            stack = push(stack, Value::Int(300)); // Extra value
906            stack = push(stack, Value::Int(400)); // Extra value
907
908            // Apply the CRITICAL SHUFFLE PATTERN that broke cem2
909            stack = rot(stack); // Rotate top 3
910            stack = swap(stack); // Swap top 2
911            stack = rot(stack); // Rotate top 3
912            stack = rot(stack); // Rotate top 3
913            stack = swap(stack); // Swap top 2
914
915            // Pop all values and find our variant
916            let mut found_variant = None;
917            while !is_empty(stack) {
918                let (rest, val) = pop(stack);
919                stack = rest;
920                if matches!(val, Value::Variant(_)) {
921                    found_variant = Some(val);
922                }
923            }
924
925            // Verify the variant is intact
926            assert!(found_variant.is_some(), "Variant was lost during shuffle!");
927
928            if let Some(Value::Variant(variant_data)) = found_variant {
929                assert_eq!(variant_data.tag, 1, "Variant tag corrupted!");
930                assert_eq!(
931                    variant_data.fields.len(),
932                    2,
933                    "Variant field count corrupted!"
934                );
935                assert_eq!(
936                    variant_data.fields[0],
937                    Value::Int(42),
938                    "First field corrupted!"
939                );
940
941                // Verify second field is Nil variant
942                if let Value::Variant(nil_data) = &variant_data.fields[1] {
943                    assert_eq!(nil_data.tag, 0, "Nested variant tag corrupted!");
944                    assert_eq!(nil_data.fields.len(), 0, "Nested variant should be empty!");
945                } else {
946                    panic!("Second field should be a Variant!");
947                }
948            }
949        }
950    }
951
952    #[test]
953    fn test_arbitrary_depth_operations() {
954        // Property: Operations should work at any stack depth
955        // Test with 100-deep stack, then manipulate top elements
956        unsafe {
957            let mut stack = std::ptr::null_mut();
958
959            // Build a 100-deep stack
960            for i in 0..100 {
961                stack = push(stack, Value::Int(i));
962            }
963
964            // Operations on top should work regardless of depth below
965            stack = dup(stack); // [99, 99, 98, 97, ..., 0]
966            stack = swap(stack); // [99, 99, 98, 97, ..., 0]
967            stack = over(stack); // [99, 99, 99, 98, 97, ..., 0]
968            stack = rot(stack); // [99, 99, 99, 98, 97, ..., 0]
969            stack = drop(stack); // [99, 99, 98, 97, ..., 0]
970
971            // Verify we can still access deep values with pick
972            stack = pick(stack, 50); // Should copy value at depth 50
973
974            // Pop and verify stack is still intact
975            let mut count = 0;
976            while !is_empty(stack) {
977                let (rest, _val) = pop(stack);
978                stack = rest;
979                count += 1;
980            }
981
982            // Started with 100, added 1 with dup, added 1 with over, dropped 1, picked 1
983            assert_eq!(count, 102);
984        }
985    }
986
987    #[test]
988    fn test_operation_composition_completeness() {
989        // Property: Any valid sequence of operations should succeed
990        // Test complex composition with multiple operation types
991        unsafe {
992            let mut stack = std::ptr::null_mut();
993
994            // Build initial state
995            for i in 1..=10 {
996                stack = push(stack, Value::Int(i));
997            }
998
999            // Complex composition: mix all operation types
1000            // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
1001            stack = dup(stack); // [10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
1002            stack = over(stack); // [10, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
1003            stack = rot(stack); // [10, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
1004            stack = swap(stack); // Top two swapped
1005            stack = nip(stack); // Remove second
1006            stack = tuck(stack); // Copy top below second
1007            stack = pick(stack, 5); // Copy from depth 5
1008            stack = drop(stack); // Remove top
1009
1010            // If we get here without panic, composition works
1011            // Verify stack still has values and is traversable
1012            let mut depth = 0;
1013            let mut current = stack;
1014            while !current.is_null() {
1015                depth += 1;
1016                current = (*current).next;
1017            }
1018
1019            assert!(depth > 0, "Stack should not be empty after operations");
1020        }
1021    }
1022
1023    #[test]
1024    fn test_pick_at_arbitrary_depths() {
1025        // Property: pick(n) should work for any n < stack_depth
1026        // Verify pick can access any depth without corruption
1027        unsafe {
1028            let mut stack = std::ptr::null_mut();
1029
1030            // Build stack with identifiable values
1031            for i in 0..50 {
1032                stack = push(stack, Value::Int(i * 10));
1033            }
1034
1035            // Pick from various depths and verify values
1036            // Stack is: [490, 480, 470, ..., 20, 10, 0]
1037            //            0    1    2         47  48  49
1038
1039            stack = pick(stack, 0); // Should get 490
1040            let (mut stack, val) = pop(stack);
1041            assert_eq!(val, Value::Int(490));
1042
1043            stack = pick(stack, 10); // Should get value at depth 10
1044            let (mut stack, val) = pop(stack);
1045            assert_eq!(val, Value::Int(390)); // (49-10) * 10
1046
1047            stack = pick(stack, 40); // Deep pick
1048            let (stack, val) = pop(stack);
1049            assert_eq!(val, Value::Int(90)); // (49-40) * 10
1050
1051            // After all these operations, stack should still be intact
1052            let mut count = 0;
1053            let mut current = stack;
1054            while !current.is_null() {
1055                count += 1;
1056                current = (*current).next;
1057            }
1058
1059            assert_eq!(count, 50, "Stack depth should be unchanged");
1060        }
1061    }
1062
1063    #[test]
1064    fn test_pick_op_equivalence_to_dup() {
1065        // pick_op(0) should behave like dup
1066        unsafe {
1067            let mut stack = std::ptr::null_mut();
1068            stack = push(stack, Value::Int(42));
1069            stack = push(stack, Value::Int(0)); // depth parameter
1070
1071            stack = pick_op(stack);
1072
1073            // Should have two 42s on stack
1074            let (stack, val1) = pop(stack);
1075            assert_eq!(val1, Value::Int(42));
1076            let (stack, val2) = pop(stack);
1077            assert_eq!(val2, Value::Int(42));
1078            assert!(stack.is_null());
1079        }
1080    }
1081
1082    #[test]
1083    fn test_pick_op_equivalence_to_over() {
1084        // pick_op(1) should behave like over
1085        unsafe {
1086            let mut stack = std::ptr::null_mut();
1087            stack = push(stack, Value::Int(10));
1088            stack = push(stack, Value::Int(20));
1089            stack = push(stack, Value::Int(1)); // depth parameter
1090
1091            stack = pick_op(stack);
1092
1093            // Should have: 10 20 10
1094            let (stack, val1) = pop(stack);
1095            assert_eq!(val1, Value::Int(10));
1096            let (stack, val2) = pop(stack);
1097            assert_eq!(val2, Value::Int(20));
1098            let (stack, val3) = pop(stack);
1099            assert_eq!(val3, Value::Int(10));
1100            assert!(stack.is_null());
1101        }
1102    }
1103
1104    #[test]
1105    fn test_pick_op_deep_access() {
1106        // Test accessing deeper stack values
1107        unsafe {
1108            let mut stack = std::ptr::null_mut();
1109            for i in 0..10 {
1110                stack = push(stack, Value::Int(i));
1111            }
1112            stack = push(stack, Value::Int(5)); // depth parameter
1113
1114            stack = pick_op(stack);
1115
1116            // Should have copied value at depth 5 (which is 4) to top
1117            let (stack, val) = pop(stack);
1118            assert_eq!(val, Value::Int(4));
1119
1120            // Stack should still have original 10 values
1121            let mut count = 0;
1122            let mut current = stack;
1123            while !current.is_null() {
1124                count += 1;
1125                current = (*current).next;
1126            }
1127            assert_eq!(count, 10);
1128        }
1129    }
1130
1131    // Note: Cannot test panic cases (negative depth, insufficient stack depth)
1132    // because extern "C" functions cannot be caught with #[should_panic].
1133    // These cases are validated at runtime with descriptive panic messages.
1134    // See examples/test-pick.seq for integration testing of valid cases.
1135
1136    #[test]
1137    fn test_roll_0_is_noop() {
1138        // roll(0) should be a no-op
1139        unsafe {
1140            let mut stack = std::ptr::null_mut();
1141            stack = push(stack, Value::Int(42));
1142            stack = push(stack, Value::Int(0)); // depth parameter
1143
1144            stack = roll(stack);
1145
1146            let (stack, val) = pop(stack);
1147            assert_eq!(val, Value::Int(42));
1148            assert!(stack.is_null());
1149        }
1150    }
1151
1152    #[test]
1153    fn test_roll_1_is_swap() {
1154        // roll(1) should be equivalent to swap
1155        unsafe {
1156            let mut stack = std::ptr::null_mut();
1157            stack = push(stack, Value::Int(1));
1158            stack = push(stack, Value::Int(2));
1159            stack = push(stack, Value::Int(1)); // depth parameter
1160
1161            stack = roll(stack);
1162
1163            // 1 2 -> 2 1
1164            let (stack, val1) = pop(stack);
1165            assert_eq!(val1, Value::Int(1));
1166            let (stack, val2) = pop(stack);
1167            assert_eq!(val2, Value::Int(2));
1168            assert!(stack.is_null());
1169        }
1170    }
1171
1172    #[test]
1173    fn test_roll_2_is_rot() {
1174        // roll(2) should be equivalent to rot
1175        unsafe {
1176            let mut stack = std::ptr::null_mut();
1177            stack = push(stack, Value::Int(1));
1178            stack = push(stack, Value::Int(2));
1179            stack = push(stack, Value::Int(3));
1180            stack = push(stack, Value::Int(2)); // depth parameter
1181
1182            stack = roll(stack);
1183
1184            // 1 2 3 -> 2 3 1 (rot brings 3rd item to top)
1185            let (stack, val1) = pop(stack);
1186            assert_eq!(val1, Value::Int(1));
1187            let (stack, val2) = pop(stack);
1188            assert_eq!(val2, Value::Int(3));
1189            let (stack, val3) = pop(stack);
1190            assert_eq!(val3, Value::Int(2));
1191            assert!(stack.is_null());
1192        }
1193    }
1194
1195    #[test]
1196    fn test_roll_3_rotates_4_items() {
1197        // roll(3) rotates 4 items: ( a b c d 3 -- b c d a )
1198        unsafe {
1199            let mut stack = std::ptr::null_mut();
1200            stack = push(stack, Value::Int(1)); // bottom
1201            stack = push(stack, Value::Int(2));
1202            stack = push(stack, Value::Int(3));
1203            stack = push(stack, Value::Int(4)); // top
1204            stack = push(stack, Value::Int(3)); // depth parameter
1205
1206            stack = roll(stack);
1207
1208            // 1 2 3 4 -> 2 3 4 1
1209            let (stack, val1) = pop(stack);
1210            assert_eq!(val1, Value::Int(1)); // was at depth 3, now on top
1211            let (stack, val2) = pop(stack);
1212            assert_eq!(val2, Value::Int(4));
1213            let (stack, val3) = pop(stack);
1214            assert_eq!(val3, Value::Int(3));
1215            let (stack, val4) = pop(stack);
1216            assert_eq!(val4, Value::Int(2));
1217            assert!(stack.is_null());
1218        }
1219    }
1220
1221    #[test]
1222    fn test_roll_deep() {
1223        // Test rolling with more items
1224        unsafe {
1225            let mut stack = std::ptr::null_mut();
1226            for i in 0..10 {
1227                stack = push(stack, Value::Int(i));
1228            }
1229            // Stack is: 9 8 7 6 5 4 3 2 1 0 (9 on top, 0 at bottom)
1230            stack = push(stack, Value::Int(5)); // depth parameter
1231
1232            stack = roll(stack);
1233
1234            // roll(5) brings item at depth 5 (which is 4) to top
1235            // Stack becomes: 4 9 8 7 6 5 3 2 1 0
1236            let (stack, val) = pop(stack);
1237            assert_eq!(val, Value::Int(4)); // 4 is now on top
1238
1239            // Verify rest of stack
1240            let (stack, val) = pop(stack);
1241            assert_eq!(val, Value::Int(9));
1242            let (stack, val) = pop(stack);
1243            assert_eq!(val, Value::Int(8));
1244            let (stack, val) = pop(stack);
1245            assert_eq!(val, Value::Int(7));
1246            let (stack, val) = pop(stack);
1247            assert_eq!(val, Value::Int(6));
1248            let (stack, val) = pop(stack);
1249            assert_eq!(val, Value::Int(5));
1250            // Note: 4 was moved to top, so now we have 3
1251            let (stack, val) = pop(stack);
1252            assert_eq!(val, Value::Int(3));
1253            let (stack, val) = pop(stack);
1254            assert_eq!(val, Value::Int(2));
1255            let (stack, val) = pop(stack);
1256            assert_eq!(val, Value::Int(1));
1257            let (stack, val) = pop(stack);
1258            assert_eq!(val, Value::Int(0));
1259            assert!(stack.is_null());
1260        }
1261    }
1262
1263    #[test]
1264    fn test_roll_with_extra_stack() {
1265        // Roll should only affect the top n+1 items, leaving rest unchanged
1266        unsafe {
1267            let mut stack = std::ptr::null_mut();
1268            stack = push(stack, Value::Int(100)); // This should not be affected
1269            stack = push(stack, Value::Int(1));
1270            stack = push(stack, Value::Int(2));
1271            stack = push(stack, Value::Int(3));
1272            stack = push(stack, Value::Int(4));
1273            stack = push(stack, Value::Int(3)); // depth parameter
1274
1275            stack = roll(stack);
1276
1277            // 1 2 3 4 -> 2 3 4 1, but 100 at bottom unchanged
1278            let (stack, val1) = pop(stack);
1279            assert_eq!(val1, Value::Int(1));
1280            let (stack, val2) = pop(stack);
1281            assert_eq!(val2, Value::Int(4));
1282            let (stack, val3) = pop(stack);
1283            assert_eq!(val3, Value::Int(3));
1284            let (stack, val4) = pop(stack);
1285            assert_eq!(val4, Value::Int(2));
1286            let (stack, val5) = pop(stack);
1287            assert_eq!(val5, Value::Int(100)); // Unchanged
1288            assert!(stack.is_null());
1289        }
1290    }
1291
1292    #[test]
1293    fn test_operations_preserve_stack_integrity() {
1294        // Property: After any operation, walking the stack should never loop
1295        // This catches next pointer corruption
1296        unsafe {
1297            let mut stack = std::ptr::null_mut();
1298
1299            for i in 0..20 {
1300                stack = push(stack, Value::Int(i));
1301            }
1302
1303            // Apply operations that manipulate next pointers heavily
1304            stack = swap(stack);
1305            stack = rot(stack);
1306            stack = swap(stack);
1307            stack = rot(stack);
1308            stack = over(stack);
1309            stack = tuck(stack);
1310
1311            // Walk stack and verify:
1312            // 1. No cycles (walk completes)
1313            // 2. No null mid-stack (all nodes valid until end)
1314            let mut visited = std::collections::HashSet::new();
1315            let mut current = stack;
1316            let mut count = 0;
1317
1318            while !current.is_null() {
1319                // Check for cycle
1320                assert!(
1321                    visited.insert(current as usize),
1322                    "Detected cycle in stack - next pointer corruption!"
1323                );
1324
1325                count += 1;
1326                current = (*current).next;
1327
1328                // Safety: prevent infinite loop in case of corruption
1329                assert!(
1330                    count < 1000,
1331                    "Stack walk exceeded reasonable depth - likely corrupted"
1332                );
1333            }
1334
1335            assert!(count > 0, "Stack should have elements");
1336        }
1337    }
1338
1339    #[test]
1340    fn test_nested_variants_with_deep_stacks() {
1341        // Property: Variants with nested variants survive deep stack operations
1342        // This combines depth + complex data structures
1343        use crate::value::VariantData;
1344
1345        unsafe {
1346            // Build deeply nested variant: Cons(1, Cons(2, Cons(3, Nil)))
1347            let nil = Value::Variant(Box::new(VariantData::new(0, vec![])));
1348            let cons3 = Value::Variant(Box::new(VariantData::new(1, vec![Value::Int(3), nil])));
1349            let cons2 = Value::Variant(Box::new(VariantData::new(1, vec![Value::Int(2), cons3])));
1350            let cons1 = Value::Variant(Box::new(VariantData::new(1, vec![Value::Int(1), cons2])));
1351
1352            // Put on deep stack
1353            let mut stack = std::ptr::null_mut();
1354            for i in 0..30 {
1355                stack = push(stack, Value::Int(i));
1356            }
1357            stack = push(stack, cons1.clone());
1358            for i in 30..60 {
1359                stack = push(stack, Value::Int(i));
1360            }
1361
1362            // Heavy shuffling in the region containing the variant
1363            for _ in 0..10 {
1364                stack = rot(stack);
1365                stack = swap(stack);
1366                stack = over(stack);
1367                stack = drop(stack);
1368            }
1369
1370            // Find and verify the nested variant is intact
1371            let mut found_variant = None;
1372            while !is_empty(stack) {
1373                let (rest, val) = pop(stack);
1374                stack = rest;
1375                if let Value::Variant(ref vdata) = val
1376                    && vdata.tag == 1
1377                    && vdata.fields.len() == 2
1378                    && let Value::Int(1) = vdata.fields[0]
1379                {
1380                    found_variant = Some(val);
1381                    break;
1382                }
1383            }
1384
1385            assert!(
1386                found_variant.is_some(),
1387                "Nested variant lost during deep stack operations"
1388            );
1389        }
1390    }
1391}