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