1 //! Working with GC `struct` objects.
2 
3 use crate::runtime::vm::VMGcRef;
4 use crate::store::StoreId;
5 use crate::vm::{VMGcHeader, VMStructRef};
6 use crate::{
7     prelude::*,
8     store::{AutoAssertNoGc, StoreContextMut, StoreOpaque},
9     AsContext, AsContextMut, EqRef, GcHeapOutOfMemory, GcRefImpl, GcRootIndex, HeapType,
10     ManuallyRooted, RefType, Rooted, StructType, Val, ValRaw, ValType, WasmTy,
11 };
12 use crate::{AnyRef, FieldType};
13 use core::mem::{self, MaybeUninit};
14 use wasmtime_environ::{GcLayout, GcStructLayout, VMGcKind, VMSharedTypeIndex};
15 
16 /// An allocator for a particular Wasm GC struct type.
17 ///
18 /// Every `StructRefPre` is associated with a particular
19 /// [`Store`][crate::Store] and a particular [StructType][crate::StructType].
20 ///
21 /// Reusing an allocator across many allocations amortizes some per-type runtime
22 /// overheads inside Wasmtime. A `StructRefPre` is to `StructRef`s as an
23 /// `InstancePre` is to `Instance`s.
24 ///
25 /// # Example
26 ///
27 /// ```
28 /// use wasmtime::*;
29 ///
30 /// # fn foo() -> Result<()> {
31 /// let mut config = Config::new();
32 /// config.wasm_function_references(true);
33 /// config.wasm_gc(true);
34 ///
35 /// let engine = Engine::new(&config)?;
36 /// let mut store = Store::new(&engine, ());
37 ///
38 /// // Define a struct type.
39 /// let struct_ty = StructType::new(
40 ///    store.engine(),
41 ///    [FieldType::new(Mutability::Var, StorageType::I8)],
42 /// )?;
43 ///
44 /// // Create an allocator for the struct type.
45 /// let allocator = StructRefPre::new(&mut store, struct_ty);
46 ///
47 /// {
48 ///     let mut scope = RootScope::new(&mut store);
49 ///
50 ///     // Allocate a bunch of instances of our struct type using the same
51 ///     // allocator! This is faster than creating a new allocator for each
52 ///     // instance we want to allocate.
53 ///     for i in 0..10 {
54 ///         StructRef::new(&mut scope, &allocator, &[Val::I32(i)])?;
55 ///     }
56 /// }
57 /// # Ok(())
58 /// # }
59 /// # foo().unwrap();
60 /// ```
61 pub struct StructRefPre {
62     store_id: StoreId,
63     ty: StructType,
64 }
65 
66 impl StructRefPre {
67     /// Create a new `StructRefPre` that is associated with the given store
68     /// and type.
69     pub fn new(mut store: impl AsContextMut, ty: StructType) -> Self {
70         Self::_new(store.as_context_mut().0, ty)
71     }
72 
73     pub(crate) fn _new(store: &mut StoreOpaque, ty: StructType) -> Self {
74         store.insert_gc_host_alloc_type(ty.registered_type().clone());
75         let store_id = store.id();
76 
77         StructRefPre { store_id, ty }
78     }
79 
80     pub(crate) fn layout(&self) -> &GcStructLayout {
81         self.ty
82             .registered_type()
83             .layout()
84             .expect("struct types have a layout")
85             .unwrap_struct()
86     }
87 
88     pub(crate) fn type_index(&self) -> VMSharedTypeIndex {
89         self.ty.registered_type().index()
90     }
91 }
92 
93 /// A reference to a GC-managed `struct` instance.
94 ///
95 /// WebAssembly `struct`s are static, fixed-length, ordered sequences of
96 /// fields. Fields are named by index, not by identifier; in this way, they are
97 /// similar to Rust's tuples. Each field is mutable or constant and stores
98 /// unpacked [`Val`][crate::Val]s or packed 8-/16-bit integers.
99 ///
100 /// Like all WebAssembly references, these are opaque and unforgeable to Wasm:
101 /// they cannot be faked and Wasm cannot, for example, cast the integer
102 /// `0x12345678` into a reference, pretend it is a valid `structref`, and trick
103 /// the host into dereferencing it and segfaulting or worse.
104 ///
105 /// Note that you can also use `Rooted<StructRef>` and
106 /// `ManuallyRooted<StructRef>` as a type parameter with
107 /// [`Func::typed`][crate::Func::typed]- and
108 /// [`Func::wrap`][crate::Func::wrap]-style APIs.
109 ///
110 /// # Example
111 ///
112 /// ```
113 /// use wasmtime::*;
114 ///
115 /// # fn foo() -> Result<()> {
116 /// let mut config = Config::new();
117 /// config.wasm_function_references(true);
118 /// config.wasm_gc(true);
119 ///
120 /// let engine = Engine::new(&config)?;
121 /// let mut store = Store::new(&engine, ());
122 ///
123 /// // Define a struct type.
124 /// let struct_ty = StructType::new(
125 ///    store.engine(),
126 ///    [FieldType::new(Mutability::Var, StorageType::I8)],
127 /// )?;
128 ///
129 /// // Create an allocator for the struct type.
130 /// let allocator = StructRefPre::new(&mut store, struct_ty);
131 ///
132 /// {
133 ///     let mut scope = RootScope::new(&mut store);
134 ///
135 ///     // Allocate an instance of the struct type.
136 ///     let my_struct = StructRef::new(&mut scope, &allocator, &[Val::I32(42)])?;
137 ///
138 ///     // That instance's field should have the expected value.
139 ///     let val = my_struct.field(&mut scope, 0)?.unwrap_i32();
140 ///     assert_eq!(val, 42);
141 ///
142 ///     // And we can update the field's value because it is a mutable field.
143 ///     my_struct.set_field(&mut scope, 0, Val::I32(36))?;
144 ///     let new_val = my_struct.field(&mut scope, 0)?.unwrap_i32();
145 ///     assert_eq!(new_val, 36);
146 /// }
147 /// # Ok(())
148 /// # }
149 /// # foo().unwrap();
150 /// ```
151 #[derive(Debug)]
152 #[repr(transparent)]
153 pub struct StructRef {
154     pub(super) inner: GcRootIndex,
155 }
156 
157 unsafe impl GcRefImpl for StructRef {
158     #[allow(private_interfaces)]
159     fn transmute_ref(index: &GcRootIndex) -> &Self {
160         // Safety: `StructRef` is a newtype of a `GcRootIndex`.
161         let me: &Self = unsafe { mem::transmute(index) };
162 
163         // Assert we really are just a newtype of a `GcRootIndex`.
164         assert!(matches!(
165             me,
166             Self {
167                 inner: GcRootIndex { .. },
168             }
169         ));
170 
171         me
172     }
173 }
174 
175 impl Rooted<StructRef> {
176     /// Upcast this `structref` into an `anyref`.
177     #[inline]
178     pub fn to_anyref(self) -> Rooted<AnyRef> {
179         self.unchecked_cast()
180     }
181 
182     /// Upcast this `structref` into an `eqref`.
183     #[inline]
184     pub fn to_eqref(self) -> Rooted<EqRef> {
185         self.unchecked_cast()
186     }
187 }
188 
189 impl ManuallyRooted<StructRef> {
190     /// Upcast this `structref` into an `anyref`.
191     #[inline]
192     pub fn to_anyref(self) -> ManuallyRooted<AnyRef> {
193         self.unchecked_cast()
194     }
195 
196     /// Upcast this `structref` into an `eqref`.
197     #[inline]
198     pub fn to_eqref(self) -> ManuallyRooted<EqRef> {
199         self.unchecked_cast()
200     }
201 }
202 
203 impl StructRef {
204     /// Synchronously allocate a new `struct` and get a reference to it.
205     ///
206     /// # Automatic Garbage Collection
207     ///
208     /// If the GC heap is at capacity, and there isn't room for allocating this
209     /// new struct, then this method will automatically trigger a synchronous
210     /// collection in an attempt to free up space in the GC heap.
211     ///
212     /// # Errors
213     ///
214     /// If the given `fields` values' types do not match the field types of the
215     /// `allocator`'s struct type, an error is returned.
216     ///
217     /// If the allocation cannot be satisfied because the GC heap is currently
218     /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory]
219     /// error is returned. The allocation might succeed on a second attempt if
220     /// you drop some rooted GC references and try again.
221     ///
222     /// # Panics
223     ///
224     /// Panics if your engine is configured for async; use
225     /// [`StructRef::new_async`][crate::StructRef::new_async] to perform
226     /// synchronous allocation instead.
227     ///
228     /// Panics if the allocator, or any of the field values, is not associated
229     /// with the given store.
230     pub fn new(
231         mut store: impl AsContextMut,
232         allocator: &StructRefPre,
233         fields: &[Val],
234     ) -> Result<Rooted<StructRef>> {
235         Self::_new(store.as_context_mut().0, allocator, fields)
236     }
237 
238     pub(crate) fn _new(
239         store: &mut StoreOpaque,
240         allocator: &StructRefPre,
241         fields: &[Val],
242     ) -> Result<Rooted<StructRef>> {
243         assert!(
244             !store.async_support(),
245             "use `StructRef::new_async` with asynchronous stores"
246         );
247         Self::type_check_fields(store, allocator, fields)?;
248         store.retry_after_gc((), |store, ()| {
249             Self::new_unchecked(store, allocator, fields)
250         })
251     }
252 
253     /// Asynchronously allocate a new `struct` and get a reference to it.
254     ///
255     /// # Automatic Garbage Collection
256     ///
257     /// If the GC heap is at capacity, and there isn't room for allocating this
258     /// new struct, then this method will automatically trigger a synchronous
259     /// collection in an attempt to free up space in the GC heap.
260     ///
261     /// # Errors
262     ///
263     /// If the given `fields` values' types do not match the field types of the
264     /// `allocator`'s struct type, an error is returned.
265     ///
266     /// If the allocation cannot be satisfied because the GC heap is currently
267     /// out of memory, then a [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory]
268     /// error is returned. The allocation might succeed on a second attempt if
269     /// you drop some rooted GC references and try again.
270     ///
271     /// # Panics
272     ///
273     /// Panics if your engine is not configured for async; use
274     /// [`StructRef::new`][crate::StructRef::new] to perform synchronous
275     /// allocation instead.
276     ///
277     /// Panics if the allocator, or any of the field values, is not associated
278     /// with the given store.
279     #[cfg(feature = "async")]
280     pub async fn new_async(
281         mut store: impl AsContextMut,
282         allocator: &StructRefPre,
283         fields: &[Val],
284     ) -> Result<Rooted<StructRef>> {
285         Self::_new_async(store.as_context_mut().0, allocator, fields).await
286     }
287 
288     #[cfg(feature = "async")]
289     pub(crate) async fn _new_async(
290         store: &mut StoreOpaque,
291         allocator: &StructRefPre,
292         fields: &[Val],
293     ) -> Result<Rooted<StructRef>> {
294         assert!(
295             store.async_support(),
296             "use `StructRef::new` with synchronous stores"
297         );
298         Self::type_check_fields(store, allocator, fields)?;
299         store
300             .retry_after_gc_async((), |store, ()| {
301                 Self::new_unchecked(store, allocator, fields)
302             })
303             .await
304     }
305 
306     /// Like `Self::new` but caller's must ensure that if the store is
307     /// configured for async, this is only ever called from on a fiber stack.
308     pub(crate) unsafe fn new_maybe_async(
309         store: &mut StoreOpaque,
310         allocator: &StructRefPre,
311         fields: &[Val],
312     ) -> Result<Rooted<StructRef>> {
313         Self::type_check_fields(store, allocator, fields)?;
314         unsafe {
315             store.retry_after_gc_maybe_async((), |store, ()| {
316                 Self::new_unchecked(store, allocator, fields)
317             })
318         }
319     }
320 
321     /// Type check the field values before allocating a new struct.
322     fn type_check_fields(
323         store: &mut StoreOpaque,
324         allocator: &StructRefPre,
325         fields: &[Val],
326     ) -> Result<(), Error> {
327         let expected_len = allocator.ty.fields().len();
328         let actual_len = fields.len();
329         ensure!(
330             actual_len == expected_len,
331             "expected {expected_len} fields, got {actual_len}"
332         );
333         for (ty, val) in allocator.ty.fields().zip(fields) {
334             assert!(
335                 val.comes_from_same_store(store),
336                 "field value comes from the wrong store",
337             );
338             let ty = ty.element_type().unpack();
339             val.ensure_matches_ty(store, ty)
340                 .context("field type mismatch")?;
341         }
342         Ok(())
343     }
344 
345     /// Given that the field values have already been type checked, allocate a
346     /// new struct.
347     ///
348     /// Does not attempt GC+retry on OOM, that is the caller's responsibility.
349     fn new_unchecked(
350         store: &mut StoreOpaque,
351         allocator: &StructRefPre,
352         fields: &[Val],
353     ) -> Result<Rooted<StructRef>> {
354         assert_eq!(
355             store.id(),
356             allocator.store_id,
357             "attempted to use a `StructRefPre` with the wrong store"
358         );
359 
360         // Allocate the struct and write each field value into the appropriate
361         // offset.
362         let structref = store
363             .gc_store_mut()?
364             .alloc_uninit_struct(allocator.type_index(), &allocator.layout())
365             .context("unrecoverable error when allocating new `structref`")?
366             .map_err(|n| GcHeapOutOfMemory::new((), n))?;
367 
368         // From this point on, if we get any errors, then the struct is not
369         // fully initialized, so we need to eagerly deallocate it before the
370         // next GC where the collector might try to interpret one of the
371         // uninitialized fields as a GC reference.
372         let mut store = AutoAssertNoGc::new(store);
373         match (|| {
374             for (index, (ty, val)) in allocator.ty.fields().zip(fields).enumerate() {
375                 structref.initialize_field(
376                     &mut store,
377                     allocator.layout(),
378                     ty.element_type(),
379                     index,
380                     *val,
381                 )?;
382             }
383             Ok(())
384         })() {
385             Ok(()) => Ok(Rooted::new(&mut store, structref.into())),
386             Err(e) => {
387                 store.gc_store_mut()?.dealloc_uninit_struct(structref);
388                 Err(e)
389             }
390         }
391     }
392 
393     #[inline]
394     pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
395         self.inner.comes_from_same_store(store)
396     }
397 
398     /// Get this `structref`'s type.
399     ///
400     /// # Errors
401     ///
402     /// Return an error if this reference has been unrooted.
403     ///
404     /// # Panics
405     ///
406     /// Panics if this reference is associated with a different store.
407     pub fn ty(&self, store: impl AsContext) -> Result<StructType> {
408         self._ty(store.as_context().0)
409     }
410 
411     pub(crate) fn _ty(&self, store: &StoreOpaque) -> Result<StructType> {
412         assert!(self.comes_from_same_store(store));
413         let index = self.type_index(store)?;
414         Ok(StructType::from_shared_type_index(store.engine(), index))
415     }
416 
417     /// Does this `structref` match the given type?
418     ///
419     /// That is, is this struct's type a subtype of the given type?
420     ///
421     /// # Errors
422     ///
423     /// Return an error if this reference has been unrooted.
424     ///
425     /// # Panics
426     ///
427     /// Panics if this reference is associated with a different store or if the
428     /// type is not associated with the store's engine.
429     pub fn matches_ty(&self, store: impl AsContext, ty: &StructType) -> Result<bool> {
430         self._matches_ty(store.as_context().0, ty)
431     }
432 
433     pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &StructType) -> Result<bool> {
434         assert!(self.comes_from_same_store(store));
435         Ok(self._ty(store)?.matches(ty))
436     }
437 
438     pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &StructType) -> Result<()> {
439         if !self.comes_from_same_store(store) {
440             bail!("function used with wrong store");
441         }
442         if self._matches_ty(store, ty)? {
443             Ok(())
444         } else {
445             let actual_ty = self._ty(store)?;
446             bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`")
447         }
448     }
449 
450     /// Get the values of this struct's fields.
451     ///
452     /// Note that `i8` and `i16` field values are zero-extended into
453     /// `Val::I32(_)`s.
454     ///
455     /// # Errors
456     ///
457     /// Return an error if this reference has been unrooted.
458     ///
459     /// # Panics
460     ///
461     /// Panics if this reference is associated with a different store.
462     pub fn fields<'a, T: 'a>(
463         &'a self,
464         store: impl Into<StoreContextMut<'a, T>>,
465     ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> {
466         self._fields(store.into().0)
467     }
468 
469     pub(crate) fn _fields<'a>(
470         &'a self,
471         store: &'a mut StoreOpaque,
472     ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> {
473         assert!(self.comes_from_same_store(store));
474         let store = AutoAssertNoGc::new(store);
475 
476         let gc_ref = self.inner.try_gc_ref(&store)?;
477         let header = store.gc_store()?.header(gc_ref);
478         debug_assert!(header.kind().matches(VMGcKind::StructRef));
479 
480         let index = header.ty().expect("structrefs should have concrete types");
481         let ty = StructType::from_shared_type_index(store.engine(), index);
482         let len = ty.fields().len();
483 
484         return Ok(Fields {
485             structref: self,
486             store,
487             index: 0,
488             len,
489         });
490 
491         struct Fields<'a, 'b> {
492             structref: &'a StructRef,
493             store: AutoAssertNoGc<'b>,
494             index: usize,
495             len: usize,
496         }
497 
498         impl Iterator for Fields<'_, '_> {
499             type Item = Val;
500 
501             #[inline]
502             fn next(&mut self) -> Option<Self::Item> {
503                 let i = self.index;
504                 debug_assert!(i <= self.len);
505                 if i >= self.len {
506                     return None;
507                 }
508                 self.index += 1;
509                 Some(self.structref._field(&mut self.store, i).unwrap())
510             }
511 
512             #[inline]
513             fn size_hint(&self) -> (usize, Option<usize>) {
514                 let len = self.len - self.index;
515                 (len, Some(len))
516             }
517         }
518 
519         impl ExactSizeIterator for Fields<'_, '_> {
520             #[inline]
521             fn len(&self) -> usize {
522                 self.len - self.index
523             }
524         }
525     }
526 
527     fn header<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMGcHeader> {
528         assert!(self.comes_from_same_store(&store));
529         let gc_ref = self.inner.try_gc_ref(store)?;
530         Ok(store.gc_store()?.header(gc_ref))
531     }
532 
533     fn structref<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMStructRef> {
534         assert!(self.comes_from_same_store(&store));
535         let gc_ref = self.inner.try_gc_ref(store)?;
536         debug_assert!(self.header(store)?.kind().matches(VMGcKind::StructRef));
537         Ok(gc_ref.as_structref_unchecked())
538     }
539 
540     fn layout(&self, store: &AutoAssertNoGc<'_>) -> Result<GcStructLayout> {
541         assert!(self.comes_from_same_store(&store));
542         let type_index = self.type_index(store)?;
543         let layout = store
544             .engine()
545             .signatures()
546             .layout(type_index)
547             .expect("struct types should have GC layouts");
548         match layout {
549             GcLayout::Struct(s) => Ok(s),
550             GcLayout::Array(_) => unreachable!(),
551         }
552     }
553 
554     fn field_ty(&self, store: &StoreOpaque, field: usize) -> Result<FieldType> {
555         let ty = self._ty(store)?;
556         match ty.field(field) {
557             Some(f) => Ok(f),
558             None => {
559                 let len = ty.fields().len();
560                 bail!("cannot access field {field}: struct only has {len} fields")
561             }
562         }
563     }
564 
565     /// Get this struct's `index`th field.
566     ///
567     /// Note that `i8` and `i16` field values are zero-extended into
568     /// `Val::I32(_)`s.
569     ///
570     /// # Errors
571     ///
572     /// Returns an `Err(_)` if the index is out of bounds or this reference has
573     /// been unrooted.
574     ///
575     /// # Panics
576     ///
577     /// Panics if this reference is associated with a different store.
578     pub fn field(&self, mut store: impl AsContextMut, index: usize) -> Result<Val> {
579         let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
580         self._field(&mut store, index)
581     }
582 
583     pub(crate) fn _field(&self, store: &mut AutoAssertNoGc<'_>, index: usize) -> Result<Val> {
584         assert!(self.comes_from_same_store(store));
585         let structref = self.structref(store)?.unchecked_copy();
586         let field_ty = self.field_ty(store, index)?;
587         let layout = self.layout(store)?;
588         Ok(structref.read_field(store, &layout, field_ty.element_type(), index))
589     }
590 
591     /// Set this struct's `index`th field.
592     ///
593     /// # Errors
594     ///
595     /// Returns an error in the following scenarios:
596     ///
597     /// * When given a value of the wrong type, such as trying to set an `f32`
598     ///   field to an `i64` value.
599     ///
600     /// * When the field is not mutable.
601     ///
602     /// * When this struct does not have an `index`th field, i.e. `index` is out
603     ///   of bounds.
604     ///
605     /// * When `value` is a GC reference that has since been unrooted.
606     ///
607     /// # Panics
608     ///
609     /// Panics if this reference is associated with a different store.
610     pub fn set_field(&self, mut store: impl AsContextMut, index: usize, value: Val) -> Result<()> {
611         self._set_field(store.as_context_mut().0, index, value)
612     }
613 
614     pub(crate) fn _set_field(
615         &self,
616         store: &mut StoreOpaque,
617         index: usize,
618         value: Val,
619     ) -> Result<()> {
620         assert!(self.comes_from_same_store(store));
621         let mut store = AutoAssertNoGc::new(store);
622 
623         let field_ty = self.field_ty(&store, index)?;
624         ensure!(
625             field_ty.mutability().is_var(),
626             "cannot set field {index}: field is not mutable"
627         );
628 
629         value
630             .ensure_matches_ty(&store, &field_ty.element_type().unpack())
631             .with_context(|| format!("cannot set field {index}: type mismatch"))?;
632 
633         let layout = self.layout(&store)?;
634         let structref = self.structref(&store)?.unchecked_copy();
635 
636         structref.write_field(&mut store, &layout, field_ty.element_type(), index, value)
637     }
638 
639     pub(crate) fn type_index(&self, store: &StoreOpaque) -> Result<VMSharedTypeIndex> {
640         let gc_ref = self.inner.try_gc_ref(store)?;
641         let header = store.gc_store()?.header(gc_ref);
642         debug_assert!(header.kind().matches(VMGcKind::StructRef));
643         Ok(header.ty().expect("structrefs should have concrete types"))
644     }
645 
646     /// Create a new `Rooted<StructRef>` from the given GC reference.
647     ///
648     /// `gc_ref` should point to a valid `structref` and should belong to the
649     /// store's GC heap. Failure to uphold these invariants is memory safe but
650     /// will lead to general incorrectness such as panics or wrong results.
651     pub(crate) fn from_cloned_gc_ref(
652         store: &mut AutoAssertNoGc<'_>,
653         gc_ref: VMGcRef,
654     ) -> Rooted<Self> {
655         debug_assert!(gc_ref.is_structref(&*store.unwrap_gc_store().gc_heap));
656         Rooted::new(store, gc_ref)
657     }
658 }
659 
660 unsafe impl WasmTy for Rooted<StructRef> {
661     #[inline]
662     fn valtype() -> ValType {
663         ValType::Ref(RefType::new(false, HeapType::Struct))
664     }
665 
666     #[inline]
667     fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
668         self.comes_from_same_store(store)
669     }
670 
671     #[inline]
672     fn dynamic_concrete_type_check(
673         &self,
674         store: &StoreOpaque,
675         _nullable: bool,
676         ty: &HeapType,
677     ) -> Result<()> {
678         match ty {
679             HeapType::Any | HeapType::Eq | HeapType::Struct => Ok(()),
680             HeapType::ConcreteStruct(ty) => self.ensure_matches_ty(store, ty),
681 
682             HeapType::Extern
683             | HeapType::NoExtern
684             | HeapType::Func
685             | HeapType::ConcreteFunc(_)
686             | HeapType::NoFunc
687             | HeapType::I31
688             | HeapType::Array
689             | HeapType::ConcreteArray(_)
690             | HeapType::None => bail!(
691                 "type mismatch: expected `(ref {ty})`, got `(ref {})`",
692                 self._ty(store)?,
693             ),
694         }
695     }
696 
697     fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
698         self.wasm_ty_store(store, ptr, ValRaw::anyref)
699     }
700 
701     unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
702         Self::wasm_ty_load(store, ptr.get_anyref(), StructRef::from_cloned_gc_ref)
703     }
704 }
705 
706 unsafe impl WasmTy for Option<Rooted<StructRef>> {
707     #[inline]
708     fn valtype() -> ValType {
709         ValType::STRUCTREF
710     }
711 
712     #[inline]
713     fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
714         self.map_or(true, |x| x.comes_from_same_store(store))
715     }
716 
717     #[inline]
718     fn dynamic_concrete_type_check(
719         &self,
720         store: &StoreOpaque,
721         nullable: bool,
722         ty: &HeapType,
723     ) -> Result<()> {
724         match self {
725             Some(s) => Rooted::<StructRef>::dynamic_concrete_type_check(s, store, nullable, ty),
726             None => {
727                 ensure!(
728                     nullable,
729                     "expected a non-null reference, but found a null reference"
730                 );
731                 Ok(())
732             }
733         }
734     }
735 
736     #[inline]
737     fn is_vmgcref_and_points_to_object(&self) -> bool {
738         self.is_some()
739     }
740 
741     fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
742         <Rooted<StructRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
743     }
744 
745     unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
746         <Rooted<StructRef>>::wasm_ty_option_load(
747             store,
748             ptr.get_anyref(),
749             StructRef::from_cloned_gc_ref,
750         )
751     }
752 }
753 
754 unsafe impl WasmTy for ManuallyRooted<StructRef> {
755     #[inline]
756     fn valtype() -> ValType {
757         ValType::Ref(RefType::new(false, HeapType::Struct))
758     }
759 
760     #[inline]
761     fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
762         self.comes_from_same_store(store)
763     }
764 
765     #[inline]
766     fn dynamic_concrete_type_check(
767         &self,
768         store: &StoreOpaque,
769         _: bool,
770         ty: &HeapType,
771     ) -> Result<()> {
772         match ty {
773             HeapType::Any | HeapType::Eq | HeapType::Struct => Ok(()),
774             HeapType::ConcreteStruct(ty) => self.ensure_matches_ty(store, ty),
775 
776             HeapType::Extern
777             | HeapType::NoExtern
778             | HeapType::Func
779             | HeapType::ConcreteFunc(_)
780             | HeapType::NoFunc
781             | HeapType::I31
782             | HeapType::Array
783             | HeapType::ConcreteArray(_)
784             | HeapType::None => bail!(
785                 "type mismatch: expected `(ref {ty})`, got `(ref {})`",
786                 self._ty(store)?,
787             ),
788         }
789     }
790 
791     fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
792         self.wasm_ty_store(store, ptr, ValRaw::anyref)
793     }
794 
795     unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
796         Self::wasm_ty_load(store, ptr.get_anyref(), StructRef::from_cloned_gc_ref)
797     }
798 }
799 
800 unsafe impl WasmTy for Option<ManuallyRooted<StructRef>> {
801     #[inline]
802     fn valtype() -> ValType {
803         ValType::STRUCTREF
804     }
805 
806     #[inline]
807     fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
808         self.as_ref()
809             .map_or(true, |x| x.comes_from_same_store(store))
810     }
811 
812     #[inline]
813     fn dynamic_concrete_type_check(
814         &self,
815         store: &StoreOpaque,
816         nullable: bool,
817         ty: &HeapType,
818     ) -> Result<()> {
819         match self {
820             Some(s) => {
821                 ManuallyRooted::<StructRef>::dynamic_concrete_type_check(s, store, nullable, ty)
822             }
823             None => {
824                 ensure!(
825                     nullable,
826                     "expected a non-null reference, but found a null reference"
827                 );
828                 Ok(())
829             }
830         }
831     }
832 
833     #[inline]
834     fn is_vmgcref_and_points_to_object(&self) -> bool {
835         self.is_some()
836     }
837 
838     fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
839         <ManuallyRooted<StructRef>>::wasm_ty_option_store(self, store, ptr, ValRaw::anyref)
840     }
841 
842     unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
843         <ManuallyRooted<StructRef>>::wasm_ty_option_load(
844             store,
845             ptr.get_anyref(),
846             StructRef::from_cloned_gc_ref,
847         )
848     }
849 }
850