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    pub refines: Option<u32>,
74}
75
76/// valtype_e in the specification
77#[derive(Debug, Clone)]
78pub enum Value<'a> {
79    Bool,
80    S(IntWidth),
81    U(IntWidth),
82    F(FloatWidth),
83    Char,
84    String,
85    List(Box<Value<'a>>),
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
139#[derive(Debug, Clone)]
140pub enum Result<'a> {
141    Unnamed(Value<'a>),
142    Named(Vec<Param<'a>>),
143}
144
145/// functype_e in the specification
146#[derive(Debug, Clone)]
147pub struct Func<'a> {
148    pub params: Vec<Param<'a>>,
149    pub result: Result<'a>,
150}
151
152/// In the spec, this does not exist, but a validation rule ensures an
153/// invariant that certain deftype_e s are of this form.
154#[derive(Debug, Clone)]
155pub enum Handleable {
156    Var(Tyvar),
157    Resource(ResourceId),
158}
159
160/// deftype_e in the specification
161#[derive(Debug, Clone)]
162pub enum Defined<'a> {
163    Handleable(Handleable),
164    Value(Value<'a>),
165    Func(Func<'a>),
166    Instance(QualifiedInstance<'a>),
167    Component(Component<'a>),
168}
169
170/// typebound_e in the specification
171#[derive(Debug, Clone)]
172pub enum TypeBound<'a> {
173    Eq(Defined<'a>),
174    SubResource,
175}
176
177/// The name of an import or export of the current
178/// component/context. Not in the spec; only used for
179/// [`BoundedTyvar::origin`] below.
180///
181/// Any string present in one of these should also be present in an
182/// [`ExternDecl::kebab_name`] in a relevant place.
183#[derive(Debug, Clone, PartialEq)]
184pub enum ImportExport<'a> {
185    Import(&'a str),
186    Export(&'a str),
187}
188impl<'a> ImportExport<'a> {
189    pub fn name(&self) -> &'a str {
190        match self {
191            ImportExport::Import(s) => s,
192            ImportExport::Export(s) => s,
193        }
194    }
195    pub fn imported(&self) -> bool {
196        match self {
197            ImportExport::Import(_) => true,
198            ImportExport::Export(_) => false,
199        }
200    }
201}
202
203/// An (optional) path through the imports/exports of a current
204/// component/context. Not in the spec; only used for
205/// [`BoundedTyvar::origin`] below.
206#[derive(Default, Debug, Clone, PartialEq)]
207pub struct TyvarOrigin<'a> {
208    /// Note that the most recent (closest) element is last
209    pub path: Option<Vec<ImportExport<'a>>>,
210}
211
212impl<'a> TyvarOrigin<'a> {
213    pub fn new() -> Self {
214        TyvarOrigin { path: Some(vec![]) }
215    }
216    pub fn push(&self, x: Option<ImportExport<'a>>) -> Self {
217        match (&self.path, x) {
218            (None, _) => TyvarOrigin { path: None },
219            (_, None) => self.clone(),
220            (Some(xs), Some(x)) => {
221                let mut xs = xs.clone();
222                xs.push(x);
223                TyvarOrigin { path: Some(xs) }
224            }
225        }
226    }
227    pub fn matches<I: Iterator<Item = &'a ImportExport<'a>>>(&self, path: I) -> bool {
228        self.path
229            .as_ref()
230            .map(|p| p.iter().rev().eq(path))
231            .unwrap_or(false)
232    }
233    pub fn is_local<
234        I: DoubleEndedIterator<Item = &'a ImportExport<'a>>
235            + ExactSizeIterator<Item = &'a ImportExport<'a>>,
236    >(
237        &self,
238        path: I,
239    ) -> Option<Vec<ImportExport<'a>>> {
240        let other = path.rev().skip(1).rev();
241        let path = self.path.as_ref()?;
242        let path = path.iter();
243        let mut path = path.rev();
244        for elem in other {
245            match path.next() {
246                None => break,
247                Some(oe) if oe != elem => return None,
248                _ => (),
249            }
250        }
251        Some(path.cloned().collect())
252    }
253    pub fn last_name(&self) -> Option<&'a str> {
254        self.path
255            .as_ref()
256            .and_then(|x| x.first())
257            .map(|ie| ie.name())
258    }
259    pub fn is_imported(&self) -> bool {
260        let Some(p) = &self.path else {
261            return false;
262        };
263        p[p.len() - 1].imported()
264    }
265}
266
267/// boundedtyvar_e in the spec
268///
269/// Because we use a de Bruijn representation of type indices, this is
270/// only the type_bound - which variable it is binding is implicit in
271/// its position in the list.
272#[derive(Debug, Clone)]
273pub struct BoundedTyvar<'a> {
274    /// This is not important for typechecking, but is used to keep
275    /// track of where a type variable originated from in order to
276    /// decide on a canonical name to be used in bindings
277    /// generation.
278    pub origin: TyvarOrigin<'a>,
279    pub bound: TypeBound<'a>,
280}
281
282impl<'a> BoundedTyvar<'a> {
283    pub fn new(bound: TypeBound<'a>) -> Self {
284        BoundedTyvar {
285            origin: TyvarOrigin::new(),
286            bound,
287        }
288    }
289    pub fn push_origin(&self, x: Option<ImportExport<'a>>) -> Self {
290        BoundedTyvar {
291            origin: self.origin.push(x),
292            ..self.clone()
293        }
294    }
295}
296
297/// externdesc_e in the specification
298#[derive(Debug, Clone)]
299pub enum ExternDesc<'a> {
300    CoreModule(CoreModule<'a>),
301    Func(Func<'a>),
302    /* TODO: First-class values (when the spec gets them) */
303    Type(Defined<'a>),
304    /// This uses an [`Instance`] rather than a [`QualifiedInstance`]
305    /// because the instance's evars need to be propagated up to the
306    /// surrounding component/instance (so that e.g. `alias`ing them
307    /// and using them in another import/export is possible).
308    Instance(Instance<'a>),
309    Component(Component<'a>),
310}
311
312/// Merely a convenience for [`Ctx::resolve_alias`]
313#[derive(Debug, Clone)]
314pub enum CoreOrComponentExternDesc<'a> {
315    Core(CoreExternDesc),
316    Component(ExternDesc<'a>),
317}
318
319/// externdecl_e in the specification
320#[derive(Debug, Clone)]
321pub struct ExternDecl<'a> {
322    pub kebab_name: &'a str,
323    pub desc: ExternDesc<'a>,
324}
325
326/// `instancetype_e` in the specification.
327///
328/// An "opened" instance, whose existential variables are recorded in
329/// some surrounding context.
330#[derive(Debug, Clone)]
331pub struct Instance<'a> {
332    pub exports: Vec<ExternDecl<'a>>,
333}
334
335/// This is an instance together with its existential variables. This
336/// concept doesn't exist as a named syntax class in the specification, but
337/// is the payload of the instance case of `deftype_e` and the output
338/// of the instance declaration inference judgement.
339#[derive(Debug, Clone)]
340pub struct QualifiedInstance<'a> {
341    /// Existential variables produced by this instance (which may be
342    /// referred to by [`exports`](Instance::exports)). These are stored in
343    /// "outside-in" order that matches how they would be written on
344    /// paper: de Bruijn index Bound(0) in the imports is the last
345    /// element in the list, and later elements can depend on earlier
346    /// ones.
347    pub evars: Vec<BoundedTyvar<'a>>,
348    pub unqualified: Instance<'a>,
349}
350
351/// componenttype_e in the specification
352#[derive(Debug, Clone)]
353pub struct Component<'a> {
354    /// Universal variables over which this component is parameterized
355    /// (which may be referred to by `imports`). These are stored in
356    /// "outside-in" order that matches how they would be written on
357    /// paper: de Bruijn index Bound(0) in the imports is the last
358    /// element in the list, and later elements can depend on earlier
359    /// ones.
360    pub uvars: Vec<BoundedTyvar<'a>>,
361    pub imports: Vec<ExternDecl<'a>>,
362    /// Since we already have [`QualifiedInstance`], we use that to
363    /// keep track of both the evars and the actual instance, unlike
364    /// in the spec; this is quite natural, since during inference the
365    /// evars are generated by the exports. However, they conceptually
366    /// belong here as much as there: instantiating a component should
367    /// add them to the context as non-imported uvars and produce an
368    /// [`Instance`], rather than a [`QualifiedInstance`] directly.
369    pub instance: QualifiedInstance<'a>,
370}
371
372// core:importdecl in the specification is wasmparser::Import
373
374/// core:importdesc in the specification
375#[derive(Debug, Clone)]
376pub enum CoreExternDesc {
377    Func(wasmparser::FuncType),
378    Table(wasmparser::TableType),
379    Memory(wasmparser::MemoryType),
380    Global(wasmparser::GlobalType),
381}
382
383/// core:exportdecl in the specification
384#[derive(Debug, Clone)]
385pub struct CoreExportDecl<'a> {
386    pub name: Name<'a>,
387    pub desc: CoreExternDesc,
388}
389
390// core:functype is wasmparser::FuncType
391
392/// core:instancetype_e in the specification
393#[derive(Debug, Clone)]
394pub struct CoreInstance<'a> {
395    pub exports: Vec<CoreExportDecl<'a>>,
396}
397
398/// core:moduletype_e in the specification
399#[derive(Debug, Clone)]
400pub struct CoreModule<'a> {
401    pub _imports: Vec<wasmparser::Import<'a>>,
402    pub _exports: Vec<CoreExportDecl<'a>>,
403}
404
405/// core:deftype_e in the specification
406#[derive(Debug, Clone)]
407pub enum CoreDefined<'a> {
408    Func(wasmparser::FuncType),
409    Module(CoreModule<'a>),
410}
411
412/// gamma_c in the specification
413#[derive(Default, Debug, Clone)]
414pub struct CoreCtx<'a> {
415    pub types: Vec<CoreDefined<'a>>,
416    pub funcs: Vec<wasmparser::FuncType>,
417    pub modules: Vec<CoreModule<'a>>,
418    pub instances: Vec<CoreInstance<'a>>,
419    pub tables: Vec<wasmparser::TableType>,
420    pub mems: Vec<wasmparser::MemoryType>,
421    pub globals: Vec<wasmparser::GlobalType>,
422}
423
424impl<'a> CoreCtx<'a> {
425    pub fn new() -> Self {
426        CoreCtx {
427            types: Vec::new(),
428            funcs: Vec::new(),
429            modules: Vec::new(),
430            instances: Vec::new(),
431            tables: Vec::new(),
432            mems: Vec::new(),
433            globals: Vec::new(),
434        }
435    }
436}
437
438/// resourcetype_e in the specification
439#[derive(Debug, Clone)]
440pub struct Resource {
441    // One day, there will be a `rep` field here...
442    pub _dtor: Option<FuncIdx>,
443}
444
445/// gamma in the specification
446#[derive(Debug, Clone)]
447pub struct Ctx<'p, 'a> {
448    pub parent: Option<&'p Ctx<'p, 'a>>,
449    pub outer_boundary: bool,
450    pub core: CoreCtx<'a>,
451    /// Universally-quantified variables, specifying for each the
452    /// known bound and whether or not it was imported. Uvars can come
453    /// from imports or component instantiations; only the imported
454    /// ones can be allowed to escape in the type of a components
455    /// exports/imports, since only those can be named outside of the
456    /// component itself.
457    pub uvars: Vec<(BoundedTyvar<'a>, bool)>,
458    /// Existentially-quantified variables, specifying for each the
459    /// known bound and, if it was locally defined, the type which
460    /// instantiates it.
461    pub evars: Vec<(BoundedTyvar<'a>, Option<Defined<'a>>)>,
462    pub rtypes: Vec<Resource>,
463    pub types: Vec<Defined<'a>>,
464    pub components: Vec<Component<'a>>,
465    pub instances: Vec<Instance<'a>>,
466    pub funcs: Vec<Func<'a>>,
467}
468
469impl<'p, 'a> Ctx<'p, 'a> {
470    pub fn new<'c>(parent: Option<&'p Ctx<'c, 'a>>, outer_boundary: bool) -> Self {
471        Ctx {
472            parent,
473            outer_boundary,
474            core: CoreCtx::new(),
475            uvars: Vec::new(),
476            evars: Vec::new(),
477            rtypes: Vec::new(),
478            types: Vec::new(),
479            components: Vec::new(),
480            instances: Vec::new(),
481            funcs: Vec::new(),
482        }
483    }
484}
485
486pub struct CtxParentIterator<'i, 'p: 'i, 'a: 'i> {
487    ctx: Option<&'i Ctx<'p, 'a>>,
488}
489impl<'i, 'p, 'a> Iterator for CtxParentIterator<'i, 'p, 'a> {
490    type Item = &'i Ctx<'p, 'a>;
491    fn next(&mut self) -> Option<Self::Item> {
492        match self.ctx {
493            Some(ctx) => {
494                self.ctx = ctx.parent;
495                Some(ctx)
496            }
497            None => None,
498        }
499    }
500}
501
502impl<'p, 'a> Ctx<'p, 'a> {
503    pub fn parents<'i>(&'i self) -> CtxParentIterator<'i, 'p, 'a> {
504        CtxParentIterator { ctx: Some(self) }
505    }
506}