wasm/validation/
validation_stack.rs

1use core::iter;
2
3use alloc::vec;
4use alloc::vec::Vec;
5
6use crate::{
7    core::reader::types::{FuncType, ResultType},
8    NumType, RefType, ValType, ValidationError,
9};
10
11#[derive(Debug, PartialEq, Eq)]
12pub struct ValidationStack {
13    stack: Vec<ValidationStackEntry>,
14    // TODO hide implementation
15    pub ctrl_stack: Vec<CtrlStackEntry>,
16}
17
18impl ValidationStack {
19    /// Initialize a new ValidationStack to validate a block of type [] -> []
20    pub fn new() -> Self {
21        Self {
22            stack: Vec::new(),
23            ctrl_stack: vec![CtrlStackEntry {
24                label_info: LabelInfo::Untyped,
25                block_ty: FuncType {
26                    params: ResultType {
27                        valtypes: Vec::new(),
28                    },
29                    returns: ResultType {
30                        valtypes: Vec::new(),
31                    },
32                },
33                height: 0,
34                unreachable: false,
35            }],
36        }
37    }
38
39    /// Initialize a new ValidationStack to validate a block of type `block_ty`
40    pub(super) fn new_for_func(block_ty: FuncType) -> Self {
41        Self {
42            stack: Vec::new(),
43            ctrl_stack: vec![CtrlStackEntry {
44                label_info: LabelInfo::Func {
45                    stps_to_backpatch: Vec::new(),
46                },
47                block_ty,
48                height: 0,
49                unreachable: false,
50            }],
51        }
52    }
53
54    pub fn len(&self) -> usize {
55        self.stack.len()
56    }
57
58    pub fn push_valtype(&mut self, valtype: ValType) {
59        self.stack.push(ValidationStackEntry::Val(valtype));
60    }
61
62    /// Similar to [`ValidationStack::pop_valtype`], because it pops a value from the stack,
63    /// but more public and doesn't actually return the popped value.
64    pub(super) fn drop_val(&mut self) -> Result<(), ValidationError> {
65        self.pop_valtype()
66            .map_err(|_| ValidationError::ExpectedAnOperand)?;
67        Ok(())
68    }
69
70    /// Mark the current control block as unreachable, removing all of the types pushed to the stack since the current control block was entered.
71    /// pop operations from the stack will yield `Ok(ValidationStackEntry::Bottom)` if the stack height is the same as the height when this control
72    /// block was entered.
73    ///
74    /// Returns `Ok(())` if called during validation of a control block. `Returns Err(Error::ValidationCtrlStackEmpty)` if no control block context is found
75    /// in the control block stack.
76    pub(super) fn make_unspecified(&mut self) -> Result<(), ValidationError> {
77        let last_ctrl_stack_entry = self
78            .ctrl_stack
79            .last_mut()
80            .ok_or(ValidationError::ValidationCtrlStackEmpty)?;
81        last_ctrl_stack_entry.unreachable = true;
82        self.stack.truncate(last_ctrl_stack_entry.height);
83        Ok(())
84    }
85
86    /// Pop a [`ValidationStackEntry`] from the [`ValidationStack`]
87    ///
88    /// # Returns
89    ///
90    /// - Returns `Ok(_)` with the former top-most [`ValidationStackEntry`] inside, if the stack had
91    ///   at least one element pushed after the current control block is entered. May also return `Ok(ValidationStackEntry::Bottom)`
92    ///   if `make_unspecified` is called within the current control block.
93    /// - Returns `Err(_)` otherwise.
94    fn pop_valtype(&mut self) -> Result<ValidationStackEntry, ValidationError> {
95        // TODO unwrapping might not be the best option
96        // TODO ugly
97        // TODO return type should be Result<()> maybe?
98        let last_ctrl_stack_entry = self.ctrl_stack.last().unwrap();
99        assert!(self.stack.len() >= last_ctrl_stack_entry.height);
100        if last_ctrl_stack_entry.height == self.stack.len() {
101            if last_ctrl_stack_entry.unreachable {
102                Ok(ValidationStackEntry::Bottom)
103            } else {
104                Err(ValidationError::EndInvalidValueStack)
105            }
106        } else {
107            //empty stack is covered with above check
108            self.stack
109                .pop()
110                .ok_or(ValidationError::EndInvalidValueStack)
111        }
112    }
113
114    /// Attempt popping `Valtype::RefType(expected_ty)` from type stack.
115    ///
116    /// # Returns
117    ///
118    /// - Returns `Ok(())` if `Valtype::RefType(expected_ty)` unifies to the item returned by `pop_valtype` operation and `Err(_)` otherwise.
119    pub fn assert_pop_ref_type(
120        &mut self,
121        expected_ty: Option<RefType>,
122    ) -> Result<(), ValidationError> {
123        match self.pop_valtype()? {
124            ValidationStackEntry::Val(ValType::RefType(ref_type)) => {
125                expected_ty.map_or(Ok(()), |ty| {
126                    (ty == ref_type)
127                        .then_some(())
128                        .ok_or(ValidationError::DifferentRefTypes(ref_type, ty))
129                })
130            }
131            ValidationStackEntry::Val(v) => Err(ValidationError::ExpectedARefType(v)),
132            ValidationStackEntry::Bottom => Ok(()),
133        }
134    }
135
136    /// Attempt popping expected_ty from type stack.
137    ///
138    /// # Returns
139    ///
140    /// - Returns `Ok(())` if expected_ty unifies to the item returned by `pop_valtype` operation and `Err(_)` otherwise.
141    pub fn assert_pop_val_type(&mut self, expected_ty: ValType) -> Result<(), ValidationError> {
142        match self.pop_valtype()? {
143            ValidationStackEntry::Val(ty) => (ty == expected_ty)
144                .then_some(())
145                .ok_or(ValidationError::InvalidValidationStackValType(Some(ty))),
146            ValidationStackEntry::Bottom => Ok(()),
147        }
148    }
149
150    // private fns to shut the borrow checker up when calling methods with mutable ref to self with immutable ref to self arguments
151    // TODO ugly but I can't come up with anything else better
152    fn assert_val_types_on_top_with_custom_stacks(
153        stack: &mut Vec<ValidationStackEntry>,
154        ctrl_stack: &[CtrlStackEntry],
155        expected_val_types: &[ValType],
156        unify_to_expected_types: bool,
157    ) -> Result<(), ValidationError> {
158        let last_ctrl_stack_entry = ctrl_stack
159            .last()
160            .ok_or(ValidationError::ValidationCtrlStackEmpty)?;
161        let stack_len = stack.len();
162
163        let rev_iterator = expected_val_types.iter().rev().enumerate();
164        for (i, expected_ty) in rev_iterator {
165            if stack_len - last_ctrl_stack_entry.height <= i {
166                if last_ctrl_stack_entry.unreachable {
167                    if unify_to_expected_types {
168                        // Unify(t2*,expected_val_types) := [t2* expected_val_types]
169                        stack.splice(
170                            stack_len - i..stack_len - i,
171                            expected_val_types[..expected_val_types.len() - i]
172                                .iter()
173                                .map(|ty| ValidationStackEntry::Val(*ty)),
174                        );
175                    } else {
176                        stack.splice(
177                            stack_len - i..stack_len - i,
178                            iter::repeat(ValidationStackEntry::Bottom)
179                                .take(expected_val_types.len() - i),
180                        );
181                    }
182                    return Ok(());
183                } else {
184                    return Err(ValidationError::EndInvalidValueStack);
185                }
186            }
187
188            // the above height check ensures this access is valid
189            let actual_ty = &mut stack[stack_len - i - 1];
190
191            match actual_ty {
192                ValidationStackEntry::Val(actual_val_ty) => {
193                    if *actual_val_ty != *expected_ty {
194                        return Err(ValidationError::EndInvalidValueStack);
195                    }
196                }
197                ValidationStackEntry::Bottom => {
198                    // Bottom will always unify to the expected ty
199                    if unify_to_expected_types {
200                        *actual_ty = ValidationStackEntry::Val(*expected_ty);
201                    }
202                }
203            }
204        }
205
206        Ok(())
207    }
208
209    fn assert_val_types_with_custom_stacks(
210        stack: &mut Vec<ValidationStackEntry>,
211        ctrl_stack: &[CtrlStackEntry],
212        expected_val_types: &[ValType],
213        unify_to_expected_types: bool,
214    ) -> Result<(), ValidationError> {
215        ValidationStack::assert_val_types_on_top_with_custom_stacks(
216            stack,
217            ctrl_stack,
218            expected_val_types,
219            unify_to_expected_types,
220        )?;
221        //if we can assert types in the above there is a last ctrl stack entry, this access is valid.
222        let last_ctrl_stack_entry = &ctrl_stack[ctrl_stack.len() - 1];
223        if stack.len() == last_ctrl_stack_entry.height + expected_val_types.len() {
224            Ok(())
225        } else {
226            Err(ValidationError::EndInvalidValueStack)
227        }
228    }
229
230    /// Assert that the types retrieved from the type stack by `pop_valtype` unify to `expected_val_types`, and
231    /// after this operation the type stack would be the same as the first time the current control block is entered.
232    /// This method will unify the types on the stack to the expected valtypes if `unify_to_expected_types` is set.
233    /// Any occurence of an error may leave the stack in an invalid state.
234    ///
235    /// # Returns
236    ///
237    /// - `Ok(_)`, the tail of the stack unifies to the `expected_val_types`
238    /// - `Err(_)` otherwise
239    ///
240    pub(super) fn assert_val_types_on_top(
241        &mut self,
242        expected_val_types: &[ValType],
243        unify_to_expected_types: bool,
244    ) -> Result<(), ValidationError> {
245        ValidationStack::assert_val_types_on_top_with_custom_stacks(
246            &mut self.stack,
247            &self.ctrl_stack,
248            expected_val_types,
249            unify_to_expected_types,
250        )
251    }
252
253    /// Assert that the types retrieved from the type stack by `pop_valtype` unify to `expected_val_types`.
254    /// This method will unify the types on the stack to the expected valtypes if `unify_to_expected_types` is set.
255    /// Any occurence of an error may leave the stack in an invalid state.
256    ///
257    /// # Returns
258    ///
259    /// - `Ok(_)`, the tail of the stack unifies to the `expected_val_types`
260    /// - `Err(_)` otherwise
261    ///
262    pub fn assert_val_types(
263        &mut self,
264        expected_val_types: &[ValType],
265        unify_to_expected_types: bool,
266    ) -> Result<(), ValidationError> {
267        ValidationStack::assert_val_types_with_custom_stacks(
268            &mut self.stack,
269            &self.ctrl_stack,
270            expected_val_types,
271            unify_to_expected_types,
272        )
273    }
274
275    /// Call `assert_val_types_on_top` for the label signature of the `label_idx`th outer control block (0 corresponds to the current control block).
276    /// Label signature of all controi blocks are the output signature of the control blocks except for the Loop block. For Loop blocks, it is the input signature.
277    /// This method will unify the types on the stack to the expected valtypes if `unify_to_expected_types` is set.
278    ///
279    /// # Returns
280    ///
281    /// - `Ok(_)`, the tail of the stack unifies to the label signature of the  `label_idx`th outer control block
282    /// - `Err(_)` otherwise
283    ///
284    pub fn assert_val_types_of_label_jump_types_on_top(
285        &mut self,
286        label_idx: usize,
287        unify_to_expected_types: bool,
288    ) -> Result<(), ValidationError> {
289        let index_of_label_in_ctrl_stack = self
290            .ctrl_stack
291            .len()
292            .checked_sub(label_idx)
293            .and_then(|i| i.checked_sub(1));
294
295        let label_types = index_of_label_in_ctrl_stack
296            .and_then(|index_of_label_in_ctrl_stack| {
297                self.ctrl_stack.get(index_of_label_in_ctrl_stack)
298            })
299            .ok_or(ValidationError::InvalidLabelIdx(label_idx))?
300            .label_types();
301        ValidationStack::assert_val_types_on_top_with_custom_stacks(
302            &mut self.stack,
303            &self.ctrl_stack,
304            label_types,
305            unify_to_expected_types,
306        )
307    }
308
309    /// Signal to this struct that a new control block is entered, and calls `assert_val_types_on_top` with the input signature of the new control block.
310    /// This method will unify the types on the stack to the expected valtypes if `unify_to_expected_types` is set.
311    ///
312    /// # Returns
313    ///
314    /// - `Ok(_)`, the tail of the stack unifies to the input signature of the  new control block
315    /// - `Err(_)` otherwise
316    ///
317    pub fn assert_push_ctrl(
318        &mut self,
319        label_info: LabelInfo,
320        block_ty: FuncType,
321        unify_to_expected_types: bool,
322    ) -> Result<(), ValidationError> {
323        self.assert_val_types_on_top(&block_ty.params.valtypes, unify_to_expected_types)?;
324        let height = self.stack.len() - block_ty.params.valtypes.len();
325        self.ctrl_stack.push(CtrlStackEntry {
326            label_info,
327            block_ty,
328            height,
329            unreachable: false,
330        });
331        Ok(())
332    }
333
334    /// Signal to this struct that the current control block is exited, and calls `assert_val_types_on_top` with the output signature of the new control block.
335    /// This method will unify the types on the stack to the expected valtypes if `unify_to_expected_types` is set.
336    ///
337    /// # Returns
338    ///
339    /// - `Ok(_)`, the tail of the stack unifies to the output signature of the current control block
340    /// - `Err(_)` otherwise
341    ///
342    pub fn assert_pop_ctrl(
343        &mut self,
344        unify_to_expected_types: bool,
345    ) -> Result<(LabelInfo, FuncType), ValidationError> {
346        let return_types = &self
347            .ctrl_stack
348            .last()
349            .ok_or(ValidationError::ValidationCtrlStackEmpty)?
350            .block_ty
351            .returns
352            .valtypes;
353        ValidationStack::assert_val_types_with_custom_stacks(
354            &mut self.stack,
355            &self.ctrl_stack,
356            return_types,
357            unify_to_expected_types,
358        )?;
359
360        //if we can assert types in the above there is a last ctrl stack entry, this access is valid.
361        let last_ctrl_stack_entry = self.ctrl_stack.pop().unwrap();
362        Ok((
363            last_ctrl_stack_entry.label_info,
364            last_ctrl_stack_entry.block_ty,
365        ))
366    }
367
368    /// Validate the `SELECT` instruction within the current control block. Returns OK(()) on success, Err(_) otherwise.
369    pub fn validate_polymorphic_select(&mut self) -> Result<(), ValidationError> {
370        //SELECT instruction has the type signature
371        //[t t i32] -> [t] where t unifies to a NumType(_) or VecType
372
373        self.assert_pop_val_type(ValType::NumType(crate::NumType::I32))?;
374
375        let first_arg = self.pop_valtype()?;
376        let second_arg = self.pop_valtype()?;
377
378        let unified_type = second_arg
379            .unify(&first_arg)
380            .ok_or(ValidationError::InvalidValidationStackValType(None))?;
381
382        // t must unify to a NumType(_) or VecType
383        if !(unified_type.unifies_to(&ValidationStackEntry::Val(ValType::NumType(NumType::I32)))
384            || unified_type.unifies_to(&ValidationStackEntry::Val(ValType::NumType(NumType::F32)))
385            || unified_type.unifies_to(&ValidationStackEntry::Val(ValType::NumType(NumType::I64)))
386            || unified_type.unifies_to(&ValidationStackEntry::Val(ValType::NumType(NumType::F64)))
387            || unified_type.unifies_to(&ValidationStackEntry::Val(ValType::VecType)))
388        {
389            return Err(ValidationError::InvalidValidationStackValType(None));
390        }
391
392        self.stack.push(unified_type);
393        Ok(())
394    }
395}
396
397/// corresponds to `opdtype` <https://webassembly.github.io/spec/core/valid/instructions.html#instructions>
398#[derive(Clone, Debug, PartialEq, Eq)]
399pub enum ValidationStackEntry {
400    Val(ValType),
401    Bottom,
402}
403
404impl ValidationStackEntry {
405    /// corresponds to whether `(self, other)` is a member of "matches" (<=) relation defined in <https://webassembly.github.io/spec/core/valid/instructions.html#instructions>
406    fn unifies_to(&self, other: &ValidationStackEntry) -> bool {
407        match self {
408            ValidationStackEntry::Bottom => true,
409            ValidationStackEntry::Val(_) => self == other,
410        }
411    }
412
413    /// convenience method that returns `Some(other)` if `self.unifies_to(other)` is true and `None` otherwise
414    fn unify(&self, other: &ValidationStackEntry) -> Option<Self> {
415        self.unifies_to(other).then(|| other.clone())
416    }
417}
418
419// TODO hide implementation
420#[derive(Clone, Debug, PartialEq, Eq)]
421pub struct CtrlStackEntry {
422    pub label_info: LabelInfo,
423    pub block_ty: FuncType,
424    pub height: usize,
425    pub unreachable: bool,
426}
427
428impl CtrlStackEntry {
429    pub fn label_types(&self) -> &[ValType] {
430        if matches!(self.label_info, LabelInfo::Loop { .. }) {
431            &self.block_ty.params.valtypes
432        } else {
433            &self.block_ty.returns.valtypes
434        }
435    }
436}
437
438// TODO replace LabelInfo with this
439// TODO hide implementation
440// TODO implementation coupled to Sidetable
441#[derive(Clone, Debug, PartialEq, Eq)]
442pub enum LabelInfo {
443    Block {
444        stps_to_backpatch: Vec<usize>,
445    },
446    Loop {
447        ip: usize,
448        stp: usize,
449    },
450    If {
451        stps_to_backpatch: Vec<usize>,
452        stp: usize,
453    },
454    Func {
455        stps_to_backpatch: Vec<usize>,
456    },
457    Untyped,
458}
459
460#[cfg(test)]
461mod tests {
462    use crate::{NumType, RefType, ValType};
463
464    use super::{CtrlStackEntry, FuncType, LabelInfo, ResultType, ValidationStack, Vec};
465
466    fn push_dummy_untyped_label(validation_stack: &mut ValidationStack) {
467        validation_stack.ctrl_stack.push(CtrlStackEntry {
468            label_info: LabelInfo::Untyped,
469            block_ty: FuncType {
470                params: ResultType {
471                    valtypes: Vec::new(),
472                },
473                returns: ResultType {
474                    valtypes: Vec::new(),
475                },
476            },
477            height: validation_stack.len(),
478            unreachable: false,
479        })
480    }
481
482    #[test]
483    fn push_then_pop() {
484        let mut stack = ValidationStack::new();
485
486        stack.push_valtype(ValType::NumType(NumType::F64));
487        stack.push_valtype(ValType::NumType(NumType::I32));
488        stack.push_valtype(ValType::VecType);
489        stack.push_valtype(ValType::RefType(RefType::ExternRef));
490
491        stack
492            .assert_pop_val_type(ValType::RefType(RefType::ExternRef))
493            .unwrap();
494        stack.assert_pop_val_type(ValType::VecType).unwrap();
495        stack
496            .assert_pop_val_type(ValType::NumType(NumType::I32))
497            .unwrap();
498        stack
499            .assert_pop_val_type(ValType::NumType(NumType::F64))
500            .unwrap();
501    }
502
503    // TODO rewrite these
504    // #[test]
505    // fn labels() {
506    //     let mut stack = ValidationStack::new();
507
508    //     stack.push_valtype(ValType::NumType(NumType::I64));
509    //     push_dummy_func_label(&mut stack);
510
511    //     push_dummy_block_label(&mut stack);
512
513    //     stack.push_valtype(ValType::VecType);
514
515    //     // This removes the `ValType::VecType` and the `LabelKind::Loop` label
516    //     let popped_label = stack.pop_label_and_above().unwrap();
517    //     assert_eq!(
518    //         popped_label,
519    //         LabelInfo {
520    //             kind: LabelKind::Loop,
521    //         }
522    //     );
523
524    //     let popped_label = stack.pop_label_and_above().unwrap();
525    //     assert_eq!(
526    //         popped_label,
527    //         LabelInfo {
528    //             kind: LabelKind::Block,
529    //         }
530    //     );
531
532    //     // The first valtype should still be there
533    //     stack.assert_pop_val_type(ValType::NumType(NumType::I64));
534    // }
535
536    #[test]
537    fn assert_valtypes() {
538        let mut stack = ValidationStack::new();
539
540        stack.push_valtype(ValType::NumType(NumType::F64));
541        stack.push_valtype(ValType::NumType(NumType::I32));
542        stack.push_valtype(ValType::NumType(NumType::F32));
543
544        stack
545            .assert_val_types(
546                &[
547                    ValType::NumType(NumType::F64),
548                    ValType::NumType(NumType::I32),
549                    ValType::NumType(NumType::F32),
550                ],
551                true,
552            )
553            .unwrap();
554
555        push_dummy_untyped_label(&mut stack);
556
557        stack.push_valtype(ValType::NumType(NumType::I32));
558
559        stack
560            .assert_val_types(&[ValType::NumType(NumType::I32)], true)
561            .unwrap();
562    }
563
564    #[test]
565    fn assert_emtpy_valtypes() {
566        let mut stack = ValidationStack::new();
567
568        stack.assert_val_types(&[], true).unwrap();
569
570        stack.push_valtype(ValType::NumType(NumType::I32));
571        push_dummy_untyped_label(&mut stack);
572
573        // Valtypes separated by a label should also not be detected
574        stack.assert_val_types(&[], true).unwrap();
575    }
576
577    #[test]
578    fn assert_valtypes_on_top() {
579        let mut stack = ValidationStack::new();
580
581        stack.assert_val_types_on_top(&[], true).unwrap();
582
583        stack.push_valtype(ValType::NumType(NumType::I32));
584        stack.push_valtype(ValType::NumType(NumType::F32));
585        stack.push_valtype(ValType::NumType(NumType::I64));
586
587        // There are always zero valtypes on top of the stack
588        stack.assert_val_types_on_top(&[], true).unwrap();
589
590        stack
591            .assert_val_types_on_top(&[ValType::NumType(NumType::I64)], true)
592            .unwrap();
593
594        stack
595            .assert_val_types_on_top(
596                &[
597                    ValType::NumType(NumType::F32),
598                    ValType::NumType(NumType::I64),
599                ],
600                true,
601            )
602            .unwrap();
603
604        stack
605            .assert_val_types_on_top(
606                &[
607                    ValType::NumType(NumType::I32),
608                    ValType::NumType(NumType::F32),
609                    ValType::NumType(NumType::I64),
610                ],
611                true,
612            )
613            .unwrap();
614    }
615
616    #[test]
617    fn unspecified() {
618        let mut stack = ValidationStack::new();
619        push_dummy_untyped_label(&mut stack);
620
621        stack.make_unspecified().unwrap();
622
623        // Now we can pop as many valtypes from the stack as we want
624        stack
625            .assert_pop_val_type(ValType::NumType(NumType::I32))
626            .unwrap();
627
628        stack
629            .assert_pop_val_type(ValType::RefType(RefType::ExternRef))
630            .unwrap();
631
632        // Let's remove the unspecified entry and the first label
633
634        // TODO hide implementation
635        stack.ctrl_stack.pop();
636
637        // Now there are no values left on the stack
638        assert_eq!(stack.assert_val_types(&[], true), Ok(()));
639    }
640
641    #[test]
642    fn unspecified2() {
643        let mut stack = ValidationStack::new();
644        push_dummy_untyped_label(&mut stack);
645
646        stack.make_unspecified().unwrap();
647
648        // Stack needs to keep track of unified types, I64 and F32 and I32 will appear.
649        stack
650            .assert_val_types(
651                &[
652                    ValType::NumType(NumType::I64),
653                    ValType::NumType(NumType::F32),
654                    ValType::NumType(NumType::I32),
655                ],
656                true,
657            )
658            .unwrap();
659
660        stack.ctrl_stack.pop();
661
662        assert_eq!(
663            stack.assert_pop_val_type(ValType::NumType(NumType::I32)),
664            Ok(())
665        );
666        assert_eq!(
667            stack.assert_pop_val_type(ValType::NumType(NumType::F32)),
668            Ok(())
669        );
670        assert_eq!(
671            stack.assert_pop_val_type(ValType::NumType(NumType::I64)),
672            Ok(())
673        );
674    }
675
676    #[test]
677    fn unspecified3() {
678        let mut stack = ValidationStack::new();
679        push_dummy_untyped_label(&mut stack);
680
681        stack.make_unspecified().unwrap();
682
683        stack.push_valtype(ValType::NumType(NumType::I32));
684
685        // Stack needs to keep track of unified types, I64 and F32 will appear under I32.
686        // Stack needs to keep track of unified types, I64 and F32 and I32 will appear.
687        stack
688            .assert_val_types(
689                &[
690                    ValType::NumType(NumType::I64),
691                    ValType::NumType(NumType::F32),
692                    ValType::NumType(NumType::I32),
693                ],
694                true,
695            )
696            .unwrap();
697
698        stack.ctrl_stack.pop();
699
700        assert_eq!(
701            stack.assert_pop_val_type(ValType::NumType(NumType::I32)),
702            Ok(())
703        );
704        assert_eq!(
705            stack.assert_pop_val_type(ValType::NumType(NumType::F32)),
706            Ok(())
707        );
708        assert_eq!(
709            stack.assert_pop_val_type(ValType::NumType(NumType::I64)),
710            Ok(())
711        );
712    }
713
714    #[test]
715    fn unspecified4() {
716        let mut stack = ValidationStack::new();
717
718        stack.push_valtype(ValType::VecType);
719        stack.push_valtype(ValType::NumType(NumType::I32));
720
721        push_dummy_untyped_label(&mut stack);
722
723        stack.make_unspecified().unwrap();
724
725        stack.push_valtype(ValType::VecType);
726        stack.push_valtype(ValType::RefType(RefType::FuncRef));
727
728        // Stack needs to keep track of unified types, I64 and F32 will appear below VecType and RefType
729        // and above I32 and VecType
730        stack
731            .assert_val_types(
732                &[
733                    ValType::NumType(NumType::I64),
734                    ValType::NumType(NumType::F32),
735                    ValType::VecType,
736                    ValType::RefType(RefType::FuncRef),
737                ],
738                true,
739            )
740            .unwrap();
741
742        stack.ctrl_stack.pop();
743
744        assert_eq!(
745            stack.assert_pop_val_type(ValType::RefType(RefType::FuncRef)),
746            Ok(())
747        );
748        assert_eq!(stack.assert_pop_val_type(ValType::VecType), Ok(()));
749        assert_eq!(
750            stack.assert_pop_val_type(ValType::NumType(NumType::F32)),
751            Ok(())
752        );
753        assert_eq!(
754            stack.assert_pop_val_type(ValType::NumType(NumType::I64)),
755            Ok(())
756        );
757        assert_eq!(
758            stack.assert_pop_val_type(ValType::NumType(NumType::I32)),
759            Ok(())
760        );
761        assert_eq!(stack.assert_pop_val_type(ValType::VecType), Ok(()));
762    }
763}