Skip to content

Commit 939917d

Browse files
authored
Add the wasmtime::NoneRef host API type for Wasm's (ref null? none) instances (#9455)
1 parent 9529243 commit 939917d

4 files changed

Lines changed: 165 additions & 0 deletions

File tree

crates/wasmtime/src/runtime/func.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,8 @@ impl Func {
615615
/// | `Rooted<StructRef>` | `(ref struct)` |
616616
/// | `Option<Rooted<ArrayRef>>` | `(ref null array)` |
617617
/// | `Rooted<ArrayRef>` | `(ref array)` |
618+
/// | `Option<NoneRef>` | `nullref` aka `(ref null none)` |
619+
/// | `NoneRef` | `(ref none)` |
618620
///
619621
/// Note that anywhere a `Rooted<T>` appears, a `ManuallyRooted<T>` may also
620622
/// be used.
@@ -1444,6 +1446,8 @@ impl Func {
14441446
/// | `(ref struct)` | `Rooted<StructRef>` |
14451447
/// | `arrayref` aka `(ref null array)` | `Option<Rooted<ArrayRef>>` |
14461448
/// | `(ref array)` | `Rooted<ArrayRef>` |
1449+
/// | `nullref` aka `(ref null none)` | `Option<NoneRef>` |
1450+
/// | `(ref none)` | `NoneRef` |
14471451
/// | `funcref` aka `(ref null func)` | `Option<Func>` |
14481452
/// | `(ref func)` | `Func` |
14491453
/// | `(ref null <func type index>)` | `Option<Func>` |

crates/wasmtime/src/runtime/gc.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ pub use disabled::*;
1111
mod noextern;
1212
pub use noextern::NoExtern;
1313

14+
mod none_ref;
15+
pub use none_ref::NoneRef;
16+
1417
use core::fmt;
1518
use core::ops::Deref;
1619

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

tests/all/func.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,8 @@ fn func_constructors() {
413413
Func::wrap(&mut store, || -> Option<NoFunc> { None });
414414
Func::wrap(&mut store, || -> NoExtern { loop {} });
415415
Func::wrap(&mut store, || -> Option<NoExtern> { None });
416+
Func::wrap(&mut store, || -> NoneRef { loop {} });
417+
Func::wrap(&mut store, || -> Option<NoneRef> { None });
416418

417419
Func::wrap(&mut store, || -> Result<()> { loop {} });
418420
Func::wrap(&mut store, || -> Result<i32> { loop {} });
@@ -444,6 +446,8 @@ fn func_constructors() {
444446
Func::wrap(&mut store, || -> Result<Option<NoFunc>> { loop {} });
445447
Func::wrap(&mut store, || -> Result<NoExtern> { loop {} });
446448
Func::wrap(&mut store, || -> Result<Option<NoExtern>> { loop {} });
449+
Func::wrap(&mut store, || -> Result<NoneRef> { loop {} });
450+
Func::wrap(&mut store, || -> Result<Option<NoneRef>> { loop {} });
447451
}
448452

449453
#[test]

0 commit comments

Comments
 (0)