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