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