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