wasm/validation/
validation_stack.rs

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