Skip to content

Commit

Permalink
Add the wasmtime::NoneRef host API type for Wasm's `(ref null? none…
Browse files Browse the repository at this point in the history
…)` instances
  • Loading branch information
fitzgen committed Oct 11, 2024
1 parent ec05264 commit f4b2a8d
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 0 deletions.
4 changes: 4 additions & 0 deletions crates/wasmtime/src/runtime/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,8 @@ impl Func {
/// | `Rooted<StructRef>` | `(ref struct)` |
/// | `Option<Rooted<ArrayRef>>` | `(ref null array)` |
/// | `Rooted<ArrayRef>` | `(ref array)` |
/// | `Option<NoneRef>` | `nullref` aka `(ref null none)` |
/// | `NoneRef` | `(ref none)` |
///
/// Note that anywhere a `Rooted<T>` appears, a `ManuallyRooted<T>` may also
/// be used.
Expand Down Expand Up @@ -1444,6 +1446,8 @@ impl Func {
/// | `(ref struct)` | `Rooted<StructRef>` |
/// | `arrayref` aka `(ref null array)` | `Option<Rooted<ArrayRef>>` |
/// | `(ref array)` | `Rooted<ArrayRef>` |
/// | `nullref` aka `(ref null none)` | `Option<NoneRef>` |
/// | `(ref none)` | `NoneRef` |
/// | `funcref` aka `(ref null func)` | `Option<Func>` |
/// | `(ref func)` | `Func` |
/// | `(ref null <func type index>)` | `Option<Func>` |
Expand Down
3 changes: 3 additions & 0 deletions crates/wasmtime/src/runtime/gc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ pub use disabled::*;
mod noextern;
pub use noextern::NoExtern;

mod none_ref;
pub use none_ref::NoneRef;

use core::fmt;
use core::ops::Deref;

Expand Down
154 changes: 154 additions & 0 deletions crates/wasmtime/src/runtime/gc/none_ref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
use crate::{
store::{AutoAssertNoGc, StoreOpaque},
HeapType, Ref, RefType, Result, Uninhabited, Val, ValRaw, ValType, WasmTy,
};
use core::mem::MaybeUninit;

/// A reference to the abstract `none` heap value.
///
/// The are no instances of `(ref none)`: it is an uninhabited type.
///
/// There is precisely one instance of `(ref null none)`, aka `nullref`: the
/// null reference.
///
/// This `NoneRef` 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 none)` (aka `Option<NoneRef>`)
/// 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 none))
/// ;; If the reference is null, return.
/// local.get 0
/// ref.is_null none
/// 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 none)`-taking function into a typed function that
/// // takes an `Option<NoneRef>` via the `Func::typed` method.
/// let f = f.typed::<Option<NoneRef>, ()>(&store)?;
///
/// // We can call the typed function, passing the null `none` reference.
/// let result = f.call(&mut store, NoneRef::null());
///
/// // The function should not have trapped, because the reference we gave it was
/// // null (as it had to be, since `NoneRef` is uninhabited).
/// assert!(result.is_ok());
/// # Ok(())
/// # }
/// ```
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct NoneRef {
_inner: Uninhabited,
}

impl NoneRef {
/// Get the null `(ref null none)` (aka `nullexternref`) reference.
#[inline]
pub fn null() -> Option<Self> {
None
}

/// Get the null `(ref null none)` (aka `nullexternref`) reference as a
/// [`Ref`].
#[inline]
pub fn null_ref() -> Ref {
Ref::Extern(None)
}

/// Get the null `(ref null none)` (aka `nullexternref`) reference as a
/// [`Val`].
#[inline]
pub fn null_val() -> Val {
Val::ExternRef(None)
}
}

unsafe impl WasmTy for NoneRef {
#[inline]
fn valtype() -> ValType {
ValType::Ref(RefType::new(false, HeapType::None))
}

#[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!("NoneRef is uninhabited")
}
}

unsafe impl WasmTy for Option<NoneRef> {
#[inline]
fn valtype() -> ValType {
ValType::Ref(RefType::new(true, HeapType::None))
}

#[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
}
}
4 changes: 4 additions & 0 deletions tests/all/func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,8 @@ fn func_constructors() {
Func::wrap(&mut store, || -> Option<NoFunc> { None });
Func::wrap(&mut store, || -> NoExtern { loop {} });
Func::wrap(&mut store, || -> Option<NoExtern> { None });
Func::wrap(&mut store, || -> NoneRef { loop {} });
Func::wrap(&mut store, || -> Option<NoneRef> { None });

Func::wrap(&mut store, || -> Result<()> { loop {} });
Func::wrap(&mut store, || -> Result<i32> { loop {} });
Expand Down Expand Up @@ -444,6 +446,8 @@ fn func_constructors() {
Func::wrap(&mut store, || -> Result<Option<NoFunc>> { loop {} });
Func::wrap(&mut store, || -> Result<NoExtern> { loop {} });
Func::wrap(&mut store, || -> Result<Option<NoExtern>> { loop {} });
Func::wrap(&mut store, || -> Result<NoneRef> { loop {} });
Func::wrap(&mut store, || -> Result<Option<NoneRef>> { loop {} });
}

#[test]
Expand Down

0 comments on commit f4b2a8d

Please sign in to comment.