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
    }
}