Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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