Skip to main content

wasmtime_environ/compile/
module_types.rs

1use crate::{
2    EngineOrModuleTypeIndex, EntityRef, ModuleInternedRecGroupIndex, ModuleInternedTypeIndex,
3    ModuleTypes, PanicOnOom as _, TypeConvert, TypeIndex, WasmArrayType, WasmCompositeInnerType,
4    WasmCompositeType, WasmExnType, WasmFieldType, WasmFuncType, WasmHeapType, WasmResult,
5    WasmStorageType, WasmStructType, WasmSubType, WasmValType,
6    collections::{TryClone as _, TryCow},
7    wasm_unsupported,
8};
9use std::{
10    collections::{HashMap, hash_map::Entry},
11    ops::Index,
12};
13use wasmparser::{UnpackedIndex, Validator, ValidatorId};
14
15/// A type marking the start of a recursion group's definition.
16///
17/// This is initialized by `ModuleTypesBuilder::start_rec_group` and then
18/// finished in `ModuleTypes::end_rec_group` after all of the types in the rec
19/// group have been defined.
20struct RecGroupStart {
21    rec_group_index: ModuleInternedRecGroupIndex,
22    start: ModuleInternedTypeIndex,
23    end: ModuleInternedTypeIndex,
24}
25
26/// A builder for [`ModuleTypes`].
27pub struct ModuleTypesBuilder {
28    /// The ID of the validator that this builder is configured for. Using a
29    /// different validator, or multiple validators, with this builder would
30    /// result in silliness because our `wasmparser::types::*Id`s are only
31    /// unique within the context of a particular validator. Getting this wrong
32    /// could result in generating calls to functions of the wrong type, for
33    /// example. So therefore we always assert that a builder instances is only
34    /// ever paired with a particular validator context.
35    validator_id: ValidatorId,
36
37    /// The canonicalized and deduplicated set of types we are building.
38    types: ModuleTypes,
39
40    /// The set of trampoline-compatible function types we have already added to
41    /// `self.types`. We do this additional level of deduping, on top of what
42    /// `wasmparser` already does, so we can quickly and easily get the
43    /// trampoline type for a given function type if we've already interned one.
44    trampoline_types: HashMap<WasmFuncType, ModuleInternedTypeIndex>,
45
46    /// An interning map for exception types corresponding to function
47    /// types used by tags. Tags are nominal, but the underlying
48    /// Wasmtime types describe only the object layout and so are
49    /// structural.
50    exception_types: HashMap<ModuleInternedTypeIndex, ModuleInternedTypeIndex>,
51
52    /// A map from already-interned `wasmparser` types to their corresponding
53    /// Wasmtime type.
54    wasmparser_to_wasmtime: HashMap<wasmparser::types::CoreTypeId, ModuleInternedTypeIndex>,
55
56    /// The set of recursion groups we have already seen and interned.
57    already_seen: HashMap<wasmparser::types::RecGroupId, ModuleInternedRecGroupIndex>,
58
59    /// If we are in the middle of defining a recursion group, this is the
60    /// metadata about the recursion group we started defining.
61    defining_rec_group: Option<RecGroupStart>,
62
63    /// Cache of the return value of [`Self::startup_func_type`].
64    startup_func_type: Option<ModuleInternedTypeIndex>,
65}
66
67impl ModuleTypesBuilder {
68    /// Construct a new `ModuleTypesBuilder` using the given validator.
69    pub fn new(validator: &Validator) -> Self {
70        Self {
71            validator_id: validator.id(),
72            types: ModuleTypes::default(),
73            trampoline_types: HashMap::default(),
74            exception_types: HashMap::default(),
75            wasmparser_to_wasmtime: HashMap::default(),
76            already_seen: HashMap::default(),
77            defining_rec_group: None,
78            startup_func_type: None,
79        }
80    }
81
82    /// Reserves space for `amt` more type signatures.
83    pub fn reserve_wasm_signatures(&mut self, amt: usize) {
84        self.types.reserve(amt);
85        self.wasmparser_to_wasmtime.reserve(amt);
86        self.already_seen.reserve(amt);
87    }
88
89    /// Get the id of the validator that this builder is configured for.
90    pub fn validator_id(&self) -> ValidatorId {
91        self.validator_id
92    }
93
94    /// Intern a recursion group and all of its types into this builder.
95    ///
96    /// If the recursion group has already been interned, then it is reused.
97    ///
98    /// Panics if given types from a different validator than the one that this
99    /// builder is associated with.
100    pub fn intern_rec_group(
101        &mut self,
102        validator_types: wasmparser::types::TypesRef<'_>,
103        rec_group_id: wasmparser::types::RecGroupId,
104    ) -> WasmResult<ModuleInternedRecGroupIndex> {
105        assert_eq!(validator_types.id(), self.validator_id);
106
107        if let Some(interned) = self.already_seen.get(&rec_group_id) {
108            return Ok(*interned);
109        }
110
111        self.define_new_rec_group(validator_types, rec_group_id)
112    }
113
114    /// Define a new recursion group that we haven't already interned.
115    fn define_new_rec_group(
116        &mut self,
117        validator_types: wasmparser::types::TypesRef<'_>,
118        rec_group_id: wasmparser::types::RecGroupId,
119    ) -> WasmResult<ModuleInternedRecGroupIndex> {
120        assert_eq!(validator_types.id(), self.validator_id);
121
122        self.start_rec_group(
123            validator_types,
124            validator_types.rec_group_elements(rec_group_id),
125        );
126
127        for id in validator_types.rec_group_elements(rec_group_id) {
128            let ty = &validator_types[id];
129            let wasm_ty = WasmparserTypeConverter::new(self, |_| {
130                unreachable!("no need to lookup indexes; we already have core type IDs")
131            })
132            .with_rec_group(validator_types, rec_group_id)
133            .convert_sub_type(ty)?;
134            self.wasm_sub_type_in_rec_group(id, wasm_ty);
135        }
136
137        let rec_group_index = self.end_rec_group(rec_group_id);
138
139        // Iterate over all the types we just defined and make sure that every
140        // function type has an associated trampoline type. This needs to happen
141        // *after* we finish defining the rec group because we may need to
142        // intern new function types, which would conflict with the contiguous
143        // range of type indices we pre-reserved for the rec group elements.
144        for ty in self.rec_group_elements(rec_group_index) {
145            if self.types[ty].is_func() {
146                let trampoline = self.intern_trampoline_type(ty);
147                self.types.set_trampoline_type(ty, trampoline);
148            }
149        }
150
151        Ok(rec_group_index)
152    }
153
154    /// Get or create the trampoline function type for the given function
155    /// type. Returns the interned type index of the trampoline function type.
156    fn intern_trampoline_type(
157        &mut self,
158        for_func_ty: ModuleInternedTypeIndex,
159    ) -> ModuleInternedTypeIndex {
160        let sub_ty = &self.types[for_func_ty];
161        let trampoline = sub_ty.unwrap_func().trampoline_type().panic_on_oom();
162
163        if let Some(idx) = self.trampoline_types.get(&trampoline) {
164            // We've already interned this trampoline type; reuse it.
165            *idx
166        } else {
167            // We have not already interned this trampoline type.
168            match trampoline {
169                // The trampoline type is the same as the original function
170                // type. We can reuse the definition and its index, but still
171                // need to intern the type into our `trampoline_types` map so we
172                // can reuse it in the future.
173                TryCow::Borrowed(f) => {
174                    self.trampoline_types
175                        .insert(f.clone_panic_on_oom(), for_func_ty);
176                    for_func_ty
177                }
178                // The trampoline type is different from the original function
179                // type. Define the trampoline type and then intern it in
180                // `trampoline_types` so we can reuse it in the future.
181                TryCow::Owned(f) => {
182                    let idx = self.types.push(WasmSubType {
183                        is_final: true,
184                        supertype: None,
185                        composite_type: WasmCompositeType {
186                            inner: WasmCompositeInnerType::Func(f.clone_panic_on_oom()),
187                            shared: sub_ty.composite_type.shared,
188                        },
189                    });
190
191                    // The trampoline type is its own trampoline type.
192                    self.types.set_trampoline_type(idx, idx);
193
194                    let next = self.types.next_ty();
195                    self.types.push_rec_group(idx..next);
196                    self.trampoline_types.insert(f, idx);
197                    idx
198                }
199            }
200        }
201    }
202
203    /// Start defining a recursion group.
204    fn start_rec_group(
205        &mut self,
206        validator_types: wasmparser::types::TypesRef<'_>,
207        elems: impl ExactSizeIterator<Item = wasmparser::types::CoreTypeId>,
208    ) {
209        log::trace!("Starting rec group of length {}", elems.len());
210
211        assert!(self.defining_rec_group.is_none());
212        assert_eq!(validator_types.id(), self.validator_id);
213
214        // Eagerly define the reverse map's entries for this rec group's types
215        // so that we can use them when converting `wasmparser` types to our
216        // types.
217        let len = elems.len();
218        for (i, wasmparser_id) in elems.enumerate() {
219            let interned = ModuleInternedTypeIndex::new(self.types.len_types() + i);
220            log::trace!(
221                "Reserving {interned:?} for {wasmparser_id:?} = {:?}",
222                validator_types[wasmparser_id]
223            );
224
225            let old_entry = self.wasmparser_to_wasmtime.insert(wasmparser_id, interned);
226            debug_assert_eq!(
227                old_entry, None,
228                "should not have already inserted {wasmparser_id:?}"
229            );
230        }
231
232        self.defining_rec_group = Some(RecGroupStart {
233            rec_group_index: self.types.next_rec_group(),
234            start: self.types.next_ty(),
235            end: ModuleInternedTypeIndex::new(self.types.len_types() + len),
236        });
237    }
238
239    /// Finish defining a recursion group.
240    fn end_rec_group(
241        &mut self,
242        rec_group_id: wasmparser::types::RecGroupId,
243    ) -> ModuleInternedRecGroupIndex {
244        let RecGroupStart {
245            rec_group_index,
246            start,
247            end,
248        } = self
249            .defining_rec_group
250            .take()
251            .expect("should be defining a rec group");
252
253        log::trace!("Ending rec group {start:?}..{end:?}");
254
255        debug_assert!(start.index() < self.types.len_types());
256        debug_assert_eq!(
257            end,
258            self.types.next_ty(),
259            "should have defined the number of types declared in `start_rec_group`"
260        );
261
262        let idx = self.types.push_rec_group(start..end);
263        debug_assert_eq!(idx, rec_group_index);
264
265        self.already_seen.insert(rec_group_id, rec_group_index);
266        rec_group_index
267    }
268
269    /// Intern a type into this builder and get its Wasmtime index.
270    ///
271    /// This will intern not only the single given type, but the type's entire
272    /// rec group. This helper method is provided as a convenience so that
273    /// callers don't have to get the type's rec group, intern the rec group,
274    /// and then look up the Wasmtime index for the original type themselves.
275    pub fn intern_type(
276        &mut self,
277        validator_types: wasmparser::types::TypesRef<'_>,
278        id: wasmparser::types::CoreTypeId,
279    ) -> WasmResult<ModuleInternedTypeIndex> {
280        assert!(self.defining_rec_group.is_none());
281        assert_eq!(validator_types.id(), self.validator_id);
282
283        let rec_group_id = validator_types.rec_group_id_of(id);
284        debug_assert!(
285            validator_types
286                .rec_group_elements(rec_group_id)
287                .any(|e| e == id)
288        );
289
290        let interned_rec_group = self.intern_rec_group(validator_types, rec_group_id)?;
291
292        let interned_type = self.wasmparser_to_wasmtime[&id];
293        debug_assert!(
294            self.rec_group_elements(interned_rec_group)
295                .any(|e| e == interned_type)
296        );
297
298        Ok(interned_type)
299    }
300
301    /// Define a new Wasm type while we are defining a rec group.
302    fn wasm_sub_type_in_rec_group(
303        &mut self,
304        id: wasmparser::types::CoreTypeId,
305        ty: WasmSubType,
306    ) -> ModuleInternedTypeIndex {
307        assert!(
308            self.defining_rec_group.is_some(),
309            "must be defining a rec group to define new types"
310        );
311
312        let module_interned_index = self.types.push(ty);
313        debug_assert_eq!(
314            self.wasmparser_to_wasmtime.get(&id),
315            Some(&module_interned_index),
316            "should have reserved the right module-interned index for this wasmparser type already"
317        );
318
319        module_interned_index
320    }
321
322    /// Define a new exception type when we see a function type used
323    /// in a tag.
324    ///
325    /// The returned `ModuleInternedTypeIndex` gives us a Wasmtime
326    /// type which corresponds to the exception object layout, but
327    /// note that these types do not exist in the Wasm spec: at the
328    /// Wasm level, only function types exist (and tags and exception
329    /// instructions reference them). For implementation reasons, we
330    /// need a separate type to describe the exception object layout,
331    /// and this registers and provides that type.
332    pub fn define_exception_type_for_tag(
333        &mut self,
334        for_func_ty: ModuleInternedTypeIndex,
335    ) -> ModuleInternedTypeIndex {
336        match self.exception_types.entry(for_func_ty) {
337            Entry::Occupied(o) => *o.get(),
338            Entry::Vacant(v) => {
339                let fields = self.types[for_func_ty]
340                    .unwrap_func()
341                    .params()
342                    .iter()
343                    .map(|valtype| WasmFieldType {
344                        element_type: WasmStorageType::Val(*valtype),
345                        mutable: false,
346                    })
347                    .collect();
348                let idx = self.types.push(WasmSubType {
349                    is_final: true,
350                    supertype: None,
351                    composite_type: WasmCompositeType {
352                        inner: WasmCompositeInnerType::Exn(WasmExnType {
353                            func_ty: EngineOrModuleTypeIndex::Module(for_func_ty),
354                            fields,
355                        }),
356                        shared: false,
357                    },
358                });
359                let next = self.types.next_ty();
360                self.types.push_rec_group(idx..next);
361                *v.insert(idx)
362            }
363        }
364    }
365
366    /// Returns the result [`ModuleTypes`] of this builder.
367    pub fn finish(self) -> ModuleTypes {
368        self.types
369    }
370
371    /// Get the elements within an already-defined rec group.
372    pub fn rec_group_elements(
373        &self,
374        rec_group: ModuleInternedRecGroupIndex,
375    ) -> impl ExactSizeIterator<Item = ModuleInternedTypeIndex> + use<> {
376        self.types.rec_group_elements(rec_group)
377    }
378
379    /// Returns an iterator over all the unique wasm types defined thus far
380    /// within this builder.
381    pub fn wasm_types(&self) -> impl Iterator<Item = (ModuleInternedTypeIndex, &WasmSubType)> {
382        self.types.wasm_types()
383    }
384
385    /// Get an iterator over all function types and their associated trampoline
386    /// type.
387    pub fn trampoline_types(
388        &self,
389    ) -> impl Iterator<Item = (ModuleInternedTypeIndex, ModuleInternedTypeIndex)> + '_ {
390        self.types.trampoline_types()
391    }
392
393    /// Get the associated trampoline type for the given function type.
394    pub fn trampoline_type(&self, ty: ModuleInternedTypeIndex) -> ModuleInternedTypeIndex {
395        self.types.trampoline_type(ty)
396    }
397
398    /// Get and unwrap a [`WasmStructType`] for the given struct index.
399    ///
400    /// # Panics
401    ///
402    /// Panics if the unwrapped type is not a struct.
403    ///
404    /// # Errors
405    ///
406    /// For now, fails with an unsupported error if the type is shared.
407    pub fn unwrap_struct(&self, ty: ModuleInternedTypeIndex) -> WasmResult<&WasmStructType> {
408        let composite_type = &self.types[ty].composite_type;
409        if composite_type.shared {
410            return Err(wasm_unsupported!("shared structs are not yet implemented"));
411        }
412        Ok(composite_type.inner.unwrap_struct())
413    }
414
415    /// Get and unwrap a [`WasmArrayType`] for the given array index.
416    ///
417    /// # Panics
418    ///
419    /// Panics if the unwrapped type is not an array.
420    ///
421    /// # Errors
422    ///
423    /// For now, fails with an unsupported error if the type is shared.
424    pub fn unwrap_array(&self, interned_ty: ModuleInternedTypeIndex) -> WasmResult<&WasmArrayType> {
425        let composite_type = &self.types[interned_ty].composite_type;
426        if composite_type.shared {
427            return Err(wasm_unsupported!("shared arrays are not yet implemented"));
428        }
429        Ok(composite_type.inner.unwrap_array())
430    }
431
432    /// Get and unwrap a [`WasmExnType`] for the given exception-type index.
433    ///
434    /// # Panics
435    ///
436    /// Panics if the unwrapped type is not an exception type.
437    ///
438    /// # Errors
439    ///
440    /// For now, fails with an unsupported error if the type is shared.
441    pub fn unwrap_exn(&self, interned_ty: ModuleInternedTypeIndex) -> WasmResult<&WasmExnType> {
442        let composite_type = &self.types[interned_ty].composite_type;
443        if composite_type.shared {
444            return Err(wasm_unsupported!(
445                "shared exceptions are not yet implemented"
446            ));
447        }
448        Ok(composite_type.inner.unwrap_exn())
449    }
450
451    /// Get and unwrap a [`WasmFuncType`] for the given function-type index.
452    ///
453    /// # Panics
454    ///
455    /// Panics if the unwrapped type is not a function type.
456    ///
457    /// # Errors
458    ///
459    /// For now, fails with an unsupported error if the type is shared.
460    pub fn unwrap_func(&self, interned_ty: ModuleInternedTypeIndex) -> WasmResult<&WasmFuncType> {
461        let composite_type = &self.types[interned_ty].composite_type;
462        if composite_type.shared {
463            return Err(wasm_unsupported!(
464                "shared functions are not yet implemented"
465            ));
466        }
467        Ok(composite_type.inner.unwrap_func())
468    }
469
470    /// Gets a type for the "module startup" function, or `[] -> []` in the wasm
471    /// type system.
472    pub fn startup_func_type(&mut self) -> ModuleInternedTypeIndex {
473        *self.startup_func_type.get_or_insert_with(|| {
474            let idx = self.types.push(WasmSubType {
475                is_final: true,
476                supertype: None,
477                composite_type: WasmCompositeType {
478                    inner: WasmCompositeInnerType::Func(WasmFuncType::new([], []).panic_on_oom()),
479                    shared: false,
480                },
481            });
482            let next = self.types.next_ty();
483            self.types.push_rec_group(idx..next);
484            idx
485        })
486    }
487
488    /// Smaller helper method to find a `ModuleInternedTypeIndex` which
489    /// corresponds to the `resource.drop` intrinsic in components, namely a
490    /// core wasm function type which takes one `i32` argument and has no
491    /// results.
492    ///
493    /// This is a bit of a hack right now as ideally this find operation
494    /// wouldn't be needed and instead the `ModuleInternedTypeIndex` itself
495    /// would be threaded through appropriately, but that's left for a future
496    /// refactoring. Try not to lean too hard on this method though.
497    pub fn find_resource_drop_signature(&self) -> Option<ModuleInternedTypeIndex> {
498        self.wasm_types()
499            .find(|(_, ty)| {
500                ty.as_func().map_or(false, |sig| {
501                    sig.params().len() == 1
502                        && sig.results().len() == 0
503                        && sig.params()[0] == WasmValType::I32
504                })
505            })
506            .map(|(i, _)| i)
507    }
508}
509
510// Forward the indexing impl to the internal `ModuleTypes`
511impl<T> Index<T> for ModuleTypesBuilder
512where
513    ModuleTypes: Index<T>,
514{
515    type Output = <ModuleTypes as Index<T>>::Output;
516
517    fn index(&self, sig: T) -> &Self::Output {
518        &self.types[sig]
519    }
520}
521
522/// A convert from `wasmparser` types to Wasmtime types.
523pub struct WasmparserTypeConverter<'a, F> {
524    types: &'a ModuleTypesBuilder,
525    lookup_type_idx: F,
526    rec_group_context: Option<(
527        wasmparser::types::TypesRef<'a>,
528        wasmparser::types::RecGroupId,
529    )>,
530}
531
532impl<'a, F> WasmparserTypeConverter<'a, F> {
533    /// Construct a new type converter from `wasmparser` types to Wasmtime types.
534    pub fn new(types: &'a ModuleTypesBuilder, lookup_type_idx: F) -> Self {
535        Self {
536            types,
537            lookup_type_idx,
538            rec_group_context: None,
539        }
540    }
541
542    /// Configure this converter to be within the context of defining the
543    /// current rec group.
544    pub fn with_rec_group(
545        &mut self,
546        wasmparser_types: wasmparser::types::TypesRef<'a>,
547        rec_group: wasmparser::types::RecGroupId,
548    ) -> &Self {
549        self.rec_group_context = Some((wasmparser_types, rec_group));
550        self
551    }
552}
553
554impl<F> TypeConvert for WasmparserTypeConverter<'_, F>
555where
556    F: Fn(TypeIndex) -> ModuleInternedTypeIndex,
557{
558    fn lookup_heap_type(&self, index: UnpackedIndex) -> WasmHeapType {
559        match index {
560            UnpackedIndex::Id(id) => {
561                let interned = self.types.wasmparser_to_wasmtime[&id];
562                let index = EngineOrModuleTypeIndex::Module(interned);
563
564                // If this is a forward reference to a type in this type's rec
565                // group that we haven't converted yet, then we won't have an
566                // entry in `wasm_types` yet. In this case, fallback to a
567                // different means of determining whether this is a concrete
568                // array vs struct vs func reference. In this case, we can use
569                // the validator's type context.
570                if let Some(ty) = self.types.types.get(interned) {
571                    assert!(!ty.composite_type.shared);
572                    match &ty.composite_type.inner {
573                        WasmCompositeInnerType::Array(_) => WasmHeapType::ConcreteArray(index),
574                        WasmCompositeInnerType::Func(_) => WasmHeapType::ConcreteFunc(index),
575                        WasmCompositeInnerType::Struct(_) => WasmHeapType::ConcreteStruct(index),
576                        WasmCompositeInnerType::Cont(_) => WasmHeapType::ConcreteCont(index),
577                        WasmCompositeInnerType::Exn(_) => WasmHeapType::ConcreteExn(index),
578                    }
579                } else if let Some((wasmparser_types, _)) = self.rec_group_context.as_ref() {
580                    let wasmparser_ty = &wasmparser_types[id].composite_type;
581                    assert!(!wasmparser_ty.shared);
582                    match &wasmparser_ty.inner {
583                        wasmparser::CompositeInnerType::Array(_) => {
584                            WasmHeapType::ConcreteArray(index)
585                        }
586                        wasmparser::CompositeInnerType::Func(_) => {
587                            WasmHeapType::ConcreteFunc(index)
588                        }
589                        wasmparser::CompositeInnerType::Struct(_) => {
590                            WasmHeapType::ConcreteStruct(index)
591                        }
592                        wasmparser::CompositeInnerType::Cont(_) => {
593                            WasmHeapType::ConcreteCont(index)
594                        }
595                    }
596                } else {
597                    panic!("forward reference to type outside of rec group?")
598                }
599            }
600
601            UnpackedIndex::Module(module_index) => {
602                let module_index = TypeIndex::from_u32(module_index);
603                let interned = (self.lookup_type_idx)(module_index);
604                let index = EngineOrModuleTypeIndex::Module(interned);
605
606                // See comment above about `wasm_types` maybe not having the
607                // converted sub type yet. However in this case we don't have a
608                // `wasmparser::types::CoreTypeId` on hand, so we have to
609                // indirectly get one by looking it up inside the current rec
610                // group.
611                if let Some(ty) = self.types.types.get(interned) {
612                    assert!(!ty.composite_type.shared);
613                    match &ty.composite_type.inner {
614                        WasmCompositeInnerType::Array(_) => WasmHeapType::ConcreteArray(index),
615                        WasmCompositeInnerType::Func(_) => WasmHeapType::ConcreteFunc(index),
616                        WasmCompositeInnerType::Struct(_) => WasmHeapType::ConcreteStruct(index),
617                        WasmCompositeInnerType::Cont(_) => WasmHeapType::ConcreteCont(index),
618                        WasmCompositeInnerType::Exn(_) => WasmHeapType::ConcreteExn(index),
619                    }
620                } else if let Some((parser_types, rec_group)) = self.rec_group_context.as_ref() {
621                    let rec_group_index = interned.index() - self.types.types.len_types();
622                    let id = parser_types
623                        .rec_group_elements(*rec_group)
624                        .nth(rec_group_index)
625                        .unwrap();
626                    let wasmparser_ty = &parser_types[id].composite_type;
627                    assert!(!wasmparser_ty.shared);
628                    match &wasmparser_ty.inner {
629                        wasmparser::CompositeInnerType::Array(_) => {
630                            WasmHeapType::ConcreteArray(index)
631                        }
632                        wasmparser::CompositeInnerType::Func(_) => {
633                            WasmHeapType::ConcreteFunc(index)
634                        }
635                        wasmparser::CompositeInnerType::Struct(_) => {
636                            WasmHeapType::ConcreteStruct(index)
637                        }
638                        wasmparser::CompositeInnerType::Cont(_) => {
639                            WasmHeapType::ConcreteCont(index)
640                        }
641                    }
642                } else {
643                    panic!("forward reference to type outside of rec group?")
644                }
645            }
646
647            UnpackedIndex::RecGroup(_) => unreachable!(),
648        }
649    }
650
651    fn lookup_type_index(&self, index: wasmparser::UnpackedIndex) -> EngineOrModuleTypeIndex {
652        match index {
653            UnpackedIndex::Id(id) => {
654                let interned = self.types.wasmparser_to_wasmtime[&id];
655                EngineOrModuleTypeIndex::Module(interned)
656            }
657            UnpackedIndex::Module(module_index) => {
658                let module_index = TypeIndex::from_u32(module_index);
659                let interned = (self.lookup_type_idx)(module_index);
660                EngineOrModuleTypeIndex::Module(interned)
661            }
662            UnpackedIndex::RecGroup(_) => unreachable!(),
663        }
664    }
665}