Skip to main content

haskelujah_runtime/
stack_chirho.rs

1// For God so loved the world that he gave his only begotten Son, that whoever
2// believes in him should not perish but have eternal life. — John 3:16
3
4//! # Evaluation stack
5//!
6//! The STG machine uses a stack of continuation frames. When evaluating
7//! an expression, the stack records what to do with the result:
8//!
9//! - `ApplyChirho` — apply the result to pending arguments
10//! - `UpdateChirho` — update a thunk with the result (lazy evaluation)
11//! - `CaseChirho` — scrutinise the result and choose a branch
12//! - `PrimOpChirho` — apply a primitive operation to the result
13
14use crate::value_chirho::{HeapAddrChirho, ValueChirho};
15
16/// A continuation frame on the evaluation stack.
17#[derive(Debug, Clone, PartialEq)]
18pub enum FrameChirho {
19    /// Apply the WHNF result to these pending arguments.
20    /// Used when we evaluate the function part of an application.
21    ApplyChirho { args_chirho: Vec<ValueChirho> },
22
23    /// Update the thunk at this address with the WHNF result.
24    /// Pushed before entering a thunk body.
25    UpdateChirho { thunk_addr_chirho: HeapAddrChirho },
26
27    /// Scrutinise the WHNF result. The `alts_chirho` index selects
28    /// which case branch to take based on the constructor tag.
29    /// The `default_chirho` is the fallback branch index.
30    CaseChirho {
31        /// Index into the code table for each constructor alt.
32        /// Keyed by DataConTagChirho.
33        alt_entries_chirho: Vec<(u16, u32)>,
34        /// Index into the code table for the default branch, if any.
35        default_entry_chirho: Option<u32>,
36        /// Saved arg registers from before the case (restored for alts
37        /// with no binders; prepended with constructor fields for alts
38        /// with binders).
39        saved_arg_regs_chirho: Vec<ValueChirho>,
40    },
41
42    /// Literal case dispatch waiting for the scrutinee to be forced.
43    /// Pushed when `CaseLitChirho` encounters a `HeapPtrChirho` scrutinee.
44    CaseLitChirho {
45        /// Literal alternatives: `(value, code_entry)`.
46        alt_entries_chirho: Vec<(ValueChirho, u32)>,
47        /// Default branch, if any.
48        default_entry_chirho: Option<u32>,
49        /// Saved arg registers to restore after forcing the scrutinee thunk.
50        saved_arg_regs_chirho: Vec<ValueChirho>,
51    },
52
53    /// A primitive operation waiting for its arguments to be forced.
54    PrimOpChirho {
55        op_chirho: PrimOpKindChirho,
56        /// Accumulated forced operands (already in WHNF / unboxed).
57        args_so_far_chirho: Vec<ValueChirho>,
58        /// Arguments still to be forced (may contain HeapPtrChirho thunks).
59        pending_args_chirho: Vec<ValueChirho>,
60        /// How many more arguments are needed.
61        remaining_chirho: u16,
62    },
63
64    /// Exception handler: catch# pushes this before evaluating the body.
65    /// If the body succeeds, the frame is popped and the result is returned.
66    /// If RuntimeErrorChirho propagates, the stack is unwound to this frame
67    /// and the handler is applied to the error message string.
68    CatchChirho {
69        /// Heap address of the handler closure (handler :: String -> IO a)
70        handler_addr_chirho: HeapAddrChirho,
71        /// Saved arg registers to restore when invoking the handler
72        saved_arg_regs_chirho: Vec<ValueChirho>,
73    },
74
75    /// try# frame: try# pushes this before evaluating the body IO action.
76    /// On success the result value is wrapped in a `Right` constructor on the
77    /// heap and returned.  On RuntimeErrorChirho the stack is unwound to this
78    /// frame and the error message string is wrapped in `Left`.
79    /// The resulting heap address (pointing to `Left msg` or `Right val`) is
80    /// returned as the IO (Either String a) value.
81    TryFrameChirho {
82        /// Saved arg registers to restore on the error path.
83        saved_arg_regs_chirho: Vec<ValueChirho>,
84    },
85}
86
87/// Kinds of primitive operations.
88#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
89pub enum PrimOpKindChirho {
90    AddIntChirho,
91    SubIntChirho,
92    MulIntChirho,
93    DivIntChirho,
94    ModIntChirho,
95    NegIntChirho,
96    EqIntChirho,
97    NeIntChirho,
98    LtIntChirho,
99    LeIntChirho,
100    GtIntChirho,
101    GeIntChirho,
102    AddFloatChirho,
103    SubFloatChirho,
104    MulFloatChirho,
105    DivFloatChirho,
106    LtFloatChirho,
107    GtFloatChirho,
108    EqCharChirho,
109    OrdCharChirho,
110    PutStrLnChirho,
111    PutStrChirho,
112    /// Print a single character: putChar :: Char -> IO ()
113    PutCharChirho,
114    /// Monadic bind (>>=) for IO: execute first action, pass result to continuation
115    BindIOChirho,
116    /// Monadic return for IO: wrap a value in IO
117    ReturnIOChirho,
118    /// Monadic then (>>) for IO: execute first action, ignore result, execute second
119    ThenIOChirho,
120    /// Read a line from stdin: getLine :: IO String
121    GetLineChirho,
122    /// Read a single character from stdin: getChar :: IO Char
123    GetCharChirho,
124    /// Read entire file contents: readFile :: FilePath -> IO String
125    ReadFileChirho,
126    /// Write string to file: writeFile :: FilePath -> String -> IO ()
127    WriteFileChirho,
128    /// Append string to file: appendFile :: FilePath -> String -> IO ()
129    AppendFileChirho,
130    /// Runtime error: error# :: String -> a (halts evaluation)
131    ErrorChirho,
132    /// Undefined value: undefined# :: a (halts evaluation)
133    UndefinedChirho,
134    /// Evaluate first arg to WHNF, return second: seq# :: a -> b -> b
135    SeqChirho,
136    /// Force to WHNF and return in IO: evaluate :: a -> IO a
137    EvaluateChirho,
138    /// Force to NF and return: force :: NFData a => a -> a
139    ForceChirho,
140    /// Identity function: id# :: a -> a (returns first arg unchanged)
141    IdChirho,
142    /// Convert an Int to its String representation: showInt# :: Int -> String
143    ShowIntChirho,
144    /// Convert a Bool to its String representation: showBool# :: Bool -> String
145    ShowBoolChirho,
146    /// Boolean negation: not# :: Bool -> Bool
147    NotBoolChirho,
148    /// String concatenation: ++# :: String -> String -> String
149    AppendStrChirho,
150    /// String equality: eqStr# :: String -> String -> Bool
151    EqStrChirho,
152    /// String less-than: ltStr# :: String -> String -> Bool
153    LtStrChirho,
154    /// String compare: compareStr# :: String -> String -> Ordering
155    CompareStrChirho,
156    /// String length: lengthStr# :: String -> Int
157    LengthStrChirho,
158    /// Show a String: showStr# :: String -> String (wraps in quotes)
159    ShowStrChirho,
160    /// Float equality: eqFloat# :: Double -> Double -> Bool
161    EqFloatChirho,
162    /// Negate float: negateFloat# :: Double -> Double
163    NegFloatChirho,
164    /// Show a Double: showFloat# :: Double -> String
165    ShowFloatChirho,
166    /// Reciprocal: recip# :: Double -> Double
167    RecipFloatChirho,
168    /// enumFromTo# :: Int -> Int -> [Int] — build a list [from..to]
169    EnumFromToChirho,
170    /// enumFrom# :: Int -> [Int] — build an infinite list [from..] (capped at from+10000)
171    EnumFromChirho,
172    /// enumFromThen# :: Int -> Int -> [Int] — build infinite list [from,then..] (capped)
173    EnumFromThenChirho,
174    /// enumFromThenTo# :: Int -> Int -> Int -> [Int] — build [from,then..to]
175    EnumFromThenToChirho,
176    /// showList# :: [a] -> String — format a list for display
177    ShowListChirho,
178    /// readInt# :: String -> Int — parse an integer from a string
179    ReadIntChirho,
180    /// readFloat# :: String -> Double — parse a double from a string
181    ReadFloatChirho,
182    /// readBool# :: String -> Bool — parse a boolean from a string
183    ReadBoolChirho,
184    /// wordsStr# :: String -> [String] — split on whitespace
185    WordsStrChirho,
186    /// unwordsStr# :: [String] -> String — join with single spaces
187    UnwordsStrChirho,
188    /// takeStr# :: Int -> String -> String — take first N chars
189    TakeStrChirho,
190    /// dropStr# :: Int -> String -> String — drop first N chars
191    DropStrChirho,
192    /// concatStr# :: [String] -> String — concatenate all strings in a list
193    ConcatStrChirho,
194    /// intercalateStr# :: String -> [String] -> String — join with separator
195    IntercalateStrChirho,
196    /// fromIntegral# :: Int -> Double — convert integer to floating point
197    FromIntegralChirho,
198    /// ceiling# :: Double -> Int — round up to nearest integer
199    CeilingChirho,
200    /// floor# :: Double -> Int — round down to nearest integer
201    FloorChirho,
202    /// round# :: Double -> Int — round to nearest integer (banker's rounding)
203    RoundChirho,
204    /// truncate# :: Double -> Int — truncate toward zero
205    TruncateChirho,
206    /// compare# :: Int -> Int -> Ordering — compare two integers
207    CompareIntChirho,
208    /// compareChar# :: Char -> Char -> Ordering — compare two characters
209    CompareCharChirho,
210    /// compareFloat# :: Double -> Double -> Ordering — compare two doubles
211    CompareFloatChirho,
212    /// quot# :: Int -> Int -> Int — truncating integer division toward zero
213    QuotIntChirho,
214    /// rem# :: Int -> Int -> Int — remainder of truncating division
215    RemIntChirho,
216    /// chr# :: Int -> Char — convert code point to character
217    ChrChirho,
218    /// ord# :: Char -> Int — convert character to code point
219    OrdChirho,
220    /// isDigit# :: Char -> Bool — test if character is a digit
221    IsDigitChirho,
222    /// isAlpha# :: Char -> Bool — test if character is alphabetic
223    IsAlphaChirho,
224    /// isAlphaNum# :: Char -> Bool — test if character is alphanumeric
225    IsAlphaNumChirho,
226    /// isUpper# :: Char -> Bool — test if character is uppercase
227    IsUpperChirho,
228    /// isLower# :: Char -> Bool — test if character is lowercase
229    IsLowerChirho,
230    /// isSpace# :: Char -> Bool — test if character is whitespace
231    IsSpaceChirho,
232    /// toLower# :: Char -> Char — convert to lowercase
233    ToLowerChirho,
234    /// toUpper# :: Char -> Char — convert to uppercase
235    ToUpperChirho,
236    /// digitToInt# :: Char -> Int — convert digit char to int
237    DigitToIntChirho,
238    /// intToDigit# :: Int -> Char — convert int to digit char
239    IntToDigitChirho,
240
241    // ── Floating math primops ──
242    /// sin# :: Double -> Double
243    SinFloatChirho,
244    /// cos# :: Double -> Double
245    CosFloatChirho,
246    /// tan# :: Double -> Double
247    TanFloatChirho,
248    /// asin# :: Double -> Double
249    AsinFloatChirho,
250    /// acos# :: Double -> Double
251    AcosFloatChirho,
252    /// atan# :: Double -> Double
253    AtanFloatChirho,
254    /// exp# :: Double -> Double
255    ExpFloatChirho,
256    /// log# :: Double -> Double
257    LogFloatChirho,
258    /// sqrt# :: Double -> Double
259    SqrtFloatChirho,
260    /// pi# :: -> Double (nullary constant, dispatched as unary ignoring arg)
261    PiFloatChirho,
262
263    // ── Power/exponentiation primops ──
264    /// ^# :: Int -> Int -> Int — integer exponentiation
265    PowIntChirho,
266    /// **# :: Double -> Double -> Double — floating-point exponentiation
267    PowFloatChirho,
268
269    // ── Show for compound types ──
270    /// showMaybe# :: Maybe a -> String — show a Maybe value
271    ShowMaybeChirho,
272    /// showTuple2# :: (a, b) -> String — show a 2-tuple
273    ShowTuple2Chirho,
274    /// showEither# :: Either a b -> String — show an Either value
275    ShowEitherChirho,
276    /// showOrdering# :: Ordering -> String — show an Ordering value
277    ShowOrderingChirho,
278
279    // ── Additional IO operations ──
280    /// getContents :: IO String — read all stdin as a single String
281    GetContentsChirho,
282    /// interact :: (String -> String) -> IO () — apply function to stdin, write result to stdout
283    InteractChirho,
284    /// print :: Show a => a -> IO () — show value then putStrLn
285    PrintChirho,
286
287    // ── String operations ──
288    /// lines# :: String -> [String] — split string by newlines
289    LinesChirho,
290    /// unlines# :: [String] -> String — join strings with newlines
291    UnlinesChirho,
292
293    // ── IORef operations ──
294    /// newIORef# :: a -> IO (IORef a) — create a new mutable reference
295    NewIORefChirho,
296    /// readIORef# :: IORef a -> IO a — read the current value
297    ReadIORefChirho,
298    /// writeIORef# :: IORef a -> a -> IO () — overwrite the value
299    WriteIORefChirho,
300    /// modifyIORef# :: IORef a -> (a -> a) -> IO () — apply function to value
301    ModifyIORefChirho,
302
303    // ── ST monad operations ──
304    /// newSTRef# :: a -> ST s (STRef s a) — create a new mutable reference in ST
305    NewSTRefChirho,
306    /// readSTRef# :: STRef s a -> ST s a — read a mutable reference in ST
307    ReadSTRefChirho,
308    /// writeSTRef# :: STRef s a -> a -> ST s () — write to a mutable reference in ST
309    WriteSTRefChirho,
310    /// modifySTRef# :: STRef s a -> (a -> a) -> ST s () — apply function to ST ref value
311    ModifySTRefChirho,
312    /// runST# :: (forall s. ST s a) -> a — execute an ST computation purely
313    RunSTChirho,
314
315    // ── Exception handling ──
316    /// catch# :: IO a -> (String -> IO a) -> IO a — run with exception handler
317    CatchChirho,
318    /// throw# :: String -> a — throw an exception (synonym for error)
319    ThrowChirho,
320    /// try# :: IO a -> IO (Either String a) — run and return Left on error, Right on success
321    TryChirho,
322    /// bracket# :: IO a -> (a -> IO b) -> (a -> IO c) -> IO c
323    /// Acquire resource, run body, release resource even on exception.
324    BracketChirho,
325    /// finally# :: IO a -> IO b -> IO a
326    /// Run action and cleanup, ensuring cleanup runs even on exception.
327    FinallyChirho,
328
329    // ── Data.Map runtime primops ──
330    /// mapEmpty# :: Map k v — create an empty map
331    MapEmptyChirho,
332    /// mapSingleton# :: k -> v -> Map k v — create a single-element map
333    MapSingletonChirho,
334    /// mapInsert# :: k -> v -> Map k v -> Map k v — insert or overwrite key
335    MapInsertChirho,
336    /// mapLookup# :: k -> Map k v -> Maybe v — lookup by key (returns heap Maybe)
337    MapLookupChirho,
338    /// mapDelete# :: k -> Map k v -> Map k v — remove key from map
339    MapDeleteChirho,
340    /// mapMember# :: k -> Map k v -> Bool — test if key is in map
341    MapMemberChirho,
342    /// mapSize# :: Map k v -> Int — number of key-value pairs
343    MapSizeChirho,
344    /// mapFromList# :: [(k, v)] -> Map k v — build map from association list
345    MapFromListChirho,
346    /// mapToList# :: Map k v -> [(k, v)] — convert map to sorted association list
347    MapToListChirho,
348    /// mapKeys# :: Map k v -> [k] — extract sorted keys as a list
349    MapKeysChirho,
350    /// mapElems# :: Map k v -> [v] — extract values (in key order) as a list
351    MapElemsChirho,
352    /// mapNull# :: Map k v -> Bool — test if map is empty
353    MapNullChirho,
354    /// mapMap# :: (v -> w) -> Map k v -> Map k w — apply function to all values
355    MapMapChirho,
356    /// mapFoldlWithKey# :: (b -> k -> v -> b) -> b -> Map k v -> b — strict left fold
357    MapFoldlWithKeyChirho,
358    /// mapFoldrWithKey# :: (k -> v -> b -> b) -> b -> Map k v -> b — right fold
359    MapFoldrWithKeyChirho,
360    /// mapUnion# :: Map k v -> Map k v -> Map k v — left-biased union
361    MapUnionChirho,
362    /// mapDifference# :: Map k v -> Map k w -> Map k v — keys in left not in right
363    MapDifferenceChirho,
364    /// mapIntersection# :: Map k v -> Map k w -> Map k v — keys in both maps
365    MapIntersectionChirho,
366    /// mapInsertWith# :: (v -> v -> v) -> k -> v -> Map k v -> Map k v — insert with combiner
367    MapInsertWithChirho,
368    /// mapFindWithDefault# :: v -> k -> Map k v -> v — lookup with default value
369    MapFindWithDefaultChirho,
370    /// mapAdjust# :: (v -> v) -> k -> Map k v -> Map k v — update value at key
371    MapAdjustChirho,
372    /// mapUnionWith# :: (v -> v -> v) -> Map k v -> Map k v -> Map k v — union with combiner
373    MapUnionWithChirho,
374    /// mapFilter# :: (v -> Bool) -> Map k v -> Map k v — keep entries matching predicate
375    MapFilterChirho,
376    /// mapFilterWithKey# :: (k -> v -> Bool) -> Map k v -> Map k v — keep entries matching key-value predicate
377    MapFilterWithKeyChirho,
378
379    // ── Data.Set runtime primops ──
380    /// setEmpty# :: Set a — create an empty set
381    SetEmptyChirho,
382    /// setSingleton# :: a -> Set a — create a single-element set
383    SetSingletonChirho,
384    /// setInsert# :: Ord a => a -> Set a -> Set a — insert element (dedup)
385    SetInsertChirho,
386    /// setMember# :: Ord a => a -> Set a -> Bool — test if element is in set
387    SetMemberChirho,
388    /// setDelete# :: Ord a => a -> Set a -> Set a — remove element
389    SetDeleteChirho,
390    /// setSize# :: Set a -> Int — number of elements
391    SetSizeChirho,
392    /// setFromList# :: Ord a => [a] -> Set a — build set from list
393    SetFromListChirho,
394    /// setToList# :: Set a -> [a] — convert set to sorted list
395    SetToListChirho,
396    /// setUnion# :: Ord a => Set a -> Set a -> Set a — union of two sets
397    SetUnionChirho,
398    /// setIntersection# :: Ord a => Set a -> Set a -> Set a — elements in both sets
399    SetIntersectionChirho,
400    /// setDifference# :: Ord a => Set a -> Set a -> Set a — elements in first not in second
401    SetDifferenceChirho,
402    /// setNull# :: Set a -> Bool — test if set is empty
403    SetNullChirho,
404    /// setMap# :: Ord b => (a -> b) -> Set a -> Set b — map over set elements
405    SetMapChirho,
406    /// setFilter# :: (a -> Bool) -> Set a -> Set a — filter elements
407    SetFilterChirho,
408    /// setFoldr# :: (a -> b -> b) -> b -> Set a -> b — right fold over set
409    SetFoldrChirho,
410
411    // ── STM (Software Transactional Memory) operations ──
412    /// newTVar# :: a -> STM (TVar a) — create a new TVar with initial value
413    NewTVarChirho,
414    /// readTVar# :: TVar a -> STM a — read the current value of a TVar
415    ReadTVarChirho,
416    /// writeTVar# :: TVar a -> a -> STM () — write a new value to a TVar
417    WriteTVarChirho,
418    /// atomically# :: STM a -> IO a — execute an STM transaction
419    AtomicallyChirho,
420    /// retry# :: STM a — abort and retry transaction when a TVar changes
421    RetryChirho,
422    /// orElse# :: STM a -> STM a -> STM a — try first, if it retries try second
423    OrElseChirho,
424}
425
426/// The evaluation stack.
427#[derive(Debug)]
428pub struct StackChirho {
429    frames_chirho: Vec<FrameChirho>,
430    /// Maximum stack depth reached (for statistics).
431    max_depth_chirho: usize,
432}
433
434impl StackChirho {
435    /// Create a new empty stack.
436    pub fn new_chirho() -> Self {
437        Self {
438            frames_chirho: Vec::new(),
439            max_depth_chirho: 0,
440        }
441    }
442
443    /// Push a continuation frame.
444    pub fn push_chirho(&mut self, frame_chirho: FrameChirho) {
445        self.frames_chirho.push(frame_chirho);
446        if self.frames_chirho.len() > self.max_depth_chirho {
447            self.max_depth_chirho = self.frames_chirho.len();
448        }
449    }
450
451    /// Pop the top continuation frame, if any.
452    pub fn pop_chirho(&mut self) -> Option<FrameChirho> {
453        self.frames_chirho.pop()
454    }
455
456    /// Peek at the top frame without popping.
457    pub fn peek_chirho(&self) -> Option<&FrameChirho> {
458        self.frames_chirho.last()
459    }
460
461    /// Whether the stack is empty.
462    pub fn is_empty_chirho(&self) -> bool {
463        self.frames_chirho.is_empty()
464    }
465
466    /// Current stack depth.
467    pub fn depth_chirho(&self) -> usize {
468        self.frames_chirho.len()
469    }
470
471    /// Maximum stack depth reached during execution.
472    pub fn max_depth_chirho(&self) -> usize {
473        self.max_depth_chirho
474    }
475
476    /// Borrow all frames (for GC root extraction).
477    pub fn frames_chirho(&self) -> &[FrameChirho] {
478        &self.frames_chirho
479    }
480
481    /// Unwind the stack looking for a `CatchChirho` or `TryFrameChirho` frame.
482    /// Returns the frame if found, popping all frames above it (including the
483    /// handler frame itself).  Returns `None` if no such frame exists.
484    pub fn unwind_to_catch_chirho(&mut self) -> Option<FrameChirho> {
485        // Search from the top of the stack downward for either handler kind.
486        let catch_pos_chirho = self.frames_chirho.iter().rposition(|f_chirho| {
487            matches!(
488                f_chirho,
489                FrameChirho::CatchChirho { .. } | FrameChirho::TryFrameChirho { .. }
490            )
491        });
492        if let Some(pos_chirho) = catch_pos_chirho {
493            // Pop everything above and including the handler frame
494            self.frames_chirho.truncate(pos_chirho + 1);
495            self.frames_chirho.pop() // the handler frame itself
496        } else {
497            None
498        }
499    }
500}
501
502#[cfg(test)]
503mod tests_chirho {
504    use super::*;
505
506    #[test]
507    fn push_pop_chirho() {
508        let mut stack_chirho = StackChirho::new_chirho();
509        assert!(stack_chirho.is_empty_chirho());
510
511        stack_chirho.push_chirho(FrameChirho::UpdateChirho {
512            thunk_addr_chirho: HeapAddrChirho(0),
513        });
514        assert_eq!(stack_chirho.depth_chirho(), 1);
515
516        stack_chirho.push_chirho(FrameChirho::ApplyChirho {
517            args_chirho: vec![ValueChirho::IntChirho(42)],
518        });
519        assert_eq!(stack_chirho.depth_chirho(), 2);
520
521        let top_chirho = stack_chirho.pop_chirho().unwrap();
522        assert!(matches!(top_chirho, FrameChirho::ApplyChirho { .. }));
523
524        let next_chirho = stack_chirho.pop_chirho().unwrap();
525        assert!(matches!(next_chirho, FrameChirho::UpdateChirho { .. }));
526
527        assert!(stack_chirho.is_empty_chirho());
528    }
529
530    #[test]
531    fn max_depth_tracked_chirho() {
532        let mut stack_chirho = StackChirho::new_chirho();
533        for _ in 0..5 {
534            stack_chirho.push_chirho(FrameChirho::UpdateChirho {
535                thunk_addr_chirho: HeapAddrChirho(0),
536            });
537        }
538        stack_chirho.pop_chirho();
539        stack_chirho.pop_chirho();
540
541        assert_eq!(stack_chirho.depth_chirho(), 3);
542        assert_eq!(stack_chirho.max_depth_chirho(), 5);
543    }
544
545    #[test]
546    fn peek_chirho() {
547        let mut stack_chirho = StackChirho::new_chirho();
548        assert!(stack_chirho.peek_chirho().is_none());
549
550        stack_chirho.push_chirho(FrameChirho::ApplyChirho {
551            args_chirho: vec![],
552        });
553        assert!(matches!(
554            stack_chirho.peek_chirho(),
555            Some(FrameChirho::ApplyChirho { .. })
556        ));
557        // peek doesn't consume
558        assert_eq!(stack_chirho.depth_chirho(), 1);
559    }
560
561    #[test]
562    fn case_frame_chirho() {
563        let frame_chirho = FrameChirho::CaseChirho {
564            alt_entries_chirho: vec![(0, 100), (1, 101)],
565            default_entry_chirho: Some(999),
566            saved_arg_regs_chirho: vec![],
567        };
568        if let FrameChirho::CaseChirho {
569            alt_entries_chirho,
570            default_entry_chirho,
571            ..
572        } = &frame_chirho
573        {
574            assert_eq!(alt_entries_chirho.len(), 2);
575            assert_eq!(*default_entry_chirho, Some(999));
576        }
577    }
578
579    #[test]
580    fn primop_frame_chirho() {
581        let frame_chirho = FrameChirho::PrimOpChirho {
582            op_chirho: PrimOpKindChirho::AddIntChirho,
583            args_so_far_chirho: vec![ValueChirho::IntChirho(10)],
584            pending_args_chirho: vec![ValueChirho::IntChirho(5)],
585            remaining_chirho: 1,
586        };
587        if let FrameChirho::PrimOpChirho {
588            op_chirho,
589            args_so_far_chirho,
590            pending_args_chirho,
591            remaining_chirho,
592        } = &frame_chirho
593        {
594            assert_eq!(*op_chirho, PrimOpKindChirho::AddIntChirho);
595            assert_eq!(args_so_far_chirho.len(), 1);
596            assert_eq!(pending_args_chirho.len(), 1);
597            assert_eq!(*remaining_chirho, 1);
598        }
599    }
600}