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    use std::sync::Arc;
492
493    /// Test helper: Create a stack with integer values
494    fn make_stack(values: &[i64]) -> Stack {
495        unsafe {
496            let mut stack = std::ptr::null_mut();
497            for &val in values {
498                stack = push(stack, Value::Int(val));
499            }
500            stack
501        }
502    }
503
504    /// Test helper: Pop all values from stack and return as Vec
505    fn drain_stack(mut stack: Stack) -> Vec<Value> {
506        unsafe {
507            let mut values = Vec::new();
508            while !is_empty(stack) {
509                let (rest, val) = pop(stack);
510                stack = rest;
511                values.push(val);
512            }
513            values
514        }
515    }
516
517    /// Test helper: Assert stack contains expected integer values (top to bottom)
518    fn assert_stack_ints(stack: Stack, expected: &[i64]) {
519        let values = drain_stack(stack);
520        let ints: Vec<i64> = values
521            .into_iter()
522            .map(|v| match v {
523                Value::Int(n) => n,
524                _ => panic!("Expected Int, got {:?}", v),
525            })
526            .collect();
527        assert_eq!(ints, expected);
528    }
529
530    #[test]
531    fn test_push_pop() {
532        unsafe {
533            let stack = std::ptr::null_mut();
534            assert!(is_empty(stack));
535
536            let stack = push(stack, Value::Int(42));
537            assert!(!is_empty(stack));
538
539            let (stack, value) = pop(stack);
540            assert_eq!(value, Value::Int(42));
541            assert!(is_empty(stack));
542        }
543    }
544
545    #[test]
546    fn test_multiple_values() {
547        unsafe {
548            let mut stack = std::ptr::null_mut();
549
550            stack = push(stack, Value::Int(1));
551            stack = push(stack, Value::Int(2));
552            stack = push(stack, Value::Int(3));
553
554            let (stack, val) = pop(stack);
555            assert_eq!(val, Value::Int(3));
556
557            let (stack, val) = pop(stack);
558            assert_eq!(val, Value::Int(2));
559
560            let (stack, val) = pop(stack);
561            assert_eq!(val, Value::Int(1));
562
563            assert!(is_empty(stack));
564        }
565    }
566
567    #[test]
568    fn test_peek() {
569        unsafe {
570            let stack = push(std::ptr::null_mut(), Value::Int(42));
571            let peeked = peek(stack);
572            assert_eq!(*peeked, Value::Int(42));
573
574            // Value still there
575            let (stack, value) = pop(stack);
576            assert_eq!(value, Value::Int(42));
577            assert!(is_empty(stack));
578        }
579    }
580
581    #[test]
582    fn test_dup() {
583        unsafe {
584            let stack = push(std::ptr::null_mut(), Value::Int(42));
585            let stack = dup(stack);
586
587            // Should have two copies of 42
588            let (stack, val1) = pop(stack);
589            assert_eq!(val1, Value::Int(42));
590
591            let (stack, val2) = pop(stack);
592            assert_eq!(val2, Value::Int(42));
593
594            assert!(is_empty(stack));
595        }
596    }
597
598    #[test]
599    fn test_drop() {
600        unsafe {
601            let mut stack = std::ptr::null_mut();
602            stack = push(stack, Value::Int(1));
603            stack = push(stack, Value::Int(2));
604            stack = push(stack, Value::Int(3));
605
606            // Drop top value (3)
607            stack = drop(stack);
608
609            // Should have 2 on top now
610            let (stack, val) = pop(stack);
611            assert_eq!(val, Value::Int(2));
612
613            let (stack, val) = pop(stack);
614            assert_eq!(val, Value::Int(1));
615
616            assert!(is_empty(stack));
617        }
618    }
619
620    #[test]
621    fn test_swap() {
622        unsafe {
623            let mut stack = std::ptr::null_mut();
624            stack = push(stack, Value::Int(1));
625            stack = push(stack, Value::Int(2));
626
627            // Swap: 1 2 -> 2 1
628            stack = swap(stack);
629
630            let (stack, val) = pop(stack);
631            assert_eq!(val, Value::Int(1));
632
633            let (stack, val) = pop(stack);
634            assert_eq!(val, Value::Int(2));
635
636            assert!(is_empty(stack));
637        }
638    }
639
640    #[test]
641    fn test_composition() {
642        // Test: compose swap + drop + dup to verify operations work together
643        // Trace:
644        // Start:  [1]
645        // push 2: [2, 1]
646        // push 3: [3, 2, 1]
647        // swap:   [2, 3, 1]  (swap top two)
648        // drop:   [3, 1]     (remove top)
649        // dup:    [3, 3, 1]  (duplicate top)
650        unsafe {
651            let mut stack = make_stack(&[1, 2, 3]);
652
653            stack = swap(stack);
654            stack = drop(stack);
655            stack = dup(stack);
656
657            assert_stack_ints(stack, &[3, 3, 1]);
658        }
659    }
660
661    #[test]
662    fn test_over() {
663        // over: ( a b -- a b a )
664        unsafe {
665            let mut stack = std::ptr::null_mut();
666            stack = push(stack, Value::Int(1));
667            stack = push(stack, Value::Int(2));
668
669            stack = over(stack); // [1, 2, 1]
670
671            let (stack, val) = pop(stack);
672            assert_eq!(val, Value::Int(1));
673
674            let (stack, val) = pop(stack);
675            assert_eq!(val, Value::Int(2));
676
677            let (stack, val) = pop(stack);
678            assert_eq!(val, Value::Int(1));
679
680            assert!(is_empty(stack));
681        }
682    }
683
684    #[test]
685    fn test_rot() {
686        // rot: ( a b c -- b c a )
687        unsafe {
688            let mut stack = std::ptr::null_mut();
689            stack = push(stack, Value::Int(1));
690            stack = push(stack, Value::Int(2));
691            stack = push(stack, Value::Int(3));
692
693            stack = rot(stack); // [1, 3, 2]
694
695            let (stack, val) = pop(stack);
696            assert_eq!(val, Value::Int(1));
697
698            let (stack, val) = pop(stack);
699            assert_eq!(val, Value::Int(3));
700
701            let (stack, val) = pop(stack);
702            assert_eq!(val, Value::Int(2));
703
704            assert!(is_empty(stack));
705        }
706    }
707
708    #[test]
709    fn test_nip() {
710        // nip: ( a b -- b )
711        unsafe {
712            let mut stack = std::ptr::null_mut();
713            stack = push(stack, Value::Int(1));
714            stack = push(stack, Value::Int(2));
715
716            stack = nip(stack); // [2]
717
718            let (stack, val) = pop(stack);
719            assert_eq!(val, Value::Int(2));
720
721            assert!(is_empty(stack));
722        }
723    }
724
725    #[test]
726    fn test_tuck() {
727        // tuck: ( a b -- b a b )
728        unsafe {
729            let mut stack = std::ptr::null_mut();
730            stack = push(stack, Value::Int(1));
731            stack = push(stack, Value::Int(2));
732
733            stack = tuck(stack); // [2, 1, 2]
734
735            let (stack, val) = pop(stack);
736            assert_eq!(val, Value::Int(2));
737
738            let (stack, val) = pop(stack);
739            assert_eq!(val, Value::Int(1));
740
741            let (stack, val) = pop(stack);
742            assert_eq!(val, Value::Int(2));
743
744            assert!(is_empty(stack));
745        }
746    }
747
748    #[test]
749    fn test_critical_shuffle_pattern() {
750        // This is THE CRITICAL TEST that failed in cem2!
751        // In cem2, this shuffle pattern caused variant field corruption because
752        // StackCell.next pointers were used for BOTH stack linking AND variant fields.
753        // When the stack was shuffled, next pointers became stale, corrupting variants.
754        //
755        // In Seq, this CANNOT happen because:
756        // - StackNode.next is ONLY for stack structure
757        // - Variant fields are stored in Box<[Value]> arrays
758        // - Values are independent of stack position
759        //
760        // Shuffle pattern: rot swap rot rot swap
761        // This was extracted from the list-reverse-helper function
762
763        unsafe {
764            let mut stack = make_stack(&[1, 2, 3, 4, 5]);
765
766            // Initial state: [5, 4, 3, 2, 1] (top to bottom)
767            //
768            // Apply the critical shuffle pattern:
769            stack = rot(stack);
770            // rot: ( a b c -- b c a )
771            // Before: [5, 4, 3, 2, 1]
772            //          ^  ^  ^
773            // After:  [3, 5, 4, 2, 1]
774
775            stack = swap(stack);
776            // swap: ( a b -- b a )
777            // Before: [3, 5, 4, 2, 1]
778            //          ^  ^
779            // After:  [5, 3, 4, 2, 1]
780
781            stack = rot(stack);
782            // rot: ( a b c -- b c a )
783            // Before: [5, 3, 4, 2, 1]
784            //          ^  ^  ^
785            // After:  [4, 5, 3, 2, 1]
786
787            stack = rot(stack);
788            // rot: ( a b c -- b c a )
789            // Before: [4, 5, 3, 2, 1]
790            //          ^  ^  ^
791            // After:  [3, 4, 5, 2, 1]
792
793            stack = swap(stack);
794            // swap: ( a b -- b a )
795            // Before: [3, 4, 5, 2, 1]
796            //          ^  ^
797            // After:  [4, 3, 5, 2, 1]
798
799            // Final state: [4, 3, 5, 2, 1] (top to bottom)
800            // Verify every value is intact - no corruption
801            assert_stack_ints(stack, &[4, 3, 5, 2, 1]);
802        }
803    }
804
805    #[test]
806    fn test_pick_0_is_dup() {
807        // pick(0) should be equivalent to dup
808        unsafe {
809            let mut stack = std::ptr::null_mut();
810            stack = push(stack, Value::Int(42));
811
812            stack = pick(stack, 0);
813
814            let (stack, val) = pop(stack);
815            assert_eq!(val, Value::Int(42));
816
817            let (stack, val) = pop(stack);
818            assert_eq!(val, Value::Int(42));
819
820            assert!(is_empty(stack));
821        }
822    }
823
824    #[test]
825    fn test_pick_1_is_over() {
826        // pick(1) should be equivalent to over
827        unsafe {
828            let mut stack = std::ptr::null_mut();
829            stack = push(stack, Value::Int(1));
830            stack = push(stack, Value::Int(2));
831
832            stack = pick(stack, 1);
833
834            let (stack, val) = pop(stack);
835            assert_eq!(val, Value::Int(1));
836
837            let (stack, val) = pop(stack);
838            assert_eq!(val, Value::Int(2));
839
840            let (stack, val) = pop(stack);
841            assert_eq!(val, Value::Int(1));
842
843            assert!(is_empty(stack));
844        }
845    }
846
847    #[test]
848    fn test_pick_deep() {
849        // Test picking from deeper in the stack
850        unsafe {
851            let mut stack = std::ptr::null_mut();
852            stack = push(stack, Value::Int(1));
853            stack = push(stack, Value::Int(2));
854            stack = push(stack, Value::Int(3));
855            stack = push(stack, Value::Int(4));
856            stack = push(stack, Value::Int(5));
857
858            // pick(3) should copy the 4th value (2) to the top
859            // Stack: [5, 4, 3, 2, 1]
860            //         0  1  2  3  <- indices
861            stack = pick(stack, 3); // [2, 5, 4, 3, 2, 1]
862
863            let (stack, val) = pop(stack);
864            assert_eq!(val, Value::Int(2));
865
866            let (stack, val) = pop(stack);
867            assert_eq!(val, Value::Int(5));
868
869            let (stack, val) = pop(stack);
870            assert_eq!(val, Value::Int(4));
871
872            let (stack, val) = pop(stack);
873            assert_eq!(val, Value::Int(3));
874
875            let (stack, val) = pop(stack);
876            assert_eq!(val, Value::Int(2));
877
878            let (stack, val) = pop(stack);
879            assert_eq!(val, Value::Int(1));
880
881            assert!(is_empty(stack));
882        }
883    }
884
885    #[test]
886    fn test_multifield_variant_survives_shuffle() {
887        // THE TEST THAT WOULD HAVE FAILED IN CEM2!
888        // Create a multi-field variant (simulating Cons(head, tail)),
889        // apply the critical shuffle pattern, and verify variant is intact
890        use crate::value::VariantData;
891
892        unsafe {
893            // Create a Cons-like variant: Cons(42, Nil)
894            // Tag 0 = Nil, Tag 1 = Cons
895            let nil = Value::Variant(Arc::new(VariantData::new(0, vec![])));
896            let cons = Value::Variant(Arc::new(VariantData::new(
897                1,
898                vec![Value::Int(42), nil.clone()],
899            )));
900
901            // Put the variant on the stack with some other values
902            let mut stack = std::ptr::null_mut();
903            stack = push(stack, Value::Int(100)); // Extra value
904            stack = push(stack, Value::Int(200)); // Extra value
905            stack = push(stack, cons.clone()); // Our variant
906            stack = push(stack, Value::Int(300)); // Extra value
907            stack = push(stack, Value::Int(400)); // Extra value
908
909            // Apply the CRITICAL SHUFFLE PATTERN that broke cem2
910            stack = rot(stack); // Rotate top 3
911            stack = swap(stack); // Swap top 2
912            stack = rot(stack); // Rotate top 3
913            stack = rot(stack); // Rotate top 3
914            stack = swap(stack); // Swap top 2
915
916            // Pop all values and find our variant
917            let mut found_variant = None;
918            while !is_empty(stack) {
919                let (rest, val) = pop(stack);
920                stack = rest;
921                if matches!(val, Value::Variant(_)) {
922                    found_variant = Some(val);
923                }
924            }
925
926            // Verify the variant is intact
927            assert!(found_variant.is_some(), "Variant was lost during shuffle!");
928
929            if let Some(Value::Variant(variant_data)) = found_variant {
930                assert_eq!(variant_data.tag, 1, "Variant tag corrupted!");
931                assert_eq!(
932                    variant_data.fields.len(),
933                    2,
934                    "Variant field count corrupted!"
935                );
936                assert_eq!(
937                    variant_data.fields[0],
938                    Value::Int(42),
939                    "First field corrupted!"
940                );
941
942                // Verify second field is Nil variant
943                if let Value::Variant(nil_data) = &variant_data.fields[1] {
944                    assert_eq!(nil_data.tag, 0, "Nested variant tag corrupted!");
945                    assert_eq!(nil_data.fields.len(), 0, "Nested variant should be empty!");
946                } else {
947                    panic!("Second field should be a Variant!");
948                }
949            }
950        }
951    }
952
953    #[test]
954    fn test_arbitrary_depth_operations() {
955        // Property: Operations should work at any stack depth
956        // Test with 100-deep stack, then manipulate top elements
957        unsafe {
958            let mut stack = std::ptr::null_mut();
959
960            // Build a 100-deep stack
961            for i in 0..100 {
962                stack = push(stack, Value::Int(i));
963            }
964
965            // Operations on top should work regardless of depth below
966            stack = dup(stack); // [99, 99, 98, 97, ..., 0]
967            stack = swap(stack); // [99, 99, 98, 97, ..., 0]
968            stack = over(stack); // [99, 99, 99, 98, 97, ..., 0]
969            stack = rot(stack); // [99, 99, 99, 98, 97, ..., 0]
970            stack = drop(stack); // [99, 99, 98, 97, ..., 0]
971
972            // Verify we can still access deep values with pick
973            stack = pick(stack, 50); // Should copy value at depth 50
974
975            // Pop and verify stack is still intact
976            let mut count = 0;
977            while !is_empty(stack) {
978                let (rest, _val) = pop(stack);
979                stack = rest;
980                count += 1;
981            }
982
983            // Started with 100, added 1 with dup, added 1 with over, dropped 1, picked 1
984            assert_eq!(count, 102);
985        }
986    }
987
988    #[test]
989    fn test_operation_composition_completeness() {
990        // Property: Any valid sequence of operations should succeed
991        // Test complex composition with multiple operation types
992        unsafe {
993            let mut stack = std::ptr::null_mut();
994
995            // Build initial state
996            for i in 1..=10 {
997                stack = push(stack, Value::Int(i));
998            }
999
1000            // Complex composition: mix all operation types
1001            // [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
1002            stack = dup(stack); // [10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
1003            stack = over(stack); // [10, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
1004            stack = rot(stack); // [10, 10, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
1005            stack = swap(stack); // Top two swapped
1006            stack = nip(stack); // Remove second
1007            stack = tuck(stack); // Copy top below second
1008            stack = pick(stack, 5); // Copy from depth 5
1009            stack = drop(stack); // Remove top
1010
1011            // If we get here without panic, composition works
1012            // Verify stack still has values and is traversable
1013            let mut depth = 0;
1014            let mut current = stack;
1015            while !current.is_null() {
1016                depth += 1;
1017                current = (*current).next;
1018            }
1019
1020            assert!(depth > 0, "Stack should not be empty after operations");
1021        }
1022    }
1023
1024    #[test]
1025    fn test_pick_at_arbitrary_depths() {
1026        // Property: pick(n) should work for any n < stack_depth
1027        // Verify pick can access any depth without corruption
1028        unsafe {
1029            let mut stack = std::ptr::null_mut();
1030
1031            // Build stack with identifiable values
1032            for i in 0..50 {
1033                stack = push(stack, Value::Int(i * 10));
1034            }
1035
1036            // Pick from various depths and verify values
1037            // Stack is: [490, 480, 470, ..., 20, 10, 0]
1038            //            0    1    2         47  48  49
1039
1040            stack = pick(stack, 0); // Should get 490
1041            let (mut stack, val) = pop(stack);
1042            assert_eq!(val, Value::Int(490));
1043
1044            stack = pick(stack, 10); // Should get value at depth 10
1045            let (mut stack, val) = pop(stack);
1046            assert_eq!(val, Value::Int(390)); // (49-10) * 10
1047
1048            stack = pick(stack, 40); // Deep pick
1049            let (stack, val) = pop(stack);
1050            assert_eq!(val, Value::Int(90)); // (49-40) * 10
1051
1052            // After all these operations, stack should still be intact
1053            let mut count = 0;
1054            let mut current = stack;
1055            while !current.is_null() {
1056                count += 1;
1057                current = (*current).next;
1058            }
1059
1060            assert_eq!(count, 50, "Stack depth should be unchanged");
1061        }
1062    }
1063
1064    #[test]
1065    fn test_pick_op_equivalence_to_dup() {
1066        // pick_op(0) should behave like dup
1067        unsafe {
1068            let mut stack = std::ptr::null_mut();
1069            stack = push(stack, Value::Int(42));
1070            stack = push(stack, Value::Int(0)); // depth parameter
1071
1072            stack = pick_op(stack);
1073
1074            // Should have two 42s on stack
1075            let (stack, val1) = pop(stack);
1076            assert_eq!(val1, Value::Int(42));
1077            let (stack, val2) = pop(stack);
1078            assert_eq!(val2, Value::Int(42));
1079            assert!(stack.is_null());
1080        }
1081    }
1082
1083    #[test]
1084    fn test_pick_op_equivalence_to_over() {
1085        // pick_op(1) should behave like over
1086        unsafe {
1087            let mut stack = std::ptr::null_mut();
1088            stack = push(stack, Value::Int(10));
1089            stack = push(stack, Value::Int(20));
1090            stack = push(stack, Value::Int(1)); // depth parameter
1091
1092            stack = pick_op(stack);
1093
1094            // Should have: 10 20 10
1095            let (stack, val1) = pop(stack);
1096            assert_eq!(val1, Value::Int(10));
1097            let (stack, val2) = pop(stack);
1098            assert_eq!(val2, Value::Int(20));
1099            let (stack, val3) = pop(stack);
1100            assert_eq!(val3, Value::Int(10));
1101            assert!(stack.is_null());
1102        }
1103    }
1104
1105    #[test]
1106    fn test_pick_op_deep_access() {
1107        // Test accessing deeper stack values
1108        unsafe {
1109            let mut stack = std::ptr::null_mut();
1110            for i in 0..10 {
1111                stack = push(stack, Value::Int(i));
1112            }
1113            stack = push(stack, Value::Int(5)); // depth parameter
1114
1115            stack = pick_op(stack);
1116
1117            // Should have copied value at depth 5 (which is 4) to top
1118            let (stack, val) = pop(stack);
1119            assert_eq!(val, Value::Int(4));
1120
1121            // Stack should still have original 10 values
1122            let mut count = 0;
1123            let mut current = stack;
1124            while !current.is_null() {
1125                count += 1;
1126                current = (*current).next;
1127            }
1128            assert_eq!(count, 10);
1129        }
1130    }
1131
1132    // Note: Cannot test panic cases (negative depth, insufficient stack depth)
1133    // because extern "C" functions cannot be caught with #[should_panic].
1134    // These cases are validated at runtime with descriptive panic messages.
1135    // See examples/test-pick.seq for integration testing of valid cases.
1136
1137    #[test]
1138    fn test_roll_0_is_noop() {
1139        // roll(0) should be a no-op
1140        unsafe {
1141            let mut stack = std::ptr::null_mut();
1142            stack = push(stack, Value::Int(42));
1143            stack = push(stack, Value::Int(0)); // depth parameter
1144
1145            stack = roll(stack);
1146
1147            let (stack, val) = pop(stack);
1148            assert_eq!(val, Value::Int(42));
1149            assert!(stack.is_null());
1150        }
1151    }
1152
1153    #[test]
1154    fn test_roll_1_is_swap() {
1155        // roll(1) should be equivalent to swap
1156        unsafe {
1157            let mut stack = std::ptr::null_mut();
1158            stack = push(stack, Value::Int(1));
1159            stack = push(stack, Value::Int(2));
1160            stack = push(stack, Value::Int(1)); // depth parameter
1161
1162            stack = roll(stack);
1163
1164            // 1 2 -> 2 1
1165            let (stack, val1) = pop(stack);
1166            assert_eq!(val1, Value::Int(1));
1167            let (stack, val2) = pop(stack);
1168            assert_eq!(val2, Value::Int(2));
1169            assert!(stack.is_null());
1170        }
1171    }
1172
1173    #[test]
1174    fn test_roll_2_is_rot() {
1175        // roll(2) should be equivalent to rot
1176        unsafe {
1177            let mut stack = std::ptr::null_mut();
1178            stack = push(stack, Value::Int(1));
1179            stack = push(stack, Value::Int(2));
1180            stack = push(stack, Value::Int(3));
1181            stack = push(stack, Value::Int(2)); // depth parameter
1182
1183            stack = roll(stack);
1184
1185            // 1 2 3 -> 2 3 1 (rot brings 3rd item to top)
1186            let (stack, val1) = pop(stack);
1187            assert_eq!(val1, Value::Int(1));
1188            let (stack, val2) = pop(stack);
1189            assert_eq!(val2, Value::Int(3));
1190            let (stack, val3) = pop(stack);
1191            assert_eq!(val3, Value::Int(2));
1192            assert!(stack.is_null());
1193        }
1194    }
1195
1196    #[test]
1197    fn test_roll_3_rotates_4_items() {
1198        // roll(3) rotates 4 items: ( a b c d 3 -- b c d a )
1199        unsafe {
1200            let mut stack = std::ptr::null_mut();
1201            stack = push(stack, Value::Int(1)); // bottom
1202            stack = push(stack, Value::Int(2));
1203            stack = push(stack, Value::Int(3));
1204            stack = push(stack, Value::Int(4)); // top
1205            stack = push(stack, Value::Int(3)); // depth parameter
1206
1207            stack = roll(stack);
1208
1209            // 1 2 3 4 -> 2 3 4 1
1210            let (stack, val1) = pop(stack);
1211            assert_eq!(val1, Value::Int(1)); // was at depth 3, now on top
1212            let (stack, val2) = pop(stack);
1213            assert_eq!(val2, Value::Int(4));
1214            let (stack, val3) = pop(stack);
1215            assert_eq!(val3, Value::Int(3));
1216            let (stack, val4) = pop(stack);
1217            assert_eq!(val4, Value::Int(2));
1218            assert!(stack.is_null());
1219        }
1220    }
1221
1222    #[test]
1223    fn test_roll_deep() {
1224        // Test rolling with more items
1225        unsafe {
1226            let mut stack = std::ptr::null_mut();
1227            for i in 0..10 {
1228                stack = push(stack, Value::Int(i));
1229            }
1230            // Stack is: 9 8 7 6 5 4 3 2 1 0 (9 on top, 0 at bottom)
1231            stack = push(stack, Value::Int(5)); // depth parameter
1232
1233            stack = roll(stack);
1234
1235            // roll(5) brings item at depth 5 (which is 4) to top
1236            // Stack becomes: 4 9 8 7 6 5 3 2 1 0
1237            let (stack, val) = pop(stack);
1238            assert_eq!(val, Value::Int(4)); // 4 is now on top
1239
1240            // Verify rest of stack
1241            let (stack, val) = pop(stack);
1242            assert_eq!(val, Value::Int(9));
1243            let (stack, val) = pop(stack);
1244            assert_eq!(val, Value::Int(8));
1245            let (stack, val) = pop(stack);
1246            assert_eq!(val, Value::Int(7));
1247            let (stack, val) = pop(stack);
1248            assert_eq!(val, Value::Int(6));
1249            let (stack, val) = pop(stack);
1250            assert_eq!(val, Value::Int(5));
1251            // Note: 4 was moved to top, so now we have 3
1252            let (stack, val) = pop(stack);
1253            assert_eq!(val, Value::Int(3));
1254            let (stack, val) = pop(stack);
1255            assert_eq!(val, Value::Int(2));
1256            let (stack, val) = pop(stack);
1257            assert_eq!(val, Value::Int(1));
1258            let (stack, val) = pop(stack);
1259            assert_eq!(val, Value::Int(0));
1260            assert!(stack.is_null());
1261        }
1262    }
1263
1264    #[test]
1265    fn test_roll_with_extra_stack() {
1266        // Roll should only affect the top n+1 items, leaving rest unchanged
1267        unsafe {
1268            let mut stack = std::ptr::null_mut();
1269            stack = push(stack, Value::Int(100)); // This should not be affected
1270            stack = push(stack, Value::Int(1));
1271            stack = push(stack, Value::Int(2));
1272            stack = push(stack, Value::Int(3));
1273            stack = push(stack, Value::Int(4));
1274            stack = push(stack, Value::Int(3)); // depth parameter
1275
1276            stack = roll(stack);
1277
1278            // 1 2 3 4 -> 2 3 4 1, but 100 at bottom unchanged
1279            let (stack, val1) = pop(stack);
1280            assert_eq!(val1, Value::Int(1));
1281            let (stack, val2) = pop(stack);
1282            assert_eq!(val2, Value::Int(4));
1283            let (stack, val3) = pop(stack);
1284            assert_eq!(val3, Value::Int(3));
1285            let (stack, val4) = pop(stack);
1286            assert_eq!(val4, Value::Int(2));
1287            let (stack, val5) = pop(stack);
1288            assert_eq!(val5, Value::Int(100)); // Unchanged
1289            assert!(stack.is_null());
1290        }
1291    }
1292
1293    #[test]
1294    fn test_operations_preserve_stack_integrity() {
1295        // Property: After any operation, walking the stack should never loop
1296        // This catches next pointer corruption
1297        unsafe {
1298            let mut stack = std::ptr::null_mut();
1299
1300            for i in 0..20 {
1301                stack = push(stack, Value::Int(i));
1302            }
1303
1304            // Apply operations that manipulate next pointers heavily
1305            stack = swap(stack);
1306            stack = rot(stack);
1307            stack = swap(stack);
1308            stack = rot(stack);
1309            stack = over(stack);
1310            stack = tuck(stack);
1311
1312            // Walk stack and verify:
1313            // 1. No cycles (walk completes)
1314            // 2. No null mid-stack (all nodes valid until end)
1315            let mut visited = std::collections::HashSet::new();
1316            let mut current = stack;
1317            let mut count = 0;
1318
1319            while !current.is_null() {
1320                // Check for cycle
1321                assert!(
1322                    visited.insert(current as usize),
1323                    "Detected cycle in stack - next pointer corruption!"
1324                );
1325
1326                count += 1;
1327                current = (*current).next;
1328
1329                // Safety: prevent infinite loop in case of corruption
1330                assert!(
1331                    count < 1000,
1332                    "Stack walk exceeded reasonable depth - likely corrupted"
1333                );
1334            }
1335
1336            assert!(count > 0, "Stack should have elements");
1337        }
1338    }
1339
1340    #[test]
1341    fn test_nested_variants_with_deep_stacks() {
1342        // Property: Variants with nested variants survive deep stack operations
1343        // This combines depth + complex data structures
1344        use crate::value::VariantData;
1345
1346        unsafe {
1347            // Build deeply nested variant: Cons(1, Cons(2, Cons(3, Nil)))
1348            let nil = Value::Variant(Arc::new(VariantData::new(0, vec![])));
1349            let cons3 = Value::Variant(Arc::new(VariantData::new(1, vec![Value::Int(3), nil])));
1350            let cons2 = Value::Variant(Arc::new(VariantData::new(1, vec![Value::Int(2), cons3])));
1351            let cons1 = Value::Variant(Arc::new(VariantData::new(1, vec![Value::Int(1), cons2])));
1352
1353            // Put on deep stack
1354            let mut stack = std::ptr::null_mut();
1355            for i in 0..30 {
1356                stack = push(stack, Value::Int(i));
1357            }
1358            stack = push(stack, cons1.clone());
1359            for i in 30..60 {
1360                stack = push(stack, Value::Int(i));
1361            }
1362
1363            // Heavy shuffling in the region containing the variant
1364            for _ in 0..10 {
1365                stack = rot(stack);
1366                stack = swap(stack);
1367                stack = over(stack);
1368                stack = drop(stack);
1369            }
1370
1371            // Find and verify the nested variant is intact
1372            let mut found_variant = None;
1373            while !is_empty(stack) {
1374                let (rest, val) = pop(stack);
1375                stack = rest;
1376                if let Value::Variant(ref vdata) = val
1377                    && vdata.tag == 1
1378                    && vdata.fields.len() == 2
1379                    && let Value::Int(1) = vdata.fields[0]
1380                {
1381                    found_variant = Some(val);
1382                    break;
1383                }
1384            }
1385
1386            assert!(
1387                found_variant.is_some(),
1388                "Nested variant lost during deep stack operations"
1389            );
1390        }
1391    }
1392}