1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
use crate::{
store::{AutoAssertNoGc, StoreOpaque},
HeapType, Ref, RefType, Result, Uninhabited, Val, ValRaw, ValType, WasmTy,
};
use core::mem::MaybeUninit;
/// A reference to the abstract `noextern` heap value.
///
/// The are no instances of `(ref noextern)`: it is an uninhabited type.
///
/// There is precisely one instance of `(ref null noextern)`, aka `nullexternref`:
/// the null reference.
///
/// This `NoExtern` Rust type's sole purpose is for use with
/// [`Func::wrap`][crate::Func::wrap]- and
/// [`Func::typed`][crate::Func::typed]-style APIs for statically typing a
/// function as taking or returning a `(ref null noextern)` (aka
/// `Option<NoExtern>`) which is always `None`.
///
/// # Example
///
/// ```
/// # use wasmtime::*;
/// # fn _foo() -> Result<()> {
/// let mut config = Config::new();
/// config.wasm_function_references(true);
/// config.wasm_gc(true);
/// let engine = Engine::new(&config)?;
///
/// let module = Module::new(
/// &engine,
/// r#"
/// (module
/// (func (export "f") (param (ref null noextern))
/// ;; If the reference is null, return.
/// local.get 0
/// ref.is_null noextern
/// br_if 0
///
/// ;; If the reference was not null (which is impossible)
/// ;; then raise a trap.
/// unreachable
/// )
/// )
/// "#,
/// )?;
///
/// let mut store = Store::new(&engine, ());
/// let instance = Instance::new(&mut store, &module, &[])?;
/// let f = instance.get_func(&mut store, "f").unwrap();
///
/// // We can cast a `(ref null noextern)`-taking function into a typed function that
/// // takes an `Option<NoExtern>` via the `Func::typed` method.
/// let f = f.typed::<Option<NoExtern>, ()>(&store)?;
///
/// // We can call the typed function, passing the null `noextern` reference.
/// let result = f.call(&mut store, NoExtern::null());
///
/// // The function should not have trapped, because the reference we gave it was
/// // null (as it had to be, since `NoExtern` is uninhabited).
/// assert!(result.is_ok());
/// # Ok(())
/// # }
/// ```
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct NoExtern {
_inner: Uninhabited,
}
impl NoExtern {
/// Get the null `(ref null noextern)` (aka `nullexternref`) reference.
#[inline]
pub fn null() -> Option<Self> {
None
}
/// Get the null `(ref null noextern)` (aka `nullexternref`) reference as a
/// [`Ref`].
#[inline]
pub fn null_ref() -> Ref {
Ref::Extern(None)
}
/// Get the null `(ref null noextern)` (aka `nullexternref`) reference as a
/// [`Val`].
#[inline]
pub fn null_val() -> Val {
Val::ExternRef(None)
}
}
unsafe impl WasmTy for NoExtern {
#[inline]
fn valtype() -> ValType {
ValType::Ref(RefType::new(false, HeapType::NoExtern))
}
#[inline]
fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
match self._inner {}
}
#[inline]
fn dynamic_concrete_type_check(&self, _: &StoreOpaque, _: bool, _: &HeapType) -> Result<()> {
match self._inner {}
}
#[inline]
fn is_vmgcref_and_points_to_object(&self) -> bool {
match self._inner {}
}
fn store(self, _store: &mut AutoAssertNoGc<'_>, _ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
match self._inner {}
}
unsafe fn load(_store: &mut AutoAssertNoGc<'_>, _ptr: &ValRaw) -> Self {
unreachable!("NoExtern is uninhabited")
}
}
unsafe impl WasmTy for Option<NoExtern> {
#[inline]
fn valtype() -> ValType {
ValType::Ref(RefType::new(true, HeapType::NoExtern))
}
#[inline]
fn compatible_with_store(&self, _store: &StoreOpaque) -> bool {
true
}
#[inline]
fn dynamic_concrete_type_check(
&self,
_store: &StoreOpaque,
_nullable: bool,
_ty: &HeapType,
) -> Result<()> {
unreachable!()
}
#[inline]
fn store(self, _store: &mut AutoAssertNoGc<'_>, ptr: &mut MaybeUninit<ValRaw>) -> Result<()> {
ptr.write(ValRaw::externref(0));
Ok(())
}
#[inline]
unsafe fn load(_store: &mut AutoAssertNoGc<'_>, ptr: &ValRaw) -> Self {
debug_assert_eq!(ptr.get_externref(), 0);
None
}
}