Skip to main content

wasmtime/runtime/gc/enabled/
structref.rs

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