witnext/
ast.rs

1use crate::Abi;
2use std::collections::{HashMap, HashSet};
3use std::rc::Rc;
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
6pub struct Id(String);
7
8impl Id {
9    pub fn new<S: AsRef<str>>(s: S) -> Self {
10        Id(s.as_ref().to_string())
11    }
12    pub fn as_str(&self) -> &str {
13        self.0.as_str()
14    }
15}
16
17impl AsRef<str> for Id {
18    fn as_ref(&self) -> &str {
19        self.0.as_ref()
20    }
21}
22
23impl PartialEq<&str> for Id {
24    fn eq(&self, rhs: &&str) -> bool {
25        PartialEq::eq(self.as_ref(), *rhs)
26    }
27}
28
29impl PartialEq<Id> for &str {
30    fn eq(&self, rhs: &Id) -> bool {
31        PartialEq::eq(*self, rhs.as_ref())
32    }
33}
34
35impl From<&str> for Id {
36    fn from(s: &str) -> Self {
37        Self::new(s)
38    }
39}
40
41#[derive(Debug, Clone)]
42pub struct Module {
43    name: Id,
44    module_id: ModuleId,
45    types: Vec<Rc<NamedType>>,
46    type_map: HashMap<Id, Rc<NamedType>>,
47
48    resources: Vec<Rc<Resource>>,
49    resource_map: HashMap<Id, Rc<Resource>>,
50
51    funcs: Vec<Rc<Function>>,
52    func_map: HashMap<Id, Rc<Function>>,
53
54    constants: Vec<Constant>,
55}
56
57#[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
58pub struct ModuleId(pub(crate) Rc<std::path::PathBuf>);
59
60impl Module {
61    pub(crate) fn new(name: Id, module_id: ModuleId) -> Module {
62        Module {
63            name,
64            module_id,
65            types: Default::default(),
66            type_map: Default::default(),
67            resources: Default::default(),
68            resource_map: Default::default(),
69            funcs: Default::default(),
70            func_map: Default::default(),
71            constants: Default::default(),
72        }
73    }
74
75    pub fn name(&self) -> &Id {
76        &self.name
77    }
78
79    pub fn module_id(&self) -> &ModuleId {
80        &self.module_id
81    }
82
83    pub(crate) fn push_type(&mut self, ty: Rc<NamedType>) {
84        assert!(self.type_map.insert(ty.name.clone(), ty.clone()).is_none());
85        self.types.push(ty);
86    }
87
88    pub(crate) fn push_resource(&mut self, r: Rc<Resource>) {
89        assert!(self
90            .resource_map
91            .insert(r.name.clone(), r.clone())
92            .is_none());
93        self.resources.push(r);
94    }
95
96    pub(crate) fn push_func(&mut self, func: Rc<Function>) {
97        assert!(self
98            .func_map
99            .insert(func.name.clone(), func.clone())
100            .is_none());
101        self.funcs.push(func);
102    }
103
104    pub(crate) fn push_constant(&mut self, constant: Constant) {
105        self.constants.push(constant);
106    }
107
108    pub fn typename(&self, name: &Id) -> Option<Rc<NamedType>> {
109        self.type_map.get(name).cloned()
110    }
111
112    pub fn typenames<'a>(&'a self) -> impl Iterator<Item = &'a Rc<NamedType>> + 'a {
113        self.types.iter()
114    }
115
116    pub fn resource(&self, name: &Id) -> Option<Rc<Resource>> {
117        self.resource_map.get(name).cloned()
118    }
119
120    pub fn resources<'a>(&'a self) -> impl Iterator<Item = &'a Rc<Resource>> + 'a {
121        self.resources.iter()
122    }
123
124    /// All of the (unique) types used as "err" variant of results returned from
125    /// functions.
126    pub fn error_types<'a>(&'a self) -> impl Iterator<Item = TypeRef> + 'a {
127        let errors: HashSet<TypeRef> = self
128            .funcs()
129            .filter_map(|f| {
130                if f.results.len() == 1 {
131                    Some(f.results[0].tref.type_().clone())
132                } else {
133                    None
134                }
135            })
136            .filter_map(|t| match &*t {
137                Type::Variant(v) => {
138                    let (_ok, err) = v.as_expected()?;
139                    Some(err?.clone())
140                }
141                _ => None,
142            })
143            .collect::<HashSet<TypeRef>>();
144        errors.into_iter()
145    }
146
147    pub fn func(&self, name: &Id) -> Option<Rc<Function>> {
148        self.func_map.get(&name).cloned()
149    }
150
151    pub fn funcs<'a>(&'a self) -> impl Iterator<Item = Rc<Function>> + 'a {
152        self.funcs.iter().cloned()
153    }
154
155    pub fn constants<'a>(&'a self) -> impl Iterator<Item = &'a Constant> + 'a {
156        self.constants.iter()
157    }
158}
159
160#[derive(Debug, Clone, PartialEq, Eq, Hash)]
161pub enum TypeRef {
162    Name(Rc<NamedType>),
163    Value(Rc<Type>),
164}
165
166impl TypeRef {
167    pub fn type_(&self) -> &Rc<Type> {
168        match self {
169            TypeRef::Name(named) => named.type_(),
170            TypeRef::Value(v) => v,
171        }
172    }
173
174    pub fn name(&self) -> Option<&NamedType> {
175        match self {
176            TypeRef::Name(n) => Some(n),
177            TypeRef::Value(_) => None,
178        }
179    }
180
181    pub fn named(&self) -> bool {
182        match self {
183            TypeRef::Name(_) => true,
184            TypeRef::Value(_) => false,
185        }
186    }
187
188    pub fn type_equal(&self, other: &TypeRef) -> bool {
189        self.type_().type_equal(other.type_())
190    }
191}
192
193#[derive(Debug, Clone, PartialEq, Eq, Hash)]
194pub struct NamedType {
195    pub name: Id,
196    pub module: ModuleId,
197    pub tref: TypeRef,
198    pub docs: String,
199}
200
201impl NamedType {
202    pub fn type_(&self) -> &Rc<Type> {
203        self.tref.type_()
204    }
205}
206
207/// Structure of all possible interface types.
208///
209/// Note that this is intended to match the interface types proposal itself.
210/// Currently this is relatively close to that with just a few `*.witx`
211/// extensions for now.
212#[derive(Debug, Clone, PartialEq, Eq, Hash)]
213pub enum Type {
214    /// A structure with named field.
215    Record(RecordDatatype),
216    /// An enumeration where a value is one of a number of variants.
217    Variant(Variant),
218    /// A "handle" which is an un-forgeable reference. Today this is an `i32`
219    /// where a module can't forge and use integers it was not already given
220    /// access to.
221    Handle(HandleDatatype),
222    /// A list of a type, stored in linear memory.
223    ///
224    /// Note that lists of `char` are specialized to indicate strings.
225    List(TypeRef),
226    /// A `witx`-specific type representing a raw mutable pointer into linear
227    /// memory
228    Pointer(TypeRef),
229    /// A `witx`-specific type representing a raw const pointer into linear
230    /// memory
231    ConstPointer(TypeRef),
232    /// A buffer type representing a window in memory
233    Buffer(Buffer),
234    /// A builtin base-case type.
235    Builtin(BuiltinType),
236}
237
238impl Type {
239    /// Returns a human-readable string to describe this type.
240    pub fn kind(&self) -> &'static str {
241        use Type::*;
242        match self {
243            Record(_) => "record",
244            Variant(_) => "variant",
245            Handle(_) => "handle",
246            List(_) => "list",
247            Pointer(_) => "pointer",
248            ConstPointer(_) => "constpointer",
249            Buffer(_) => "buffer",
250            Builtin(_) => "builtin",
251        }
252    }
253
254    /// Returns whether the in-memory representation of this type will always be
255    /// valid regardless of the value of all the bits in memory.
256    ///
257    /// This is only true for numerical types, pointers, and records of these
258    /// values. This is used for canonical lifting/lowering of lists.
259    pub fn all_bits_valid(&self) -> bool {
260        match self {
261            Type::Record(r) => r.members.iter().all(|t| t.tref.type_().all_bits_valid()),
262
263            Type::Builtin(BuiltinType::Char)
264            | Type::Variant(_)
265            | Type::Handle(_)
266            | Type::Buffer(_)
267            | Type::List(_) => false,
268
269            Type::Builtin(BuiltinType::U8 { .. })
270            | Type::Builtin(BuiltinType::S8)
271            | Type::Builtin(BuiltinType::U16)
272            | Type::Builtin(BuiltinType::S16)
273            | Type::Builtin(BuiltinType::U32 { .. })
274            | Type::Builtin(BuiltinType::S32)
275            | Type::Builtin(BuiltinType::U64)
276            | Type::Builtin(BuiltinType::S64)
277            | Type::Builtin(BuiltinType::F32)
278            | Type::Builtin(BuiltinType::F64)
279            | Type::Pointer(_)
280            | Type::ConstPointer(_) => true,
281        }
282    }
283
284    pub fn type_equal(&self, other: &Type) -> bool {
285        match self {
286            Type::Record(a) => match other {
287                Type::Record(b) => a.type_equal(b),
288                _ => false,
289            },
290            Type::Variant(a) => match other {
291                Type::Variant(b) => a.type_equal(b),
292                _ => false,
293            },
294            Type::Handle(a) => match other {
295                Type::Handle(b) => a.type_equal(b),
296                _ => false,
297            },
298            Type::List(a) => match other {
299                Type::List(b) => a.type_equal(b),
300                _ => false,
301            },
302            Type::Pointer(a) => match other {
303                Type::Pointer(b) => a.type_equal(b),
304                _ => false,
305            },
306            Type::ConstPointer(a) => match other {
307                Type::ConstPointer(b) => a.type_equal(b),
308                _ => false,
309            },
310            Type::Builtin(a) => match other {
311                Type::Builtin(b) => a == b,
312                _ => false,
313            },
314            Type::Buffer(a) => match other {
315                Type::Buffer(b) => a.type_equal(b),
316                _ => false,
317            },
318        }
319    }
320}
321
322#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
323pub enum BuiltinType {
324    /// This is a 32-bit unicode scalar value, not a code point.
325    ///
326    /// Same as the Rust language's `char` type.
327    Char,
328    /// An 8-bit unsigned integer.
329    U8 {
330        /// Indicates whether this type is intended to represent the `char`
331        /// type in the C language. The C `char` type is often unsigned, but
332        /// it's language-specific. At an interface-types level this is an
333        /// unsigned byte but binding generators may wish to bind this as the
334        /// language-specific representation for a C character instead.
335        ///
336        /// This is also currently used exclusively in conjunction with `@witx
337        /// pointer` to hint that it's pointing to unicode string data as well.
338        lang_c_char: bool,
339    },
340    /// A 16-bit unsigned integer.
341    U16,
342    /// A 32-bit unsigned integer.
343    U32 {
344        /// Indicates that this 32-bit value should actually be considered a
345        /// pointer-like value in language bindings. At the interface types
346        /// layer this is always a 32-bit unsigned value, but binding
347        /// generators may wish to instead bind this as the equivalent of C's
348        /// `size_t` for convenience with other APIs.
349        ///
350        /// This allows witx authors to communicate the intent that the
351        /// argument or return-value is pointer-like.
352        lang_ptr_size: bool,
353    },
354    /// A 64-bit unsigned integer.
355    U64,
356    /// An 8-bit signed integer
357    S8,
358    /// A 16-bit signed integer
359    S16,
360    /// A 32-bit signed integer
361    S32,
362    /// A 64-bit signed integer
363    S64,
364    /// A 32-bit floating point value.
365    F32,
366    /// A 64-bit floating point value.
367    F64,
368}
369
370#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
371pub enum IntRepr {
372    U8,
373    U16,
374    U32,
375    U64,
376}
377
378impl IntRepr {
379    pub fn to_builtin(&self) -> BuiltinType {
380        match self {
381            IntRepr::U8 => BuiltinType::U8 { lang_c_char: false },
382            IntRepr::U16 => BuiltinType::U16,
383            IntRepr::U32 => BuiltinType::U32 {
384                lang_ptr_size: false,
385            },
386            IntRepr::U64 => BuiltinType::U64,
387        }
388    }
389}
390
391/// A struct-like value with named fields.
392///
393/// Records map to `struct`s in most languages where this is a type with a
394/// number of named fields that all have their own particular type. Field order
395/// dictates layout in memory.
396#[derive(Debug, Clone, PartialEq, Eq, Hash)]
397pub struct RecordDatatype {
398    /// A hint as to what this record might be.
399    ///
400    /// Note that in the future this will only be a hint, not a control of the
401    /// actual representation itself. At this time though the record layout of
402    /// bitflags is different from other types.
403    pub kind: RecordKind,
404
405    /// A list of named fields for this record.
406    pub members: Vec<RecordMember>,
407}
408
409/// Different kinds of records used for hinting various language-specific types.
410#[derive(Debug, Clone, PartialEq, Eq, Hash)]
411pub enum RecordKind {
412    /// A tuple where the name of all fields are consecutive integers starting
413    /// at "0".
414    Tuple,
415    /// A record where all fields are `bool`s. Currently represented as an
416    /// integer with bits set or not set.
417    Bitflags(IntRepr),
418    /// All other structures.
419    Other,
420}
421
422#[derive(Debug, Clone, PartialEq, Eq, Hash)]
423pub struct RecordMember {
424    pub name: Id,
425    pub tref: TypeRef,
426    pub docs: String,
427}
428
429impl RecordDatatype {
430    pub fn is_tuple(&self) -> bool {
431        match self.kind {
432            RecordKind::Tuple => true,
433            _ => false,
434        }
435    }
436
437    pub fn bitflags_repr(&self) -> Option<IntRepr> {
438        match self.kind {
439            RecordKind::Bitflags(i) => Some(i),
440            _ => None,
441        }
442    }
443
444    pub fn type_equal(&self, other: &RecordDatatype) -> bool {
445        // Note that eventually we'll probably want to ignore ABI-style
446        // differences where the fields are reordered but have the same types.
447        // That's more of a subtyping-style check, however, and would require a
448        // bit more infrastructure so we just go for strict equality for now.
449        self.members.len() == other.members.len()
450            && self
451                .members
452                .iter()
453                .zip(&other.members)
454                .all(|(a, b)| a.type_equal(b))
455    }
456}
457
458impl RecordMember {
459    pub fn type_equal(&self, other: &RecordMember) -> bool {
460        self.name == other.name && self.tref.type_equal(&other.tref)
461    }
462}
463
464impl RecordKind {
465    pub fn infer(members: &[RecordMember]) -> RecordKind {
466        if members.len() == 0 {
467            return RecordKind::Other;
468        }
469
470        // Structs-of-bools are classified to get represented as bitflags.
471        if members.iter().all(|t| is_bool(&t.tref)) {
472            match members.len() {
473                n if n <= 8 => return RecordKind::Bitflags(IntRepr::U8),
474                n if n <= 16 => return RecordKind::Bitflags(IntRepr::U16),
475                n if n <= 32 => return RecordKind::Bitflags(IntRepr::U32),
476                n if n <= 64 => return RecordKind::Bitflags(IntRepr::U64),
477                _ => {}
478            }
479        }
480
481        // Members with consecutive integer names get represented as tuples.
482        if members
483            .iter()
484            .enumerate()
485            .all(|(i, m)| m.name.as_str().parse().ok() == Some(i))
486        {
487            return RecordKind::Tuple;
488        }
489
490        return RecordKind::Other;
491
492        fn is_bool(t: &TypeRef) -> bool {
493            match &**t.type_() {
494                Type::Variant(v) => v.is_bool(),
495                _ => false,
496            }
497        }
498    }
499}
500
501/// A type which represents how values can be one of a set of possible cases.
502///
503/// This type maps to an `enum` in languages like Rust, but doesn't have an
504/// equivalent in languages like JS or C. The closest analog in C is a tagged
505/// union, but a `Variant` is always consistent whereas a tagged union in C
506/// could be mis-tagged or such.
507///
508/// Variants are used to represent one of a possible set of types. For example
509/// an enum-like variant, a result that is either success or failure, or even a
510/// simple `bool`. Variants are primarily used heavily with various kinds of
511/// shorthands in the `*.witx` format to represent idioms in languages.
512#[derive(Debug, Clone, PartialEq, Eq, Hash)]
513pub struct Variant {
514    /// The bit representation of the width of this variant's tag when the
515    /// variant is stored in memory.
516    pub tag_repr: IntRepr,
517    /// The possible cases that values of this variant type can take.
518    pub cases: Vec<Case>,
519}
520
521impl Variant {
522    pub fn infer_repr(cases: usize) -> IntRepr {
523        match cases {
524            n if n < u8::max_value() as usize => IntRepr::U8,
525            n if n < u16::max_value() as usize => IntRepr::U16,
526            n if n < u32::max_value() as usize => IntRepr::U32,
527            n if n < u64::max_value() as usize => IntRepr::U64,
528            _ => panic!("too many cases to fit in a repr"),
529        }
530    }
531
532    /// If this variant looks like an `option` shorthand, return the type
533    /// associated with option.
534    ///
535    /// Only matches variants fo the form:
536    ///
537    /// ```text
538    /// (variant
539    ///     (case "none")
540    ///     (case "some" ty))
541    /// ```
542    pub fn as_option(&self) -> Option<&TypeRef> {
543        if self.cases.len() != 2 {
544            return None;
545        }
546        if self.cases[0].name != "none" || self.cases[0].tref.is_some() {
547            return None;
548        }
549        if self.cases[1].name != "some" {
550            return None;
551        }
552        self.cases[1].tref.as_ref()
553    }
554
555    /// If this variant looks like an `expected` shorthand, return the ok/err
556    /// types associated with this result.
557    ///
558    /// Only matches variants fo the form:
559    ///
560    /// ```text
561    /// (variant
562    ///     (case "ok" ok?)
563    ///     (case "err" err?))
564    /// ```
565    pub fn as_expected(&self) -> Option<(Option<&TypeRef>, Option<&TypeRef>)> {
566        if self.cases.len() != 2 {
567            return None;
568        }
569        if self.cases[0].name != "ok" {
570            return None;
571        }
572        if self.cases[1].name != "err" {
573            return None;
574        }
575        Some((self.cases[0].tref.as_ref(), self.cases[1].tref.as_ref()))
576    }
577
578    /// Returns whether this variant type is "bool-like" meaning that it matches
579    /// this type:
580    ///
581    /// ```text
582    /// (variant
583    ///     (case "false")
584    ///     (case "true"))
585    /// ```
586    pub fn is_bool(&self) -> bool {
587        self.cases.len() == 2
588            && self.cases[0].name == "false"
589            && self.cases[1].name == "true"
590            && self.cases[0].tref.is_none()
591            && self.cases[1].tref.is_none()
592    }
593
594    /// Returns whether this variant type is "enum-like" meaning that all of its
595    /// cases have no payload associated with them.
596    pub fn is_enum(&self) -> bool {
597        self.cases.iter().all(|c| c.tref.is_none())
598    }
599
600    pub fn type_equal(&self, other: &Variant) -> bool {
601        // See the comment in `RecordDatatype::type_equal` for why strict
602        // positional equality is required here
603        self.tag_repr == other.tag_repr
604            && self.cases.len() == other.cases.len()
605            && self
606                .cases
607                .iter()
608                .zip(&other.cases)
609                .all(|(a, b)| a.type_equal(b))
610    }
611}
612
613/// One of a number of possible types that a `Variant` can take.
614#[derive(Debug, Clone, PartialEq, Eq, Hash)]
615pub struct Case {
616    /// The name of this case and how to identify it.
617    pub name: Id,
618    /// An optional payload type for this case and data that can be associated
619    /// with it.
620    pub tref: Option<TypeRef>,
621    /// Documentation for this case.
622    pub docs: String,
623}
624
625impl Case {
626    pub fn type_equal(&self, other: &Case) -> bool {
627        self.name == other.name
628            && match (&self.tref, &other.tref) {
629                (Some(a), Some(b)) => a.type_equal(b),
630                (None, None) => true,
631                _ => false,
632            }
633    }
634}
635
636#[derive(Debug, Clone, PartialEq, Eq, Hash)]
637pub struct Resource {
638    /// The local name within the module this resource is defined within. This
639    /// may differ from the id of the resource itself.
640    pub name: Id,
641    /// The unique id assigned to this resource.
642    pub resource_id: ResourceId,
643    /// Documentation in the defining module, if any.
644    pub docs: String,
645}
646
647/// A unique id used to determine whether two handles are nominally referring
648/// to the same resource.
649///
650/// An id is composed of the definition location (a module id) and the original
651/// name within that module.
652#[derive(Debug, Clone, PartialEq, Eq, Hash)]
653pub struct ResourceId {
654    pub name: Id,
655    pub module_id: ModuleId,
656}
657
658#[derive(Debug, Clone, PartialEq, Eq, Hash)]
659pub struct HandleDatatype {
660    /// The resource that this handle references, used for determining if two
661    /// handle types are nominally equal to one another.
662    pub resource_id: ResourceId,
663}
664
665impl HandleDatatype {
666    pub fn type_equal(&self, other: &HandleDatatype) -> bool {
667        self.resource_id == other.resource_id
668    }
669}
670
671#[derive(Debug, Clone, PartialEq, Eq, Hash)]
672pub struct Function {
673    pub abi: Abi,
674    pub name: Id,
675    pub params: Vec<Param>,
676    pub results: Vec<Param>,
677    pub noreturn: bool,
678    pub docs: String,
679}
680
681#[derive(Debug, Clone, PartialEq, Eq, Hash)]
682pub struct Param {
683    pub name: Id,
684    pub tref: TypeRef,
685    pub docs: String,
686}
687
688#[derive(Debug, Clone, PartialEq, Eq, Hash)]
689pub struct Constant {
690    pub ty: Id,
691    pub name: Id,
692    pub value: u64,
693    pub docs: String,
694}
695
696#[derive(Debug, Clone, PartialEq, Eq, Hash)]
697pub struct Buffer {
698    /// Whether or not this is an `out` buffer (`true`) or an `in` buffer
699    /// (`false`)
700    pub out: bool,
701
702    /// The type of items this buffer contains
703    pub tref: TypeRef,
704}
705
706impl Buffer {
707    pub fn type_equal(&self, other: &Buffer) -> bool {
708        self.out == other.out && self.tref.type_equal(&other.tref)
709    }
710}