Skip to main content

submilli_wasm/value/
types.rs

1//! Type descriptors: `ValType`, `FuncType`, `MemoryType`, etc. (wasmtime-compatible).
2
3use crate::canon::CanonicalTypeId;
4use crate::engine::Engine;
5use crate::value::gc_type::{ArrayType, StructType};
6use crate::value::tag_type::TagType;
7
8/// A wasm value type. (Public/boundary type — the serializable internal form is `canon::IrVal`.)
9#[derive(Clone, Debug, PartialEq, Eq, Hash)]
10pub enum ValType {
11    I32,
12    I64,
13    F32,
14    F64,
15    V128,
16    Ref(RefType),
17}
18
19/// Mutability of a global.
20#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
21pub enum Mutability {
22    Const,
23    Var,
24}
25
26/// A reference type: nullability plus a heap type.
27#[derive(Clone, Debug, PartialEq, Eq, Hash)]
28pub struct RefType {
29    nullable: bool,
30    heap: HeapType,
31}
32
33impl RefType {
34    pub fn new(is_nullable: bool, heap_type: HeapType) -> RefType {
35        RefType {
36            nullable: is_nullable,
37            heap: heap_type,
38        }
39    }
40
41    /// A *nullable* reference to `heap` — the shape of the wasmtime `*REF` constants (see
42    /// [`ref_const`](super::ref_const)).
43    pub(crate) const fn new_nullable(heap: HeapType) -> RefType {
44        RefType {
45            nullable: true,
46            heap,
47        }
48    }
49
50    pub fn is_nullable(&self) -> bool {
51        self.nullable
52    }
53
54    pub fn heap_type(&self) -> &HeapType {
55        &self.heap
56    }
57}
58
59/// The heap type of a reference: the abstract hierarchies (func/extern/any + bottoms) plus
60/// concrete (defined) types, each carrying an engine-interned descriptor handle (`FuncType`/
61/// `StructType`/`ArrayType`). Matches `wasmtime::HeapType`. (Boundary type — the serializable
62/// internal form is `canon::IrHeap`.)
63#[derive(Clone, Debug, PartialEq, Eq, Hash)]
64#[non_exhaustive]
65pub enum HeapType {
66    Func,
67    NoFunc,
68    Extern,
69    NoExtern,
70    Any,
71    Eq,
72    I31,
73    Struct,
74    /// A concrete struct type.
75    ConcreteStruct(StructType),
76    Array,
77    /// A concrete array type.
78    ConcreteArray(ArrayType),
79    Exn,
80    NoExn,
81    None,
82    /// A concrete function type.
83    ConcreteFunc(FuncType),
84}
85
86impl HeapType {
87    /// Is `self` a subtype of `other` in the **abstract** lattice (the three disjoint
88    /// hierarchies + bottoms), plus concrete-type placement by hierarchy and id equality
89    /// (`self == other`). Concrete-to-concrete *declared* subtyping (the supertype chain) is
90    /// resolved separately against the type registry (`Engine::is_subtype`).
91    pub(crate) fn matches(&self, other: &HeapType) -> bool {
92        use HeapType as H;
93        if self == other {
94            return true;
95        }
96        matches!(
97            (self, other),
98            (H::NoFunc, H::Func | H::ConcreteFunc(_))
99                | (H::ConcreteFunc(_), H::Func)
100                | (H::NoExtern, H::Extern)
101                | (H::NoExn, H::Exn)
102                | (H::I31 | H::Struct | H::Array | H::Eq, H::Any)
103                | (H::I31 | H::Struct | H::Array, H::Eq)
104                | (H::ConcreteStruct(_), H::Struct | H::Eq | H::Any)
105                | (H::ConcreteArray(_), H::Array | H::Eq | H::Any)
106                | (
107                    H::None,
108                    H::Any
109                        | H::Eq
110                        | H::I31
111                        | H::Struct
112                        | H::Array
113                        | H::ConcreteStruct(_)
114                        | H::ConcreteArray(_)
115                )
116        )
117    }
118}
119
120impl RefType {
121    /// Reference subtyping: heap-type subtyping, and a non-nullable ref is a subtype of a
122    /// nullable one (but not vice-versa).
123    pub(crate) fn matches(&self, other: &RefType) -> bool {
124        (!self.nullable || other.nullable) && self.heap.matches(&other.heap)
125    }
126}
127
128impl ValType {
129    /// Value subtyping: references by [`RefType::matches`], everything else by equality.
130    pub(crate) fn matches(&self, other: &ValType) -> bool {
131        match (self, other) {
132            (ValType::Ref(a), ValType::Ref(b)) => a.matches(b),
133            _ => self == other,
134        }
135    }
136}
137
138/// Whether every concrete type descriptor nested in `ty` belongs to `engine`. Canonical ids are
139/// registry-local and must never cross an engine boundary, even when the reference is null.
140pub(crate) fn val_type_belongs_to_engine(ty: &ValType, engine: &Engine) -> bool {
141    let ValType::Ref(reference) = ty else {
142        return true;
143    };
144    match reference.heap_type() {
145        HeapType::ConcreteStruct(ty) => ty.engine().same(engine),
146        HeapType::ConcreteArray(ty) => ty.engine().same(engine),
147        HeapType::ConcreteFunc(ty) => ty.engine().same(engine),
148        _ => true,
149    }
150}
151
152/// A function signature — an engine-interned handle (identity by canonical type id; the
153/// structure is materialized from the engine registry). Matches `wasmtime::FuncType`.
154///
155/// Refcounted (#27i): the handle holds one registration on its type's rec group; `Clone` increfs
156/// and `Drop` decrefs, so the group is reclaimed once the last handle (and any `Module`/`Store`
157/// holding it) is gone.
158pub struct FuncType {
159    engine: Engine,
160    id: CanonicalTypeId,
161}
162
163impl FuncType {
164    pub fn new(
165        engine: &Engine,
166        params: impl IntoIterator<Item = ValType>,
167        results: impl IntoIterator<Item = ValType>,
168    ) -> FuncType {
169        let params: Vec<ValType> = params.into_iter().collect();
170        let results: Vec<ValType> = results.into_iter().collect();
171        assert!(
172            params
173                .iter()
174                .chain(&results)
175                .all(|ty| val_type_belongs_to_engine(ty, engine)),
176            "function type contains a concrete type from a different engine"
177        );
178        // `intern_func_type` returns a type with one registration; this handle adopts it.
179        let id = engine.intern_func_type(&params, &results);
180        FuncType {
181            engine: engine.clone(),
182            id,
183        }
184    }
185
186    /// Wraps an already-interned canonical id, taking a fresh registration (internal — used by the
187    /// registry/module materialization boundary).
188    pub(crate) fn from_id(engine: &Engine, id: CanonicalTypeId) -> FuncType {
189        engine.incref_type(id);
190        FuncType {
191            engine: engine.clone(),
192            id,
193        }
194    }
195
196    /// The engine-canonical type id (internal identity).
197    pub(crate) fn canonical_id(&self) -> CanonicalTypeId {
198        self.id
199    }
200
201    pub(crate) fn engine(&self) -> &Engine {
202        &self.engine
203    }
204
205    pub fn params(&self) -> impl ExactSizeIterator<Item = ValType> {
206        self.signature().0.into_iter()
207    }
208
209    pub fn results(&self) -> impl ExactSizeIterator<Item = ValType> {
210        self.signature().1.into_iter()
211    }
212
213    // This handle holds a reference on its type, so the lookup cannot miss while it lives; the
214    // `wasmtime` signatures above have no error to report one with.
215    fn signature(&self) -> (Vec<ValType>, Vec<ValType>) {
216        self.engine
217            .func_sig(self.id)
218            .expect("live FuncType registration must retain its signature")
219    }
220}
221
222impl Clone for FuncType {
223    fn clone(&self) -> Self {
224        self.engine.incref_type(self.id);
225        FuncType {
226            engine: self.engine.clone(),
227            id: self.id,
228        }
229    }
230}
231
232impl Drop for FuncType {
233    fn drop(&mut self) {
234        self.engine.decref_type(self.id);
235    }
236}
237
238impl PartialEq for FuncType {
239    fn eq(&self, other: &Self) -> bool {
240        self.id == other.id
241    }
242}
243
244impl Eq for FuncType {}
245
246impl core::hash::Hash for FuncType {
247    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
248        self.id.hash(state);
249    }
250}
251
252impl core::fmt::Debug for FuncType {
253    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
254        f.debug_struct("FuncType")
255            .field("id", &self.id)
256            .finish_non_exhaustive()
257    }
258}
259
260/// A linear memory type (limits in 64 KiB pages).
261#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
262pub struct MemoryType {
263    minimum: u64,
264    maximum: Option<u64>,
265    memory64: bool,
266}
267
268impl MemoryType {
269    pub fn new(minimum: u32, maximum: Option<u32>) -> MemoryType {
270        MemoryType {
271            minimum: u64::from(minimum),
272            maximum: maximum.map(u64::from),
273            memory64: false,
274        }
275    }
276
277    pub fn new64(minimum: u64, maximum: Option<u64>) -> MemoryType {
278        MemoryType {
279            minimum,
280            maximum,
281            memory64: true,
282        }
283    }
284
285    pub fn minimum(&self) -> u64 {
286        self.minimum
287    }
288
289    pub fn maximum(&self) -> Option<u64> {
290        self.maximum
291    }
292
293    pub fn is_64(&self) -> bool {
294        self.memory64
295    }
296}
297
298/// A global type: content type plus mutability. (Boundary type; internal storage is IR.)
299#[derive(Clone, Debug, PartialEq, Eq, Hash)]
300pub struct GlobalType {
301    content: ValType,
302    mutability: Mutability,
303}
304
305impl GlobalType {
306    pub fn new(content: ValType, mutability: Mutability) -> GlobalType {
307        GlobalType {
308            content,
309            mutability,
310        }
311    }
312
313    pub fn content(&self) -> &ValType {
314        &self.content
315    }
316
317    pub fn mutability(&self) -> Mutability {
318        self.mutability
319    }
320}
321
322/// A table type: element reference type plus limits and index width. (Boundary type; internal
323/// storage is IR.)
324#[derive(Clone, Debug, PartialEq, Eq, Hash)]
325pub struct TableType {
326    element: RefType,
327    minimum: u64,
328    maximum: Option<u64>,
329    table64: bool,
330}
331
332impl TableType {
333    pub fn new(element: RefType, min: u32, max: Option<u32>) -> TableType {
334        TableType {
335            element,
336            minimum: u64::from(min),
337            maximum: max.map(u64::from),
338            table64: false,
339        }
340    }
341
342    pub fn new64(element: RefType, min: u64, max: Option<u64>) -> TableType {
343        TableType {
344            element,
345            minimum: min,
346            maximum: max,
347            table64: true,
348        }
349    }
350
351    pub fn element(&self) -> &RefType {
352        &self.element
353    }
354
355    pub fn minimum(&self) -> u64 {
356        self.minimum
357    }
358
359    pub fn maximum(&self) -> Option<u64> {
360        self.maximum
361    }
362
363    pub fn is_64(&self) -> bool {
364        self.table64
365    }
366}
367
368/// The type of an importable/exportable external item.
369#[derive(Clone, Debug)]
370pub enum ExternType {
371    Func(FuncType),
372    Memory(MemoryType),
373    Global(GlobalType),
374    Table(TableType),
375    Tag(TagType),
376}
377
378/// Metadata about a module import.
379#[derive(Clone, Debug)]
380pub struct ImportType<'module> {
381    module: &'module str,
382    name: &'module str,
383    ty: ExternType,
384}
385
386impl<'module> ImportType<'module> {
387    pub(crate) fn new(module: &'module str, name: &'module str, ty: ExternType) -> Self {
388        ImportType { module, name, ty }
389    }
390
391    pub fn module(&self) -> &'module str {
392        self.module
393    }
394
395    pub fn name(&self) -> &'module str {
396        self.name
397    }
398
399    pub fn ty(&self) -> ExternType {
400        self.ty.clone()
401    }
402}
403
404/// Metadata about a module export.
405#[derive(Clone, Debug)]
406pub struct ExportType<'module> {
407    name: &'module str,
408    ty: ExternType,
409}
410
411impl<'module> ExportType<'module> {
412    pub(crate) fn new(name: &'module str, ty: ExternType) -> Self {
413        ExportType { name, ty }
414    }
415
416    pub fn name(&self) -> &'module str {
417        self.name
418    }
419
420    pub fn ty(&self) -> ExternType {
421        self.ty.clone()
422    }
423}