Skip to main content

hyperlight_component_util/
etypes.rs

1/*
2Copyright 2025 The Hyperlight Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15 */
16
17/// Elaborated component model types
18///
19/// This has the basic type definitions for the elaborated types. They
20/// correspond roughly to the "Elaborated Types" section in the
21/// specification.
22use crate::structure::*;
23
24#[derive(Debug, Clone, PartialEq, Copy)]
25pub struct Name<'a> {
26    pub name: &'a str,
27}
28
29#[derive(Debug, Clone, PartialEq, Copy)]
30pub enum IntWidth {
31    I8,
32    I16,
33    I32,
34    I64,
35}
36impl IntWidth {
37    pub fn width(self) -> u8 {
38        match self {
39            IntWidth::I8 => 8,
40            IntWidth::I16 => 16,
41            IntWidth::I32 => 32,
42            IntWidth::I64 => 64,
43        }
44    }
45}
46
47#[derive(Debug, Clone, PartialEq, Copy)]
48pub enum FloatWidth {
49    F32,
50    F64,
51}
52impl FloatWidth {
53    pub fn width(self) -> u8 {
54        match self {
55            FloatWidth::F32 => 32,
56            FloatWidth::F64 => 64,
57        }
58    }
59}
60
61/// recordfield_e in the specification
62#[derive(Debug, Clone)]
63pub struct RecordField<'a> {
64    pub name: Name<'a>,
65    pub ty: Value<'a>,
66}
67
68/// variantcase_e in the specification
69#[derive(Debug, Clone)]
70pub struct VariantCase<'a> {
71    pub name: Name<'a>,
72    pub ty: Option<Value<'a>>,
73}
74
75/// valtype_e in the specification
76#[derive(Debug, Clone)]
77pub enum Value<'a> {
78    Bool,
79    S(IntWidth),
80    U(IntWidth),
81    F(FloatWidth),
82    Char,
83    String,
84    List(Box<Value<'a>>),
85    FixList(Box<Value<'a>>, u32),
86    Record(Vec<RecordField<'a>>),
87    Tuple(Vec<Value<'a>>),
88    Flags(Vec<Name<'a>>),
89    Variant(Vec<VariantCase<'a>>),
90    Enum(Vec<Name<'a>>),
91    Option(Box<Value<'a>>),
92    Result(Box<Option<Value<'a>>>, Box<Option<Value<'a>>>),
93    Own(Handleable),
94    Borrow(Handleable),
95    /// This records that a type variable was once here, and is used
96    /// to enforce export namedness checks.
97    Var(Option<Tyvar>, Box<Value<'a>>),
98}
99
100/// Global resource identifier
101#[derive(Debug, Clone, Copy, PartialEq)]
102pub struct ResourceId {
103    pub(super) id: u32,
104}
105
106/// To make certain substitutions easier, free type variables are
107/// divided into Universal and Existential variables.  Each is
108/// represented by a pair of indices: the first index is an index into
109/// [`Ctx::parents()`], specifying parent context has the variable
110/// definition in it, and the second is an index into that context's
111/// [`Ctx::uvars`] or [`Ctx::evars`].
112#[derive(Debug, Clone)]
113pub enum FreeTyvar {
114    U(u32, u32),
115    E(u32, u32),
116}
117
118/// We explicitly distinguish between bound type variables, which are
119/// can only only present on types that are themselves inside a
120/// [`QualifiedInstance`] or [`Component`], and free type variables
121/// that are used while constructing or deconstructing such a type in
122/// a [`Ctx`].
123#[derive(Debug, Clone)]
124pub enum Tyvar {
125    /// A bound type variable as a de Bruijn index (0 is the innermost
126    /// binder)
127    Bound(u32),
128    /// A free type variable, whose bounds/other information are
129    /// stored in the context
130    Free(FreeTyvar),
131}
132
133#[derive(Debug, Clone)]
134pub struct Param<'a> {
135    pub name: Name<'a>,
136    pub ty: Value<'a>,
137}
138
139pub type Result<'a> = Option<Value<'a>>;
140
141/// functype_e in the specification
142#[derive(Debug, Clone)]
143pub struct Func<'a> {
144    pub params: Vec<Param<'a>>,
145    pub result: Result<'a>,
146}
147
148/// In the spec, this does not exist, but a validation rule ensures an
149/// invariant that certain deftype_e s are of this form.
150#[derive(Debug, Clone)]
151pub enum Handleable {
152    Var(Tyvar),
153    Resource(ResourceId),
154}
155
156/// deftype_e in the specification
157#[derive(Debug, Clone)]
158pub enum Defined<'a> {
159    Handleable(Handleable),
160    Value(Value<'a>),
161    Func(Func<'a>),
162    Instance(QualifiedInstance<'a>),
163    Component(Component<'a>),
164}
165
166/// typebound_e in the specification
167#[derive(Debug, Clone)]
168pub enum TypeBound<'a> {
169    Eq(Defined<'a>),
170    SubResource,
171}
172
173/// The name of an import or export of the current
174/// component/context. Not in the spec; only used for
175/// [`BoundedTyvar::origin`] below.
176///
177/// Any string present in one of these should also be present in an
178/// [`ExternDecl::kebab_name`] in a relevant place.
179#[derive(Debug, Clone, PartialEq)]
180pub enum ImportExport<'a> {
181    Import(&'a str),
182    Export(&'a str),
183}
184impl<'a> ImportExport<'a> {
185    pub fn name(&self) -> &'a str {
186        match self {
187            ImportExport::Import(s) => s,
188            ImportExport::Export(s) => s,
189        }
190    }
191    pub fn imported(&self) -> bool {
192        match self {
193            ImportExport::Import(_) => true,
194            ImportExport::Export(_) => false,
195        }
196    }
197}
198
199/// An (optional) path through the imports/exports of a current
200/// component/context. Not in the spec; only used for
201/// [`BoundedTyvar::origin`] below.
202#[derive(Default, Debug, Clone, PartialEq)]
203pub struct TyvarOrigin<'a> {
204    /// Note that the most recent (closest) element is last
205    pub path: Option<Vec<ImportExport<'a>>>,
206}
207
208impl<'a> TyvarOrigin<'a> {
209    pub fn new() -> Self {
210        TyvarOrigin { path: Some(vec![]) }
211    }
212    pub fn push(&self, x: Option<ImportExport<'a>>) -> Self {
213        match (&self.path, x) {
214            (None, _) => TyvarOrigin { path: None },
215            (_, None) => self.clone(),
216            (Some(xs), Some(x)) => {
217                let mut xs = xs.clone();
218                xs.push(x);
219                TyvarOrigin { path: Some(xs) }
220            }
221        }
222    }
223    pub fn matches<I: Iterator<Item = &'a ImportExport<'a>>>(&self, path: I) -> bool {
224        self.path
225            .as_ref()
226            .map(|p| p.iter().rev().eq(path))
227            .unwrap_or(false)
228    }
229    pub fn is_local<
230        I: DoubleEndedIterator<Item = &'a ImportExport<'a>>
231            + ExactSizeIterator<Item = &'a ImportExport<'a>>,
232    >(
233        &self,
234        path: I,
235    ) -> Option<Vec<ImportExport<'a>>> {
236        let other = path.rev().skip(1).rev();
237        let path = self.path.as_ref()?;
238        let path = path.iter();
239        let mut path = path.rev();
240        for elem in other {
241            match path.next() {
242                None => break,
243                Some(oe) if oe != elem => return None,
244                _ => (),
245            }
246        }
247        Some(path.cloned().collect())
248    }
249    pub fn last_name(&self) -> Option<&'a str> {
250        self.path
251            .as_ref()
252            .and_then(|x| x.first())
253            .map(|ie| ie.name())
254    }
255    pub fn is_imported(&self) -> bool {
256        let Some(p) = &self.path else {
257            return false;
258        };
259        p[p.len() - 1].imported()
260    }
261}
262
263/// boundedtyvar_e in the spec
264///
265/// Because we use a de Bruijn representation of type indices, this is
266/// only the type_bound - which variable it is binding is implicit in
267/// its position in the list.
268#[derive(Debug, Clone)]
269pub struct BoundedTyvar<'a> {
270    /// This is not important for typechecking, but is used to keep
271    /// track of where a type variable originated from in order to
272    /// decide on a canonical name to be used in bindings
273    /// generation.
274    pub origin: TyvarOrigin<'a>,
275    pub bound: TypeBound<'a>,
276}
277
278impl<'a> BoundedTyvar<'a> {
279    pub fn new(bound: TypeBound<'a>) -> Self {
280        BoundedTyvar {
281            origin: TyvarOrigin::new(),
282            bound,
283        }
284    }
285    pub fn push_origin(&self, x: Option<ImportExport<'a>>) -> Self {
286        BoundedTyvar {
287            origin: self.origin.push(x),
288            ..self.clone()
289        }
290    }
291}
292
293/// externdesc_e in the specification
294#[derive(Debug, Clone)]
295pub enum ExternDesc<'a> {
296    CoreModule(CoreModule<'a>),
297    Func(Func<'a>),
298    /* TODO: First-class values (when the spec gets them) */
299    Type(Defined<'a>),
300    /// This uses an [`Instance`] rather than a [`QualifiedInstance`]
301    /// because the instance's evars need to be propagated up to the
302    /// surrounding component/instance (so that e.g. `alias`ing them
303    /// and using them in another import/export is possible).
304    Instance(Instance<'a>),
305    Component(Component<'a>),
306}
307
308/// Merely a convenience for [`Ctx::resolve_alias`]
309#[derive(Debug, Clone)]
310pub enum CoreOrComponentExternDesc<'a> {
311    Core(CoreExternDesc),
312    Component(ExternDesc<'a>),
313}
314
315/// externdecl_e in the specification
316#[derive(Debug, Clone)]
317pub struct ExternDecl<'a> {
318    pub kebab_name: &'a str,
319    pub desc: ExternDesc<'a>,
320}
321
322/// `instancetype_e` in the specification.
323///
324/// An "opened" instance, whose existential variables are recorded in
325/// some surrounding context.
326#[derive(Debug, Clone)]
327pub struct Instance<'a> {
328    pub exports: Vec<ExternDecl<'a>>,
329}
330
331/// This is an instance together with its existential variables. This
332/// concept doesn't exist as a named syntax class in the specification, but
333/// is the payload of the instance case of `deftype_e` and the output
334/// of the instance declaration inference judgement.
335#[derive(Debug, Clone)]
336pub struct QualifiedInstance<'a> {
337    /// Existential variables produced by this instance (which may be
338    /// referred to by [`exports`](Instance::exports)). These are stored in
339    /// "outside-in" order that matches how they would be written on
340    /// paper: de Bruijn index Bound(0) in the imports is the last
341    /// element in the list, and later elements can depend on earlier
342    /// ones.
343    pub evars: Vec<BoundedTyvar<'a>>,
344    pub unqualified: Instance<'a>,
345}
346
347/// componenttype_e in the specification
348#[derive(Debug, Clone)]
349pub struct Component<'a> {
350    /// Universal variables over which this component is parameterized
351    /// (which may be referred to by `imports`). These are stored in
352    /// "outside-in" order that matches how they would be written on
353    /// paper: de Bruijn index Bound(0) in the imports is the last
354    /// element in the list, and later elements can depend on earlier
355    /// ones.
356    pub uvars: Vec<BoundedTyvar<'a>>,
357    pub imports: Vec<ExternDecl<'a>>,
358    /// Since we already have [`QualifiedInstance`], we use that to
359    /// keep track of both the evars and the actual instance, unlike
360    /// in the spec; this is quite natural, since during inference the
361    /// evars are generated by the exports. However, they conceptually
362    /// belong here as much as there: instantiating a component should
363    /// add them to the context as non-imported uvars and produce an
364    /// [`Instance`], rather than a [`QualifiedInstance`] directly.
365    pub instance: QualifiedInstance<'a>,
366}
367
368// core:importdecl in the specification is wasmparser::Import
369
370/// core:importdesc in the specification
371#[derive(Debug, Clone)]
372pub enum CoreExternDesc {
373    Func(wasmparser::FuncType),
374    Table(wasmparser::TableType),
375    Memory(wasmparser::MemoryType),
376    Global(wasmparser::GlobalType),
377}
378
379/// core:exportdecl in the specification
380#[derive(Debug, Clone)]
381pub struct CoreExportDecl<'a> {
382    pub name: Name<'a>,
383    pub desc: CoreExternDesc,
384}
385
386// core:functype is wasmparser::FuncType
387
388/// core:instancetype_e in the specification
389#[derive(Debug, Clone)]
390pub struct CoreInstance<'a> {
391    pub exports: Vec<CoreExportDecl<'a>>,
392}
393
394/// core:moduletype_e in the specification
395#[derive(Debug, Clone)]
396pub struct CoreModule<'a> {
397    pub _imports: Vec<wasmparser::Import<'a>>,
398    pub _exports: Vec<CoreExportDecl<'a>>,
399}
400
401/// core:deftype_e in the specification
402#[derive(Debug, Clone)]
403pub enum CoreDefined<'a> {
404    Func(wasmparser::FuncType),
405    Module(CoreModule<'a>),
406}
407
408/// gamma_c in the specification
409#[derive(Default, Debug, Clone)]
410pub struct CoreCtx<'a> {
411    pub types: Vec<CoreDefined<'a>>,
412    pub funcs: Vec<wasmparser::FuncType>,
413    pub modules: Vec<CoreModule<'a>>,
414    pub instances: Vec<CoreInstance<'a>>,
415    pub tables: Vec<wasmparser::TableType>,
416    pub mems: Vec<wasmparser::MemoryType>,
417    pub globals: Vec<wasmparser::GlobalType>,
418}
419
420impl<'a> CoreCtx<'a> {
421    pub fn new() -> Self {
422        CoreCtx {
423            types: Vec::new(),
424            funcs: Vec::new(),
425            modules: Vec::new(),
426            instances: Vec::new(),
427            tables: Vec::new(),
428            mems: Vec::new(),
429            globals: Vec::new(),
430        }
431    }
432}
433
434/// resourcetype_e in the specification
435#[derive(Debug, Clone)]
436pub struct Resource {
437    // One day, there will be a `rep` field here...
438    pub _dtor: Option<FuncIdx>,
439}
440
441/// gamma in the specification
442#[derive(Debug, Clone)]
443pub struct Ctx<'p, 'a> {
444    pub parent: Option<&'p Ctx<'p, 'a>>,
445    pub outer_boundary: bool,
446    pub core: CoreCtx<'a>,
447    /// Universally-quantified variables, specifying for each the
448    /// known bound and whether or not it was imported. Uvars can come
449    /// from imports or component instantiations; only the imported
450    /// ones can be allowed to escape in the type of a components
451    /// exports/imports, since only those can be named outside of the
452    /// component itself.
453    pub uvars: Vec<(BoundedTyvar<'a>, bool)>,
454    /// Existentially-quantified variables, specifying for each the
455    /// known bound and, if it was locally defined, the type which
456    /// instantiates it.
457    pub evars: Vec<(BoundedTyvar<'a>, Option<Defined<'a>>)>,
458    pub rtypes: Vec<Resource>,
459    pub types: Vec<Defined<'a>>,
460    pub components: Vec<Component<'a>>,
461    pub instances: Vec<Instance<'a>>,
462    pub funcs: Vec<Func<'a>>,
463}
464
465impl<'p, 'a> Ctx<'p, 'a> {
466    pub fn new<'c>(parent: Option<&'p Ctx<'c, 'a>>, outer_boundary: bool) -> Self {
467        Ctx {
468            parent,
469            outer_boundary,
470            core: CoreCtx::new(),
471            uvars: Vec::new(),
472            evars: Vec::new(),
473            rtypes: Vec::new(),
474            types: Vec::new(),
475            components: Vec::new(),
476            instances: Vec::new(),
477            funcs: Vec::new(),
478        }
479    }
480}
481
482pub struct CtxParentIterator<'i, 'p: 'i, 'a: 'i> {
483    ctx: Option<&'i Ctx<'p, 'a>>,
484}
485impl<'i, 'p, 'a> Iterator for CtxParentIterator<'i, 'p, 'a> {
486    type Item = &'i Ctx<'p, 'a>;
487    fn next(&mut self) -> Option<Self::Item> {
488        match self.ctx {
489            Some(ctx) => {
490                self.ctx = ctx.parent;
491                Some(ctx)
492            }
493            None => None,
494        }
495    }
496}
497
498impl<'p, 'a> Ctx<'p, 'a> {
499    pub fn parents<'i>(&'i self) -> CtxParentIterator<'i, 'p, 'a> {
500        CtxParentIterator { ctx: Some(self) }
501    }
502}