seq_runtime/
variant_ops.rs

1//! Variant field access operations for Seq
2//!
3//! Provides runtime functions for accessing variant fields, tags, and metadata.
4//! These are used to work with composite data created by operations like string-split.
5
6use crate::stack::{Stack, pop, push};
7use crate::value::Value;
8use std::sync::Arc;
9
10/// Get the number of fields in a variant
11///
12/// Stack effect: ( Variant -- Int )
13///
14/// # Safety
15/// Stack must have a Variant on top
16#[unsafe(no_mangle)]
17pub unsafe extern "C" fn patch_seq_variant_field_count(stack: Stack) -> Stack {
18    unsafe {
19        let (stack, value) = pop(stack);
20
21        match value {
22            Value::Variant(variant_data) => {
23                let count = variant_data.fields.len() as i64;
24                push(stack, Value::Int(count))
25            }
26            _ => panic!("variant-field-count: expected Variant, got {:?}", value),
27        }
28    }
29}
30
31/// Get the tag of a variant
32///
33/// Stack effect: ( Variant -- Symbol )
34///
35/// # Safety
36/// Stack must have a Variant on top
37#[unsafe(no_mangle)]
38pub unsafe extern "C" fn patch_seq_variant_tag(stack: Stack) -> Stack {
39    unsafe {
40        let (stack, value) = pop(stack);
41
42        match value {
43            Value::Variant(variant_data) => {
44                // Return the tag as a Symbol
45                push(stack, Value::Symbol(variant_data.tag.clone()))
46            }
47            _ => panic!("variant-tag: expected Variant, got {:?}", value),
48        }
49    }
50}
51
52/// Compare a symbol tag with a C string literal
53///
54/// Used by pattern matching codegen to dispatch on variant tags.
55/// The stack should have a Symbol on top (typically from variant-tag).
56/// Compares with the provided C string and pushes Bool result.
57///
58/// Stack effect: ( Symbol -- Bool )
59///
60/// # Safety
61/// - Stack must have a Symbol on top
62/// - c_str must be a valid null-terminated C string
63#[unsafe(no_mangle)]
64pub unsafe extern "C" fn patch_seq_symbol_eq_cstr(stack: Stack, c_str: *const i8) -> Stack {
65    use std::ffi::CStr;
66
67    unsafe {
68        let (stack, value) = pop(stack);
69        let symbol_str = match value {
70            Value::Symbol(s) => s,
71            _ => panic!("symbol_eq_cstr: expected Symbol, got {:?}", value),
72        };
73
74        let expected = CStr::from_ptr(c_str)
75            .to_str()
76            .expect("Invalid UTF-8 in variant name");
77
78        let is_equal = symbol_str.as_str() == expected;
79        push(stack, Value::Bool(is_equal))
80    }
81}
82
83/// Get a field from a variant at the given index
84///
85/// Stack effect: ( Variant Int -- Value )
86///
87/// Returns a clone of the field value at the specified index.
88/// Panics if index is out of bounds (this is a programming bug, not recoverable).
89/// Use variant.field-count to check bounds first if needed.
90///
91/// # Safety
92/// Stack must have a Variant and Int on top
93#[unsafe(no_mangle)]
94pub unsafe extern "C" fn patch_seq_variant_field_at(stack: Stack) -> Stack {
95    unsafe {
96        let (stack, index_val) = pop(stack);
97        let index = match index_val {
98            Value::Int(i) => i,
99            _ => panic!(
100                "variant-field-at: expected Int (index), got {:?}",
101                index_val
102            ),
103        };
104
105        if index < 0 {
106            panic!("variant-field-at: index cannot be negative: {}", index);
107        }
108
109        let (stack, variant_val) = pop(stack);
110
111        match variant_val {
112            Value::Variant(variant_data) => {
113                let idx = index as usize;
114                if idx >= variant_data.fields.len() {
115                    panic!(
116                        "variant-field-at: index {} out of bounds (variant has {} fields)",
117                        index,
118                        variant_data.fields.len()
119                    );
120                }
121
122                // Clone the field value and push it
123                let field = variant_data.fields[idx].clone();
124                push(stack, field)
125            }
126            _ => panic!("variant-field-at: expected Variant, got {:?}", variant_val),
127        }
128    }
129}
130
131// ============================================================================
132// Type-safe variant constructors with fixed arity
133// Now accept Symbol as tag for dynamic variant construction (SON support)
134// ============================================================================
135
136/// Create a variant with 0 fields (just a tag)
137///
138/// Stack effect: ( Symbol -- Variant )
139///
140/// # Safety
141/// Stack must have at least one Symbol (the tag) on top
142#[unsafe(no_mangle)]
143pub unsafe extern "C" fn patch_seq_make_variant_0(stack: Stack) -> Stack {
144    use crate::value::VariantData;
145
146    unsafe {
147        let (stack, tag_val) = pop(stack);
148        let tag = match tag_val {
149            Value::Symbol(s) => s,
150            _ => panic!("make-variant-0: expected Symbol (tag), got {:?}", tag_val),
151        };
152
153        let variant = Value::Variant(Arc::new(VariantData::new(tag, vec![])));
154        push(stack, variant)
155    }
156}
157
158/// Create a variant with 1 field
159///
160/// Stack effect: ( field1 Symbol -- Variant )
161///
162/// # Safety
163/// Stack must have field1 and Symbol tag on top
164#[unsafe(no_mangle)]
165pub unsafe extern "C" fn patch_seq_make_variant_1(stack: Stack) -> Stack {
166    use crate::value::VariantData;
167
168    unsafe {
169        let (stack, tag_val) = pop(stack);
170        let tag = match tag_val {
171            Value::Symbol(s) => s,
172            _ => panic!("make-variant-1: expected Symbol (tag), got {:?}", tag_val),
173        };
174
175        let (stack, field1) = pop(stack);
176        let variant = Value::Variant(Arc::new(VariantData::new(tag, vec![field1])));
177        push(stack, variant)
178    }
179}
180
181/// Create a variant with 2 fields
182///
183/// Stack effect: ( field1 field2 Symbol -- Variant )
184///
185/// # Safety
186/// Stack must have field1, field2, and Symbol tag on top
187#[unsafe(no_mangle)]
188pub unsafe extern "C" fn patch_seq_make_variant_2(stack: Stack) -> Stack {
189    use crate::value::VariantData;
190
191    unsafe {
192        let (stack, tag_val) = pop(stack);
193        let tag = match tag_val {
194            Value::Symbol(s) => s,
195            _ => panic!("make-variant-2: expected Symbol (tag), got {:?}", tag_val),
196        };
197
198        let (stack, field2) = pop(stack);
199        let (stack, field1) = pop(stack);
200        let variant = Value::Variant(Arc::new(VariantData::new(tag, vec![field1, field2])));
201        push(stack, variant)
202    }
203}
204
205/// Create a variant with 3 fields
206///
207/// Stack effect: ( field1 field2 field3 Symbol -- Variant )
208///
209/// # Safety
210/// Stack must have field1, field2, field3, and Symbol tag on top
211#[unsafe(no_mangle)]
212pub unsafe extern "C" fn patch_seq_make_variant_3(stack: Stack) -> Stack {
213    use crate::value::VariantData;
214
215    unsafe {
216        let (stack, tag_val) = pop(stack);
217        let tag = match tag_val {
218            Value::Symbol(s) => s,
219            _ => panic!("make-variant-3: expected Symbol (tag), got {:?}", tag_val),
220        };
221
222        let (stack, field3) = pop(stack);
223        let (stack, field2) = pop(stack);
224        let (stack, field1) = pop(stack);
225        let variant = Value::Variant(Arc::new(VariantData::new(
226            tag,
227            vec![field1, field2, field3],
228        )));
229        push(stack, variant)
230    }
231}
232
233/// Create a variant with 4 fields
234///
235/// Stack effect: ( field1 field2 field3 field4 Symbol -- Variant )
236///
237/// # Safety
238/// Stack must have field1, field2, field3, field4, and Symbol tag on top
239#[unsafe(no_mangle)]
240pub unsafe extern "C" fn patch_seq_make_variant_4(stack: Stack) -> Stack {
241    use crate::value::VariantData;
242
243    unsafe {
244        let (stack, tag_val) = pop(stack);
245        let tag = match tag_val {
246            Value::Symbol(s) => s,
247            _ => panic!("make-variant-4: expected Symbol (tag), got {:?}", tag_val),
248        };
249
250        let (stack, field4) = pop(stack);
251        let (stack, field3) = pop(stack);
252        let (stack, field2) = pop(stack);
253        let (stack, field1) = pop(stack);
254        let variant = Value::Variant(Arc::new(VariantData::new(
255            tag,
256            vec![field1, field2, field3, field4],
257        )));
258        push(stack, variant)
259    }
260}
261
262// Re-exports for internal use
263pub use patch_seq_make_variant_0 as make_variant_0;
264pub use patch_seq_make_variant_1 as make_variant_1;
265pub use patch_seq_make_variant_2 as make_variant_2;
266pub use patch_seq_make_variant_3 as make_variant_3;
267pub use patch_seq_make_variant_4 as make_variant_4;
268
269/// Append a value to a variant, returning a new variant
270///
271/// Stack effect: ( Variant Value -- Variant' )
272///
273/// Creates a new variant with the same tag as the input, but with
274/// the new value appended to its fields. The original variant is
275/// not modified (functional/immutable style).
276///
277/// Example: For arrays, `[1, 2] 3 variant-append` produces `[1, 2, 3]`
278/// Example: For objects, `{} "key" variant-append "val" variant-append` builds `{"key": "val"}`
279///
280/// # Safety
281/// Stack must have a Variant and a Value on top
282#[unsafe(no_mangle)]
283pub unsafe extern "C" fn patch_seq_variant_append(stack: Stack) -> Stack {
284    use crate::value::VariantData;
285
286    unsafe {
287        // Pop the value to append
288        let (stack, value) = pop(stack);
289
290        // Pop the variant
291        let (stack, variant_val) = pop(stack);
292
293        match variant_val {
294            Value::Variant(variant_data) => {
295                // Create new fields vector with the appended value
296                let mut new_fields = variant_data.fields.to_vec();
297                new_fields.push(value);
298
299                // Create new variant with same tag (clone the SeqString)
300                let new_variant = Value::Variant(Arc::new(VariantData::new(
301                    variant_data.tag.clone(),
302                    new_fields,
303                )));
304
305                push(stack, new_variant)
306            }
307            _ => panic!("variant-append: expected Variant, got {:?}", variant_val),
308        }
309    }
310}
311
312/// Get the last field from a variant
313///
314/// Stack effect: ( Variant -- Value )
315///
316/// Returns a clone of the last field. Panics if the variant has no fields.
317/// This is the "peek" operation for using variants as stacks.
318///
319/// # Safety
320/// Stack must have a Variant on top
321#[unsafe(no_mangle)]
322pub unsafe extern "C" fn patch_seq_variant_last(stack: Stack) -> Stack {
323    unsafe {
324        let (stack, variant_val) = pop(stack);
325
326        match variant_val {
327            Value::Variant(variant_data) => {
328                if variant_data.fields.is_empty() {
329                    panic!("variant-last: variant has no fields");
330                }
331
332                let last = variant_data.fields.last().unwrap().clone();
333                push(stack, last)
334            }
335            _ => panic!("variant-last: expected Variant, got {:?}", variant_val),
336        }
337    }
338}
339
340/// Get all but the last field from a variant
341///
342/// Stack effect: ( Variant -- Variant' )
343///
344/// Returns a new variant with the same tag but without the last field.
345/// Panics if the variant has no fields.
346/// This is the "pop" operation for using variants as stacks (without returning the popped value).
347///
348/// # Safety
349/// Stack must have a Variant on top
350#[unsafe(no_mangle)]
351pub unsafe extern "C" fn patch_seq_variant_init(stack: Stack) -> Stack {
352    use crate::value::VariantData;
353
354    unsafe {
355        let (stack, variant_val) = pop(stack);
356
357        match variant_val {
358            Value::Variant(variant_data) => {
359                if variant_data.fields.is_empty() {
360                    panic!("variant-init: variant has no fields");
361                }
362
363                // Create new fields without the last element
364                let new_fields: Vec<Value> =
365                    variant_data.fields[..variant_data.fields.len() - 1].to_vec();
366
367                let new_variant = Value::Variant(Arc::new(VariantData::new(
368                    variant_data.tag.clone(),
369                    new_fields,
370                )));
371
372                push(stack, new_variant)
373            }
374            _ => panic!("variant-init: expected Variant, got {:?}", variant_val),
375        }
376    }
377}
378
379/// Unpack a variant's fields onto the stack
380///
381/// Takes a field count as parameter and:
382/// - Pops the variant from the stack
383/// - Pushes each field (0..field_count) in order
384///
385/// Stack effect: ( Variant -- field0 field1 ... fieldN-1 )
386///
387/// Used by match expression codegen to extract variant fields.
388///
389/// # Safety
390/// Stack must have a Variant on top with at least `field_count` fields
391#[unsafe(no_mangle)]
392pub unsafe extern "C" fn patch_seq_unpack_variant(stack: Stack, field_count: i64) -> Stack {
393    unsafe {
394        let (mut stack, variant_val) = pop(stack);
395
396        match variant_val {
397            Value::Variant(variant_data) => {
398                let count = field_count as usize;
399                if count > variant_data.fields.len() {
400                    panic!(
401                        "unpack-variant: requested {} fields but variant only has {}",
402                        count,
403                        variant_data.fields.len()
404                    );
405                }
406
407                // Push each field in order (field0 first, then field1, etc.)
408                for i in 0..count {
409                    stack = push(stack, variant_data.fields[i].clone());
410                }
411
412                stack
413            }
414            _ => panic!("unpack-variant: expected Variant, got {:?}", variant_val),
415        }
416    }
417}
418
419// Public re-exports with short names for internal use
420pub use patch_seq_unpack_variant as unpack_variant;
421pub use patch_seq_variant_append as variant_append;
422pub use patch_seq_variant_field_at as variant_field_at;
423pub use patch_seq_variant_field_count as variant_field_count;
424pub use patch_seq_variant_init as variant_init;
425pub use patch_seq_variant_last as variant_last;
426pub use patch_seq_variant_tag as variant_tag;
427
428#[cfg(test)]
429mod tests {
430    use super::*;
431    use crate::seqstring::global_string;
432    use crate::value::VariantData;
433
434    #[test]
435    fn test_variant_field_count() {
436        unsafe {
437            // Create a variant with 3 fields
438            let variant = Value::Variant(Arc::new(VariantData::new(
439                global_string("TestTag".to_string()),
440                vec![Value::Int(10), Value::Int(20), Value::Int(30)],
441            )));
442
443            let stack = crate::stack::alloc_test_stack();
444            let stack = push(stack, variant);
445            let stack = variant_field_count(stack);
446
447            let (_stack, result) = pop(stack);
448            assert_eq!(result, Value::Int(3));
449        }
450    }
451
452    #[test]
453    fn test_variant_tag() {
454        unsafe {
455            // Create a variant with tag "MyTag"
456            let variant = Value::Variant(Arc::new(VariantData::new(
457                global_string("MyTag".to_string()),
458                vec![Value::Int(10)],
459            )));
460
461            let stack = crate::stack::alloc_test_stack();
462            let stack = push(stack, variant);
463            let stack = variant_tag(stack);
464
465            let (_stack, result) = pop(stack);
466            assert_eq!(result, Value::Symbol(global_string("MyTag".to_string())));
467        }
468    }
469
470    #[test]
471    fn test_variant_field_at() {
472        unsafe {
473            let str1 = global_string("hello".to_string());
474            let str2 = global_string("world".to_string());
475
476            // Create a variant with mixed fields
477            let variant = Value::Variant(Arc::new(VariantData::new(
478                global_string("TestTag".to_string()),
479                vec![
480                    Value::String(str1.clone()),
481                    Value::Int(42),
482                    Value::String(str2.clone()),
483                ],
484            )));
485
486            // Test accessing field 0
487            let stack = crate::stack::alloc_test_stack();
488            let stack = push(stack, variant.clone());
489            let stack = push(stack, Value::Int(0));
490            let stack = variant_field_at(stack);
491
492            let (_stack, result) = pop(stack);
493            assert_eq!(result, Value::String(str1.clone()));
494
495            // Test accessing field 1
496            let stack = push(stack, variant.clone());
497            let stack = push(stack, Value::Int(1));
498            let stack = variant_field_at(stack);
499
500            let (_stack, result) = pop(stack);
501            assert_eq!(result, Value::Int(42));
502
503            // Test accessing field 2
504            let stack = push(stack, variant.clone());
505            let stack = push(stack, Value::Int(2));
506            let stack = variant_field_at(stack);
507
508            let (_stack, result) = pop(stack);
509            assert_eq!(result, Value::String(str2));
510        }
511    }
512
513    #[test]
514    fn test_variant_field_count_empty() {
515        unsafe {
516            // Create a variant with no fields
517            let variant = Value::Variant(Arc::new(VariantData::new(
518                global_string("Empty".to_string()),
519                vec![],
520            )));
521
522            let stack = crate::stack::alloc_test_stack();
523            let stack = push(stack, variant);
524            let stack = variant_field_count(stack);
525
526            let (_stack, result) = pop(stack);
527            assert_eq!(result, Value::Int(0));
528        }
529    }
530
531    #[test]
532    fn test_make_variant_with_fields() {
533        unsafe {
534            // Create: 10 20 30 :Tag make-variant-3
535            // Should produce variant with tag :Tag and fields [10, 20, 30]
536            let stack = crate::stack::alloc_test_stack();
537            let stack = push(stack, Value::Int(10)); // field 0
538            let stack = push(stack, Value::Int(20)); // field 1
539            let stack = push(stack, Value::Int(30)); // field 2
540            let stack = push(stack, Value::Symbol(global_string("Tag".to_string()))); // tag
541
542            let stack = make_variant_3(stack);
543
544            let (_stack, result) = pop(stack);
545
546            match result {
547                Value::Variant(v) => {
548                    assert_eq!(v.tag.as_str(), "Tag");
549                    assert_eq!(v.fields.len(), 3);
550                    assert_eq!(v.fields[0], Value::Int(10));
551                    assert_eq!(v.fields[1], Value::Int(20));
552                    assert_eq!(v.fields[2], Value::Int(30));
553                }
554                _ => panic!("Expected Variant"),
555            }
556        }
557    }
558
559    #[test]
560    fn test_make_variant_empty() {
561        unsafe {
562            // Create: :None make-variant-0
563            // Should produce variant with tag :None and no fields
564            let stack = crate::stack::alloc_test_stack();
565            let stack = push(stack, Value::Symbol(global_string("None".to_string()))); // tag
566
567            let stack = make_variant_0(stack);
568
569            let (_stack, result) = pop(stack);
570
571            match result {
572                Value::Variant(v) => {
573                    assert_eq!(v.tag.as_str(), "None");
574                    assert_eq!(v.fields.len(), 0);
575                }
576                _ => panic!("Expected Variant"),
577            }
578        }
579    }
580
581    #[test]
582    fn test_make_variant_with_mixed_types() {
583        unsafe {
584            let s = global_string("hello".to_string());
585
586            // Create variant with mixed field types using make-variant-3
587            let stack = crate::stack::alloc_test_stack();
588            let stack = push(stack, Value::Int(42));
589            let stack = push(stack, Value::String(s.clone()));
590            let stack = push(stack, Value::Float(3.5));
591            let stack = push(stack, Value::Symbol(global_string("Mixed".to_string()))); // tag
592
593            let stack = make_variant_3(stack);
594
595            let (_stack, result) = pop(stack);
596
597            match result {
598                Value::Variant(v) => {
599                    assert_eq!(v.tag.as_str(), "Mixed");
600                    assert_eq!(v.fields.len(), 3);
601                    assert_eq!(v.fields[0], Value::Int(42));
602                    assert_eq!(v.fields[1], Value::String(s));
603                    assert_eq!(v.fields[2], Value::Float(3.5));
604                }
605                _ => panic!("Expected Variant"),
606            }
607        }
608    }
609
610    #[test]
611    fn test_variant_append() {
612        unsafe {
613            // Create an empty variant (tag Array)
614            let stack = crate::stack::alloc_test_stack();
615            let stack = push(stack, Value::Symbol(global_string("Array".to_string()))); // tag
616            let stack = make_variant_0(stack);
617
618            // Append a value
619            let stack = push(stack, Value::Int(42));
620            let stack = variant_append(stack);
621
622            // Check result
623            let (_stack, result) = pop(stack);
624            match result {
625                Value::Variant(v) => {
626                    assert_eq!(v.tag.as_str(), "Array");
627                    assert_eq!(v.fields.len(), 1);
628                    assert_eq!(v.fields[0], Value::Int(42));
629                }
630                _ => panic!("Expected Variant"),
631            }
632        }
633    }
634
635    #[test]
636    fn test_variant_append_multiple() {
637        unsafe {
638            // Create an empty variant (tag Object)
639            let stack = crate::stack::alloc_test_stack();
640            let stack = push(stack, Value::Symbol(global_string("Object".to_string()))); // tag
641            let stack = make_variant_0(stack);
642
643            // Append key
644            let key = global_string("name".to_string());
645            let stack = push(stack, Value::String(key.clone()));
646            let stack = variant_append(stack);
647
648            // Append value
649            let val = global_string("John".to_string());
650            let stack = push(stack, Value::String(val.clone()));
651            let stack = variant_append(stack);
652
653            // Check result - should have 2 fields
654            let (_stack, result) = pop(stack);
655            match result {
656                Value::Variant(v) => {
657                    assert_eq!(v.tag.as_str(), "Object");
658                    assert_eq!(v.fields.len(), 2);
659                    assert_eq!(v.fields[0], Value::String(key));
660                    assert_eq!(v.fields[1], Value::String(val));
661                }
662                _ => panic!("Expected Variant"),
663            }
664        }
665    }
666
667    #[test]
668    fn test_variant_last() {
669        unsafe {
670            // Create a variant with 3 fields
671            let variant = Value::Variant(Arc::new(VariantData::new(
672                global_string("List".to_string()),
673                vec![Value::Int(10), Value::Int(20), Value::Int(30)],
674            )));
675
676            let stack = crate::stack::alloc_test_stack();
677            let stack = push(stack, variant);
678            let stack = variant_last(stack);
679
680            let (_stack, result) = pop(stack);
681            assert_eq!(result, Value::Int(30));
682        }
683    }
684
685    #[test]
686    fn test_variant_init() {
687        unsafe {
688            // Create a variant with 3 fields
689            let variant = Value::Variant(Arc::new(VariantData::new(
690                global_string("Custom".to_string()),
691                vec![Value::Int(10), Value::Int(20), Value::Int(30)],
692            )));
693
694            let stack = crate::stack::alloc_test_stack();
695            let stack = push(stack, variant);
696            let stack = variant_init(stack);
697
698            let (_stack, result) = pop(stack);
699            match result {
700                Value::Variant(v) => {
701                    assert_eq!(v.tag.as_str(), "Custom"); // tag preserved
702                    assert_eq!(v.fields.len(), 2);
703                    assert_eq!(v.fields[0], Value::Int(10));
704                    assert_eq!(v.fields[1], Value::Int(20));
705                }
706                _ => panic!("Expected Variant"),
707            }
708        }
709    }
710
711    #[test]
712    fn test_variant_stack_operations() {
713        // Test using variant as a stack: append, append, last, init, last
714        unsafe {
715            // Create empty "stack" variant (tag Stack)
716            let stack = crate::stack::alloc_test_stack();
717            let stack = push(stack, Value::Symbol(global_string("Stack".to_string()))); // tag
718            let stack = make_variant_0(stack);
719
720            // Append 10
721            let stack = push(stack, Value::Int(10));
722            let stack = variant_append(stack);
723
724            // Append 20
725            let stack = push(stack, Value::Int(20));
726            let stack = variant_append(stack);
727
728            // Now have variant with [10, 20] on stack
729            // Dup and get last (should be 20)
730            let (stack, variant) = pop(stack);
731            let stack = push(stack, variant.clone());
732            let stack = push(stack, variant);
733            let stack = variant_last(stack);
734            let (stack, top) = pop(stack);
735            assert_eq!(top, Value::Int(20));
736
737            // Now use init to remove last element
738            let stack = variant_init(stack);
739
740            // Dup and get last (should now be 10)
741            let (stack, variant) = pop(stack);
742            let stack = push(stack, variant.clone());
743            let stack = push(stack, variant);
744            let stack = variant_last(stack);
745            let (stack, top) = pop(stack);
746            assert_eq!(top, Value::Int(10));
747
748            // Verify remaining variant has 1 field
749            let (_stack, result) = pop(stack);
750            match result {
751                Value::Variant(v) => {
752                    assert_eq!(v.fields.len(), 1);
753                    assert_eq!(v.fields[0], Value::Int(10));
754                }
755                _ => panic!("Expected Variant"),
756            }
757        }
758    }
759
760    #[test]
761    fn test_variant_clone_is_o1() {
762        // Regression test: Ensure deeply nested variants clone in O(1) time
763        // This would have been O(2^n) with Box before the Arc change
764        let mut variant = Value::Variant(Arc::new(VariantData::new(
765            global_string("Level0".to_string()),
766            vec![],
767        )));
768
769        // Build a deeply nested structure (100 levels)
770        for i in 0..100 {
771            variant = Value::Variant(Arc::new(VariantData::new(
772                global_string(format!("Level{}", i)),
773                vec![variant.clone()],
774            )));
775        }
776
777        // Clone should be O(1) - just incrementing Arc refcount
778        let start = std::time::Instant::now();
779        for _ in 0..1000 {
780            let _copy = variant.clone();
781        }
782        let elapsed = start.elapsed();
783
784        // 1000 clones of a 100-deep structure should take < 1ms with Arc
785        // With Box it would take seconds or longer
786        assert!(
787            elapsed.as_millis() < 10,
788            "Clone took {:?} - should be O(1) with Arc",
789            elapsed
790        );
791    }
792
793    #[test]
794    fn test_variant_arc_sharing() {
795        // Test that Arc properly shares data (refcount increases, not deep copy)
796        let inner = Value::Variant(Arc::new(VariantData::new(
797            global_string("Inner".to_string()),
798            vec![Value::Int(42)],
799        )));
800        let outer = Value::Variant(Arc::new(VariantData::new(
801            global_string("Outer".to_string()),
802            vec![inner.clone()],
803        )));
804
805        // Clone should share the same Arc, not deep copy
806        let outer_clone = outer.clone();
807
808        // Both should have the same inner value
809        match (&outer, &outer_clone) {
810            (Value::Variant(a), Value::Variant(b)) => {
811                // Arc::ptr_eq would be ideal but fields are Box<[Value]>
812                // Instead verify the values are equal (they share the same data)
813                assert_eq!(a.tag, b.tag);
814                assert_eq!(a.fields.len(), b.fields.len());
815            }
816            _ => panic!("Expected Variants"),
817        }
818    }
819
820    #[test]
821    fn test_variant_thread_safe_sharing() {
822        // Test that variants can be safely shared between threads
823        // This validates the Send + Sync implementation
824        use std::sync::Arc as StdArc;
825        use std::thread;
826
827        let variant = Value::Variant(Arc::new(VariantData::new(
828            global_string("ThreadSafe".to_string()),
829            vec![Value::Int(1), Value::Int(2), Value::Int(3)],
830        )));
831
832        // Wrap in Arc for thread sharing
833        let shared = StdArc::new(variant);
834
835        let handles: Vec<_> = (0..4)
836            .map(|_| {
837                let v = StdArc::clone(&shared);
838                thread::spawn(move || {
839                    // Access the variant from another thread
840                    match &*v {
841                        Value::Variant(data) => {
842                            assert_eq!(data.tag.as_str(), "ThreadSafe");
843                            assert_eq!(data.fields.len(), 3);
844                        }
845                        _ => panic!("Expected Variant"),
846                    }
847                })
848            })
849            .collect();
850
851        for h in handles {
852            h.join().expect("Thread panicked");
853        }
854    }
855}