1 //! Implement a registry of types: function, struct, and array definitions.
2 //!
3 //! Helps implement fast indirect call signature checking, reference type
4 //! downcasting, and etc...
5 
6 use crate::Engine;
7 use crate::error::OutOfMemory;
8 use crate::prelude::*;
9 use crate::sync::RwLock;
10 use crate::vm::GcRuntime;
11 use alloc::sync::Arc;
12 use core::cell::Cell;
13 use core::iter;
14 use core::{
15     borrow::Borrow,
16     fmt::{self, Debug},
17     hash::{Hash, Hasher},
18     ops::Range,
19     sync::atomic::{
20         AtomicBool, AtomicUsize,
21         Ordering::{AcqRel, Acquire, Release},
22     },
23 };
24 use wasmtime_core::slab::{Id as SlabId, Slab};
25 use wasmtime_environ::{
26     EngineOrModuleTypeIndex, EntityRef, GcLayout, ModuleInternedTypeIndex, ModuleTypes, TypeTrace,
27     Undo, VMSharedTypeIndex, WasmRecGroup, WasmSubType,
28     collections::TryCow,
29     iter_entity_range,
30     packed_option::{PackedOption, ReservedValue},
31 };
32 
33 // ### Notes on the Lifetime Management of Types
34 //
35 // All defined types from all Wasm modules loaded into Wasmtime are interned
36 // into their engine's `TypeRegistry`.
37 //
38 // With Wasm MVP, managing type lifetimes within the registry was easy: we only
39 // cared about canonicalizing types so that `call_indirect` was fast and we
40 // didn't waste memory on many copies of the same function type definition.
41 // Function types could only take and return simple scalars (i32/f64/etc...) and
42 // there were no type-to-type references. We could simply deduplicate function
43 // types and reference count their entries in the registry.
44 //
45 // The typed function references and GC proposals change everything. The former
46 // introduced function types that take a reference to a function of another
47 // specific type. This is a type-to-type reference. The latter introduces struct
48 // and array types that can have references to other struct, array, and function
49 // types, as well as recursion groups that allow cyclic references between
50 // types. Now type canonicalization additionally enables fast type checks and
51 // downcasts *across* modules: so that two modules which define the same struct
52 // type, for example, can pass instances of that struct type to each other, and
53 // we can quickly check that those instances are in fact of the expected types.
54 //
55 // But how do we manage the lifetimes of types that can reference other types as
56 // Wasm modules are dynamically loaded and unloaded from the engine? These
57 // modules can define subsets of the same types and there can be cyclic type
58 // references. Dynamic lifetimes, sharing, and cycles is a classic combination
59 // of constraints that push a design towards a tracing garbage collector (or,
60 // equivalently, a reference-counting collector with a cycle collector).
61 //
62 // However, we can rely on the following properties:
63 //
64 // 1. The unit of type canonicalization is a whole recursion group.
65 //
66 // 2. Type-to-type reference cycles may only happen within a recursion group and
67 //    therefore type-to-type references across recursion groups are acyclic.
68 //
69 // Therefore, our type registry makes the following design decisions:
70 //
71 // * We manage the lifetime of whole recursion groups, not individual
72 //   types. That is, every type in the recursion group stays alive as long as
73 //   any type in the recursion group is kept alive. This is effectively mandated
74 //   by property (1) and the hash consing it implies.
75 //
76 // * We still use naive reference counting to manage the lifetimes of recursion
77 //   groups. A type-to-type reference that crosses the boundary from recursion
78 //   group A to recursion group B will increment B's reference count when A is
79 //   first registered and decrement B's reference count when A is removed from
80 //   the registry. Because of property (2) we don't need to worry about cycles,
81 //   which are the classic weakness of reference counting.
82 
83 /// Represents a collection of shared types.
84 ///
85 /// This is used to register shared types with a shared type registry.
86 ///
87 /// The collection will unregister any contained types with the registry
88 /// when dropped.
89 pub struct TypeCollection {
90     engine: Engine,
91     rec_groups: TryVec<RecGroupEntry>,
92     types: TryPrimaryMap<ModuleInternedTypeIndex, VMSharedTypeIndex>,
93     trampolines: TrySecondaryMap<VMSharedTypeIndex, PackedOption<ModuleInternedTypeIndex>>,
94 }
95 
96 impl Debug for TypeCollection {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result97     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98         let TypeCollection {
99             engine: _,
100             rec_groups,
101             types,
102             trampolines,
103         } = self;
104         f.debug_struct("TypeCollection")
105             .field("rec_groups", rec_groups)
106             .field("types", types)
107             .field("trampolines", trampolines)
108             .finish_non_exhaustive()
109     }
110 }
111 
112 impl Engine {
113     /// Registers the given types in this engine, re-canonicalizing them for
114     /// runtime usage.
115     #[must_use = "types are only registered as long as the `TypeCollection` is live"]
register_and_canonicalize_types<'a, I>( &self, module_types: &mut ModuleTypes, env_modules: I, ) -> Result<TypeCollection, OutOfMemory> where I: IntoIterator<Item = &'a mut wasmtime_environ::Module>, I::IntoIter: ExactSizeIterator,116     pub(crate) fn register_and_canonicalize_types<'a, I>(
117         &self,
118         module_types: &mut ModuleTypes,
119         env_modules: I,
120     ) -> Result<TypeCollection, OutOfMemory>
121     where
122         I: IntoIterator<Item = &'a mut wasmtime_environ::Module>,
123         I::IntoIter: ExactSizeIterator,
124     {
125         if cfg!(debug_assertions) {
126             module_types
127                 .trace(&mut |idx| match idx {
128                     EngineOrModuleTypeIndex::Module(_) => Ok(()),
129                     EngineOrModuleTypeIndex::Engine(_) | EngineOrModuleTypeIndex::RecGroup(_) => {
130                         Err(idx)
131                     }
132                 })
133                 .expect("should only have module type indices");
134         }
135 
136         let engine = self.clone();
137         let registry = engine.signatures();
138         let gc_runtime = engine.gc_runtime().map(|rt| &**rt);
139 
140         // First, register these types in this engine's registry.
141         let (rec_groups, types) = registry
142             .0
143             .write()
144             .register_module_types(gc_runtime, module_types)?;
145 
146         // Then build our map from each function type's engine index to the
147         // module-index of its trampoline. Trampoline functions are queried by
148         // module-index in a compiled module, and doing this engine-to-module
149         // resolution now means we don't need to do it on the function call hot
150         // path.
151         let mut trampolines = TrySecondaryMap::with_capacity(types.len())?;
152         for (module_ty, module_trampoline_ty) in module_types.trampoline_types() {
153             let shared_ty = types[module_ty];
154             let trampoline_shared_ty = registry.trampoline_type(shared_ty);
155             trampolines
156                 .insert(trampoline_shared_ty, Some(module_trampoline_ty).into())
157                 .expect("reserved space");
158         }
159 
160         // Finally, to ensure that no matter which API from which layer
161         // (`wasmtime::runtime::vm` vs `wasmtime_environ`, etc...) we use to
162         // grab an entity's type, we will always end up with a type that has
163         // `VMSharedTypeIndex` rather than `ModuleInternedTypeIndex` type
164         // references, we canonicalize both the `ModuleTypes` and
165         // `wasmtime_environ::Module`s for runtime usage. All our type-of-X APIs
166         // ultimately use one of these two structures.
167         module_types.canonicalize_for_runtime_usage(&mut |idx| types[idx]);
168         for module in env_modules {
169             module.canonicalize_for_runtime_usage(&mut |idx| types[idx]);
170         }
171 
172         Ok(TypeCollection {
173             engine,
174             rec_groups,
175             types,
176             trampolines,
177         })
178     }
179 }
180 
181 impl TypeCollection {
182     /// Treats the type collection as a map from a module type index to
183     /// registered shared type indexes.
184     ///
185     /// This is used for looking up module shared type indexes during module
186     /// instantiation.
as_module_map(&self) -> &TryPrimaryMap<ModuleInternedTypeIndex, VMSharedTypeIndex>187     pub fn as_module_map(&self) -> &TryPrimaryMap<ModuleInternedTypeIndex, VMSharedTypeIndex> {
188         &self.types
189     }
190 
191     /// Gets the shared type index given a module type index.
192     #[inline]
shared_type(&self, index: ModuleInternedTypeIndex) -> Option<VMSharedTypeIndex>193     pub fn shared_type(&self, index: ModuleInternedTypeIndex) -> Option<VMSharedTypeIndex> {
194         let shared_ty = self.types.get(index).copied();
195         log::trace!("TypeCollection::shared_type({index:?}) -> {shared_ty:?}");
196         shared_ty
197     }
198 
199     /// Get the module-level type index of the trampoline type for the given
200     /// engine-level function type, if any.
201     ///
202     /// This allows callers to look up the pre-compiled wasm-to-native
203     /// trampoline in this type collection's associated module.
204     ///
205     /// See the docs for `WasmFuncType::trampoline_type` for details on
206     /// trampoline types.
207     #[inline]
trampoline_type(&self, ty: VMSharedTypeIndex) -> Option<ModuleInternedTypeIndex>208     pub fn trampoline_type(&self, ty: VMSharedTypeIndex) -> Option<ModuleInternedTypeIndex> {
209         let trampoline_ty = self.trampolines[ty].expand();
210         log::trace!("TypeCollection::trampoline_type({ty:?}) -> {trampoline_ty:?}");
211         trampoline_ty
212     }
213 }
214 
215 impl Drop for TypeCollection {
drop(&mut self)216     fn drop(&mut self) {
217         if !self.rec_groups.is_empty() {
218             self.engine
219                 .signatures()
220                 .0
221                 .write()
222                 .unregister_type_collection(self);
223         }
224     }
225 }
226 
227 #[inline]
shared_type_index_to_slab_id(index: VMSharedTypeIndex) -> SlabId228 fn shared_type_index_to_slab_id(index: VMSharedTypeIndex) -> SlabId {
229     assert!(!index.is_reserved_value());
230     SlabId::from_raw(index.bits())
231 }
232 
233 #[inline]
slab_id_to_shared_type_index(id: SlabId) -> VMSharedTypeIndex234 fn slab_id_to_shared_type_index(id: SlabId) -> VMSharedTypeIndex {
235     let index = VMSharedTypeIndex::new(id.into_raw());
236     assert!(!index.is_reserved_value());
237     index
238 }
239 
240 /// A Wasm type that has been registered in the engine's `TypeRegistry`.
241 ///
242 /// Prevents its associated type from being unregistered while it is alive.
243 ///
244 /// Automatically unregisters the type on drop. (Unless other `RegisteredTypes`
245 /// are keeping the type registered).
246 ///
247 /// Dereferences to its underlying `WasmSubType`.
248 pub struct RegisteredType {
249     engine: Engine,
250     entry: RecGroupEntry,
251     ty: Arc<WasmSubType>,
252     index: VMSharedTypeIndex,
253     layout: Option<GcLayout>,
254 }
255 
256 impl Debug for RegisteredType {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result257     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258         let RegisteredType {
259             engine: _,
260             entry: _,
261             ty,
262             index,
263             layout,
264         } = self;
265         f.debug_struct("RegisteredType")
266             .field("index", index)
267             .field("ty", ty)
268             .field("layout", layout)
269             .finish_non_exhaustive()
270     }
271 }
272 
273 impl Clone for RegisteredType {
clone(&self) -> Self274     fn clone(&self) -> Self {
275         self.clone_panic_on_oom()
276     }
277 }
278 
279 impl TryClone for RegisteredType {
try_clone(&self) -> Result<Self, OutOfMemory>280     fn try_clone(&self) -> Result<Self, OutOfMemory> {
281         self.engine.signatures().debug_assert_contains(self.index);
282         self.entry.incref("RegisteredType::try_clone");
283         Ok(RegisteredType {
284             engine: self.engine.clone(),
285             entry: self.entry.clone(),
286             ty: self.ty.clone(),
287             index: self.index,
288             layout: self.layout.clone(),
289         })
290     }
291 }
292 
293 impl Drop for RegisteredType {
drop(&mut self)294     fn drop(&mut self) {
295         self.engine.signatures().debug_assert_contains(self.index);
296         if self.entry.decref("RegisteredType::drop") {
297             self.engine
298                 .signatures()
299                 .0
300                 .write()
301                 .unregister_entry(self.entry.clone());
302         }
303     }
304 }
305 
306 impl core::ops::Deref for RegisteredType {
307     type Target = WasmSubType;
308 
deref(&self) -> &Self::Target309     fn deref(&self) -> &Self::Target {
310         &self.ty
311     }
312 }
313 
314 impl PartialEq for RegisteredType {
eq(&self, other: &Self) -> bool315     fn eq(&self, other: &Self) -> bool {
316         self.engine.signatures().debug_assert_contains(self.index);
317         other.engine.signatures().debug_assert_contains(other.index);
318 
319         let eq = self.index == other.index && Engine::same(&self.engine, &other.engine);
320 
321         if cfg!(debug_assertions) && eq {
322             // If they are the same, then their rec group entries and
323             // `WasmSubType`s had better also be the same.
324             assert!(Arc::ptr_eq(&self.entry.0, &other.entry.0));
325             assert_eq!(self.ty, other.ty);
326         }
327 
328         eq
329     }
330 }
331 
332 impl Eq for RegisteredType {}
333 
334 impl Hash for RegisteredType {
hash<H: Hasher>(&self, state: &mut H)335     fn hash<H: Hasher>(&self, state: &mut H) {
336         self.engine.signatures().debug_assert_contains(self.index);
337         let ptr = Arc::as_ptr(&self.entry.0);
338         ptr.hash(state);
339     }
340 }
341 
342 impl RegisteredType {
343     /// Constructs a new `RegisteredType`, registering the given type with the
344     /// engine's `TypeRegistry`.
new(engine: &Engine, ty: WasmSubType) -> Result<RegisteredType, OutOfMemory>345     pub fn new(engine: &Engine, ty: WasmSubType) -> Result<RegisteredType, OutOfMemory> {
346         let (entry, index, ty, layout) = {
347             log::trace!("RegisteredType::new({ty:?})");
348 
349             let gc_runtime = engine.gc_runtime().map(|rt| &**rt);
350             let mut inner = engine.signatures().0.write();
351 
352             // It shouldn't be possible for users to construct non-canonical
353             // types via the embedding API, and the only other types they can
354             // get are already-canonicalized types from modules, so we shouldn't
355             // ever get non-canonical types here. Furthermore, this is only
356             // called internally to Wasmtime, so we shouldn't ever have an
357             // engine mismatch; those should be caught earlier.
358             inner.assert_canonicalized_for_runtime_usage_in_this_registry(&ty);
359 
360             let entry = inner.register_singleton_rec_group(gc_runtime, ty)?;
361 
362             let index = entry.0.shared_type_indices[0];
363             let id = shared_type_index_to_slab_id(index);
364             let ty = inner.types[id].clone().unwrap();
365             let layout = inner.type_to_gc_layout.get(index).and_then(|l| l.clone());
366 
367             (entry, index, ty, layout)
368         };
369 
370         Ok(RegisteredType::from_parts(
371             engine.clone(),
372             entry,
373             index,
374             ty,
375             layout,
376         ))
377     }
378 
379     /// Create an owning handle to the given index's associated type.
380     ///
381     /// This will prevent the associated type from being unregistered as long as
382     /// the returned `RegisteredType` is kept alive.
root(engine: &Engine, index: VMSharedTypeIndex) -> RegisteredType383     pub fn root(engine: &Engine, index: VMSharedTypeIndex) -> RegisteredType {
384         engine.signatures().debug_assert_contains(index);
385 
386         let (entry, ty, layout) = {
387             let id = shared_type_index_to_slab_id(index);
388             let inner = engine.signatures().0.read();
389 
390             let ty = inner.types[id].clone().unwrap();
391             let entry = inner.type_to_rec_group[index].clone().unwrap();
392             let layout = inner.type_to_gc_layout.get(index).and_then(|l| l.clone());
393 
394             // NB: make sure to incref while the lock is held to prevent:
395             //
396             // * This thread: read locks registry, gets entry E, unlocks registry
397             // * Other thread: drops `RegisteredType` for entry E, decref
398             //   reaches zero, write locks registry, unregisters entry
399             // * This thread: increfs entry, but it isn't in the registry anymore
400             entry.incref("RegisteredType::root");
401 
402             (entry, ty, layout)
403         };
404 
405         RegisteredType::from_parts(engine.clone(), entry, index, ty, layout)
406     }
407 
408     /// Construct a new `RegisteredType`.
409     ///
410     /// It is the caller's responsibility to ensure that the entry's reference
411     /// count has already been incremented.
from_parts( engine: Engine, entry: RecGroupEntry, index: VMSharedTypeIndex, ty: Arc<WasmSubType>, layout: Option<GcLayout>, ) -> Self412     fn from_parts(
413         engine: Engine,
414         entry: RecGroupEntry,
415         index: VMSharedTypeIndex,
416         ty: Arc<WasmSubType>,
417         layout: Option<GcLayout>,
418     ) -> Self {
419         log::trace!(
420             "RegisteredType::from_parts({engine:?}, {entry:?}, {index:?}, {ty:?}, {layout:?})"
421         );
422         engine.signatures().debug_assert_contains(index);
423         debug_assert!(
424             entry.0.registrations.load(Acquire) != 0,
425             "entry should have a non-zero registration count"
426         );
427         RegisteredType {
428             engine,
429             entry,
430             ty,
431             index,
432             layout,
433         }
434     }
435 
436     /// Get the engine whose registry this type is registered within.
engine(&self) -> &Engine437     pub fn engine(&self) -> &Engine {
438         &self.engine
439     }
440 
441     /// Get this registered type's index.
index(&self) -> VMSharedTypeIndex442     pub fn index(&self) -> VMSharedTypeIndex {
443         self.index
444     }
445 
446     /// Get this registered type's GC layout, if any.
447     ///
448     /// Only struct and array types have GC layouts; function types do not have
449     /// layouts.
450     #[cfg(feature = "gc")]
layout(&self) -> Option<&GcLayout>451     pub fn layout(&self) -> Option<&GcLayout> {
452         self.layout.as_ref()
453     }
454 }
455 
456 /// An entry in the type registry.
457 ///
458 /// Implements `Borrow`, `Eq`, and `Hash` by forwarding to the underlying Wasm
459 /// rec group, so that this can be a hash consing key. (We can't use
460 /// `Arc<RecGroupEntryInner>` directly for this purpose because `Arc<T>` doesn't
461 /// implement `Borrow<U>` when `T: Borrow<U>`).
462 #[derive(Clone)]
463 struct RecGroupEntry(Arc<RecGroupEntryInner>);
464 
465 impl TryClone for RecGroupEntry {
try_clone(&self) -> Result<Self, OutOfMemory>466     fn try_clone(&self) -> Result<Self, OutOfMemory> {
467         Ok(self.clone())
468     }
469 }
470 
471 impl Debug for RecGroupEntry {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result472     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
473         struct FormatAsPtr<'a, P>(&'a P);
474         impl<P: fmt::Pointer> Debug for FormatAsPtr<'_, P> {
475             fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
476                 write!(f, "{:#p}", *self.0)
477             }
478         }
479 
480         f.debug_tuple("RecGroupEntry")
481             .field(&FormatAsPtr(&self.0))
482             .finish()
483     }
484 }
485 
486 struct RecGroupEntryInner {
487     /// The Wasm rec group, canonicalized for hash consing.
488     hash_consing_key: WasmRecGroup,
489 
490     /// The shared type indices for each type in this rec group.
491     shared_type_indices: Box<[VMSharedTypeIndex]>,
492 
493     /// The number of times that this entry has been registered in the
494     /// `TypeRegistryInner`.
495     ///
496     /// This is an atomic counter so that cloning a `RegisteredType`, and
497     /// temporarily keeping a type registered, doesn't require locking the full
498     /// registry.
499     registrations: AtomicUsize,
500 
501     /// Whether this entry has already been unregistered from the
502     /// `TypeRegistryInner`.
503     ///
504     /// This flag exists to detect and avoid double-unregistration bugs that
505     /// could otherwise occur in rare cases. See the comments in
506     /// `TypeRegistryInner::unregister_type` for details.
507     unregistered: AtomicBool,
508 }
509 
510 impl PartialEq for RecGroupEntry {
eq(&self, other: &Self) -> bool511     fn eq(&self, other: &Self) -> bool {
512         self.0.hash_consing_key == other.0.hash_consing_key
513     }
514 }
515 
516 impl Eq for RecGroupEntry {}
517 
518 impl Hash for RecGroupEntry {
hash<H: Hasher>(&self, state: &mut H)519     fn hash<H: Hasher>(&self, state: &mut H) {
520         self.0.hash_consing_key.hash(state);
521     }
522 }
523 
524 impl Borrow<WasmRecGroup> for RecGroupEntry {
525     #[inline]
borrow(&self) -> &WasmRecGroup526     fn borrow(&self) -> &WasmRecGroup {
527         &self.0.hash_consing_key
528     }
529 }
530 
531 impl RecGroupEntry {
532     /// Allocate a new, default `RecGroupEntryInner`.
533     ///
534     /// The resulting value should be initialized via `RecGroupEntry::init`.
new_inner() -> Result<Arc<RecGroupEntryInner>, OutOfMemory>535     fn new_inner() -> Result<Arc<RecGroupEntryInner>, OutOfMemory> {
536         try_new(RecGroupEntryInner {
537             hash_consing_key: Default::default(),
538             shared_type_indices: Default::default(),
539             registrations: AtomicUsize::new(0),
540             unregistered: AtomicBool::new(false),
541         })
542     }
543 
544     /// Initialize a `Arc<RecGroupEntryInner>` (as created by
545     /// `RecGroupEntry::new_inner`) and wrap it into a `RecGroupEntry`.
init( mut inner: Arc<RecGroupEntryInner>, key: WasmRecGroup, shared_types: Box<[VMSharedTypeIndex]>, ) -> Self546     fn init(
547         mut inner: Arc<RecGroupEntryInner>,
548         key: WasmRecGroup,
549         shared_types: Box<[VMSharedTypeIndex]>,
550     ) -> Self {
551         debug_assert!(key.is_canonicalized_for_hash_consing());
552 
553         let RecGroupEntryInner {
554             hash_consing_key,
555             shared_type_indices,
556             registrations: _,
557             unregistered: _,
558         } = Arc::get_mut(&mut inner).expect("must have the only handle to this inner entry");
559 
560         debug_assert!(shared_type_indices.is_empty());
561         *shared_type_indices = shared_types;
562 
563         debug_assert!(hash_consing_key.types.is_empty());
564         *hash_consing_key = key;
565 
566         RecGroupEntry(inner)
567     }
568 
569     /// Increment the registration count.
incref(&self, why: &str)570     fn incref(&self, why: &str) {
571         let old_count = self.0.registrations.fetch_add(1, AcqRel);
572         log::trace!("incref({self:?}) -> count {}: {why}", old_count + 1);
573     }
574 
575     /// Decrement the registration count and return `true` if the registration
576     /// count reached zero and this entry should be removed from the registry.
577     #[must_use = "caller must remove entry from registry if `decref` returns `true`"]
decref(&self, why: &str) -> bool578     fn decref(&self, why: &str) -> bool {
579         let old_count = self.0.registrations.fetch_sub(1, AcqRel);
580         debug_assert_ne!(old_count, 0);
581         log::trace!("decref({self:?}) -> count {}: {why}", old_count - 1);
582         old_count == 1
583     }
584 }
585 
586 #[derive(Debug, Default)]
587 struct TypeRegistryInner {
588     // A hash map from a canonicalized-for-hash-consing rec group to its
589     // `VMSharedTypeIndex`es.
590     //
591     // There is an entry in this map for every rec group we have already
592     // registered. Before registering new rec groups, we first check this map to
593     // see if we've already registered an identical rec group that we should
594     // reuse instead.
595     hash_consing_map: TryHashSet<RecGroupEntry>,
596 
597     // A map from `VMSharedTypeIndex::bits()` to the type index's associated
598     // Wasm type.
599     //
600     // These types are always canonicalized for runtime usage.
601     //
602     // These are only `None` during the process of inserting a new rec group
603     // into the registry, where we need registered `VMSharedTypeIndex`es for
604     // forward type references within the rec group, but have not actually
605     // inserted all the types within the rec group yet.
606     types: Slab<Option<Arc<WasmSubType>>>,
607 
608     // A map that lets you walk backwards from a `VMSharedTypeIndex` to its
609     // `RecGroupEntry`.
610     type_to_rec_group: TrySecondaryMap<VMSharedTypeIndex, Option<RecGroupEntry>>,
611 
612     // A map from a registered type to its complete list of supertypes.
613     //
614     // The supertypes are ordered from super- to subtype, i.e. the immediate
615     // parent supertype is the last element and the least-upper-bound of all
616     // supertypes is the first element.
617     //
618     // Types without any supertypes are omitted from this map. This means that
619     // we never allocate any backing storage for this map when Wasm GC is not in
620     // use.
621     type_to_supertypes: TrySecondaryMap<VMSharedTypeIndex, Option<Box<[VMSharedTypeIndex]>>>,
622 
623     // A map from each registered function type to its trampoline type.
624     //
625     // Note that when a function type is its own trampoline type, then we omit
626     // the entry in this map as a memory optimization. This means that if only
627     // core Wasm function types are ever used, then we will never allocate any
628     // backing storage for this map. As a nice bonus, this also avoids cycles (a
629     // function type referencing itself) that our naive reference counting
630     // doesn't play well with.
631     type_to_trampoline: TrySecondaryMap<VMSharedTypeIndex, PackedOption<VMSharedTypeIndex>>,
632 
633     // A map from each registered GC type to its layout.
634     //
635     // Function types do not have an entry in this map. Similar to the
636     // `type_to_{supertypes,trampoline}` maps, we completely omit the `None`
637     // entries for these types as a memory optimization.
638     type_to_gc_layout: TrySecondaryMap<VMSharedTypeIndex, Option<GcLayout>>,
639 
640     // An explicit stack of entries that we are in the middle of dropping. Used
641     // to avoid recursion when dropping a type that is holding the last
642     // reference to another type, etc...
643     drop_stack: TryVec<RecGroupEntry>,
644 }
645 
646 impl TypeRegistryInner {
647     #[inline]
648     #[track_caller]
debug_assert_registered(&self, index: VMSharedTypeIndex)649     fn debug_assert_registered(&self, index: VMSharedTypeIndex) {
650         debug_assert!(
651             !index.is_reserved_value(),
652             "should have an actual VMSharedTypeIndex, not the reserved value"
653         );
654         debug_assert!(
655             self.types.contains(shared_type_index_to_slab_id(index)),
656             "registry's slab should contain {index:?}",
657         );
658         debug_assert!(
659             self.types[shared_type_index_to_slab_id(index)].is_some(),
660             "registry's slab should actually contain a type for {index:?}",
661         );
662         debug_assert!(
663             self.type_to_rec_group[index].is_some(),
664             "{index:?} should have an associated rec group entry"
665         );
666     }
667 
668     #[inline]
669     #[track_caller]
debug_assert_all_registered(&self, entry: &RecGroupEntry)670     fn debug_assert_all_registered(&self, entry: &RecGroupEntry) {
671         if cfg!(debug_assertions) {
672             for &ty in &entry.0.shared_type_indices {
673                 self.debug_assert_registered(ty);
674             }
675         }
676     }
677 
register_module_types( &mut self, gc_runtime: Option<&dyn GcRuntime>, types: &ModuleTypes, ) -> Result< ( TryVec<RecGroupEntry>, TryPrimaryMap<ModuleInternedTypeIndex, VMSharedTypeIndex>, ), OutOfMemory, >678     fn register_module_types(
679         &mut self,
680         gc_runtime: Option<&dyn GcRuntime>,
681         types: &ModuleTypes,
682     ) -> Result<
683         (
684             TryVec<RecGroupEntry>,
685             TryPrimaryMap<ModuleInternedTypeIndex, VMSharedTypeIndex>,
686         ),
687         OutOfMemory,
688     > {
689         log::trace!("Start registering module types");
690 
691         // The engine's type registry entries for these module types.
692         let mut entries = TryVec::with_capacity(types.rec_groups().len())?;
693 
694         // The map from a module type index to an engine type index for these
695         // module types.
696         let mut map = TryPrimaryMap::<ModuleInternedTypeIndex, VMSharedTypeIndex>::with_capacity(
697             types.wasm_types().len(),
698         )?;
699 
700         for (_rec_group_index, module_group) in types.rec_groups() {
701             let entry = self.register_rec_group(
702                 gc_runtime,
703                 &map,
704                 module_group.clone(),
705                 iter_entity_range(module_group.clone()).map(|ty| types[ty].try_clone()),
706             )?;
707 
708             // Update the module-to-engine map with this rec group's
709             // newly-registered types.
710             for (module_ty, engine_ty) in
711                 iter_entity_range(module_group).zip(entry.0.shared_type_indices.iter())
712             {
713                 let module_ty2 = map.push(*engine_ty).expect("reserved capacity");
714                 assert_eq!(module_ty, module_ty2);
715             }
716 
717             entries.push(entry).expect("reserved capacity");
718         }
719 
720         log::trace!("End registering module types");
721 
722         Ok((entries, map))
723     }
724 
725     /// Register a rec group in this registry.
726     ///
727     /// The rec group may be either module-level canonical (i.e. straight from
728     /// `wasmparser`) or engine-level canonicalized for runtime usage in this
729     /// registry. It may *not* be engine-level canonicalized for hash consing or
730     /// engine-level canonicalized for a different type registry instance.
731     ///
732     /// If this rec group is determined to be a duplicate of an
733     /// already-registered rec group, the existing rec group is reused.
734     ///
735     /// Parameters:
736     ///
737     /// * `map`: A map that we use to canonicalize inter-group type references
738     ///   from module-canonical to engine-canonical indices. This must contain
739     ///   entries for each inter-group type reference that this rec group
740     ///   contains.
741     ///
742     /// * `range`: The range of (module-level) types defined by this rec
743     ///   group. This is used to determine which type references inside this rec
744     ///   group are inter- vs intra-group.
745     ///
746     /// * `types`: The types defined within this rec group. Must have the same
747     ///   length as `range`.
748     ///
749     /// The returned entry will have already had its reference count incremented
750     /// on behalf of callers.
register_rec_group( &mut self, gc_runtime: Option<&dyn GcRuntime>, map: &TryPrimaryMap<ModuleInternedTypeIndex, VMSharedTypeIndex>, range: Range<ModuleInternedTypeIndex>, types: impl ExactSizeIterator<Item = Result<WasmSubType, OutOfMemory>>, ) -> Result<RecGroupEntry, OutOfMemory>751     fn register_rec_group(
752         &mut self,
753         gc_runtime: Option<&dyn GcRuntime>,
754         map: &TryPrimaryMap<ModuleInternedTypeIndex, VMSharedTypeIndex>,
755         range: Range<ModuleInternedTypeIndex>,
756         types: impl ExactSizeIterator<Item = Result<WasmSubType, OutOfMemory>>,
757     ) -> Result<RecGroupEntry, OutOfMemory> {
758         log::trace!("registering rec group of length {}", types.len());
759         debug_assert_eq!(iter_entity_range(range.clone()).len(), types.len());
760 
761         // We need two different canonicalization of this rec group: one for
762         // hash-consing and another for runtime usage within this
763         // engine. However, we only need the latter if this is a new rec group
764         // that hasn't been registered before. Therefore, we only eagerly create
765         // the hash-consing canonicalized version, and while we lazily
766         // canonicalize for runtime usage in this engine, we must still eagerly
767         // clone and set aside the original, non-canonicalized types for that
768         // potential engine canonicalization eventuality.
769         let mut non_canon_types = TryVec::with_capacity(types.len())?;
770         let hash_consing_key = WasmRecGroup {
771             types: types
772                 .zip(iter_entity_range(range.clone()))
773                 .map(|(ty, module_index)| {
774                     let mut ty = ty?;
775                     non_canon_types
776                         .push((module_index, ty.try_clone()?))
777                         .expect("reserved capacity");
778                     ty.canonicalize_for_hash_consing(range.clone(), &mut |idx| {
779                         debug_assert!(idx < range.clone().start);
780                         map[idx]
781                     });
782                     Ok(ty)
783                 })
784                 .try_collect::<Box<[_]>, OutOfMemory>()?,
785         };
786 
787         // Any references in the hash-consing key to types outside of this rec
788         // group may only be to fully-registered types.
789         if cfg!(debug_assertions) {
790             hash_consing_key
791                 .trace_engine_indices::<_, ()>(&mut |index| Ok(self.debug_assert_registered(index)))
792                 .unwrap();
793         }
794 
795         // If we've already registered this rec group before, reuse it.
796         if let Some(entry) = self.hash_consing_map.get(&hash_consing_key) {
797             log::trace!("hash-consing map hit: reusing {entry:?}");
798             assert_eq!(entry.0.unregistered.load(Acquire), false);
799             self.debug_assert_all_registered(entry);
800             entry.incref("hash-consing map hit");
801             Ok(entry.clone())
802         } else {
803             log::trace!("hash-consing map miss: making new registration");
804             self.register_new_rec_group(gc_runtime, map, range, hash_consing_key, non_canon_types)
805         }
806     }
807 
808     /// Register a new rec group that is not already in this registry.
register_new_rec_group( &mut self, gc_runtime: Option<&(dyn GcRuntime + 'static)>, map: &TryPrimaryMap<ModuleInternedTypeIndex, VMSharedTypeIndex>, range: Range<ModuleInternedTypeIndex>, hash_consing_key: WasmRecGroup, mut non_canon_types: TryVec<(ModuleInternedTypeIndex, WasmSubType)>, ) -> Result<RecGroupEntry, OutOfMemory>809     fn register_new_rec_group(
810         &mut self,
811         gc_runtime: Option<&(dyn GcRuntime + 'static)>,
812         map: &TryPrimaryMap<ModuleInternedTypeIndex, VMSharedTypeIndex>,
813         range: Range<ModuleInternedTypeIndex>,
814         hash_consing_key: WasmRecGroup,
815         mut non_canon_types: TryVec<(ModuleInternedTypeIndex, WasmSubType)>,
816     ) -> Result<RecGroupEntry, OutOfMemory> {
817         debug_assert!(hash_consing_key.is_canonicalized_for_hash_consing());
818         debug_assert_eq!(self.hash_consing_map.contains(&hash_consing_key), false);
819 
820         // XXX: Do not write new entries to `self` or its arenas in this block
821         // of code, as we will not rollback those changes on OOM.
822         let entry = {
823             // First, pre-allocate capacity in our arenas for this rec group, as
824             // much as possible.
825             let num_types = non_canon_types.len();
826             self.reserve_capacity_for_rec_group(num_types)?;
827             let mut shared_type_indices = TryVec::new();
828             shared_type_indices.reserve_exact(num_types)?;
829             let entry_inner = RecGroupEntry::new_inner()?;
830 
831             // Assign a `VMSharedTypeIndex` to each type.
832             let shared_type_indices =
833                 self.assign_shared_type_indices(&non_canon_types, shared_type_indices);
834 
835             // Initialize the rec group entry, now that we have all its parts.
836             RecGroupEntry::init(entry_inner, hash_consing_key, shared_type_indices)
837         };
838 
839         // XXX: From the creation of the `Undo` and afterwards, we can write to
840         // `self` and its arenas. We must, however, always ensure that the
841         // `Undo` fully rolls back the changes made below, so keep this in mind
842         // if adding/removing/changing the arenas in `self`.
843         let did_incref = Cell::new(false);
844         let entry2 = entry.clone();
845         let mut registry = Undo::new(self, |registry| {
846             registry.remove_entry_impl(&entry2, did_incref.get());
847             registry.drain_drop_stack();
848         });
849 
850         registry.canonicalize_entry_types_for_runtime_usage(
851             map,
852             &entry,
853             non_canon_types.iter_mut().map(|(_, ty)| ty),
854             range.clone(),
855         );
856 
857         // Inter-group edges: increment the referenced group's ref
858         // count, because these other rec groups shouldn't be dropped
859         // while this new rec group is still alive.
860         registry.incref_outgoing_edges(&entry);
861         did_incref.set(true);
862 
863         registry.insert_entry_types(&entry, non_canon_types.into_iter().map(|(_, ty)| ty))?;
864         registry.insert_entry_rec_groups(&entry);
865         registry.insert_entry_supertypes(&entry)?;
866         registry.insert_entry_trampolines(gc_runtime, &entry)?;
867         registry.insert_entry_gc_layouts(gc_runtime, &entry)?;
868 
869         let is_new_entry = registry.hash_consing_map.insert(entry.clone())?;
870         debug_assert!(is_new_entry);
871 
872         // We successfully registered the rec group! Commit our changes,
873         // increment the rec group's registration count to one, and return it.
874         registry.debug_assert_all_registered(&entry);
875         Undo::commit(registry);
876         entry.incref("creation");
877         Ok(entry)
878     }
879 
880     /// Implementation of removing a rec group entry.
881     ///
882     /// Shared by both unregistering a rec group and rolling back changes when
883     /// we OOM in `register_new_rec_group`.
884     ///
885     /// The order of removals should be the reverse of the insertions in
886     /// `register_new_rec_group`.
remove_entry_impl(&mut self, entry: &RecGroupEntry, should_decref: bool)887     fn remove_entry_impl(&mut self, entry: &RecGroupEntry, should_decref: bool) {
888         // All entries we are removing should *really* be ready for
889         // unregistration.
890         assert_eq!(entry.0.registrations.load(Acquire), 0);
891         assert_eq!(entry.0.unregistered.load(Acquire), false);
892 
893         // We are taking responsibility for unregistering this entry, so prevent
894         // anyone else from attempting to do it again. See race (2) in the
895         // comment in `unregister_entry`.
896         entry.0.unregistered.store(true, Release);
897 
898         // Perform the removals in the opposite order as the insertions in
899         // `register_new_rec_group`.
900         self.hash_consing_map.remove(&entry.0.hash_consing_key);
901         self.remove_entry_gc_layouts(&entry);
902         self.remove_entry_trampolines(&entry);
903         self.remove_entry_supertypes(&entry);
904         self.remove_entry_rec_groups(&entry);
905         self.remove_entry_types(&entry);
906         if should_decref {
907             self.decref_outgoing_edges(&entry);
908         }
909     }
910 
911     /// Insert the rec group entry's types into the `self.types` arena.
insert_entry_types( &mut self, entry: &RecGroupEntry, sub_types: impl ExactSizeIterator<Item = WasmSubType>, ) -> Result<(), OutOfMemory>912     fn insert_entry_types(
913         &mut self,
914         entry: &RecGroupEntry,
915         sub_types: impl ExactSizeIterator<Item = WasmSubType>,
916     ) -> Result<(), OutOfMemory> {
917         debug_assert_eq!(entry.0.shared_type_indices.len(), sub_types.len());
918         for (ty_idx, sub_ty) in entry.0.shared_type_indices.iter().copied().zip(sub_types) {
919             // NB: Do not use
920             // `assert_canonicalized_for_runtime_usage_in_this_registry` because
921             // the types this type references may not be fully registered yet,
922             // as we are in the middle of a rec group's registration.
923             debug_assert!(sub_ty.is_canonicalized_for_runtime_usage());
924 
925             let id = shared_type_index_to_slab_id(ty_idx);
926             debug_assert!(self.types.contains(id));
927             debug_assert!(self.types[id].is_none());
928             self.types[id] = Some(try_new(sub_ty)?);
929         }
930         Ok(())
931     }
932 
933     /// Remove the rec group entry's types into the `self.types` arena.
remove_entry_types(&mut self, entry: &RecGroupEntry)934     fn remove_entry_types(&mut self, entry: &RecGroupEntry) {
935         for &ty in &entry.0.shared_type_indices {
936             let id = shared_type_index_to_slab_id(ty);
937             debug_assert!(self.types.contains(id));
938             self.types.dealloc(id);
939         }
940     }
941 
942     /// Insert the rec group backlink for each type in this rec group.
insert_entry_rec_groups(&mut self, entry: &RecGroupEntry)943     fn insert_entry_rec_groups(&mut self, entry: &RecGroupEntry) {
944         debug_assert!(self.type_to_rec_group.capacity() >= self.types.len());
945         for &ty in &entry.0.shared_type_indices {
946             debug_assert!(self.type_to_rec_group[ty].is_none());
947             debug_assert!(ty.index() < self.type_to_rec_group.capacity());
948             self.type_to_rec_group
949                 .insert(ty, Some(entry.clone()))
950                 .expect("reserved capacity");
951         }
952     }
953 
954     /// Remove the rec group's type's backlinks to the rec group.
remove_entry_rec_groups(&mut self, entry: &RecGroupEntry)955     fn remove_entry_rec_groups(&mut self, entry: &RecGroupEntry) {
956         for &ty in &entry.0.shared_type_indices {
957             debug_assert!(ty.index() < self.type_to_rec_group.capacity());
958             self.type_to_rec_group.remove(ty);
959         }
960     }
961 
962     /// Insert the supertype information for this rec group's types.
insert_entry_supertypes(&mut self, entry: &RecGroupEntry) -> Result<(), OutOfMemory>963     fn insert_entry_supertypes(&mut self, entry: &RecGroupEntry) -> Result<(), OutOfMemory> {
964         for &ty in &entry.0.shared_type_indices {
965             let id = shared_type_index_to_slab_id(ty);
966             if let Some(supertype) = self.types[id].as_ref().unwrap().supertype {
967                 debug_assert!(self.type_to_supertypes.capacity() <= self.types.capacity());
968                 if self.type_to_supertypes.capacity() < self.types.capacity() {
969                     log::trace!("type_to_supertypes.resize({})", self.types.capacity());
970                     self.type_to_supertypes.resize(self.types.capacity())?;
971                 }
972 
973                 let supertype = supertype.unwrap_engine_type_index();
974                 let supers_supertypes = self.supertypes(supertype);
975                 let supertypes = supers_supertypes
976                     .iter()
977                     .copied()
978                     .chain(iter::once(supertype))
979                     .try_collect()?;
980 
981                 self.type_to_supertypes
982                     .insert(ty, Some(supertypes))
983                     .expect("reserved capacity");
984             }
985         }
986         Ok(())
987     }
988 
989     /// Remove the rec group's associated supertype information.
remove_entry_supertypes(&mut self, entry: &RecGroupEntry)990     fn remove_entry_supertypes(&mut self, entry: &RecGroupEntry) {
991         // We delay allocating space for this arena until we actually see a type
992         // with a supertype, so early exit if we never allocated any space.
993         if self.type_to_supertypes.capacity() == 0 {
994             return;
995         }
996 
997         for &ty in &entry.0.shared_type_indices {
998             self.type_to_supertypes.remove(ty);
999         }
1000     }
1001 
1002     /// Insert the trampoline information for this rec group's types.
insert_entry_trampolines( &mut self, gc_runtime: Option<&(dyn GcRuntime + 'static)>, entry: &RecGroupEntry, ) -> Result<(), OutOfMemory>1003     fn insert_entry_trampolines(
1004         &mut self,
1005         gc_runtime: Option<&(dyn GcRuntime + 'static)>,
1006         entry: &RecGroupEntry,
1007     ) -> Result<(), OutOfMemory> {
1008         for &ty_idx in &entry.0.shared_type_indices {
1009             let id = shared_type_index_to_slab_id(ty_idx);
1010             debug_assert!(self.types.contains(id));
1011             debug_assert!(self.types[id].is_some());
1012             let sub_ty = self.types[id].as_ref().unwrap();
1013 
1014             let Some(func_ty) = sub_ty.as_func() else {
1015                 continue;
1016             };
1017 
1018             let trampoline_ty = match func_ty.trampoline_type()? {
1019                 TryCow::Owned(ty) => ty,
1020                 TryCow::Borrowed(ty) if !sub_ty.is_final || sub_ty.supertype.is_some() => {
1021                     ty.try_clone()?
1022                 }
1023                 TryCow::Borrowed(_) => {
1024                     // The function type is its own trampoline type. Leave its entry
1025                     // in `type_to_trampoline` empty to signal this.
1026                     debug_assert!(func_ty.is_trampoline_type());
1027                     log::trace!("trampoline_type({ty_idx:?}) = {ty_idx:?}");
1028                     continue;
1029                 }
1030             };
1031 
1032             debug_assert!(self.type_to_trampoline.capacity() <= self.types.capacity());
1033             if self.type_to_trampoline.capacity() < self.types.capacity() {
1034                 log::trace!("type_to_trampoline.resize({})", self.types.capacity());
1035                 self.type_to_trampoline.resize(self.types.capacity())?;
1036             }
1037 
1038             let trampoline_sub_ty = WasmSubType {
1039                 is_final: true,
1040                 supertype: None,
1041                 composite_type: wasmtime_environ::WasmCompositeType {
1042                     shared: sub_ty.composite_type.shared,
1043                     inner: wasmtime_environ::WasmCompositeInnerType::Func(trampoline_ty),
1044                 },
1045             };
1046 
1047             // This will recursively call into rec group registration, but at
1048             // most once since trampoline function types are their own
1049             // trampoline type.
1050             let trampoline_entry =
1051                 self.register_singleton_rec_group(gc_runtime, trampoline_sub_ty)?;
1052 
1053             assert_eq!(trampoline_entry.0.shared_type_indices.len(), 1);
1054             let trampoline_index = trampoline_entry.0.shared_type_indices[0];
1055             self.debug_assert_registered(trampoline_index);
1056             debug_assert_ne!(ty_idx, trampoline_index);
1057 
1058             self.type_to_trampoline
1059                 .insert(ty_idx, Some(trampoline_index).into())
1060                 .expect("reserved capacity");
1061 
1062             log::trace!("trampoline_type({ty_idx:?}) = {trampoline_index:?}");
1063         }
1064 
1065         Ok(())
1066     }
1067 
1068     /// Remove the rec group's associated trampoline information.
remove_entry_trampolines(&mut self, entry: &RecGroupEntry)1069     fn remove_entry_trampolines(&mut self, entry: &RecGroupEntry) {
1070         // We delay allocating space for this arena until we actually see a
1071         // function type that is not its own trampoline type, so early exit if
1072         // we never allocated any space.
1073         if self.type_to_trampoline.capacity() == 0 {
1074             return;
1075         }
1076 
1077         for &ty in &entry.0.shared_type_indices {
1078             if let Some(tramp_ty) = self.type_to_trampoline.remove(ty).and_then(|x| x.expand()) {
1079                 self.debug_assert_registered(tramp_ty);
1080                 let tramp_entry = self.type_to_rec_group[tramp_ty].as_ref().unwrap();
1081                 if tramp_entry.decref("dropping rec group's trampoline-type references") {
1082                     self.push_to_drop_stack(tramp_entry.clone());
1083                 }
1084             }
1085         }
1086 
1087         self.drain_drop_stack();
1088     }
1089 
1090     /// Insert the GC layout information for this rec group's types.
insert_entry_gc_layouts( &mut self, gc_runtime: Option<&(dyn GcRuntime + 'static)>, entry: &RecGroupEntry, ) -> Result<(), OutOfMemory>1091     fn insert_entry_gc_layouts(
1092         &mut self,
1093         gc_runtime: Option<&(dyn GcRuntime + 'static)>,
1094         entry: &RecGroupEntry,
1095     ) -> Result<(), OutOfMemory> {
1096         let Some(gc_runtime) = gc_runtime else {
1097             // If we don't have a GC runtime, then we won't have any GC types
1098             // and don't have to remember any GC layouts.
1099             debug_assert!(entry.0.shared_type_indices.iter().all(|ty| {
1100                 let id = shared_type_index_to_slab_id(*ty);
1101                 let sub_ty = self.types[id].as_ref().unwrap();
1102                 assert!(!sub_ty.composite_type.shared);
1103                 matches!(
1104                     &sub_ty.composite_type.inner,
1105                     wasmtime_environ::WasmCompositeInnerType::Func(_)
1106                 )
1107             }));
1108             return Ok(());
1109         };
1110 
1111         for &ty_idx in &entry.0.shared_type_indices {
1112             let id = shared_type_index_to_slab_id(ty_idx);
1113             let sub_ty = self.types[id].as_ref().unwrap();
1114             assert!(!sub_ty.composite_type.shared);
1115 
1116             let gc_layout = match &sub_ty.composite_type.inner {
1117                 wasmtime_environ::WasmCompositeInnerType::Func(_) => continue,
1118                 wasmtime_environ::WasmCompositeInnerType::Array(a) => {
1119                     gc_runtime.layouts().array_layout(a).into()
1120                 }
1121                 wasmtime_environ::WasmCompositeInnerType::Struct(s) => {
1122                     try_new::<Arc<_>>(gc_runtime.layouts().struct_layout(s))?.into()
1123                 }
1124                 wasmtime_environ::WasmCompositeInnerType::Exn(e) => {
1125                     try_new::<Arc<_>>(gc_runtime.layouts().exn_layout(e))?.into()
1126                 }
1127                 wasmtime_environ::WasmCompositeInnerType::Cont(_) => continue, // FIXME: #10248 stack switching support.
1128             };
1129 
1130             debug_assert!(self.type_to_gc_layout.capacity() <= self.types.capacity());
1131             if self.type_to_gc_layout.capacity() < self.types.capacity() {
1132                 log::trace!("type_to_gc_layout.resize({})", self.types.capacity());
1133                 self.type_to_gc_layout.resize(self.types.capacity())?;
1134             }
1135 
1136             self.type_to_gc_layout
1137                 .insert(ty_idx, Some(gc_layout))
1138                 .expect("reserved capacity");
1139         }
1140 
1141         Ok(())
1142     }
1143 
1144     /// Remove the rec group's associated GC layout information.
remove_entry_gc_layouts(&mut self, entry: &RecGroupEntry)1145     fn remove_entry_gc_layouts(&mut self, entry: &RecGroupEntry) {
1146         // We delay allocating space for this arena until we actually see a GC
1147         // type, so early exit if we never allocated any space.
1148         if self.type_to_gc_layout.capacity() == 0 {
1149             return;
1150         }
1151 
1152         for ty in &entry.0.shared_type_indices {
1153             self.type_to_gc_layout.remove(*ty);
1154         }
1155     }
1156 
1157     /// Assign a `VMSharedTypeIndex` to each type in a rec group.
assign_shared_type_indices( &mut self, non_canon_types: &[(ModuleInternedTypeIndex, WasmSubType)], mut shared_type_indices: TryVec<VMSharedTypeIndex>, ) -> Box<[VMSharedTypeIndex]>1158     fn assign_shared_type_indices(
1159         &mut self,
1160         non_canon_types: &[(ModuleInternedTypeIndex, WasmSubType)],
1161         mut shared_type_indices: TryVec<VMSharedTypeIndex>,
1162     ) -> Box<[VMSharedTypeIndex]> {
1163         debug_assert_eq!(non_canon_types.len(), shared_type_indices.capacity());
1164         debug_assert!(shared_type_indices.is_empty());
1165         debug_assert!(
1166             self.types.capacity() - self.types.len() >= non_canon_types.len(),
1167             "should have reserved capacity"
1168         );
1169         for (module_index, ty) in non_canon_types.iter() {
1170             let engine_index =
1171                 slab_id_to_shared_type_index(self.types.alloc(None).expect("have capacity"));
1172             log::trace!("reserved {engine_index:?} for {module_index:?} = non-canonical {ty:?}");
1173             shared_type_indices
1174                 .push(engine_index)
1175                 .expect("reserved capacity");
1176         }
1177 
1178         debug_assert_eq!(shared_type_indices.len(), shared_type_indices.capacity());
1179         shared_type_indices
1180             .into_boxed_slice()
1181             .expect("capacity should be exact")
1182     }
1183 
1184     /// For each cross-rec group type reference inside `entry`, increment the
1185     /// referenced rec group's registration count.
incref_outgoing_edges(&mut self, entry: &RecGroupEntry)1186     fn incref_outgoing_edges(&mut self, entry: &RecGroupEntry) {
1187         let key = &entry.0.hash_consing_key;
1188         debug_assert!(key.is_canonicalized_for_hash_consing());
1189         key.trace_engine_indices::<_, ()>(&mut |index| {
1190             self.debug_assert_registered(index);
1191             let other_entry = self.type_to_rec_group[index].as_ref().unwrap();
1192             assert_eq!(other_entry.0.unregistered.load(Acquire), false);
1193             other_entry.incref("new rec group's type references");
1194             Ok(())
1195         })
1196         .unwrap();
1197     }
1198 
1199     /// For each cross-rec group type reference inside `entry`, decrement the
1200     /// referenced rec group's registration count.
decref_outgoing_edges(&mut self, entry: &RecGroupEntry)1201     fn decref_outgoing_edges(&mut self, entry: &RecGroupEntry) {
1202         let key = &entry.0.hash_consing_key;
1203         debug_assert!(key.is_canonicalized_for_hash_consing());
1204         key.trace_engine_indices::<_, ()>(&mut |other_index| {
1205             self.debug_assert_registered(other_index);
1206             let other_entry = self.type_to_rec_group[other_index].as_ref().unwrap();
1207             assert_eq!(other_entry.0.unregistered.load(Acquire), false);
1208             if other_entry.decref("dropping rec group's type references") {
1209                 self.push_to_drop_stack(other_entry.clone());
1210             }
1211             Ok(())
1212         })
1213         .unwrap();
1214         self.drain_drop_stack();
1215     }
1216 
1217     /// Pre-allocate capacity for a rec group of size `num_types` in our various
1218     /// arenas.
reserve_capacity_for_rec_group(&mut self, num_types: usize) -> Result<(), OutOfMemory>1219     fn reserve_capacity_for_rec_group(&mut self, num_types: usize) -> Result<(), OutOfMemory> {
1220         log::trace!("Reserving capacity for rec group of {num_types} types");
1221 
1222         // NB: Destructure to make sure we are considering every field in
1223         // `self`.
1224         let TypeRegistryInner {
1225             hash_consing_map,
1226             types,
1227             type_to_rec_group,
1228             drop_stack,
1229 
1230             // We only insert entries into these `SecondaryMap`s if/when we
1231             // encounter GC types (which is rare, even when GC types are
1232             // enabled).
1233             type_to_supertypes: _,
1234             type_to_trampoline: _,
1235             type_to_gc_layout: _,
1236         } = self;
1237 
1238         // This map contains rec groups, not types, so only reserve space for
1239         // one additional entry.
1240         log::trace!("    hash_consing_map.reserve(1)");
1241         hash_consing_map.reserve(1)?;
1242 
1243         log::trace!("    types.reserve({num_types})");
1244         types.reserve(num_types)?;
1245         let types_capacity = types.capacity();
1246 
1247         log::trace!("    type_to_rec_group.resize({types_capacity})");
1248         type_to_rec_group.resize(types_capacity)?;
1249 
1250         log::trace!("    type_to_rec_group.reserve({types_capacity})");
1251         debug_assert!(drop_stack.is_empty());
1252         drop_stack.reserve(types_capacity)?;
1253 
1254         Ok(())
1255     }
1256 
1257     /// Canonicalize all the types inside a rec group for runtime usage within
1258     /// this registry.
canonicalize_entry_types_for_runtime_usage<'a>( &self, map: &TryPrimaryMap<ModuleInternedTypeIndex, VMSharedTypeIndex>, entry: &RecGroupEntry, sub_tys: impl ExactSizeIterator<Item = &'a mut WasmSubType>, range: Range<ModuleInternedTypeIndex>, )1259     fn canonicalize_entry_types_for_runtime_usage<'a>(
1260         &self,
1261         map: &TryPrimaryMap<ModuleInternedTypeIndex, VMSharedTypeIndex>,
1262         entry: &RecGroupEntry,
1263         sub_tys: impl ExactSizeIterator<Item = &'a mut WasmSubType>,
1264         range: Range<ModuleInternedTypeIndex>,
1265     ) {
1266         debug_assert_eq!(sub_tys.len(), entry.0.shared_type_indices.len());
1267         for (engine_index, ty) in entry.0.shared_type_indices.iter().copied().zip(sub_tys) {
1268             self.canonicalize_type_for_runtime_usage(map, &entry, engine_index, ty, range.clone());
1269         }
1270     }
1271 
1272     /// Canonicalize one type for runtime usage within this registry.
canonicalize_type_for_runtime_usage( &self, map: &TryPrimaryMap<ModuleInternedTypeIndex, VMSharedTypeIndex>, entry: &RecGroupEntry, engine_index: VMSharedTypeIndex, ty: &mut WasmSubType, range: Range<ModuleInternedTypeIndex>, )1273     fn canonicalize_type_for_runtime_usage(
1274         &self,
1275         map: &TryPrimaryMap<ModuleInternedTypeIndex, VMSharedTypeIndex>,
1276         entry: &RecGroupEntry,
1277         engine_index: VMSharedTypeIndex,
1278         ty: &mut WasmSubType,
1279         range: Range<ModuleInternedTypeIndex>,
1280     ) {
1281         log::trace!("canonicalizing {engine_index:?} for runtime usage");
1282         ty.canonicalize_for_runtime_usage(&mut |module_index| {
1283             if module_index < range.start {
1284                 let engine_index = map[module_index];
1285                 log::trace!("    cross-group {module_index:?} becomes {engine_index:?}");
1286                 self.debug_assert_registered(engine_index);
1287                 engine_index
1288             } else {
1289                 assert!(module_index < range.end);
1290                 let rec_group_offset = module_index.as_u32() - range.start.as_u32();
1291                 let rec_group_offset = usize::try_from(rec_group_offset).unwrap();
1292                 let engine_index = entry.0.shared_type_indices[rec_group_offset];
1293                 log::trace!("    intra-group {module_index:?} becomes {engine_index:?}");
1294                 assert!(!engine_index.is_reserved_value());
1295                 assert!(
1296                     self.types
1297                         .contains(shared_type_index_to_slab_id(engine_index))
1298                 );
1299                 engine_index
1300             }
1301         });
1302     }
1303 
1304     /// Assert that the given type is canonicalized for runtime usage this
1305     /// registry, and that every type it references is also registered in this
1306     /// registry.
1307     #[track_caller]
assert_canonicalized_for_runtime_usage_in_this_registry(&self, ty: &WasmSubType)1308     fn assert_canonicalized_for_runtime_usage_in_this_registry(&self, ty: &WasmSubType) {
1309         ty.trace::<_, ()>(&mut |index| match index {
1310             EngineOrModuleTypeIndex::RecGroup(_) | EngineOrModuleTypeIndex::Module(_) => {
1311                 panic!("not canonicalized for runtime usage: {ty:?}")
1312             }
1313             EngineOrModuleTypeIndex::Engine(idx) => {
1314                 self.debug_assert_registered(idx);
1315                 Ok(())
1316             }
1317         })
1318         .unwrap();
1319     }
1320 
1321     /// Get the supertypes list for the given type.
1322     ///
1323     /// The supertypes are listed in super-to-sub order. `ty` itself is not
1324     /// included in the list.
supertypes(&self, ty: VMSharedTypeIndex) -> &[VMSharedTypeIndex]1325     fn supertypes(&self, ty: VMSharedTypeIndex) -> &[VMSharedTypeIndex] {
1326         self.type_to_supertypes
1327             .get(ty)
1328             .and_then(|s| s.as_deref())
1329             .unwrap_or(&[])
1330     }
1331 
1332     /// Register a rec group consisting of a single type.
1333     ///
1334     /// The type must already be canonicalized for runtime usage in this
1335     /// registry.
1336     ///
1337     /// The returned entry will have already had its reference count incremented
1338     /// on behalf of callers.
register_singleton_rec_group( &mut self, gc_runtime: Option<&dyn GcRuntime>, ty: WasmSubType, ) -> Result<RecGroupEntry, OutOfMemory>1339     fn register_singleton_rec_group(
1340         &mut self,
1341         gc_runtime: Option<&dyn GcRuntime>,
1342         ty: WasmSubType,
1343     ) -> Result<RecGroupEntry, OutOfMemory> {
1344         self.assert_canonicalized_for_runtime_usage_in_this_registry(&ty);
1345 
1346         // This type doesn't have any module-level type references, since it is
1347         // already canonicalized for runtime usage in this registry, so an empty
1348         // map suffices.
1349         let map = TryPrimaryMap::default();
1350 
1351         // This must have `range.len() == 1`, even though we know this type
1352         // doesn't have any intra-group type references, to satisfy
1353         // `register_rec_group`'s preconditions.
1354         let range = ModuleInternedTypeIndex::from_bits(u32::MAX - 1)
1355             ..ModuleInternedTypeIndex::from_bits(u32::MAX);
1356 
1357         self.register_rec_group(gc_runtime, &map, range, iter::once(Ok(ty)))
1358     }
1359 
1360     /// Unregister all of a type collection's rec groups.
unregister_type_collection(&mut self, collection: &TypeCollection)1361     fn unregister_type_collection(&mut self, collection: &TypeCollection) {
1362         log::trace!("Begin unregistering `TypeCollection`");
1363         for entry in &collection.rec_groups {
1364             self.debug_assert_all_registered(entry);
1365             if entry.decref("TypeRegistryInner::unregister_type_collection") {
1366                 self.unregister_entry(entry.clone());
1367             }
1368         }
1369         log::trace!("Finished unregistering `TypeCollection`");
1370     }
1371 
1372     /// Remove a zero-refcount entry from the registry.
1373     ///
1374     /// This does *not* decrement the entry's registration count, it should
1375     /// instead be invoked only after a previous decrement operation observed
1376     /// zero remaining registrations.
unregister_entry(&mut self, entry: RecGroupEntry)1377     fn unregister_entry(&mut self, entry: RecGroupEntry) {
1378         log::trace!("Attempting to unregister {entry:?}");
1379         debug_assert!(self.drop_stack.is_empty());
1380 
1381         // There are two races to guard against before we can unregister the
1382         // entry, even though it was on the drop stack:
1383         //
1384         // 1. Although an entry has to reach zero registrations before it is
1385         //    enqueued in the drop stack, we need to double check whether the
1386         //    entry is *still* at zero registrations. This is because someone
1387         //    else can resurrect the entry in between when the
1388         //    zero-registrations count was first observed and when we actually
1389         //    acquire the lock to unregister it. In this example, we have
1390         //    threads A and B, an existing rec group entry E, and a rec group
1391         //    entry E' that is a duplicate of E:
1392         //
1393         //    Thread A                        | Thread B
1394         //    --------------------------------+-----------------------------
1395         //    acquire(type registry lock)     |
1396         //                                    |
1397         //                                    | decref(E) --> 0
1398         //                                    |
1399         //                                    | block_on(type registry lock)
1400         //                                    |
1401         //    register(E') == incref(E) --> 1 |
1402         //                                    |
1403         //    release(type registry lock)     |
1404         //                                    |
1405         //                                    | acquire(type registry lock)
1406         //                                    |
1407         //                                    | unregister(E)         !!!!!!
1408         //
1409         //    If we aren't careful, we can unregister a type while it is still
1410         //    in use!
1411         //
1412         //    The fix in this case is that we skip unregistering the entry if
1413         //    its reference count is non-zero, since that means it was
1414         //    concurrently resurrected and is now in use again.
1415         //
1416         // 2. In a slightly more convoluted version of (1), where an entry is
1417         //    resurrected but then dropped *again*, someone might attempt to
1418         //    unregister an entry a second time:
1419         //
1420         //    Thread A                        | Thread B
1421         //    --------------------------------|-----------------------------
1422         //    acquire(type registry lock)     |
1423         //                                    |
1424         //                                    | decref(E) --> 0
1425         //                                    |
1426         //                                    | block_on(type registry lock)
1427         //                                    |
1428         //    register(E') == incref(E) --> 1 |
1429         //                                    |
1430         //    release(type registry lock)     |
1431         //                                    |
1432         //    decref(E) --> 0                 |
1433         //                                    |
1434         //    acquire(type registry lock)     |
1435         //                                    |
1436         //    unregister(E)                   |
1437         //                                    |
1438         //    release(type registry lock)     |
1439         //                                    |
1440         //                                    | acquire(type registry lock)
1441         //                                    |
1442         //                                    | unregister(E)         !!!!!!
1443         //
1444         //    If we aren't careful, we can unregister a type twice, which leads
1445         //    to panics and registry corruption!
1446         //
1447         //    To detect this scenario and avoid the double-unregistration bug,
1448         //    we maintain an `unregistered` flag on entries. We set this flag
1449         //    once an entry is unregistered and therefore, even if it is
1450         //    enqueued in the drop stack multiple times, we only actually
1451         //    unregister the entry the first time.
1452         //
1453         // A final note: we don't need to worry about any concurrent
1454         // modifications during the middle of this function's execution, only
1455         // between (a) when we first observed a zero-registrations count and
1456         // decided to unregister the type, and (b) when we acquired the type
1457         // registry's lock so that we could perform that unregistration. This is
1458         // because this method has exclusive access to `&mut self` -- that is,
1459         // we have a write lock on the whole type registry -- and therefore no
1460         // one else can create new references to this zero-registration entry
1461         // and bring it back to life (which would require finding it in
1462         // `self.hash_consing_map`, which no one else has access to, because we
1463         // now have an exclusive lock on `self`).
1464 
1465         // Handle scenario (1) from above.
1466         let registrations = entry.0.registrations.load(Acquire);
1467         if registrations != 0 {
1468             log::trace!(
1469                 "    {entry:?} was concurrently resurrected and no longer has \
1470                  zero registrations (registrations -> {registrations})",
1471             );
1472             assert_eq!(entry.0.unregistered.load(Acquire), false);
1473             return;
1474         }
1475 
1476         // Handle scenario (2) from above.
1477         if entry.0.unregistered.load(Acquire) {
1478             log::trace!(
1479                 "    {entry:?} was concurrently resurrected, dropped again, \
1480                  and already unregistered"
1481             );
1482             return;
1483         }
1484 
1485         // Okay, we are really going to unregister this entry. Enqueue it on the
1486         // drop stack.
1487         debug_assert!(self.drop_stack.capacity() >= self.types.capacity());
1488         self.push_to_drop_stack(entry);
1489         self.drain_drop_stack();
1490     }
1491 
1492     /// Enqueue an entry for dropping.
1493     ///
1494     /// Should always be followed by some call to `drain_drop_stack()`, although
1495     /// it is fine to associate many `push_to_drop_stack()` calls with one final
1496     /// `drain_drop_stack()` call.
push_to_drop_stack(&mut self, entry: RecGroupEntry)1497     fn push_to_drop_stack(&mut self, entry: RecGroupEntry) {
1498         log::trace!("Pushing entry to drop stack: {entry:?}");
1499         self.drop_stack
1500             .push(entry)
1501             .expect("always have space in `drop_stack` for all types");
1502     }
1503 
1504     /// Keep unregistering entries until the drop stack is empty.
1505     ///
1506     /// This is logically a recursive process where if we unregister a type that
1507     /// was the only thing keeping another type alive, we then recursively
1508     /// unregister that other type as well. However, we use an explicit drop
1509     /// stack to avoid recursion and the potential stack overflows that
1510     /// recursion implies.
drain_drop_stack(&mut self)1511     fn drain_drop_stack(&mut self) {
1512         if self.drop_stack.is_empty() {
1513             return;
1514         }
1515         log::trace!("Draining drop stack");
1516         while let Some(entry) = self.drop_stack.pop() {
1517             log::trace!("Begin unregistering {entry:?}");
1518             self.debug_assert_all_registered(&entry);
1519             self.remove_entry_impl(&entry, true);
1520             log::trace!("End unregistering {entry:?}");
1521         }
1522     }
1523 }
1524 
1525 // `TypeRegistryInner` implements `Drop` in debug builds to assert that
1526 // all types have been unregistered for the registry.
1527 #[cfg(debug_assertions)]
1528 impl Drop for TypeRegistryInner {
drop(&mut self)1529     fn drop(&mut self) {
1530         let TypeRegistryInner {
1531             hash_consing_map,
1532             types,
1533             type_to_rec_group,
1534             type_to_supertypes,
1535             type_to_trampoline,
1536             type_to_gc_layout,
1537             drop_stack,
1538         } = self;
1539         assert!(
1540             hash_consing_map.is_empty(),
1541             "type registry not empty: hash consing map is not empty: {hash_consing_map:#?}"
1542         );
1543         assert!(
1544             types.is_empty(),
1545             "type registry not empty: types slab is not empty: {types:#?}"
1546         );
1547         assert!(
1548             type_to_rec_group.is_empty() || type_to_rec_group.values().all(|x| x.is_none()),
1549             "type registry not empty: type-to-rec-group map is not empty: {type_to_rec_group:#?}"
1550         );
1551         assert!(
1552             type_to_supertypes.is_empty() || type_to_supertypes.values().all(|x| x.is_none()),
1553             "type registry not empty: type-to-supertypes map is not empty: {type_to_supertypes:#?}"
1554         );
1555         assert!(
1556             type_to_trampoline.is_empty() || type_to_trampoline.values().all(|x| x.is_none()),
1557             "type registry not empty: type-to-trampoline map is not empty: {type_to_trampoline:#?}"
1558         );
1559         assert!(
1560             type_to_gc_layout.is_empty() || type_to_gc_layout.values().all(|x| x.is_none()),
1561             "type registry not empty: type-to-gc-layout map is not empty: {type_to_gc_layout:#?}"
1562         );
1563         assert!(
1564             drop_stack.is_empty(),
1565             "type registry not empty: drop stack is not empty: {drop_stack:#?}"
1566         );
1567     }
1568 }
1569 
1570 /// Implements a shared type registry.
1571 ///
1572 /// WebAssembly requires that the caller and callee types in an indirect
1573 /// call must match. To implement this efficiently, keep a registry of all
1574 /// types, shared by all instances, so that call sites can just do an
1575 /// index comparison.
1576 #[derive(Debug)]
1577 pub struct TypeRegistry(RwLock<TypeRegistryInner>);
1578 
1579 impl TypeRegistry {
1580     /// Creates a new shared type registry.
new() -> Self1581     pub fn new() -> Self {
1582         Self(RwLock::new(TypeRegistryInner::default()))
1583     }
1584 
1585     #[inline]
debug_assert_contains(&self, index: VMSharedTypeIndex)1586     pub fn debug_assert_contains(&self, index: VMSharedTypeIndex) {
1587         if cfg!(debug_assertions) {
1588             self.0.read().debug_assert_registered(index);
1589         }
1590     }
1591 
1592     /// Looks up a function type from a shared type index.
1593     ///
1594     /// This does *NOT* prevent the type from being unregistered while you are
1595     /// still using the resulting value! Use the `RegisteredType::root`
1596     /// constructor if you need to ensure that property and you don't have some
1597     /// other mechanism already keeping the type registered.
borrow(&self, index: VMSharedTypeIndex) -> Option<Arc<WasmSubType>>1598     pub fn borrow(&self, index: VMSharedTypeIndex) -> Option<Arc<WasmSubType>> {
1599         let id = shared_type_index_to_slab_id(index);
1600         let inner = self.0.read();
1601         inner.types.get(id).and_then(|ty| ty.clone())
1602     }
1603 
1604     /// Get the GC layout for the given index's type.
1605     ///
1606     /// Returns `None` for types that do not have GC layouts (i.e. function
1607     /// types).
layout(&self, index: VMSharedTypeIndex) -> Option<GcLayout>1608     pub fn layout(&self, index: VMSharedTypeIndex) -> Option<GcLayout> {
1609         let inner = self.0.read();
1610         inner.type_to_gc_layout.get(index).and_then(|l| l.clone())
1611     }
1612 
1613     /// Get the trampoline type for the given function type index.
1614     ///
1615     /// Panics for non-function type indices.
trampoline_type(&self, index: VMSharedTypeIndex) -> VMSharedTypeIndex1616     pub fn trampoline_type(&self, index: VMSharedTypeIndex) -> VMSharedTypeIndex {
1617         let slab_id = shared_type_index_to_slab_id(index);
1618         let inner = self.0.read();
1619         inner.debug_assert_registered(index);
1620 
1621         let ty = inner.types[slab_id].as_ref().unwrap();
1622         debug_assert!(
1623             ty.is_func(),
1624             "cannot get the trampoline type of a non-function type: {index:?} = {ty:?}"
1625         );
1626 
1627         match inner.type_to_trampoline.get(index).and_then(|x| x.expand()) {
1628             Some(ty) => ty,
1629             None => {
1630                 // The function type is its own trampoline type.
1631                 index
1632             }
1633         }
1634     }
1635 
1636     /// Is type `sub` a subtype of `sup`?
1637     #[inline]
is_subtype(&self, sub: VMSharedTypeIndex, sup: VMSharedTypeIndex) -> bool1638     pub fn is_subtype(&self, sub: VMSharedTypeIndex, sup: VMSharedTypeIndex) -> bool {
1639         if cfg!(debug_assertions) {
1640             self.0.read().debug_assert_registered(sub);
1641             self.0.read().debug_assert_registered(sup);
1642         }
1643 
1644         if sub == sup {
1645             return true;
1646         }
1647 
1648         self.is_subtype_slow(sub, sup)
1649     }
1650 
is_subtype_slow(&self, sub: VMSharedTypeIndex, sup: VMSharedTypeIndex) -> bool1651     fn is_subtype_slow(&self, sub: VMSharedTypeIndex, sup: VMSharedTypeIndex) -> bool {
1652         // Do the O(1) subtype checking trick:
1653         //
1654         // In a type system with single inheritance, the subtyping relationships
1655         // between all types form a set of trees. The root of each tree is a
1656         // type that has no supertype; each node's immediate children are the
1657         // types that directly subtype that node.
1658         //
1659         // For example, consider these types:
1660         //
1661         //     class Base {}
1662         //     class A subtypes Base {}
1663         //     class B subtypes Base {}
1664         //     class C subtypes A {}
1665         //     class D subtypes A {}
1666         //     class E subtypes C {}
1667         //
1668         // These types produce the following tree:
1669         //
1670         //                Base
1671         //               /    \
1672         //              A      B
1673         //             / \
1674         //            C   D
1675         //           /
1676         //          E
1677         //
1678         // Note the following properties:
1679         //
1680         // 1. If `sub` is a subtype of `sup` (either directly or transitively)
1681         //    then `sup` *must* be on the path from `sub` up to the root of
1682         //    `sub`'s tree.
1683         //
1684         // 2. Additionally, `sup` *must* be the `i`th node down from the root in
1685         //    that path, where `i` is the length of the path from `sup` to its
1686         //    tree's root.
1687         //
1688         // Therefore, if we have the path to the root for each type (we do) then
1689         // we can simply check if `sup` is at index `supertypes(sup).len()`
1690         // within `supertypes(sub)`.
1691         let inner = self.0.read();
1692         let sub_supertypes = inner.supertypes(sub);
1693         let sup_supertypes = inner.supertypes(sup);
1694         sub_supertypes.get(sup_supertypes.len()) == Some(&sup)
1695     }
1696 }
1697