Skip to main content

wasmtime/runtime/gc/enabled/
structref.rs

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