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