1 //! Working with GC `struct` objects.
2 
3 use crate::runtime::vm::VMGcRef;
4 use crate::store::StoreId;
5 use crate::vm::{GcLayout, GcStructLayout, VMGcHeader, VMStructRef};
6 use crate::{
7     prelude::*,
8     store::{AutoAssertNoGc, StoreContextMut, StoreOpaque},
9     AsContext, AsContextMut, GcHeapOutOfMemory, GcRefImpl, GcRootIndex, HeapType, ManuallyRooted,
10     RefType, RootSet, Rooted, StructType, Val, ValRaw, ValType, WasmTy,
11 };
12 use crate::{AnyRef, FieldType};
13 use core::mem::{self, MaybeUninit};
14 use wasmtime_environ::{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 = match StructRef::new(&mut scope, &allocator, &[Val::I32(42)]) {
137 ///         Ok(s) => s,
138 ///         // If the heap is out of memory, then do a GC and try again.
139 ///         Err(e) if e.is::<GcHeapOutOfMemory<()>>() => {
140 ///             // Do a GC! Note: in an async context, you'd want to do
141 ///             // `scope.as_context_mut().gc_async().await`.
142 ///             scope.as_context_mut().gc();
143 ///
144 ///             StructRef::new(&mut scope, &allocator, &[Val::I32(42)])?
145 ///         }
146 ///         Err(e) => return Err(e),
147 ///     };
148 ///
149 ///     // That instance's field should have the expected value.
150 ///     let val = my_struct.field(&mut scope, 0)?.unwrap_i32();
151 ///     assert_eq!(val, 42);
152 ///
153 ///     // And we can update the field's value because it is a mutable field.
154 ///     my_struct.set_field(&mut scope, 0, Val::I32(36))?;
155 ///     let new_val = my_struct.field(&mut scope, 0)?.unwrap_i32();
156 ///     assert_eq!(new_val, 36);
157 /// }
158 /// # Ok(())
159 /// # }
160 /// # foo().unwrap();
161 /// ```
162 #[derive(Debug)]
163 #[repr(transparent)]
164 pub struct StructRef {
165     pub(super) inner: GcRootIndex,
166 }
167 
168 unsafe impl GcRefImpl for StructRef {
169     #[allow(private_interfaces)]
170     fn transmute_ref(index: &GcRootIndex) -> &Self {
171         // Safety: `StructRef` is a newtype of a `GcRootIndex`.
172         let me: &Self = unsafe { mem::transmute(index) };
173 
174         // Assert we really are just a newtype of a `GcRootIndex`.
175         assert!(matches!(
176             me,
177             Self {
178                 inner: GcRootIndex { .. },
179             }
180         ));
181 
182         me
183     }
184 }
185 
186 impl Rooted<StructRef> {
187     /// Upcast this `structref` into an `anyref`.
188     #[inline]
189     pub fn to_anyref(self) -> Rooted<AnyRef> {
190         self.unchecked_cast()
191     }
192 }
193 
194 impl ManuallyRooted<StructRef> {
195     /// Upcast this `structref` into an `anyref`.
196     #[inline]
197     pub fn to_anyref(self) -> ManuallyRooted<AnyRef> {
198         self.unchecked_cast()
199     }
200 }
201 
202 impl StructRef {
203     /// Allocate a new `struct` and get a reference to it.
204     ///
205     /// # Errors
206     ///
207     /// If the given `fields` values' types do not match the field types of the
208     /// `allocator`'s struct type, an error is returned.
209     ///
210     /// If the allocation cannot be satisfied because the GC heap is currently
211     /// out of memory, but performing a garbage collection might free up space
212     /// such that retrying the allocation afterwards might succeed, then a
213     /// [`GcHeapOutOfMemory<()>`][crate::GcHeapOutOfMemory] error is returned.
214     ///
215     /// # Panics
216     ///
217     /// Panics if the allocator, or any of the field values, is not associated
218     /// with the given store.
219     pub fn new(
220         mut store: impl AsContextMut,
221         allocator: &StructRefPre,
222         fields: &[Val],
223     ) -> Result<Rooted<StructRef>> {
224         Self::_new(store.as_context_mut().0, allocator, fields)
225     }
226 
227     pub(crate) fn _new(
228         store: &mut StoreOpaque,
229         allocator: &StructRefPre,
230         fields: &[Val],
231     ) -> Result<Rooted<StructRef>> {
232         assert_eq!(
233             store.id(),
234             allocator.store_id,
235             "attempted to use a `StructRefPre` with the wrong store"
236         );
237 
238         // Type check the given values against the field types.
239         let expected_len = allocator.ty.fields().len();
240         let actual_len = fields.len();
241         ensure!(
242             actual_len == expected_len,
243             "expected {expected_len} fields, got {actual_len}"
244         );
245         for (ty, val) in allocator.ty.fields().zip(fields) {
246             assert!(
247                 val.comes_from_same_store(store),
248                 "field value comes from the wrong store",
249             );
250             let ty = ty.element_type().unpack();
251             val.ensure_matches_ty(store, ty)
252                 .context("field type mismatch")?;
253         }
254 
255         // Allocate the struct and write each field value into the appropriate
256         // offset.
257         let structref = store
258             .gc_store_mut()?
259             .alloc_uninit_struct(allocator.type_index(), &allocator.layout())
260             .err2anyhow()
261             .context("unrecoverable error when allocating new `structref`")?
262             .ok_or_else(|| GcHeapOutOfMemory::new(()))
263             .err2anyhow()?;
264 
265         // From this point on, if we get any errors, then the struct is not
266         // fully initialized, so we need to eagerly deallocate it before the
267         // next GC where the collector might try to interpret one of the
268         // uninitialized fields as a GC reference.
269         let mut store = AutoAssertNoGc::new(store);
270         match (|| {
271             for (index, (ty, val)) in allocator.ty.fields().zip(fields).enumerate() {
272                 structref.initialize_field(
273                     &mut store,
274                     allocator.layout(),
275                     ty.element_type(),
276                     index,
277                     val.clone(),
278                 )?;
279             }
280             Ok(())
281         })() {
282             Ok(()) => Ok(Rooted::new(&mut store, structref.into())),
283             Err(e) => {
284                 store.gc_store_mut()?.dealloc_uninit_struct(structref);
285                 Err(e)
286             }
287         }
288     }
289 
290     #[inline]
291     pub(crate) fn comes_from_same_store(&self, store: &StoreOpaque) -> bool {
292         self.inner.comes_from_same_store(store)
293     }
294 
295     /// Get this `structref`'s type.
296     ///
297     /// # Errors
298     ///
299     /// Return an error if this reference has been unrooted.
300     ///
301     /// # Panics
302     ///
303     /// Panics if this reference is associated with a different store.
304     pub fn ty(&self, store: impl AsContext) -> Result<StructType> {
305         self._ty(store.as_context().0)
306     }
307 
308     pub(crate) fn _ty(&self, store: &StoreOpaque) -> Result<StructType> {
309         assert!(self.comes_from_same_store(store));
310         let index = self.type_index(store)?;
311         Ok(StructType::from_shared_type_index(store.engine(), index))
312     }
313 
314     /// Does this `structref` match the given type?
315     ///
316     /// That is, is this struct's type a subtype of the given type?
317     ///
318     /// # Errors
319     ///
320     /// Return an error if this reference has been unrooted.
321     ///
322     /// # Panics
323     ///
324     /// Panics if this reference is associated with a different store or if the
325     /// type is not associated with the store's engine.
326     pub fn matches_ty(&self, store: impl AsContext, ty: &StructType) -> Result<bool> {
327         self._matches_ty(store.as_context().0, ty)
328     }
329 
330     pub(crate) fn _matches_ty(&self, store: &StoreOpaque, ty: &StructType) -> Result<bool> {
331         assert!(self.comes_from_same_store(store));
332         Ok(self._ty(store)?.matches(ty))
333     }
334 
335     pub(crate) fn ensure_matches_ty(&self, store: &StoreOpaque, ty: &StructType) -> Result<()> {
336         if !self.comes_from_same_store(store) {
337             bail!("function used with wrong store");
338         }
339         if self._matches_ty(store, ty)? {
340             Ok(())
341         } else {
342             let actual_ty = self._ty(store)?;
343             bail!("type mismatch: expected `(ref {ty})`, found `(ref {actual_ty})`")
344         }
345     }
346 
347     /// Get the values of this struct's fields.
348     ///
349     /// Note that `i8` and `i16` field values are zero-extended into
350     /// `Val::I32(_)`s.
351     ///
352     /// # Errors
353     ///
354     /// Return an error if this reference has been unrooted.
355     ///
356     /// # Panics
357     ///
358     /// Panics if this reference is associated with a different store.
359     pub fn fields<'a, T: 'a>(
360         &'a self,
361         store: impl Into<StoreContextMut<'a, T>>,
362     ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> {
363         self._fields(store.into().0)
364     }
365 
366     pub(crate) fn _fields<'a>(
367         &'a self,
368         store: &'a mut StoreOpaque,
369     ) -> Result<impl ExactSizeIterator<Item = Val> + 'a> {
370         assert!(self.comes_from_same_store(store));
371         let store = AutoAssertNoGc::new(store);
372 
373         let gc_ref = self.inner.try_gc_ref(&store)?;
374         let header = store.gc_store()?.header(gc_ref);
375         debug_assert!(header.kind().matches(VMGcKind::StructRef));
376 
377         let index = header.ty().expect("structrefs should have concrete types");
378         let ty = StructType::from_shared_type_index(store.engine(), index);
379         let len = ty.fields().len();
380 
381         return Ok(Fields {
382             structref: self,
383             store,
384             index: 0,
385             len,
386         });
387 
388         struct Fields<'a, 'b> {
389             structref: &'a StructRef,
390             store: AutoAssertNoGc<'b>,
391             index: usize,
392             len: usize,
393         }
394 
395         impl Iterator for Fields<'_, '_> {
396             type Item = Val;
397 
398             #[inline]
399             fn next(&mut self) -> Option<Self::Item> {
400                 let i = self.index;
401                 debug_assert!(i <= self.len);
402                 if i >= self.len {
403                     return None;
404                 }
405                 self.index += 1;
406                 Some(self.structref._field(&mut self.store, i).unwrap())
407             }
408 
409             #[inline]
410             fn size_hint(&self) -> (usize, Option<usize>) {
411                 let len = self.len - self.index;
412                 (len, Some(len))
413             }
414         }
415 
416         impl ExactSizeIterator for Fields<'_, '_> {
417             #[inline]
418             fn len(&self) -> usize {
419                 self.len - self.index
420             }
421         }
422     }
423 
424     fn header<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMGcHeader> {
425         assert!(self.comes_from_same_store(&store));
426         let gc_ref = self.inner.try_gc_ref(store)?;
427         Ok(store.gc_store()?.header(gc_ref))
428     }
429 
430     fn structref<'a>(&self, store: &'a AutoAssertNoGc<'_>) -> Result<&'a VMStructRef> {
431         assert!(self.comes_from_same_store(&store));
432         let gc_ref = self.inner.try_gc_ref(store)?;
433         debug_assert!(self.header(store)?.kind().matches(VMGcKind::StructRef));
434         Ok(gc_ref.as_structref_unchecked())
435     }
436 
437     fn layout(&self, store: &AutoAssertNoGc<'_>) -> Result<GcStructLayout> {
438         assert!(self.comes_from_same_store(&store));
439         let type_index = self.type_index(store)?;
440         let layout = store
441             .engine()
442             .signatures()
443             .layout(type_index)
444             .expect("struct types should have GC layouts");
445         match layout {
446             GcLayout::Struct(s) => Ok(s),
447             GcLayout::Array(_) => unreachable!(),
448         }
449     }
450 
451     fn field_ty(&self, store: &StoreOpaque, field: usize) -> Result<FieldType> {
452         let ty = self._ty(store)?;
453         match ty.field(field) {
454             Some(f) => Ok(f),
455             None => {
456                 let len = ty.fields().len();
457                 bail!("cannot access field {field}: struct only has {len} fields")
458             }
459         }
460     }
461 
462     /// Get this struct's `index`th field.
463     ///
464     /// Note that `i8` and `i16` field values are zero-extended into
465     /// `Val::I32(_)`s.
466     ///
467     /// # Errors
468     ///
469     /// Returns an `Err(_)` if the index is out of bounds or this reference has
470     /// been unrooted.
471     ///
472     /// # Panics
473     ///
474     /// Panics if this reference is associated with a different store.
475     pub fn field(&self, mut store: impl AsContextMut, index: usize) -> Result<Val> {
476         let mut store = AutoAssertNoGc::new(store.as_context_mut().0);
477         self._field(&mut store, index)
478     }
479 
480     pub(crate) fn _field(&self, store: &mut AutoAssertNoGc<'_>, index: usize) -> Result<Val> {
481         assert!(self.comes_from_same_store(store));
482         let structref = self.structref(store)?.unchecked_copy();
483         let field_ty = self.field_ty(store, index)?;
484         let layout = self.layout(store)?;
485         Ok(structref.read_field(store, &layout, field_ty.element_type(), index))
486     }
487 
488     /// Set this struct's `index`th field.
489     ///
490     /// # Errors
491     ///
492     /// Returns an error in the following scenarios:
493     ///
494     /// * When given a value of the wrong type, such as trying to set an `f32`
495     ///   field to an `i64` value.
496     ///
497     /// * When the field is not mutable.
498     ///
499     /// * When this struct does not have an `index`th field, i.e. `index` is out
500     ///   of bounds.
501     ///
502     /// * When `value` is a GC reference that has since been unrooted.
503     ///
504     /// # Panics
505     ///
506     /// Panics if this reference is associated with a different store.
507     pub fn set_field(&self, mut store: impl AsContextMut, index: usize, value: Val) -> Result<()> {
508         self._set_field(store.as_context_mut().0, index, value)
509     }
510 
511     pub(crate) fn _set_field(
512         &self,
513         store: &mut StoreOpaque,
514         index: usize,
515         value: Val,
516     ) -> Result<()> {
517         assert!(self.comes_from_same_store(store));
518         let mut store = AutoAssertNoGc::new(store);
519 
520         let field_ty = self.field_ty(&store, index)?;
521         ensure!(
522             field_ty.mutability().is_var(),
523             "cannot set field {index}: field is not mutable"
524         );
525 
526         value
527             .ensure_matches_ty(&store, &field_ty.element_type().unpack())
528             .with_context(|| format!("cannot set field {index}: type mismatch"))?;
529 
530         let layout = self.layout(&store)?;
531         let structref = self.structref(&store)?.unchecked_copy();
532 
533         structref.write_field(&mut store, &layout, field_ty.element_type(), index, value)
534     }
535 
536     pub(crate) fn type_index(&self, store: &StoreOpaque) -> Result<VMSharedTypeIndex> {
537         let gc_ref = self.inner.unchecked_try_gc_ref(store)?;
538         let header = store.gc_store()?.header(gc_ref);
539         debug_assert!(header.kind().matches(VMGcKind::StructRef));
540         Ok(header.ty().expect("structrefs should have concrete types"))
541     }
542 
543     /// Create a new `Rooted<StructRef>` from the given GC reference.
544     ///
545     /// `gc_ref` should point to a valid `structref` and should belong to the
546     /// store's GC heap. Failure to uphold these invariants is memory safe but
547     /// will lead to general incorrectness such as panics or wrong results.
548     pub(crate) fn from_cloned_gc_ref(
549         store: &mut AutoAssertNoGc<'_>,
550         gc_ref: VMGcRef,
551     ) -> Rooted<Self> {
552         debug_assert!(!gc_ref.is_i31());
553         Rooted::new(store, gc_ref)
554     }
555 }
556 
557 unsafe impl WasmTy for Rooted<StructRef> {
558     #[inline]
559     fn valtype() -> ValType {
560         ValType::Ref(RefType::new(false, HeapType::Struct))
561     }
562 
563     #[inline]
564     fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
565         self.comes_from_same_store(store)
566     }
567 
568     #[inline]
569     fn dynamic_concrete_type_check(
570         &self,
571         store: &StoreOpaque,
572         _nullable: bool,
573         ty: &HeapType,
574     ) -> Result<()> {
575         match ty {
576             HeapType::Any | HeapType::Eq | HeapType::Struct => Ok(()),
577             HeapType::ConcreteStruct(ty) => self.ensure_matches_ty(store, ty),
578 
579             HeapType::Extern
580             | HeapType::NoExtern
581             | HeapType::Func
582             | HeapType::ConcreteFunc(_)
583             | HeapType::NoFunc
584             | HeapType::I31
585             | HeapType::Array
586             | HeapType::ConcreteArray(_)
587             | HeapType::None => bail!(
588                 "type mismatch: expected `(ref {ty})`, got `(ref {})`",
589                 self._ty(store)?,
590             ),
591         }
592     }
593 
594     fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
595         let gc_ref = self.inner.try_clone_gc_ref(store)?;
596         let r64 = gc_ref.as_r64();
597         store.gc_store_mut()?.expose_gc_ref_to_wasm(gc_ref);
598         debug_assert_ne!(r64, 0);
599         let anyref = u32::try_from(r64).unwrap();
600         ptr.write(ValRaw::anyref(anyref));
601         Ok(())
602     }
603 
604     unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
605         let raw = ptr.get_anyref();
606         debug_assert_ne!(raw, 0);
607         let gc_ref = VMGcRef::from_r64(raw.into())
608             .expect("valid r64")
609             .expect("non-null");
610         let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref);
611         StructRef::from_cloned_gc_ref(store, gc_ref)
612     }
613 }
614 
615 unsafe impl WasmTy for Option<Rooted<StructRef>> {
616     #[inline]
617     fn valtype() -> ValType {
618         ValType::STRUCTREF
619     }
620 
621     #[inline]
622     fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
623         self.map_or(true, |x| x.comes_from_same_store(store))
624     }
625 
626     #[inline]
627     fn dynamic_concrete_type_check(
628         &self,
629         store: &StoreOpaque,
630         nullable: bool,
631         ty: &HeapType,
632     ) -> Result<()> {
633         match self {
634             Some(s) => Rooted::<StructRef>::dynamic_concrete_type_check(s, store, nullable, ty),
635             None => {
636                 ensure!(
637                     nullable,
638                     "expected a non-null reference, but found a null reference"
639                 );
640                 Ok(())
641             }
642         }
643     }
644 
645     #[inline]
646     fn is_vmgcref_and_points_to_object(&self) -> bool {
647         self.is_some()
648     }
649 
650     fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
651         match self {
652             Some(r) => r.store(store, ptr),
653             None => {
654                 ptr.write(ValRaw::anyref(0));
655                 Ok(())
656             }
657         }
658     }
659 
660     unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
661         let gc_ref = VMGcRef::from_r64(ptr.get_anyref().into()).expect("valid r64")?;
662         let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref);
663         Some(StructRef::from_cloned_gc_ref(store, gc_ref))
664     }
665 }
666 
667 unsafe impl WasmTy for ManuallyRooted<StructRef> {
668     #[inline]
669     fn valtype() -> ValType {
670         ValType::Ref(RefType::new(false, HeapType::Struct))
671     }
672 
673     #[inline]
674     fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
675         self.comes_from_same_store(store)
676     }
677 
678     #[inline]
679     fn dynamic_concrete_type_check(
680         &self,
681         store: &StoreOpaque,
682         _: bool,
683         ty: &HeapType,
684     ) -> Result<()> {
685         match ty {
686             HeapType::Any | HeapType::Eq | HeapType::Struct => Ok(()),
687             HeapType::ConcreteStruct(ty) => self.ensure_matches_ty(store, ty),
688 
689             HeapType::Extern
690             | HeapType::NoExtern
691             | HeapType::Func
692             | HeapType::ConcreteFunc(_)
693             | HeapType::NoFunc
694             | HeapType::I31
695             | HeapType::Array
696             | HeapType::ConcreteArray(_)
697             | HeapType::None => bail!(
698                 "type mismatch: expected `(ref {ty})`, got `(ref {})`",
699                 self._ty(store)?,
700             ),
701         }
702     }
703 
704     fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
705         let gc_ref = self.inner.try_clone_gc_ref(store)?;
706         let r64 = gc_ref.as_r64();
707         store.gc_store_mut()?.expose_gc_ref_to_wasm(gc_ref);
708         debug_assert_ne!(r64, 0);
709         let anyref = u32::try_from(r64).unwrap();
710         ptr.write(ValRaw::anyref(anyref));
711         Ok(())
712     }
713 
714     unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
715         let raw = ptr.get_anyref();
716         debug_assert_ne!(raw, 0);
717         let gc_ref = VMGcRef::from_r64(raw.into())
718             .expect("valid r64")
719             .expect("non-null");
720         let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref);
721         RootSet::with_lifo_scope(store, |store| {
722             let rooted = StructRef::from_cloned_gc_ref(store, gc_ref);
723             rooted
724                 ._to_manually_rooted(store)
725                 .expect("rooted is in scope")
726         })
727     }
728 }
729 
730 unsafe impl WasmTy for Option<ManuallyRooted<StructRef>> {
731     #[inline]
732     fn valtype() -> ValType {
733         ValType::STRUCTREF
734     }
735 
736     #[inline]
737     fn compatible_with_store(&self, store: &StoreOpaque) -> bool {
738         self.as_ref()
739             .map_or(true, |x| x.comes_from_same_store(store))
740     }
741 
742     #[inline]
743     fn dynamic_concrete_type_check(
744         &self,
745         store: &StoreOpaque,
746         nullable: bool,
747         ty: &HeapType,
748     ) -> Result<()> {
749         match self {
750             Some(s) => {
751                 ManuallyRooted::<StructRef>::dynamic_concrete_type_check(s, store, nullable, ty)
752             }
753             None => {
754                 ensure!(
755                     nullable,
756                     "expected a non-null reference, but found a null reference"
757                 );
758                 Ok(())
759             }
760         }
761     }
762 
763     #[inline]
764     fn is_vmgcref_and_points_to_object(&self) -> bool {
765         self.is_some()
766     }
767 
768     fn store(self, store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
769         match self {
770             Some(r) => r.store(store, ptr),
771             None => {
772                 ptr.write(ValRaw::anyref(0));
773                 Ok(())
774             }
775         }
776     }
777 
778     unsafe fn load(store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
779         let raw = ptr.get_anyref();
780         debug_assert_ne!(raw, 0);
781         let gc_ref = VMGcRef::from_r64(raw.into()).expect("valid r64")?;
782         let gc_ref = store.unwrap_gc_store_mut().clone_gc_ref(&gc_ref);
783         RootSet::with_lifo_scope(store, |store| {
784             let rooted = StructRef::from_cloned_gc_ref(store, gc_ref);
785             Some(
786                 rooted
787                     ._to_manually_rooted(store)
788                     .expect("rooted is in scope"),
789             )
790         })
791     }
792 }
793