Skip to main content

cranelift_isle/
sema.rs

1//! Semantic analysis.
2//!
3//! This module primarily contains the type environment and term environment.
4//!
5//! The type environment is constructed by analyzing an input AST. The type
6//! environment records the types used in the input source and the types of our
7//! various rules and symbols. ISLE's type system is intentionally easy to
8//! check, only requires a single pass over the AST, and doesn't require any
9//! unification or anything like that.
10//!
11//! The term environment is constructed from both the AST and type
12//! environment. It is sort of a typed and reorganized AST that more directly
13//! reflects ISLE semantics than the input ISLE source code (where as the AST is
14//! the opposite).
15
16use crate::ast;
17use crate::error::*;
18use crate::files::Files;
19use crate::lexer::Pos;
20use crate::log;
21use crate::stablemapset::{StableMap, StableSet};
22use std::collections::BTreeMap;
23use std::collections::BTreeSet;
24use std::collections::HashMap;
25use std::collections::hash_map::Entry;
26use std::fmt;
27
28declare_id!(
29    /// The id of an interned symbol.
30    Sym
31);
32declare_id!(
33    /// The id of an interned type inside the `TypeEnv`.
34    TypeId
35);
36declare_id!(
37    /// The id of a variant inside an enum.
38    VariantId
39);
40declare_id!(
41    /// The id of a field inside a variant.
42    FieldId
43);
44declare_id!(
45    /// The id of an interned term inside the `TermEnv`.
46    TermId
47);
48declare_id!(
49    /// The id of an interned rule inside the `TermEnv`.
50    RuleId
51);
52declare_id!(
53    /// The id of a bound variable inside a `Bindings`.
54    VarId
55);
56
57/// The type environment.
58///
59/// Keeps track of which symbols and rules have which types.
60#[derive(Debug)]
61pub struct TypeEnv {
62    /// Arena of interned symbol names.
63    ///
64    /// Referred to indirectly via `Sym` indices.
65    pub syms: Vec<String>,
66
67    /// Map of already-interned symbol names to their `Sym` ids.
68    pub sym_map: StableMap<String, Sym>,
69
70    /// Arena of type definitions.
71    ///
72    /// Referred to indirectly via `TypeId`s.
73    pub types: Vec<Type>,
74
75    /// A map from a type name symbol to its `TypeId`.
76    pub type_map: StableMap<Sym, TypeId>,
77
78    /// The types of constant symbols.
79    pub const_types: StableMap<Sym, TypeId>,
80
81    /// Type errors that we've found so far during type checking.
82    pub errors: Vec<Error>,
83}
84
85/// A built-in type.
86#[derive(Copy, Clone, Debug, PartialEq, Eq)]
87#[repr(u8)]
88pub enum BuiltinType {
89    /// The type of booleans, with values `true` and `false`.
90    Bool,
91    /// The types of fixed-width integers.
92    Int(IntType),
93}
94
95/// A built-in fixed-width integer type.
96#[derive(Copy, Clone, Debug, PartialEq, Eq)]
97pub enum IntType {
98    /// Unsigned, 8 bits.
99    U8,
100    /// Unsigned, 16 bits.
101    U16,
102    /// Unsigned, 32 bits.
103    U32,
104    /// Unsigned, 64 bits.
105    U64,
106    /// Unsigned, 128 bits.
107    U128,
108    /// Unsigned, enough bits to hold a pointer.
109    USize,
110    /// Signed, 8 bits.
111    I8,
112    /// Signed, 16 bits.
113    I16,
114    /// Signed, 32 bits.
115    I32,
116    /// Signed, 64 bits.
117    I64,
118    /// Signed, 128 bits.
119    I128,
120    /// Unsigned, enough bits to hold a pointer.
121    ISize,
122}
123
124impl IntType {
125    /// Get the integer type's name.
126    pub fn name(&self) -> &'static str {
127        match self {
128            IntType::U8 => "u8",
129            IntType::U16 => "u16",
130            IntType::U32 => "u32",
131            IntType::U64 => "u64",
132            IntType::U128 => "u128",
133            IntType::USize => "usize",
134            IntType::I8 => "i8",
135            IntType::I16 => "i16",
136            IntType::I32 => "i32",
137            IntType::I64 => "i64",
138            IntType::I128 => "i128",
139            IntType::ISize => "isize",
140        }
141    }
142
143    /// Is this integer type signed?
144    pub fn is_signed(&self) -> bool {
145        match self {
146            IntType::U8
147            | IntType::U16
148            | IntType::U32
149            | IntType::U64
150            | IntType::U128
151            | IntType::USize => false,
152
153            IntType::I8
154            | IntType::I16
155            | IntType::I32
156            | IntType::I64
157            | IntType::I128
158            | IntType::ISize => true,
159        }
160    }
161}
162
163impl fmt::Display for IntType {
164    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165        write!(f, "{}", self.name())
166    }
167}
168
169impl BuiltinType {
170    /// All the built-in types.
171    pub const ALL: &'static [Self] = &[
172        Self::Bool,
173        Self::Int(IntType::U8),
174        Self::Int(IntType::U16),
175        Self::Int(IntType::U32),
176        Self::Int(IntType::U64),
177        Self::Int(IntType::U128),
178        Self::Int(IntType::USize),
179        Self::Int(IntType::I8),
180        Self::Int(IntType::I16),
181        Self::Int(IntType::I32),
182        Self::Int(IntType::I64),
183        Self::Int(IntType::I128),
184        Self::Int(IntType::ISize),
185    ];
186
187    /// Get the built-in type's name.
188    pub fn name(&self) -> &'static str {
189        match self {
190            BuiltinType::Bool => "bool",
191            BuiltinType::Int(it) => it.name(),
192        }
193    }
194
195    /// Get the built-in type's size.
196    pub const fn to_usize(&self) -> usize {
197        match self {
198            Self::Bool => 0,
199            Self::Int(ty) => *ty as usize + 1,
200        }
201    }
202}
203
204impl TypeId {
205    /// TypeId for builtin type.
206    pub const fn builtin(builtin: BuiltinType) -> Self {
207        Self(builtin.to_usize())
208    }
209
210    /// TypeId for `bool`.
211    pub const BOOL: Self = Self::builtin(BuiltinType::Bool);
212
213    /// TypeId for `u8`.
214    pub const U8: Self = Self::builtin(BuiltinType::Int(IntType::U8));
215    /// TypeId for `u16`.
216    pub const U16: Self = Self::builtin(BuiltinType::Int(IntType::U16));
217    /// TypeId for `u32`.
218    pub const U32: Self = Self::builtin(BuiltinType::Int(IntType::U32));
219    /// TypeId for `u64`.
220    pub const U64: Self = Self::builtin(BuiltinType::Int(IntType::U64));
221    /// TypeId for `u128`.
222    pub const U128: Self = Self::builtin(BuiltinType::Int(IntType::U128));
223    /// TypeId for `usize`.
224    pub const USIZE: Self = Self::builtin(BuiltinType::Int(IntType::USize));
225
226    /// TypeId for `i8`.
227    pub const I8: Self = Self::builtin(BuiltinType::Int(IntType::I8));
228    /// TypeId for `i16`.
229    pub const I16: Self = Self::builtin(BuiltinType::Int(IntType::I16));
230    /// TypeId for `i32`.
231    pub const I32: Self = Self::builtin(BuiltinType::Int(IntType::I32));
232    /// TypeId for `i64`.
233    pub const I64: Self = Self::builtin(BuiltinType::Int(IntType::I64));
234    /// TypeId for `i128`.
235    pub const I128: Self = Self::builtin(BuiltinType::Int(IntType::I128));
236    /// TypeId for `isize`.
237    pub const ISIZE: Self = Self::builtin(BuiltinType::Int(IntType::ISize));
238}
239
240/// A type.
241#[derive(Clone, Debug, PartialEq, Eq)]
242pub enum Type {
243    /// Built-in types. Always in scope, not defined anywhere in source.
244    Builtin(BuiltinType),
245
246    /// A primitive, `Copy` type.
247    ///
248    /// These are always defined externally, and we allow literals of these
249    /// types to pass through from ISLE source code to the emitted Rust code.
250    Primitive(TypeId, Sym, Pos),
251
252    /// A sum type.
253    ///
254    /// Note that enums with only one variant are equivalent to a "struct".
255    Enum {
256        /// The name of this enum.
257        name: Sym,
258        /// This `enum`'s type id.
259        id: TypeId,
260        /// Is this `enum` defined in external Rust code?
261        ///
262        /// If so, ISLE will not emit a definition for it. If not, then it will
263        /// emit a Rust definition for it.
264        is_extern: bool,
265        /// Whether this type should *not* derive `Debug`.
266        ///
267        /// Incompatible with `is_extern`.
268        is_nodebug: bool,
269        /// The different variants for this enum.
270        variants: Vec<Variant>,
271        /// The ISLE source position where this `enum` is defined.
272        pos: Pos,
273    },
274
275    /// A Rust struct.
276    ///
277    /// Pretty much the same as an enum with a single variant, but will emit a Rust `struct` at codegen time instead of
278    /// an enum.
279    Struct {
280        /// The name of this enum.
281        name: Sym,
282        /// This `enum`'s type id.
283        id: TypeId,
284        /// Is this `struct` defined in external Rust code?
285        ///
286        /// If so, ISLE will not emit a definition for it. If not, then it will
287        /// emit a Rust definition for it.
288        is_extern: bool,
289        /// Whether this type should *not* derive `Debug`.
290        ///
291        /// Incompatible with `is_extern`.
292        is_nodebug: bool,
293        /// The different variants for this struct.
294        fields: Fields,
295        /// The ISLE source position where this `enum` is defined.
296        pos: Pos,
297    },
298}
299
300impl Type {
301    /// Get the ID of this `Type`.
302    pub fn id(&self) -> TypeId {
303        match self {
304            Self::Primitive(id, _, _) | Self::Enum { id, .. } | Self::Struct { id, .. } => *id,
305            Self::Builtin(b) => TypeId::builtin(*b),
306        }
307    }
308
309    /// Get the name of this `Type`.
310    pub fn name<'a>(&self, tyenv: &'a TypeEnv) -> &'a str {
311        match self {
312            Self::Builtin(ty) => ty.name(),
313            Self::Primitive(_, name, _) | Self::Enum { name, .. } | Type::Struct { name, .. } => {
314                &tyenv.syms[name.index()]
315            }
316        }
317    }
318
319    /// Get the position where this type was defined.
320    pub fn pos(&self) -> Option<Pos> {
321        match self {
322            Self::Builtin(..) => None,
323            Self::Primitive(_, _, pos) | Self::Enum { pos, .. } | Type::Struct { pos, .. } => {
324                Some(*pos)
325            }
326        }
327    }
328
329    /// Is this a primitive type?
330    pub fn is_prim(&self) -> bool {
331        matches!(self, Type::Primitive(..))
332    }
333
334    /// Is this a built-in integer type?
335    pub fn is_int(&self) -> bool {
336        matches!(self, Self::Builtin(BuiltinType::Int(_)))
337    }
338}
339
340/// A variant of an enum.
341#[derive(Clone, Debug, PartialEq, Eq)]
342pub struct Variant {
343    /// The name of this variant.
344    pub name: Sym,
345
346    /// The full, prefixed-with-the-enum's-name name of this variant.
347    ///
348    /// E.g. if the enum is `Foo` and this variant is `Bar`, then the
349    /// `fullname` is `Foo.Bar`.
350    pub fullname: Sym,
351
352    /// The id of this variant, i.e. the index of this variant within its
353    /// enum's `Type::Enum::variants`.
354    pub id: VariantId,
355
356    /// The data fields of this enum variant.
357    pub fields: Fields,
358
359    /// The ISLE source position where this variant is defined.
360    pub pos: Pos,
361}
362
363/// The fields of a struct or enum variant, formatted as a struct or tuple.
364#[derive(Clone, PartialEq, Eq, Debug)]
365pub enum Fields {
366    /// a struct or enum variant without fields
367    Unit,
368    /// Named fields
369    Struct(StructFields),
370    /// Unnamed fields like a tuple
371    Tuple(TupleFields),
372}
373
374impl Fields {
375    /// Vec of all field types
376    pub fn types(&self) -> Vec<TypeId> {
377        match self {
378            Fields::Unit => Vec::new(),
379            Fields::Struct(fields) => fields.fields.iter().map(|f| f.ty).collect(),
380            Fields::Tuple(fields) => fields.fields.iter().map(|f| f.ty).collect(),
381        }
382    }
383}
384
385/// A List of named fields of a struct.
386#[derive(Clone, PartialEq, Eq, Debug)]
387pub struct StructFields {
388    /// the fields
389    pub fields: Vec<StructField>,
390}
391
392/// One named field of a struct or enum variant.
393#[derive(Clone, Debug, PartialEq, Eq)]
394pub struct StructField {
395    /// The name of this field.
396    pub name: Sym,
397    /// This field's id.
398    pub id: FieldId,
399    /// The type of this field.
400    pub ty: TypeId,
401}
402
403/// A List of unnamed fields of a tuple.
404#[derive(Clone, PartialEq, Eq, Debug)]
405pub struct TupleFields {
406    /// the fields
407    pub fields: Vec<TupleField>,
408}
409
410/// One unnamed field of a tuple.
411#[derive(Clone, Debug, PartialEq, Eq)]
412pub struct TupleField {
413    /// This field's id.
414    pub id: FieldId,
415    /// The type of this field.
416    pub ty: TypeId,
417}
418
419/// The term environment.
420///
421/// This is sort of a typed and reorganized AST that more directly reflects ISLE
422/// semantics than the input ISLE source code (where as the AST is the
423/// opposite).
424#[derive(Clone, Debug)]
425pub struct TermEnv {
426    /// Arena of interned terms defined in this ISLE program.
427    ///
428    /// This is indexed by `TermId`.
429    pub terms: Vec<Term>,
430
431    /// A map from am interned `Term`'s name to its `TermId`.
432    pub term_map: StableMap<Sym, TermId>,
433
434    /// Arena of interned rules defined in this ISLE program.
435    ///
436    /// This is indexed by `RuleId`.
437    pub rules: Vec<Rule>,
438
439    /// A map from an interned `Rule`'s name to its `RuleId`.
440    pub rule_map: StableMap<Sym, RuleId>,
441
442    /// Map from (inner_ty, outer_ty) pairs to term IDs, giving the
443    /// defined implicit type-converter terms we can try to use to fit
444    /// types together.
445    pub converters: StableMap<(TypeId, TypeId), TermId>,
446
447    /// Flag for whether to expand internal extractors in the
448    /// translation from the AST to sema.
449    pub expand_internal_extractors: bool,
450}
451
452/// A term.
453///
454/// Maps parameter types to result types if this is a constructor term, or
455/// result types to parameter types if this is an extractor term. Or both if
456/// this term can be either a constructor or an extractor.
457#[derive(Clone, Debug, PartialEq, Eq)]
458pub struct Term {
459    /// This term's id.
460    pub id: TermId,
461    /// The source position where this term was declared.
462    pub decl_pos: Pos,
463    /// The name of this term.
464    pub name: Sym,
465    /// The parameter types to this term.
466    pub arg_tys: Vec<TypeId>,
467    /// The result types of this term.
468    pub ret_ty: TypeId,
469    /// The kind of this term.
470    pub kind: TermKind,
471}
472
473/// Flags from a term's declaration with `(decl ...)`.
474#[derive(Copy, Clone, Debug, PartialEq, Eq)]
475pub struct TermFlags {
476    /// Whether the term is marked as `pure`.
477    pub pure: bool,
478    /// Whether the term is marked as `multi`.
479    pub multi: bool,
480    /// Whether the term is marked as `partial`.
481    pub partial: bool,
482    /// Whether the term is marked as `rec`.
483    pub rec: bool,
484}
485
486impl TermFlags {
487    /// Return a new `TermFlags` suitable for a term on the LHS of a rule.
488    pub fn on_lhs(mut self) -> Self {
489        self.pure = true;
490        self.partial = true;
491        self
492    }
493}
494
495/// The kind of a term.
496#[derive(Clone, Debug, PartialEq, Eq)]
497pub enum TermKind {
498    /// An enum variant constructor or extractor.
499    EnumVariant {
500        /// Which variant of the enum: e.g. for enum type `A` if a term is
501        /// `(A.A1 ...)` then the variant ID corresponds to `A1`.
502        variant: VariantId,
503    },
504    /// A struct constructor or extractor.
505    Struct,
506    /// A term declared via a `(decl ...)` form.
507    Decl {
508        /// Flags from the term's declaration.
509        flags: TermFlags,
510        /// The kind of this term's constructor, if any.
511        constructor_kind: Option<ConstructorKind>,
512        /// The kind of this term's extractor, if any.
513        extractor_kind: Option<ExtractorKind>,
514    },
515}
516
517/// The kind of a constructor for a term.
518#[derive(Clone, Debug, PartialEq, Eq)]
519pub enum ConstructorKind {
520    /// A term with "internal" rules that work in the forward direction. Becomes
521    /// a compiled Rust function in the generated code.
522    InternalConstructor,
523    /// A term defined solely by an external constructor function.
524    ExternalConstructor {
525        /// The external name of the constructor function.
526        name: Sym,
527    },
528}
529
530/// The kind of an extractor for a term.
531#[derive(Clone, Debug, PartialEq, Eq)]
532pub enum ExtractorKind {
533    /// A term that defines an "extractor macro" in the LHS of a pattern. Its
534    /// arguments take patterns and are simply substituted with the given
535    /// patterns when used.
536    InternalExtractor {
537        /// This extractor's pattern.
538        template: ast::Pattern,
539    },
540    /// A term defined solely by an external extractor function.
541    ExternalExtractor {
542        /// The external name of the extractor function.
543        name: Sym,
544        /// Is the external extractor infallible?
545        infallible: bool,
546        /// The position where this external extractor was declared.
547        pos: Pos,
548    },
549}
550
551/// How many values a function can return.
552#[derive(Clone, Copy, Debug, Eq, PartialEq)]
553pub enum ReturnKind {
554    /// Exactly one return value.
555    Plain,
556    /// Zero or one return values.
557    Option,
558    /// Zero or more return values.
559    Iterator,
560}
561
562/// An external function signature.
563#[derive(Clone, Debug)]
564pub struct ExternalSig {
565    /// The name of the external function.
566    pub func_name: String,
567    /// The name of the external function, prefixed with the context trait.
568    pub full_name: String,
569    /// The types of this function signature's parameters.
570    pub param_tys: Vec<TypeId>,
571    /// The types of this function signature's results.
572    pub ret_tys: Vec<TypeId>,
573    /// How many values can this function return?
574    pub ret_kind: ReturnKind,
575}
576
577impl Term {
578    /// Get this term's type.
579    pub fn ty(&self) -> TypeId {
580        self.ret_ty
581    }
582
583    fn check_args_count<T>(&self, args: &[T], tyenv: &mut TypeEnv, pos: Pos, sym: &ast::Ident) {
584        if self.arg_tys.len() != args.len() {
585            tyenv.report_error(
586                pos,
587                format!(
588                    "Incorrect argument count for term '{}': got {}, expect {}",
589                    sym.0,
590                    args.len(),
591                    self.arg_tys.len()
592                ),
593            );
594        }
595    }
596
597    /// Is this term an enum variant?
598    pub fn is_enum_variant(&self) -> bool {
599        matches!(self.kind, TermKind::EnumVariant { .. })
600    }
601
602    /// Is this term a struct?
603    pub fn is_struct(&self) -> bool {
604        matches!(self.kind, TermKind::Struct { .. })
605    }
606
607    /// Is this term partial?
608    pub fn is_partial(&self) -> bool {
609        matches!(
610            self.kind,
611            TermKind::Decl {
612                flags: TermFlags { partial: true, .. },
613                ..
614            }
615        )
616    }
617
618    /// Is this term marked as recursive?
619    pub fn is_recursive(&self) -> bool {
620        matches!(
621            self.kind,
622            TermKind::Decl {
623                flags: TermFlags { rec: true, .. },
624                ..
625            }
626        )
627    }
628
629    /// Does this term have a constructor?
630    pub fn has_constructor(&self) -> bool {
631        matches!(
632            self.kind,
633            TermKind::EnumVariant { .. }
634                | TermKind::Struct
635                | TermKind::Decl {
636                    constructor_kind: Some(_),
637                    ..
638                }
639        )
640    }
641
642    /// Does this term have an extractor?
643    pub fn has_extractor(&self) -> bool {
644        matches!(
645            self.kind,
646            TermKind::EnumVariant { .. }
647                | TermKind::Struct
648                | TermKind::Decl {
649                    extractor_kind: Some(_),
650                    ..
651                }
652        )
653    }
654
655    /// Is this term's extractor external?
656    pub fn has_external_extractor(&self) -> bool {
657        matches!(
658            self.kind,
659            TermKind::Decl {
660                extractor_kind: Some(ExtractorKind::ExternalExtractor { .. }),
661                ..
662            }
663        )
664    }
665
666    /// Is this term's constructor external?
667    pub fn has_external_constructor(&self) -> bool {
668        matches!(
669            self.kind,
670            TermKind::Decl {
671                constructor_kind: Some(ConstructorKind::ExternalConstructor { .. }),
672                ..
673            }
674        )
675    }
676
677    /// Is this term's constructor internal?
678    pub fn has_internal_constructor(&self) -> bool {
679        matches!(
680            self.kind,
681            TermKind::Decl {
682                constructor_kind: Some(ConstructorKind::InternalConstructor { .. }),
683                ..
684            }
685        )
686    }
687
688    /// Get this term's extractor's external function signature, if any.
689    pub fn extractor_sig(&self, tyenv: &TypeEnv) -> Option<ExternalSig> {
690        match &self.kind {
691            TermKind::Decl {
692                flags,
693                extractor_kind: Some(kind),
694                ..
695            } => {
696                let (func_name, full_name, infallible) = match kind {
697                    ExtractorKind::InternalExtractor { .. } => {
698                        let name = format!("extractor_{}", tyenv.syms[self.name.index()]);
699                        (name.clone(), name, false)
700                    }
701                    ExtractorKind::ExternalExtractor {
702                        name, infallible, ..
703                    } => (
704                        tyenv.syms[name.index()].clone(),
705                        format!("C::{}", tyenv.syms[name.index()]),
706                        *infallible,
707                    ),
708                };
709                let ret_kind = if flags.multi {
710                    ReturnKind::Iterator
711                } else if infallible {
712                    ReturnKind::Plain
713                } else {
714                    ReturnKind::Option
715                };
716                Some(ExternalSig {
717                    func_name,
718                    full_name,
719                    param_tys: vec![self.ret_ty],
720                    ret_tys: self.arg_tys.clone(),
721                    ret_kind,
722                })
723            }
724            _ => None,
725        }
726    }
727
728    /// Get this term's constructor's external function signature, if any.
729    pub fn constructor_sig(&self, tyenv: &TypeEnv) -> Option<ExternalSig> {
730        match &self.kind {
731            TermKind::Decl {
732                constructor_kind: Some(kind),
733                flags,
734                ..
735            } => {
736                let (func_name, full_name) = match kind {
737                    ConstructorKind::InternalConstructor => {
738                        let name = format!("constructor_{}", tyenv.syms[self.name.index()]);
739                        (name.clone(), name)
740                    }
741                    ConstructorKind::ExternalConstructor { name } => (
742                        tyenv.syms[name.index()].clone(),
743                        format!("C::{}", tyenv.syms[name.index()]),
744                    ),
745                };
746                let ret_kind = if flags.multi {
747                    ReturnKind::Iterator
748                } else if flags.partial {
749                    ReturnKind::Option
750                } else {
751                    ReturnKind::Plain
752                };
753                Some(ExternalSig {
754                    func_name,
755                    full_name,
756                    param_tys: self.arg_tys.clone(),
757                    ret_tys: vec![self.ret_ty],
758                    ret_kind,
759                })
760            }
761            _ => None,
762        }
763    }
764}
765
766/// A term rewrite rule.
767#[derive(Clone, Debug)]
768pub struct Rule {
769    /// This rule's id.
770    pub id: RuleId,
771    /// The left-hand side pattern that this rule matches.
772    pub root_term: TermId,
773    /// Patterns to test against the root term's arguments.
774    pub args: Vec<Pattern>,
775    /// Any subpattern "if-let" clauses.
776    pub iflets: Vec<IfLet>,
777    /// The right-hand side expression that this rule evaluates upon successful
778    /// match.
779    pub rhs: Expr,
780    /// Variable names used in this rule, indexed by [VarId].
781    pub vars: Vec<BoundVar>,
782    /// The priority of this rule, defaulted to 0 if it was missing in the source.
783    pub prio: i64,
784    /// The source position where this rule is defined.
785    pub pos: Pos,
786    /// The optional name for this rule.
787    pub name: Option<Sym>,
788}
789
790/// A name bound in a pattern or let-expression.
791#[derive(Clone, Debug)]
792pub struct BoundVar {
793    /// The identifier used for this variable within the scope of the current [Rule].
794    pub id: VarId,
795    /// The variable's name.
796    pub name: Sym,
797    /// The type of the value this variable is bound to.
798    pub ty: TypeId,
799    /// A counter used to check whether this variable is still in scope during
800    /// semantic analysis. Not meaningful afterward.
801    scope: usize,
802}
803
804/// An `if-let` clause with a subpattern match on an expr after the
805/// main LHS matches.
806#[derive(Clone, Debug)]
807pub struct IfLet {
808    /// The left-hand side pattern that this `if-let` clause matches
809    /// against the expression below.
810    pub lhs: Pattern,
811    /// The right-hand side expression that this pattern
812    /// evaluates. Must be pure.
813    pub rhs: Expr,
814}
815
816/// A left-hand side pattern of some rule.
817#[derive(Clone, Debug, PartialEq, Eq)]
818pub enum Pattern {
819    /// Bind a variable of the given type from the current value.
820    ///
821    /// Keep matching on the value with the subpattern.
822    BindPattern(TypeId, VarId, Box<Pattern>),
823
824    /// Match the current value against an already bound variable with the given
825    /// type.
826    Var(TypeId, VarId),
827
828    /// Match the current value against a constant boolean.
829    ConstBool(TypeId, bool),
830
831    /// Match the current value against a constant integer of the given integer
832    /// type.
833    ConstInt(TypeId, i128),
834
835    /// Match the current value against a constant primitive value of the given
836    /// primitive type.
837    ConstPrim(TypeId, Sym),
838
839    /// Match the current value against the given extractor term with the given
840    /// arguments.
841    Term(TypeId, TermId, Vec<Pattern>),
842
843    /// Match anything of the given type successfully.
844    Wildcard(TypeId),
845
846    /// Match all of the following patterns of the given type.
847    And(TypeId, Vec<Pattern>),
848}
849
850/// A right-hand side expression of some rule.
851#[derive(Clone, Debug, PartialEq, Eq)]
852pub enum Expr {
853    /// Invoke this term constructor with the given arguments.
854    Term(TypeId, TermId, Vec<Expr>),
855    /// Get the value of a variable that was bound in the left-hand side.
856    Var(TypeId, VarId),
857    /// Get a constant boolean.
858    ConstBool(TypeId, bool),
859    /// Get a constant integer.
860    ConstInt(TypeId, i128),
861    /// Get a constant primitive.
862    ConstPrim(TypeId, Sym),
863    /// Evaluate the nested expressions and bind their results to the given
864    /// variables, then evaluate the body expression.
865    Let {
866        /// The type of the result of this let expression.
867        ty: TypeId,
868        /// The expressions that are evaluated and bound to the given variables.
869        bindings: Vec<(VarId, TypeId, Box<Expr>)>,
870        /// The body expression that is evaluated after the bindings.
871        body: Box<Expr>,
872    },
873}
874
875/// Visitor interface for [Pattern]s. Visitors can assign an arbitrary identifier to each
876/// subpattern, which is threaded through to subsequent calls into the visitor.
877pub trait PatternVisitor {
878    /// The type of subpattern identifiers.
879    type PatternId: Copy;
880
881    /// Match if `a` and `b` have equal values.
882    fn add_match_equal(&mut self, a: Self::PatternId, b: Self::PatternId, ty: TypeId);
883    /// Match if `input` is the given boolean constant.
884    fn add_match_bool(&mut self, input: Self::PatternId, ty: TypeId, bool_val: bool);
885    /// Match if `input` is the given integer constant.
886    fn add_match_int(&mut self, input: Self::PatternId, ty: TypeId, int_val: i128);
887    /// Match if `input` is the given primitive constant.
888    fn add_match_prim(&mut self, input: Self::PatternId, ty: TypeId, val: Sym);
889
890    /// Match if `input` is the given enum variant. Returns an identifier for each field within the
891    /// enum variant. The length of the return list must equal the length of `arg_tys`.
892    fn add_match_variant(
893        &mut self,
894        input: Self::PatternId,
895        input_ty: TypeId,
896        arg_tys: &[TypeId],
897        variant: VariantId,
898    ) -> Vec<Self::PatternId>;
899
900    /// Extract the `input` struct into its fields. Returns an identifier for each field within the
901    /// struct. The length of the return list must equal the length of `arg_tys`.
902    fn add_extract_struct(
903        &mut self,
904        input: Self::PatternId,
905        input_ty: TypeId,
906        arg_tys: &[TypeId],
907    ) -> Vec<Self::PatternId>;
908
909    /// Match if the given external extractor succeeds on `input`. Returns an identifier for each
910    /// return value from the external extractor. The length of the return list must equal the
911    /// length of `output_tys`.
912    fn add_extract(
913        &mut self,
914        input: Self::PatternId,
915        input_ty: TypeId,
916        output_tys: Vec<TypeId>,
917        term: TermId,
918        infallible: bool,
919        multi: bool,
920    ) -> Vec<Self::PatternId>;
921}
922
923impl Pattern {
924    /// Get this pattern's type.
925    pub fn ty(&self) -> TypeId {
926        match *self {
927            Self::BindPattern(t, ..) => t,
928            Self::Var(t, ..) => t,
929            Self::ConstBool(t, ..) => t,
930            Self::ConstInt(t, ..) => t,
931            Self::ConstPrim(t, ..) => t,
932            Self::Term(t, ..) => t,
933            Self::Wildcard(t, ..) => t,
934            Self::And(t, ..) => t,
935        }
936    }
937
938    /// Recursively visit every sub-pattern.
939    pub fn visit<V: PatternVisitor>(
940        &self,
941        visitor: &mut V,
942        input: V::PatternId,
943        termenv: &TermEnv,
944        vars: &mut HashMap<VarId, V::PatternId>,
945    ) {
946        match *self {
947            Pattern::BindPattern(_ty, var, ref subpat) => {
948                // Bind the appropriate variable and recurse.
949                assert!(!vars.contains_key(&var));
950                vars.insert(var, input);
951                subpat.visit(visitor, input, termenv, vars);
952            }
953            Pattern::Var(ty, var) => {
954                // Assert that the value matches the existing bound var.
955                let var_val = vars
956                    .get(&var)
957                    .copied()
958                    .expect("Variable should already be bound");
959                visitor.add_match_equal(input, var_val, ty);
960            }
961            Pattern::ConstBool(ty, value) => visitor.add_match_bool(input, ty, value),
962            Pattern::ConstInt(ty, value) => visitor.add_match_int(input, ty, value),
963            Pattern::ConstPrim(ty, value) => visitor.add_match_prim(input, ty, value),
964            Pattern::Term(ty, term, ref args) => {
965                // Determine whether the term has an external extractor or not.
966                let termdata = &termenv.terms[term.index()];
967                let arg_values = match &termdata.kind {
968                    TermKind::EnumVariant { variant } => {
969                        visitor.add_match_variant(input, ty, &termdata.arg_tys, *variant)
970                    }
971                    TermKind::Struct => visitor.add_extract_struct(input, ty, &termdata.arg_tys),
972                    TermKind::Decl {
973                        extractor_kind: None,
974                        ..
975                    } => {
976                        panic!("Pattern invocation of undefined term body")
977                    }
978                    TermKind::Decl {
979                        flags,
980                        extractor_kind,
981                        ..
982                    } => {
983                        // Evaluate all `input` args.
984                        let output_tys = args.iter().map(|arg| arg.ty()).collect();
985
986                        let infallible = match extractor_kind {
987                            Some(ExtractorKind::ExternalExtractor { infallible, .. }) => {
988                                *infallible
989                            }
990                            _ => false,
991                        };
992
993                        // Invoke the extractor.
994                        visitor.add_extract(
995                            input,
996                            termdata.ret_ty,
997                            output_tys,
998                            term,
999                            infallible && !flags.multi,
1000                            flags.multi,
1001                        )
1002                    }
1003                };
1004                for (pat, val) in args.iter().zip(arg_values) {
1005                    pat.visit(visitor, val, termenv, vars);
1006                }
1007            }
1008            Pattern::And(_ty, ref children) => {
1009                for child in children {
1010                    child.visit(visitor, input, termenv, vars);
1011                }
1012            }
1013            Pattern::Wildcard(_ty) => {
1014                // Nothing!
1015            }
1016        }
1017    }
1018}
1019
1020/// Visitor interface for [Expr]s. Visitors can return an arbitrary identifier for each
1021/// subexpression, which is threaded through to subsequent calls into the visitor.
1022pub trait ExprVisitor {
1023    /// The type of subexpression identifiers.
1024    type ExprId: Copy;
1025
1026    /// Construct a constant boolean.
1027    fn add_const_bool(&mut self, ty: TypeId, val: bool) -> Self::ExprId;
1028    /// Construct a constant integer.
1029    fn add_const_int(&mut self, ty: TypeId, val: i128) -> Self::ExprId;
1030    /// Construct a primitive constant.
1031    fn add_const_prim(&mut self, ty: TypeId, val: Sym) -> Self::ExprId;
1032
1033    /// Construct an enum variant with the given `inputs` assigned to the variant's fields in order.
1034    fn add_create_variant(
1035        &mut self,
1036        inputs: Vec<(Self::ExprId, TypeId)>,
1037        ty: TypeId,
1038        variant: VariantId,
1039    ) -> Self::ExprId;
1040
1041    /// Construct a struct with the given `inputs` assigned to the struct's fields in order.
1042    fn add_create_struct(
1043        &mut self,
1044        inputs: Vec<(Self::ExprId, TypeId)>,
1045        ty: TypeId,
1046    ) -> Self::ExprId;
1047
1048    /// Call an external constructor with the given `inputs` as arguments.
1049    fn add_construct(
1050        &mut self,
1051        inputs: Vec<(Self::ExprId, TypeId)>,
1052        ty: TypeId,
1053        term: TermId,
1054        pure: bool,
1055        infallible: bool,
1056        multi: bool,
1057        rec: bool,
1058    ) -> Self::ExprId;
1059}
1060
1061impl Expr {
1062    /// Get this expression's type.
1063    pub fn ty(&self) -> TypeId {
1064        match *self {
1065            Self::Term(t, ..) => t,
1066            Self::Var(t, ..) => t,
1067            Self::ConstBool(t, ..) => t,
1068            Self::ConstInt(t, ..) => t,
1069            Self::ConstPrim(t, ..) => t,
1070            Self::Let { ty: t, .. } => t,
1071        }
1072    }
1073
1074    /// Recursively visit every subexpression.
1075    pub fn visit<V: ExprVisitor>(
1076        &self,
1077        visitor: &mut V,
1078        termenv: &TermEnv,
1079        vars: &HashMap<VarId, V::ExprId>,
1080    ) -> V::ExprId {
1081        log!("Expr::visit: expr {:?}", self);
1082        match *self {
1083            Expr::ConstBool(ty, val) => visitor.add_const_bool(ty, val),
1084            Expr::ConstInt(ty, val) => visitor.add_const_int(ty, val),
1085            Expr::ConstPrim(ty, val) => visitor.add_const_prim(ty, val),
1086            Expr::Let {
1087                ty: _ty,
1088                ref bindings,
1089                ref body,
1090            } => {
1091                let mut vars = vars.clone();
1092                for &(var, _var_ty, ref var_expr) in bindings {
1093                    let var_value = var_expr.visit(visitor, termenv, &vars);
1094                    vars.insert(var, var_value);
1095                }
1096                body.visit(visitor, termenv, &vars)
1097            }
1098            Expr::Var(_ty, var_id) => *vars.get(&var_id).unwrap(),
1099            Expr::Term(ty, term, ref arg_exprs) => {
1100                let termdata = &termenv.terms[term.index()];
1101                let arg_values_tys = arg_exprs
1102                    .iter()
1103                    .map(|arg_expr| arg_expr.visit(visitor, termenv, vars))
1104                    .zip(termdata.arg_tys.iter().copied())
1105                    .collect();
1106                match &termdata.kind {
1107                    TermKind::EnumVariant { variant } => {
1108                        visitor.add_create_variant(arg_values_tys, ty, *variant)
1109                    }
1110                    TermKind::Struct => visitor.add_create_struct(arg_values_tys, ty),
1111                    TermKind::Decl {
1112                        constructor_kind: Some(_),
1113                        flags,
1114                        ..
1115                    } => {
1116                        visitor.add_construct(
1117                            arg_values_tys,
1118                            ty,
1119                            term,
1120                            flags.pure,
1121                            /* infallible = */ !flags.partial,
1122                            flags.multi,
1123                            flags.rec,
1124                        )
1125                    }
1126                    TermKind::Decl {
1127                        constructor_kind: None,
1128                        ..
1129                    } => panic!("Should have been caught by typechecking"),
1130                }
1131            }
1132        }
1133    }
1134
1135    fn visit_in_rule<V: RuleVisitor>(
1136        &self,
1137        visitor: &mut V,
1138        termenv: &TermEnv,
1139        vars: &HashMap<VarId, <V::PatternVisitor as PatternVisitor>::PatternId>,
1140    ) -> V::Expr {
1141        let var_exprs = vars
1142            .iter()
1143            .map(|(&var, &val)| (var, visitor.pattern_as_expr(val)))
1144            .collect();
1145        visitor.add_expr(|visitor| VisitedExpr {
1146            ty: self.ty(),
1147            value: self.visit(visitor, termenv, &var_exprs),
1148        })
1149    }
1150}
1151
1152/// Information about an expression after it has been fully visited in [RuleVisitor::add_expr].
1153#[derive(Clone, Copy)]
1154pub struct VisitedExpr<V: ExprVisitor> {
1155    /// The type of the top-level expression.
1156    pub ty: TypeId,
1157    /// The identifier returned by the visitor for the top-level expression.
1158    pub value: V::ExprId,
1159}
1160
1161/// Visitor interface for [Rule]s. Visitors must be able to visit patterns by implementing
1162/// [PatternVisitor], and to visit expressions by providing a type that implements [ExprVisitor].
1163pub trait RuleVisitor {
1164    /// The type of pattern visitors constructed by [RuleVisitor::add_pattern].
1165    type PatternVisitor: PatternVisitor;
1166    /// The type of expression visitors constructed by [RuleVisitor::add_expr].
1167    type ExprVisitor: ExprVisitor;
1168    /// The type returned from [RuleVisitor::add_expr], which may be exchanged for a subpattern
1169    /// identifier using [RuleVisitor::expr_as_pattern].
1170    type Expr;
1171
1172    /// Visit one of the arguments to the top-level pattern.
1173    fn add_arg(
1174        &mut self,
1175        index: usize,
1176        ty: TypeId,
1177    ) -> <Self::PatternVisitor as PatternVisitor>::PatternId;
1178
1179    /// Visit a pattern, used once for the rule's left-hand side and once for each if-let. You can
1180    /// determine which part of the rule the pattern comes from based on whether the `PatternId`
1181    /// passed to the first call to this visitor came from `add_arg` or `expr_as_pattern`.
1182    fn add_pattern<F>(&mut self, visitor: F)
1183    where
1184        F: FnOnce(&mut Self::PatternVisitor);
1185
1186    /// Visit an expression, used once for each if-let and once for the rule's right-hand side.
1187    fn add_expr<F>(&mut self, visitor: F) -> Self::Expr
1188    where
1189        F: FnOnce(&mut Self::ExprVisitor) -> VisitedExpr<Self::ExprVisitor>;
1190
1191    /// Given an expression from [RuleVisitor::add_expr], return an identifier that can be used with
1192    /// a pattern visitor in [RuleVisitor::add_pattern].
1193    fn expr_as_pattern(
1194        &mut self,
1195        expr: Self::Expr,
1196    ) -> <Self::PatternVisitor as PatternVisitor>::PatternId;
1197
1198    /// Given an identifier from the pattern visitor, return an identifier that can be used with
1199    /// the expression visitor.
1200    fn pattern_as_expr(
1201        &mut self,
1202        pattern: <Self::PatternVisitor as PatternVisitor>::PatternId,
1203    ) -> <Self::ExprVisitor as ExprVisitor>::ExprId;
1204}
1205
1206impl Rule {
1207    /// Recursively visit every pattern and expression in this rule. Returns the [RuleVisitor::Expr]
1208    /// that was returned from [RuleVisitor::add_expr] when that function was called on the rule's
1209    /// right-hand side.
1210    pub fn visit<V: RuleVisitor>(&self, visitor: &mut V, termenv: &TermEnv) -> V::Expr {
1211        let mut vars = HashMap::new();
1212
1213        // Visit the pattern, starting from the root input value.
1214        let termdata = &termenv.terms[self.root_term.index()];
1215        for (i, (subpat, &arg_ty)) in self.args.iter().zip(termdata.arg_tys.iter()).enumerate() {
1216            let value = visitor.add_arg(i, arg_ty);
1217            visitor.add_pattern(|visitor| subpat.visit(visitor, value, termenv, &mut vars));
1218        }
1219
1220        // Visit the `if-let` clauses, using `V::ExprVisitor` for the sub-exprs (right-hand sides).
1221        for iflet in self.iflets.iter() {
1222            let subexpr = iflet.rhs.visit_in_rule(visitor, termenv, &vars);
1223            let value = visitor.expr_as_pattern(subexpr);
1224            visitor.add_pattern(|visitor| iflet.lhs.visit(visitor, value, termenv, &mut vars));
1225        }
1226
1227        // Visit the rule's right-hand side, making use of the bound variables from the pattern.
1228        self.rhs.visit_in_rule(visitor, termenv, &vars)
1229    }
1230
1231    /// Identifier is a name or position for referring to the rule.
1232    pub fn identifier(&self, tyenv: &TypeEnv, files: &Files) -> String {
1233        match self.name {
1234            Some(sym) => tyenv.syms[sym.index()].clone(),
1235            None => self.pos.pretty_print_line(files),
1236        }
1237    }
1238}
1239
1240/// Given an `Option<T>`, unwrap the inner `T` value, or `continue` if it is
1241/// `None`.
1242///
1243/// Useful for when we encountered an error earlier in our analysis but kept
1244/// going to find more errors, and now we've run into some missing data that
1245/// would have been filled in if we didn't hit that original error, but we want
1246/// to keep going to find more errors.
1247macro_rules! unwrap_or_continue {
1248    ($e:expr) => {
1249        match $e {
1250            Some(x) => x,
1251            None => continue,
1252        }
1253    };
1254}
1255
1256impl Default for TypeEnv {
1257    fn default() -> Self {
1258        Self {
1259            syms: BuiltinType::ALL
1260                .iter()
1261                .map(|bt| String::from(bt.name()))
1262                .collect(),
1263            sym_map: BuiltinType::ALL
1264                .iter()
1265                .enumerate()
1266                .map(|(idx, bt)| (String::from(bt.name()), Sym(idx)))
1267                .collect(),
1268            types: BuiltinType::ALL
1269                .iter()
1270                .map(|bt| Type::Builtin(*bt))
1271                .collect(),
1272            type_map: BuiltinType::ALL
1273                .iter()
1274                .enumerate()
1275                .map(|(idx, _)| (Sym(idx), TypeId(idx)))
1276                .collect(),
1277            const_types: StableMap::new(),
1278            errors: vec![],
1279        }
1280    }
1281}
1282
1283impl TypeEnv {
1284    /// Construct the type environment from the AST.
1285    pub fn from_ast(defs: &[ast::Def]) -> Result<TypeEnv, Vec<Error>> {
1286        let mut tyenv = TypeEnv::default();
1287
1288        // Traverse defs, assigning type IDs to type names. We'll fill
1289        // in types on a second pass.
1290        for def in defs {
1291            match def {
1292                &ast::Def::Type(ref td) => {
1293                    let tid = TypeId(tyenv.type_map.len());
1294                    let name = tyenv.intern_mut(&td.name);
1295
1296                    if let Some(existing) = tyenv.type_map.get(&name).copied() {
1297                        tyenv.report_error(
1298                            td.pos,
1299                            format!("Type with name '{}' defined more than once", td.name.0),
1300                        );
1301                        let pos = unwrap_or_continue!(tyenv.types.get(existing.index())).pos();
1302                        match pos {
1303                            Some(pos) => tyenv.report_error(
1304                                pos,
1305                                format!("Type with name '{}' already defined here", td.name.0),
1306                            ),
1307                            None => tyenv.report_error(
1308                                td.pos,
1309                                format!("Type with name '{}' is a built-in type", td.name.0),
1310                            ),
1311                        }
1312                        continue;
1313                    }
1314
1315                    tyenv.type_map.insert(name, tid);
1316                }
1317                _ => {}
1318            }
1319        }
1320
1321        // Now lower AST nodes to type definitions, raising errors
1322        // where typenames of fields are undefined or field names are
1323        // duplicated.
1324        for def in defs {
1325            match def {
1326                &ast::Def::Type(ref td) => {
1327                    let tid = tyenv.types.len();
1328                    if let Some(ty) = tyenv.type_from_ast(TypeId(tid), td) {
1329                        tyenv.types.push(ty);
1330                    }
1331                }
1332                _ => {}
1333            }
1334        }
1335
1336        // Now collect types for extern constants.
1337        for def in defs {
1338            if let &ast::Def::Extern(ast::Extern::Const {
1339                ref name,
1340                ref ty,
1341                pos,
1342            }) = def
1343            {
1344                let ty = match tyenv.get_type_by_name(ty) {
1345                    Some(ty) => ty,
1346                    None => {
1347                        tyenv.report_error(pos, "Unknown type for constant");
1348                        continue;
1349                    }
1350                };
1351                let name = tyenv.intern_mut(name);
1352                tyenv.const_types.insert(name, ty);
1353            }
1354        }
1355
1356        tyenv.return_errors()?;
1357
1358        Ok(tyenv)
1359    }
1360
1361    fn return_errors(&mut self) -> Result<(), Vec<Error>> {
1362        if self.errors.is_empty() {
1363            Ok(())
1364        } else {
1365            Err(std::mem::take(&mut self.errors))
1366        }
1367    }
1368
1369    fn type_from_ast(&mut self, tid: TypeId, ty: &ast::Type) -> Option<Type> {
1370        let name = self.intern(&ty.name).unwrap();
1371        match &ty.ty {
1372            &ast::TypeValue::Primitive(ref id, ..) => {
1373                if ty.is_nodebug {
1374                    self.report_error(ty.pos, "primitive types cannot be marked `nodebug`");
1375                    return None;
1376                }
1377                if ty.is_extern {
1378                    self.report_error(ty.pos, "primitive types cannot be marked `extern`");
1379                    return None;
1380                }
1381                Some(Type::Primitive(tid, self.intern_mut(id), ty.pos))
1382            }
1383            &ast::TypeValue::Enum(ref ty_variants, ..) => {
1384                if ty.is_extern && ty.is_nodebug {
1385                    self.report_error(ty.pos, "external types cannot be marked `nodebug`");
1386                    return None;
1387                }
1388
1389                let mut variants = vec![];
1390                for variant in ty_variants {
1391                    let combined_ident = ast::Variant::full_name(&ty.name, &variant.name);
1392                    let fullname = self.intern_mut(&combined_ident);
1393                    let name = self.intern_mut(&variant.name);
1394                    let id = VariantId(variants.len());
1395                    if variants.iter().any(|v: &Variant| v.name == name) {
1396                        self.report_error(
1397                            variant.pos,
1398                            format!("Duplicate variant name in type: '{}'", variant.name.0),
1399                        );
1400                        return None;
1401                    }
1402                    let fields = self.fields_from_ast(&variant.fields, Some(&variant.name))?;
1403                    variants.push(Variant {
1404                        name,
1405                        fullname,
1406                        id,
1407                        fields,
1408                        pos: variant.pos,
1409                    });
1410                }
1411                Some(Type::Enum {
1412                    name,
1413                    id: tid,
1414                    is_extern: ty.is_extern,
1415                    is_nodebug: ty.is_nodebug,
1416                    variants,
1417                    pos: ty.pos,
1418                })
1419            }
1420            &ast::TypeValue::Struct(ref fields, _) => {
1421                if ty.is_extern && ty.is_nodebug {
1422                    self.report_error(ty.pos, "external types cannot be marked `nodebug`");
1423                    return None;
1424                }
1425                Some(Type::Struct {
1426                    name,
1427                    id: tid,
1428                    is_extern: ty.is_extern,
1429                    is_nodebug: ty.is_nodebug,
1430                    fields: self.fields_from_ast(&fields, None)?,
1431                    pos: ty.pos,
1432                })
1433            }
1434        }
1435    }
1436
1437    fn fields_from_ast(
1438        &mut self,
1439        fields: &ast::Fields,
1440        variant_name: Option<&ast::Ident>,
1441    ) -> Option<Fields> {
1442        match fields {
1443            ast::Fields::Unit => Some(Fields::Unit),
1444            ast::Fields::Struct(fields) => Some(Fields::Struct(
1445                self.struct_fields_from_ast(fields, variant_name)?,
1446            )),
1447            ast::Fields::Tuple(fields) => Some(Fields::Tuple(
1448                self.tuple_fields_from_ast(fields, variant_name)?,
1449            )),
1450        }
1451    }
1452
1453    fn struct_fields_from_ast(
1454        &mut self,
1455        fields: &ast::StructFields,
1456        variant_name: Option<&ast::Ident>,
1457    ) -> Option<StructFields> {
1458        let mut out = Vec::with_capacity(fields.fields.len());
1459        for field in &fields.fields {
1460            let field_name = self.intern_mut(&field.name);
1461            if out.iter().any(|f: &StructField| f.name == field_name) {
1462                let msg = if let Some(variant_name) = variant_name {
1463                    format!(
1464                        "Duplicate field name '{}' in variant '{}' of type",
1465                        field.name.0, variant_name.0
1466                    )
1467                } else {
1468                    format!("Duplicate field name '{}'", field.name.0)
1469                };
1470                self.report_error(field.pos, msg);
1471                return None;
1472            }
1473            let field_tid = match self.get_type_by_name(&field.ty) {
1474                Some(tid) => tid,
1475                None => {
1476                    let msg = if let Some(variant_name) = variant_name {
1477                        format!(
1478                            "Unknown type '{}' for field '{}' in variant '{}'",
1479                            field.ty.0, field.name.0, variant_name.0
1480                        )
1481                    } else {
1482                        format!("Unknown type '{}' for field '{}'", field.ty.0, field.name.0)
1483                    };
1484                    self.report_error(field.ty.1, msg);
1485                    return None;
1486                }
1487            };
1488            out.push(StructField {
1489                name: field_name,
1490                id: FieldId(out.len()),
1491                ty: field_tid,
1492            });
1493        }
1494        Some(StructFields { fields: out })
1495    }
1496
1497    fn tuple_fields_from_ast(
1498        &mut self,
1499        fields: &ast::TupleFields,
1500        variant_name: Option<&ast::Ident>,
1501    ) -> Option<TupleFields> {
1502        let mut out = Vec::with_capacity(fields.fields.len());
1503        for field in &fields.fields {
1504            let field_tid = match self.get_type_by_name(&field.ty) {
1505                Some(tid) => tid,
1506                None => {
1507                    let msg = if let Some(variant_name) = variant_name {
1508                        format!(
1509                            "Unknown type '{}' for tuple field '{}' in variant '{}'",
1510                            field.ty.0, field.index, variant_name.0
1511                        )
1512                    } else {
1513                        format!(
1514                            "Unknown type '{}' for tuple field '{}'",
1515                            field.ty.0, field.index
1516                        )
1517                    };
1518                    self.report_error(field.ty.1, msg);
1519                    return None;
1520                }
1521            };
1522            out.push(TupleField {
1523                id: FieldId(out.len()),
1524                ty: field_tid,
1525            });
1526        }
1527        Some(TupleFields { fields: out })
1528    }
1529
1530    fn error(&self, pos: Pos, msg: impl Into<String>) -> Error {
1531        Error::TypeError {
1532            msg: msg.into(),
1533            span: Span::new_single(pos),
1534        }
1535    }
1536
1537    fn report_error(&mut self, pos: Pos, msg: impl Into<String>) {
1538        let err = self.error(pos, msg);
1539        self.errors.push(err);
1540    }
1541
1542    fn intern_mut(&mut self, ident: &ast::Ident) -> Sym {
1543        if let Some(s) = self.sym_map.get(&ident.0).copied() {
1544            s
1545        } else {
1546            let s = Sym(self.syms.len());
1547            self.syms.push(ident.0.clone());
1548            self.sym_map.insert(ident.0.clone(), s);
1549            s
1550        }
1551    }
1552
1553    /// Lookup symbol ID for the given identifier.
1554    pub fn intern(&self, ident: &ast::Ident) -> Option<Sym> {
1555        self.sym_map.get(&ident.0).copied()
1556    }
1557
1558    /// Lookup type by name.
1559    pub fn get_type_by_name(&self, sym: &ast::Ident) -> Option<TypeId> {
1560        self.intern(sym)
1561            .and_then(|sym| self.type_map.get(&sym))
1562            .copied()
1563    }
1564
1565    /// Lookup the term corresponding to the given enum variant.
1566    pub fn get_variant(&self, ty: TypeId, variant: VariantId) -> &Variant {
1567        let ty = &self.types[ty.index()];
1568        let Type::Enum { variants, .. } = ty else {
1569            unreachable!("provided type must be an enum")
1570        };
1571        &variants[variant.index()]
1572    }
1573}
1574
1575#[derive(Clone, Debug, Default)]
1576struct Bindings {
1577    /// All bindings accumulated so far within the current rule, including let-
1578    /// bindings which have gone out of scope.
1579    seen: Vec<BoundVar>,
1580    /// Counter for unique scope IDs within this set of bindings.
1581    next_scope: usize,
1582    /// Stack of the scope IDs for bindings which are currently in scope.
1583    in_scope: Vec<usize>,
1584}
1585
1586impl Bindings {
1587    fn enter_scope(&mut self) {
1588        self.in_scope.push(self.next_scope);
1589        self.next_scope += 1;
1590    }
1591
1592    fn exit_scope(&mut self) {
1593        self.in_scope.pop();
1594    }
1595
1596    fn add_var(&mut self, name: Sym, ty: TypeId) -> VarId {
1597        let id = VarId(self.seen.len());
1598        let var = BoundVar {
1599            id,
1600            name,
1601            ty,
1602            scope: *self
1603                .in_scope
1604                .last()
1605                .expect("enter_scope should be called before add_var"),
1606        };
1607        log!("binding var {:?}", var);
1608        self.seen.push(var);
1609        id
1610    }
1611
1612    fn lookup(&self, name: Sym) -> Option<&BoundVar> {
1613        self.seen
1614            .iter()
1615            .rev()
1616            .find(|binding| binding.name == name && self.in_scope.contains(&binding.scope))
1617    }
1618}
1619
1620impl TermEnv {
1621    /// Construct the term environment from the AST and the type environment.
1622    pub fn from_ast(
1623        tyenv: &mut TypeEnv,
1624        defs: &[ast::Def],
1625        expand_internal_extractors: bool,
1626    ) -> Result<TermEnv, Vec<Error>> {
1627        let mut env = TermEnv {
1628            terms: vec![],
1629            term_map: StableMap::new(),
1630            rules: vec![],
1631            rule_map: StableMap::new(),
1632            converters: StableMap::new(),
1633            expand_internal_extractors,
1634        };
1635
1636        env.collect_pragmas(defs);
1637        env.collect_term_sigs(tyenv, defs);
1638        env.collect_enum_variant_terms(tyenv);
1639        tyenv.return_errors()?;
1640        env.collect_constructors(tyenv, defs);
1641        env.collect_extractor_templates(tyenv, defs);
1642        tyenv.return_errors()?;
1643        env.collect_converters(tyenv, defs);
1644        tyenv.return_errors()?;
1645        env.collect_externs(tyenv, defs);
1646        tyenv.return_errors()?;
1647        env.collect_rules(tyenv, defs);
1648        env.check_for_undefined_decls(tyenv, defs);
1649        env.check_for_expr_terms_without_constructors(tyenv, defs);
1650        tyenv.return_errors()?;
1651
1652        Ok(env)
1653    }
1654
1655    fn collect_pragmas(&mut self, _: &[ast::Def]) {
1656        // currently, no pragmas are defined, but the infrastructure is useful to keep around
1657        return;
1658    }
1659
1660    fn collect_term_sigs(&mut self, tyenv: &mut TypeEnv, defs: &[ast::Def]) {
1661        for def in defs {
1662            match def {
1663                &ast::Def::Decl(ref decl) => {
1664                    let name = tyenv.intern_mut(&decl.term);
1665                    if let Some(tid) = self.term_map.get(&name) {
1666                        tyenv.report_error(
1667                            decl.pos,
1668                            format!("Duplicate decl for '{}'", decl.term.0),
1669                        );
1670                        tyenv.report_error(
1671                            self.terms[tid.index()].decl_pos,
1672                            format!("Duplicate decl for '{}'", decl.term.0),
1673                        );
1674                    }
1675
1676                    if decl.multi && decl.partial {
1677                        tyenv.report_error(
1678                            decl.pos,
1679                            format!("Term '{}' can't be both multi and partial", decl.term.0),
1680                        );
1681                    }
1682
1683                    let arg_tys = decl
1684                        .arg_tys
1685                        .iter()
1686                        .map(|id| {
1687                            tyenv.get_type_by_name(id).ok_or_else(|| {
1688                                tyenv.report_error(id.1, format!("Unknown arg type: '{}'", id.0));
1689                            })
1690                        })
1691                        .collect::<Result<Vec<_>, _>>();
1692                    let arg_tys = match arg_tys {
1693                        Ok(a) => a,
1694                        Err(_) => {
1695                            continue;
1696                        }
1697                    };
1698                    let ret_ty = match tyenv.get_type_by_name(&decl.ret_ty) {
1699                        Some(t) => t,
1700                        None => {
1701                            tyenv.report_error(
1702                                decl.ret_ty.1,
1703                                format!("Unknown return type: '{}'", decl.ret_ty.0),
1704                            );
1705                            continue;
1706                        }
1707                    };
1708
1709                    let tid = TermId(self.terms.len());
1710                    self.term_map.insert(name, tid);
1711                    let flags = TermFlags {
1712                        pure: decl.pure,
1713                        multi: decl.multi,
1714                        partial: decl.partial,
1715                        rec: decl.rec,
1716                    };
1717                    self.terms.push(Term {
1718                        id: tid,
1719                        decl_pos: decl.pos,
1720                        name,
1721                        arg_tys,
1722                        ret_ty,
1723                        kind: TermKind::Decl {
1724                            flags,
1725                            constructor_kind: None,
1726                            extractor_kind: None,
1727                        },
1728                    });
1729                }
1730                _ => {}
1731            }
1732        }
1733    }
1734
1735    fn collect_enum_variant_terms(&mut self, tyenv: &mut TypeEnv) {
1736        'types: for i in 0..tyenv.types.len() {
1737            let ty = &tyenv.types[i];
1738            match ty {
1739                &Type::Enum {
1740                    pos,
1741                    id,
1742                    ref variants,
1743                    ..
1744                } => {
1745                    for variant in variants {
1746                        if self.term_map.contains_key(&variant.fullname) {
1747                            let variant_name = tyenv.syms[variant.fullname.index()].clone();
1748                            tyenv.report_error(
1749                                pos,
1750                                format!("Duplicate enum variant constructor: '{variant_name}'",),
1751                            );
1752                            continue 'types;
1753                        }
1754                        let tid = TermId(self.terms.len());
1755                        let arg_tys = variant.fields.types();
1756                        let ret_ty = id;
1757                        self.terms.push(Term {
1758                            id: tid,
1759                            decl_pos: variant.pos,
1760                            name: variant.fullname,
1761                            arg_tys,
1762                            ret_ty,
1763                            kind: TermKind::EnumVariant {
1764                                variant: variant.id,
1765                            },
1766                        });
1767                        self.term_map.insert(variant.fullname, tid);
1768                    }
1769                }
1770                &Type::Struct {
1771                    pos,
1772                    id,
1773                    name,
1774                    ref fields,
1775                    ..
1776                } => {
1777                    if self.term_map.contains_key(&name) {
1778                        let name = tyenv.syms[name.index()].clone();
1779                        tyenv.report_error(pos, format!("Duplicate struct constructor: '{name}'",));
1780                        continue 'types;
1781                    }
1782                    let tid = TermId(self.terms.len());
1783                    let arg_tys = fields.types();
1784                    let ret_ty = id;
1785                    self.terms.push(Term {
1786                        id: tid,
1787                        decl_pos: pos,
1788                        name,
1789                        arg_tys,
1790                        ret_ty,
1791                        kind: TermKind::Struct,
1792                    });
1793                    self.term_map.insert(name, tid);
1794                }
1795                _ => {}
1796            }
1797        }
1798    }
1799
1800    fn collect_constructors(&mut self, tyenv: &mut TypeEnv, defs: &[ast::Def]) {
1801        for def in defs {
1802            log!("collect_constructors from def: {:?}", def);
1803            match def {
1804                &ast::Def::Rule(ref rule) => {
1805                    let pos = rule.pos;
1806                    let term = match rule.pattern.root_term() {
1807                        Some(t) => t,
1808                        None => {
1809                            tyenv.report_error(
1810                                pos,
1811                                "Rule does not have a term at the LHS root".to_string(),
1812                            );
1813                            continue;
1814                        }
1815                    };
1816                    let term = match self.get_term_by_name(tyenv, &term) {
1817                        Some(tid) => tid,
1818                        None => {
1819                            tyenv
1820                                .report_error(pos, "Rule LHS root term is not defined".to_string());
1821                            continue;
1822                        }
1823                    };
1824                    let termdata = &mut self.terms[term.index()];
1825                    match &mut termdata.kind {
1826                        TermKind::Decl {
1827                            constructor_kind, ..
1828                        } => {
1829                            match constructor_kind {
1830                                None => {
1831                                    *constructor_kind = Some(ConstructorKind::InternalConstructor);
1832                                }
1833                                Some(ConstructorKind::InternalConstructor) => {
1834                                    // OK, no error; multiple rules can apply to
1835                                    // one internal constructor term.
1836                                }
1837                                Some(ConstructorKind::ExternalConstructor { .. }) => {
1838                                    tyenv.report_error(
1839                                        pos,
1840                                        "Rule LHS root term is incorrect kind; cannot \
1841                                         be external constructor"
1842                                            .to_string(),
1843                                    );
1844                                    continue;
1845                                }
1846                            }
1847                        }
1848                        TermKind::EnumVariant { .. } => {
1849                            tyenv.report_error(
1850                                pos,
1851                                "Rule LHS root term is incorrect kind; cannot be enum variant"
1852                                    .to_string(),
1853                            );
1854                            continue;
1855                        }
1856                        TermKind::Struct => {
1857                            tyenv.report_error(
1858                                pos,
1859                                "Rule LHS root term is incorrect kind; cannot be struct"
1860                                    .to_string(),
1861                            );
1862                            continue;
1863                        }
1864                    }
1865                }
1866                _ => {}
1867            }
1868        }
1869    }
1870
1871    fn collect_extractor_templates(&mut self, tyenv: &mut TypeEnv, defs: &[ast::Def]) {
1872        let mut extractor_call_graph = BTreeMap::new();
1873
1874        for def in defs {
1875            if let &ast::Def::Extractor(ref ext) = def {
1876                let term = match self.get_term_by_name(tyenv, &ext.term) {
1877                    Some(x) => x,
1878                    None => {
1879                        tyenv.report_error(
1880                            ext.pos,
1881                            "Extractor macro body definition on a non-existent term".to_string(),
1882                        );
1883                        return;
1884                    }
1885                };
1886
1887                let template = ext.template.make_macro_template(&ext.args[..]);
1888                log!("extractor def: {:?} becomes template {:?}", def, template);
1889
1890                let mut callees = BTreeSet::new();
1891                template.terms(&mut |pos, t| {
1892                    if let Some(term) = self.get_term_by_name(tyenv, t) {
1893                        callees.insert(term);
1894                    } else {
1895                        tyenv.report_error(
1896                            pos,
1897                            format!(
1898                                "`{}` extractor definition references unknown term `{}`",
1899                                ext.term.0, t.0
1900                            ),
1901                        );
1902                    }
1903                });
1904                extractor_call_graph.insert(term, callees);
1905
1906                let termdata = &mut self.terms[term.index()];
1907                match &mut termdata.kind {
1908                    TermKind::EnumVariant { .. } => {
1909                        tyenv.report_error(
1910                            ext.pos,
1911                            "Extractor macro body defined on term of incorrect kind; cannot be an \
1912                             enum variant",
1913                        );
1914                        continue;
1915                    }
1916                    TermKind::Struct { .. } => {
1917                        tyenv.report_error(
1918                            ext.pos,
1919                            "Extractor macro body defined on term of incorrect kind; cannot be a \
1920                             struct",
1921                        );
1922                        continue;
1923                    }
1924                    TermKind::Decl {
1925                        flags,
1926                        extractor_kind,
1927                        ..
1928                    } => match extractor_kind {
1929                        None => {
1930                            if flags.multi {
1931                                tyenv.report_error(
1932                                    ext.pos,
1933                                    "A term declared with `multi` cannot have an internal extractor.".to_string());
1934                                continue;
1935                            }
1936                            *extractor_kind = Some(ExtractorKind::InternalExtractor { template });
1937                        }
1938                        Some(ext_kind) => {
1939                            tyenv.report_error(
1940                                ext.pos,
1941                                "Duplicate extractor definition".to_string(),
1942                            );
1943                            let pos = match ext_kind {
1944                                ExtractorKind::InternalExtractor { template } => template.pos(),
1945                                ExtractorKind::ExternalExtractor { pos, .. } => *pos,
1946                            };
1947                            tyenv.report_error(
1948                                pos,
1949                                "Extractor was already defined here".to_string(),
1950                            );
1951                            continue;
1952                        }
1953                    },
1954                }
1955            }
1956        }
1957
1958        // Check for cycles in the extractor call graph.
1959        let mut stack = vec![];
1960        'outer: for root in extractor_call_graph.keys().copied() {
1961            stack.clear();
1962            stack.push((root, vec![root], StableSet::new()));
1963
1964            while let Some((caller, path, mut seen)) = stack.pop() {
1965                let is_new = seen.insert(caller);
1966                if is_new {
1967                    if let Some(callees) = extractor_call_graph.get(&caller) {
1968                        stack.extend(callees.iter().map(|callee| {
1969                            let mut path = path.clone();
1970                            path.push(*callee);
1971                            (*callee, path, seen.clone())
1972                        }));
1973                    }
1974                } else {
1975                    let pos = match &self.terms[caller.index()].kind {
1976                        TermKind::Decl {
1977                            extractor_kind: Some(ExtractorKind::InternalExtractor { template }),
1978                            ..
1979                        } => template.pos(),
1980                        _ => {
1981                            // There must have already been errors recorded.
1982                            assert!(!tyenv.errors.is_empty());
1983                            continue 'outer;
1984                        }
1985                    };
1986
1987                    let path: Vec<_> = path
1988                        .iter()
1989                        .map(|sym| tyenv.syms[sym.index()].as_str())
1990                        .collect();
1991                    let msg = format!(
1992                        "`{}` extractor definition is recursive: {}",
1993                        tyenv.syms[root.index()],
1994                        path.join(" -> ")
1995                    );
1996                    tyenv.report_error(pos, msg);
1997                    continue 'outer;
1998                }
1999            }
2000        }
2001    }
2002
2003    fn collect_converters(&mut self, tyenv: &mut TypeEnv, defs: &[ast::Def]) {
2004        for def in defs {
2005            match def {
2006                &ast::Def::Converter(ast::Converter {
2007                    ref term,
2008                    ref inner_ty,
2009                    ref outer_ty,
2010                    pos,
2011                }) => {
2012                    let inner_ty_id = match tyenv.get_type_by_name(inner_ty) {
2013                        Some(ty) => ty,
2014                        None => {
2015                            tyenv.report_error(
2016                                inner_ty.1,
2017                                format!("Unknown inner type for converter: '{}'", inner_ty.0),
2018                            );
2019                            continue;
2020                        }
2021                    };
2022
2023                    let outer_ty_id = match tyenv.get_type_by_name(outer_ty) {
2024                        Some(ty) => ty,
2025                        None => {
2026                            tyenv.report_error(
2027                                outer_ty.1,
2028                                format!("Unknown outer type for converter: '{}'", outer_ty.0),
2029                            );
2030                            continue;
2031                        }
2032                    };
2033
2034                    let term_id = match self.get_term_by_name(tyenv, term) {
2035                        Some(term_id) => term_id,
2036                        None => {
2037                            tyenv.report_error(
2038                                term.1,
2039                                format!("Unknown term for converter: '{}'", term.0),
2040                            );
2041                            continue;
2042                        }
2043                    };
2044
2045                    match self.converters.entry((inner_ty_id, outer_ty_id)) {
2046                        Entry::Vacant(v) => {
2047                            v.insert(term_id);
2048                        }
2049                        Entry::Occupied(_) => {
2050                            tyenv.report_error(
2051                                pos,
2052                                format!(
2053                                    "Converter already exists for this type pair: '{}', '{}'",
2054                                    inner_ty.0, outer_ty.0
2055                                ),
2056                            );
2057                            continue;
2058                        }
2059                    }
2060                }
2061                _ => {}
2062            }
2063        }
2064    }
2065
2066    fn collect_externs(&mut self, tyenv: &mut TypeEnv, defs: &[ast::Def]) {
2067        for def in defs {
2068            match def {
2069                &ast::Def::Extern(ast::Extern::Constructor {
2070                    ref term,
2071                    ref func,
2072                    pos,
2073                }) => {
2074                    let func_sym = tyenv.intern_mut(func);
2075                    let term_id = match self.get_term_by_name(tyenv, term) {
2076                        Some(term) => term,
2077                        None => {
2078                            tyenv.report_error(
2079                                pos,
2080                                format!("Constructor declared on undefined term '{}'", term.0),
2081                            );
2082                            continue;
2083                        }
2084                    };
2085                    let termdata = &mut self.terms[term_id.index()];
2086                    match &mut termdata.kind {
2087                        TermKind::Decl {
2088                            constructor_kind, ..
2089                        } => match constructor_kind {
2090                            None => {
2091                                *constructor_kind =
2092                                    Some(ConstructorKind::ExternalConstructor { name: func_sym });
2093                            }
2094                            Some(ConstructorKind::InternalConstructor) => {
2095                                tyenv.report_error(
2096                                    pos,
2097                                    format!(
2098                                        "External constructor declared on term that already has rules: {}",
2099                                        term.0,
2100                                    ),
2101                                );
2102                            }
2103                            Some(ConstructorKind::ExternalConstructor { .. }) => {
2104                                tyenv.report_error(
2105                                    pos,
2106                                    "Duplicate external constructor definition".to_string(),
2107                                );
2108                            }
2109                        },
2110                        TermKind::EnumVariant { .. } => {
2111                            tyenv.report_error(
2112                                pos,
2113                                format!(
2114                                    "External constructor cannot be defined on enum variant: {}",
2115                                    term.0,
2116                                ),
2117                            );
2118                        }
2119                        TermKind::Struct { .. } => {
2120                            tyenv.report_error(
2121                                pos,
2122                                format!(
2123                                    "External constructor cannot be defined on a struct: {}",
2124                                    term.0,
2125                                ),
2126                            );
2127                        }
2128                    }
2129                }
2130                &ast::Def::Extern(ast::Extern::Extractor {
2131                    ref term,
2132                    ref func,
2133                    pos,
2134                    infallible,
2135                }) => {
2136                    let func_sym = tyenv.intern_mut(func);
2137                    let term_id = match self.get_term_by_name(tyenv, term) {
2138                        Some(term) => term,
2139                        None => {
2140                            tyenv.report_error(
2141                                pos,
2142                                format!("Extractor declared on undefined term '{}'", term.0),
2143                            );
2144                            continue;
2145                        }
2146                    };
2147
2148                    let termdata = &mut self.terms[term_id.index()];
2149
2150                    match &mut termdata.kind {
2151                        TermKind::Decl { extractor_kind, .. } => match extractor_kind {
2152                            None => {
2153                                *extractor_kind = Some(ExtractorKind::ExternalExtractor {
2154                                    name: func_sym,
2155                                    infallible,
2156                                    pos,
2157                                });
2158                            }
2159                            Some(ExtractorKind::ExternalExtractor { pos: pos2, .. }) => {
2160                                tyenv.report_error(
2161                                    pos,
2162                                    "Duplicate external extractor definition".to_string(),
2163                                );
2164                                tyenv.report_error(
2165                                    *pos2,
2166                                    "External extractor already defined".to_string(),
2167                                );
2168                                continue;
2169                            }
2170                            Some(ExtractorKind::InternalExtractor { template }) => {
2171                                tyenv.report_error(
2172                                    pos,
2173                                    "Cannot define external extractor for term that already has an \
2174                                     internal extractor macro body defined"
2175                                        .to_string(),
2176                                );
2177                                tyenv.report_error(
2178                                    template.pos(),
2179                                    "Internal extractor macro body already defined".to_string(),
2180                                );
2181                                continue;
2182                            }
2183                        },
2184                        TermKind::EnumVariant { .. } => {
2185                            tyenv.report_error(
2186                                pos,
2187                                format!("Cannot define extractor for enum variant '{}'", term.0),
2188                            );
2189                            continue;
2190                        }
2191                        TermKind::Struct { .. } => {
2192                            tyenv.report_error(
2193                                pos,
2194                                format!("Cannot define extractor for struct '{}'", term.0),
2195                            );
2196                            continue;
2197                        }
2198                    }
2199                }
2200                _ => {}
2201            }
2202        }
2203    }
2204
2205    fn collect_rules(&mut self, tyenv: &mut TypeEnv, defs: &[ast::Def]) {
2206        for def in defs {
2207            match def {
2208                &ast::Def::Rule(ref rule) => {
2209                    let pos = rule.pos;
2210                    let mut bindings = Bindings::default();
2211                    bindings.enter_scope();
2212
2213                    let (sym, args) = if let ast::Pattern::Term { sym, args, .. } = &rule.pattern {
2214                        (sym, args)
2215                    } else {
2216                        tyenv.report_error(
2217                            pos,
2218                            "Rule does not have a term at the root of its left-hand side"
2219                                .to_string(),
2220                        );
2221                        continue;
2222                    };
2223
2224                    let root_term = if let Some(term) = self.get_term_by_name(tyenv, sym) {
2225                        term
2226                    } else {
2227                        tyenv.report_error(
2228                            pos,
2229                            "Cannot define a rule for an unknown term".to_string(),
2230                        );
2231                        continue;
2232                    };
2233
2234                    let termdata = &self.terms[root_term.index()];
2235
2236                    let flags = match &termdata.kind {
2237                        TermKind::Decl { flags, .. } => *flags,
2238                        _ => {
2239                            tyenv.report_error(
2240                                pos,
2241                                "Cannot define a rule on a left-hand-side that is an enum variant"
2242                                    .to_string(),
2243                            );
2244                            continue;
2245                        }
2246                    };
2247
2248                    termdata.check_args_count(args, tyenv, pos, sym);
2249                    let args = self.translate_args(args, termdata, tyenv, &mut bindings);
2250
2251                    let iflets = rule
2252                        .iflets
2253                        .iter()
2254                        .filter_map(|iflet| {
2255                            self.translate_iflet(tyenv, iflet, &mut bindings, flags)
2256                        })
2257                        .collect();
2258                    let rhs = unwrap_or_continue!(self.translate_expr(
2259                        tyenv,
2260                        &rule.expr,
2261                        Some(termdata.ret_ty),
2262                        &mut bindings,
2263                        flags,
2264                    ));
2265
2266                    bindings.exit_scope();
2267
2268                    let prio = if let Some(prio) = rule.prio {
2269                        if flags.multi {
2270                            tyenv.report_error(
2271                                pos,
2272                                "Cannot set rule priorities in multi-terms".to_string(),
2273                            );
2274                        }
2275                        prio
2276                    } else {
2277                        0
2278                    };
2279
2280                    let rid = RuleId(self.rules.len());
2281                    self.rules.push(Rule {
2282                        id: rid,
2283                        root_term,
2284                        args,
2285                        iflets,
2286                        rhs,
2287                        vars: bindings.seen,
2288                        prio,
2289                        name: rule.name.as_ref().map(|i| tyenv.intern_mut(i)),
2290                        pos,
2291                    });
2292                }
2293                _ => {}
2294            }
2295        }
2296
2297        // Populate default rule names.
2298        //
2299        // Unnamed rules that are the only rule for their root term adopt the
2300        // name of the root term.
2301        let mut term_rule_count: HashMap<TermId, usize> = HashMap::new();
2302        for rule in &self.rules {
2303            *term_rule_count.entry(rule.root_term).or_default() += 1;
2304        }
2305
2306        for rule in &mut self.rules {
2307            if rule.name.is_none()
2308                && term_rule_count
2309                    .get(&rule.root_term)
2310                    .copied()
2311                    .unwrap_or_default()
2312                    == 1
2313            {
2314                let term = &self.terms[rule.root_term.index()];
2315                rule.name = Some(term.name);
2316            }
2317        }
2318
2319        // Populate rule name map.
2320        for rule in &self.rules {
2321            let Some(name) = rule.name else { continue };
2322            match self.rule_map.entry(name) {
2323                Entry::Vacant(e) => {
2324                    e.insert(rule.id);
2325                }
2326                Entry::Occupied(_) => {
2327                    tyenv.report_error(
2328                        rule.pos,
2329                        format!("Duplicate rule name: '{}'", tyenv.syms[name.index()]),
2330                    );
2331                }
2332            }
2333        }
2334    }
2335
2336    fn check_for_undefined_decls(&self, tyenv: &mut TypeEnv, defs: &[ast::Def]) {
2337        for def in defs {
2338            if let ast::Def::Decl(decl) = def {
2339                let term = self.get_term_by_name(tyenv, &decl.term).unwrap();
2340                let term = &self.terms[term.index()];
2341                if !term.has_constructor() && !term.has_extractor() {
2342                    tyenv.report_error(
2343                        decl.pos,
2344                        format!(
2345                            "no rules, extractor, or external definition for declaration '{}'",
2346                            decl.term.0
2347                        ),
2348                    );
2349                }
2350            }
2351        }
2352    }
2353
2354    fn check_for_expr_terms_without_constructors(&self, tyenv: &mut TypeEnv, defs: &[ast::Def]) {
2355        for def in defs {
2356            if let ast::Def::Rule(rule) = def {
2357                rule.expr.terms(&mut |pos, ident| {
2358                    let term = match self.get_term_by_name(tyenv, ident) {
2359                        None => {
2360                            debug_assert!(!tyenv.errors.is_empty());
2361                            return;
2362                        }
2363                        Some(t) => t,
2364                    };
2365                    let term = &self.terms[term.index()];
2366                    if !term.has_constructor() {
2367                        tyenv.report_error(
2368                            pos,
2369                            format!(
2370                                "term `{}` cannot be used in an expression because \
2371                                 it does not have a constructor",
2372                                ident.0
2373                            ),
2374                        )
2375                    }
2376                });
2377            }
2378        }
2379    }
2380
2381    fn maybe_implicit_convert_pattern(
2382        &self,
2383        tyenv: &mut TypeEnv,
2384        pattern: &ast::Pattern,
2385        inner_ty: TypeId,
2386        outer_ty: TypeId,
2387    ) -> Option<ast::Pattern> {
2388        if let Some(converter_term) = self.converters.get(&(inner_ty, outer_ty)) {
2389            if self.terms[converter_term.index()].has_extractor() {
2390                // This is a little awkward: we have to
2391                // convert back to an Ident, to be
2392                // re-resolved. The pos doesn't matter
2393                // as it shouldn't result in a lookup
2394                // failure.
2395                let converter_term_ident = ast::Ident(
2396                    tyenv.syms[self.terms[converter_term.index()].name.index()].clone(),
2397                    pattern.pos(),
2398                );
2399                let expanded_pattern = ast::Pattern::Term {
2400                    sym: converter_term_ident,
2401                    pos: pattern.pos(),
2402                    args: vec![pattern.clone()],
2403                };
2404
2405                return Some(expanded_pattern);
2406            }
2407        }
2408        None
2409    }
2410
2411    fn translate_pattern(
2412        &self,
2413        tyenv: &mut TypeEnv,
2414        pat: &ast::Pattern,
2415        expected_ty: TypeId,
2416        bindings: &mut Bindings,
2417    ) -> Option<Pattern> {
2418        log!("translate_pattern: {:?}", pat);
2419        log!("translate_pattern: bindings = {:?}", bindings);
2420        match pat {
2421            // TODO: flag on primitive type decl indicating it's an integer type?
2422            &ast::Pattern::ConstInt { val, pos } => {
2423                let ty = &tyenv.types[expected_ty.index()];
2424                if !ty.is_int() && !ty.is_prim() {
2425                    tyenv.report_error(
2426                        pos,
2427                        format!(
2428                            "expected non-integer type {}, but found integer literal '{}'",
2429                            ty.name(tyenv),
2430                            val,
2431                        ),
2432                    );
2433                }
2434                Some(Pattern::ConstInt(expected_ty, val))
2435            }
2436            &ast::Pattern::ConstBool { val, pos } => {
2437                if expected_ty != TypeId::BOOL {
2438                    tyenv.report_error(
2439                        pos,
2440                        format!(
2441                            "Boolean literal '{val}' has type {} but we need {} in context",
2442                            BuiltinType::Bool.name(),
2443                            tyenv.types[expected_ty.index()].name(tyenv)
2444                        ),
2445                    )
2446                }
2447                Some(Pattern::ConstBool(TypeId::BOOL, val))
2448            }
2449            &ast::Pattern::ConstPrim { ref val, pos } => {
2450                let val = tyenv.intern_mut(val);
2451                let const_ty = match tyenv.const_types.get(&val) {
2452                    Some(ty) => *ty,
2453                    None => {
2454                        tyenv.report_error(pos, "Unknown constant");
2455                        return None;
2456                    }
2457                };
2458                if expected_ty != const_ty {
2459                    tyenv.report_error(pos, "Type mismatch for constant");
2460                }
2461                Some(Pattern::ConstPrim(const_ty, val))
2462            }
2463            &ast::Pattern::Wildcard { .. } => Some(Pattern::Wildcard(expected_ty)),
2464            &ast::Pattern::And { ref subpats, .. } => {
2465                // If any of the subpatterns fails to type-check, we'll report
2466                // an error at that point. Here, just skip it and keep looking
2467                // for more errors.
2468                let children = subpats
2469                    .iter()
2470                    .filter_map(|subpat| {
2471                        self.translate_pattern(tyenv, subpat, expected_ty, bindings)
2472                    })
2473                    .collect();
2474                Some(Pattern::And(expected_ty, children))
2475            }
2476            &ast::Pattern::BindPattern {
2477                ref var,
2478                ref subpat,
2479                pos,
2480            } => {
2481                let subpat = self.translate_pattern(tyenv, subpat, expected_ty, bindings)?;
2482
2483                // The sub-pattern's type should be `expected_ty`. If it isn't,
2484                // we've already reported a type error about it, but continue
2485                // using the type we actually found in hopes that we'll
2486                // generate fewer follow-on error messages.
2487                let ty = subpat.ty();
2488
2489                let name = tyenv.intern_mut(var);
2490                if bindings.lookup(name).is_some() {
2491                    tyenv.report_error(
2492                        pos,
2493                        format!("Re-bound variable name in LHS pattern: '{}'", var.0),
2494                    );
2495                    // Try to keep going.
2496                }
2497                let id = bindings.add_var(name, ty);
2498                Some(Pattern::BindPattern(ty, id, Box::new(subpat)))
2499            }
2500            &ast::Pattern::Var { ref var, pos } => {
2501                // Look up the variable; if it has already been bound,
2502                // then this becomes a `Var` node (which matches the
2503                // existing bound value), otherwise it becomes a
2504                // `BindPattern` with a wildcard subpattern to capture
2505                // at this location.
2506                let name = tyenv.intern_mut(var);
2507                match bindings.lookup(name) {
2508                    None => {
2509                        let id = bindings.add_var(name, expected_ty);
2510                        Some(Pattern::BindPattern(
2511                            expected_ty,
2512                            id,
2513                            Box::new(Pattern::Wildcard(expected_ty)),
2514                        ))
2515                    }
2516                    Some(bv) => {
2517                        if expected_ty != bv.ty {
2518                            tyenv.report_error(
2519                                pos,
2520                                format!(
2521                                    "Mismatched types: pattern expects type '{}' but already-bound var '{}' has type '{}'",
2522                                    tyenv.types[expected_ty.index()].name(tyenv),
2523                                    var.0,
2524                                    tyenv.types[bv.ty.index()].name(tyenv),
2525                                ),
2526                            );
2527                            // Try to keep going for more errors.
2528                        }
2529                        Some(Pattern::Var(bv.ty, bv.id))
2530                    }
2531                }
2532            }
2533            &ast::Pattern::Term {
2534                ref sym,
2535                ref args,
2536                pos,
2537            } => {
2538                // Look up the term.
2539                let tid = match self.get_term_by_name(tyenv, sym) {
2540                    Some(t) => t,
2541                    None => {
2542                        tyenv.report_error(pos, format!("Unknown term in pattern: '{}'", sym.0));
2543                        return None;
2544                    }
2545                };
2546
2547                let termdata = &self.terms[tid.index()];
2548
2549                // Get the return type and arg types. Verify the
2550                // expected type of this pattern, if any, against the
2551                // return type of the term. Insert an implicit
2552                // converter if needed.
2553                let ret_ty = termdata.ret_ty;
2554                if expected_ty != ret_ty {
2555                    // Can we do an implicit type conversion? Look
2556                    // up the converter term, if any. If one has
2557                    // been registered, and the term has an
2558                    // extractor, then build an expanded AST node
2559                    // right here and recurse on it.
2560                    if let Some(expanded_pattern) =
2561                        self.maybe_implicit_convert_pattern(tyenv, pat, ret_ty, expected_ty)
2562                    {
2563                        return self.translate_pattern(
2564                            tyenv,
2565                            &expanded_pattern,
2566                            expected_ty,
2567                            bindings,
2568                        );
2569                    }
2570
2571                    tyenv.report_error(
2572                        pos,
2573                        format!(
2574                            "Mismatched types: pattern expects type '{}' but term has return type '{}'",
2575                            tyenv.types[expected_ty.index()].name(tyenv),
2576                            tyenv.types[ret_ty.index()].name(tyenv),
2577                        ),
2578                    );
2579                    // Try to keep going for more errors.
2580                }
2581
2582                termdata.check_args_count(args, tyenv, pos, sym);
2583
2584                // TODO: check that multi-extractors are only used in terms declared `multi`
2585
2586                match &termdata.kind {
2587                    TermKind::EnumVariant { .. } | TermKind::Struct => {}
2588                    TermKind::Decl {
2589                        extractor_kind: Some(ExtractorKind::ExternalExtractor { .. }),
2590                        ..
2591                    } => {}
2592                    TermKind::Decl {
2593                        extractor_kind: Some(ExtractorKind::InternalExtractor { template }),
2594                        ..
2595                    } => {
2596                        if self.expand_internal_extractors {
2597                            // Expand the extractor macro! We create a map
2598                            // from macro args to AST pattern trees and
2599                            // then evaluate the template with these
2600                            // substitutions.
2601                            log!("internal extractor macro args = {:?}", args);
2602                            let pat = template.subst_macro_args(&args)?;
2603                            return self.translate_pattern(tyenv, &pat, expected_ty, bindings);
2604                        }
2605                    }
2606                    TermKind::Decl {
2607                        extractor_kind: None,
2608                        ..
2609                    } => {
2610                        tyenv.report_error(
2611                            pos,
2612                            format!(
2613                                "Cannot use term '{}' that does not have a defined extractor in a \
2614                                 left-hand side pattern",
2615                                sym.0
2616                            ),
2617                        );
2618                    }
2619                }
2620
2621                let subpats = self.translate_args(args, termdata, tyenv, bindings);
2622                Some(Pattern::Term(ret_ty, tid, subpats))
2623            }
2624            &ast::Pattern::MacroArg { .. } => unreachable!(),
2625        }
2626    }
2627
2628    fn translate_args(
2629        &self,
2630        args: &Vec<ast::Pattern>,
2631        termdata: &Term,
2632        tyenv: &mut TypeEnv,
2633        bindings: &mut Bindings,
2634    ) -> Vec<Pattern> {
2635        args.iter()
2636            .zip(termdata.arg_tys.iter())
2637            .filter_map(|(arg, &arg_ty)| self.translate_pattern(tyenv, arg, arg_ty, bindings))
2638            .collect()
2639    }
2640
2641    fn maybe_implicit_convert_expr(
2642        &self,
2643        tyenv: &mut TypeEnv,
2644        expr: &ast::Expr,
2645        inner_ty: TypeId,
2646        outer_ty: TypeId,
2647    ) -> Option<ast::Expr> {
2648        // Is there a converter for this type mismatch?
2649        if let Some(converter_term) = self.converters.get(&(inner_ty, outer_ty)) {
2650            if self.terms[converter_term.index()].has_constructor() {
2651                let converter_ident = ast::Ident(
2652                    tyenv.syms[self.terms[converter_term.index()].name.index()].clone(),
2653                    expr.pos(),
2654                );
2655                return Some(ast::Expr::Term {
2656                    sym: converter_ident,
2657                    pos: expr.pos(),
2658                    args: vec![expr.clone()],
2659                });
2660            }
2661        }
2662        None
2663    }
2664
2665    fn translate_expr(
2666        &self,
2667        tyenv: &mut TypeEnv,
2668        expr: &ast::Expr,
2669        ty: Option<TypeId>,
2670        bindings: &mut Bindings,
2671        root_flags: TermFlags,
2672    ) -> Option<Expr> {
2673        log!("translate_expr: {:?}", expr);
2674        match expr {
2675            &ast::Expr::Term {
2676                ref sym,
2677                ref args,
2678                pos,
2679            } => {
2680                // Look up the term.
2681                let name = tyenv.intern_mut(&sym);
2682                let tid = match self.term_map.get(&name) {
2683                    Some(&t) => t,
2684                    None => {
2685                        // Maybe this was actually a variable binding and the user has placed
2686                        // parens around it by mistake? (See #4775.)
2687                        if bindings.lookup(name).is_some() {
2688                            tyenv.report_error(
2689                                pos,
2690                                format!(
2691                                    "Unknown term in expression: '{}'. Variable binding under this name exists; try removing the parens?", sym.0));
2692                        } else {
2693                            tyenv.report_error(
2694                                pos,
2695                                format!("Unknown term in expression: '{}'", sym.0),
2696                            );
2697                        }
2698                        return None;
2699                    }
2700                };
2701                let termdata = &self.terms[tid.index()];
2702
2703                // Get the return type and arg types. Verify the
2704                // expected type of this pattern, if any, against the
2705                // return type of the term, and determine whether we
2706                // are doing an implicit conversion. Report an error
2707                // if types don't match and no conversion is possible.
2708                let ret_ty = termdata.ret_ty;
2709                let ty = if ty.is_some() && ret_ty != ty.unwrap() {
2710                    // Is there a converter for this type mismatch?
2711                    if let Some(expanded_expr) =
2712                        self.maybe_implicit_convert_expr(tyenv, expr, ret_ty, ty.unwrap())
2713                    {
2714                        return self.translate_expr(
2715                            tyenv,
2716                            &expanded_expr,
2717                            ty,
2718                            bindings,
2719                            root_flags,
2720                        );
2721                    }
2722
2723                    tyenv.report_error(
2724                        pos,
2725                        format!("Mismatched types: expression expects type '{}' but term has return type '{}'",
2726                                tyenv.types[ty.unwrap().index()].name(tyenv),
2727                                tyenv.types[ret_ty.index()].name(tyenv)));
2728
2729                    // Keep going, to discover more errors.
2730                    ret_ty
2731                } else {
2732                    ret_ty
2733                };
2734
2735                if let TermKind::Decl { flags, .. } = &termdata.kind {
2736                    // On the left-hand side of a rule or in a pure term, only pure terms may be
2737                    // used.
2738                    let pure_required = root_flags.pure;
2739                    if pure_required && !flags.pure {
2740                        tyenv.report_error(
2741                            pos,
2742                            format!(
2743                                "Used non-pure constructor '{}' in pure expression context",
2744                                sym.0
2745                            ),
2746                        );
2747                    }
2748
2749                    // Multi-terms may only be used inside other multi-terms.
2750                    if !root_flags.multi && flags.multi {
2751                        tyenv.report_error(
2752                            pos,
2753                            format!(
2754                                "Used multi-constructor '{}' but this rule is not in a multi-term",
2755                                sym.0
2756                            ),
2757                        );
2758                    }
2759
2760                    // Partial terms may always be used on the left-hand side of a rule. On the
2761                    // right-hand side they may only be used inside other partial terms.
2762                    let partial_allowed = root_flags.partial;
2763                    if !partial_allowed && flags.partial {
2764                        tyenv.report_error(
2765                            pos,
2766                            format!(
2767                                "Rule can't use partial constructor '{}' on RHS; \
2768                                try moving it to if-let{}",
2769                                sym.0,
2770                                if root_flags.multi {
2771                                    ""
2772                                } else {
2773                                    " or make this rule's term partial too"
2774                                }
2775                            ),
2776                        );
2777                    }
2778                }
2779
2780                termdata.check_args_count(args, tyenv, pos, sym);
2781
2782                // Resolve subexpressions.
2783                let subexprs = args
2784                    .iter()
2785                    .zip(termdata.arg_tys.iter())
2786                    .filter_map(|(arg, &arg_ty)| {
2787                        self.translate_expr(tyenv, arg, Some(arg_ty), bindings, root_flags)
2788                    })
2789                    .collect();
2790
2791                Some(Expr::Term(ty, tid, subexprs))
2792            }
2793            &ast::Expr::Var { ref name, pos } => {
2794                let sym = tyenv.intern_mut(name);
2795                // Look through bindings, innermost (most recent) first.
2796                let bv = match bindings.lookup(sym) {
2797                    None => {
2798                        tyenv.report_error(pos, format!("Unknown variable '{}'", name.0));
2799                        return None;
2800                    }
2801                    Some(bv) => bv,
2802                };
2803
2804                // Verify type. Maybe do an implicit conversion.
2805                if ty.is_some() && bv.ty != ty.unwrap() {
2806                    // Is there a converter for this type mismatch?
2807                    if let Some(expanded_expr) =
2808                        self.maybe_implicit_convert_expr(tyenv, expr, bv.ty, ty.unwrap())
2809                    {
2810                        return self.translate_expr(
2811                            tyenv,
2812                            &expanded_expr,
2813                            ty,
2814                            bindings,
2815                            root_flags,
2816                        );
2817                    }
2818
2819                    tyenv.report_error(
2820                        pos,
2821                        format!(
2822                            "Variable '{}' has type {} but we need {} in context",
2823                            name.0,
2824                            tyenv.types[bv.ty.index()].name(tyenv),
2825                            tyenv.types[ty.unwrap().index()].name(tyenv)
2826                        ),
2827                    );
2828                }
2829
2830                Some(Expr::Var(bv.ty, bv.id))
2831            }
2832            &ast::Expr::ConstBool { val, pos } => {
2833                match ty {
2834                    Some(ty) if ty != TypeId::BOOL => tyenv.report_error(
2835                        pos,
2836                        format!(
2837                            "Boolean literal '{val}' has type {} but we need {} in context",
2838                            BuiltinType::Bool.name(),
2839                            tyenv.types[ty.index()].name(tyenv)
2840                        ),
2841                    ),
2842                    Some(..) | None => {}
2843                };
2844                Some(Expr::ConstBool(TypeId::BOOL, val))
2845            }
2846            &ast::Expr::ConstInt { val, pos } => {
2847                let Some(ty) = ty else {
2848                    tyenv.report_error(
2849                        pos,
2850                        "integer literal in a context that needs an explicit type".to_string(),
2851                    );
2852                    return None;
2853                };
2854
2855                let typ = &tyenv.types[ty.index()];
2856
2857                if !typ.is_int() && !typ.is_prim() {
2858                    tyenv.report_error(
2859                        pos,
2860                        format!(
2861                            "expected non-integer type {}, but found integer literal '{}'",
2862                            tyenv.types[ty.index()].name(tyenv),
2863                            val,
2864                        ),
2865                    );
2866                }
2867                Some(Expr::ConstInt(ty, val))
2868            }
2869            &ast::Expr::ConstPrim { ref val, pos } => {
2870                let val = tyenv.intern_mut(val);
2871                let const_ty = match tyenv.const_types.get(&val) {
2872                    Some(ty) => *ty,
2873                    None => {
2874                        tyenv.report_error(pos, "Unknown constant");
2875                        return None;
2876                    }
2877                };
2878                if ty.is_some() && const_ty != ty.unwrap() {
2879                    tyenv.report_error(
2880                        pos,
2881                        format!(
2882                            "Constant '{}' has wrong type: expected {}, but is actually {}",
2883                            tyenv.syms[val.index()],
2884                            tyenv.types[ty.unwrap().index()].name(tyenv),
2885                            tyenv.types[const_ty.index()].name(tyenv)
2886                        ),
2887                    );
2888                    return None;
2889                }
2890                Some(Expr::ConstPrim(const_ty, val))
2891            }
2892            &ast::Expr::Let {
2893                ref defs,
2894                ref body,
2895                pos,
2896            } => {
2897                bindings.enter_scope();
2898
2899                // For each new binding...
2900                let mut let_defs = vec![];
2901                for def in defs {
2902                    // Check that the given variable name does not already exist.
2903                    let name = tyenv.intern_mut(&def.var);
2904
2905                    // Look up the type.
2906                    let tid = match tyenv.get_type_by_name(&def.ty) {
2907                        Some(tid) => tid,
2908                        None => {
2909                            tyenv.report_error(
2910                                pos,
2911                                format!("Unknown type {} for variable '{}'", def.ty.0, def.var.0),
2912                            );
2913                            continue;
2914                        }
2915                    };
2916
2917                    // Evaluate the variable's value.
2918                    let val = Box::new(unwrap_or_continue!(self.translate_expr(
2919                        tyenv,
2920                        &def.val,
2921                        Some(tid),
2922                        bindings,
2923                        root_flags,
2924                    )));
2925
2926                    // Bind the var with the given type.
2927                    let id = bindings.add_var(name, tid);
2928                    let_defs.push((id, tid, val));
2929                }
2930
2931                // Evaluate the body, expecting the type of the overall let-expr.
2932                let body = Box::new(self.translate_expr(tyenv, body, ty, bindings, root_flags)?);
2933                let body_ty = body.ty();
2934
2935                // Pop the bindings.
2936                bindings.exit_scope();
2937
2938                Some(Expr::Let {
2939                    ty: body_ty,
2940                    bindings: let_defs,
2941                    body,
2942                })
2943            }
2944        }
2945    }
2946
2947    fn translate_iflet(
2948        &self,
2949        tyenv: &mut TypeEnv,
2950        iflet: &ast::IfLet,
2951        bindings: &mut Bindings,
2952        root_flags: TermFlags,
2953    ) -> Option<IfLet> {
2954        // Translate the expr first. The `if-let` and `if` forms are part of the left-hand side of
2955        // the rule.
2956        let rhs = self.translate_expr(tyenv, &iflet.expr, None, bindings, root_flags.on_lhs())?;
2957        let lhs = self.translate_pattern(tyenv, &iflet.pattern, rhs.ty(), bindings)?;
2958
2959        Some(IfLet { lhs, rhs })
2960    }
2961
2962    /// Lookup term by name.
2963    pub fn get_term_by_name(&self, tyenv: &TypeEnv, sym: &ast::Ident) -> Option<TermId> {
2964        tyenv
2965            .intern(sym)
2966            .and_then(|sym| self.term_map.get(&sym))
2967            .copied()
2968    }
2969
2970    /// Lookup rule by name.
2971    pub fn get_rule_by_name(&self, tyenv: &TypeEnv, sym: &ast::Ident) -> Option<RuleId> {
2972        tyenv
2973            .intern(sym)
2974            .and_then(|sym| self.rule_map.get(&sym))
2975            .copied()
2976    }
2977
2978    /// Lookup the term corresponding to the given enum variant.
2979    pub fn get_variant_term(&self, tyenv: &TypeEnv, ty: TypeId, variant: VariantId) -> TermId {
2980        let variant = tyenv.get_variant(ty, variant);
2981        self.term_map[&variant.fullname]
2982    }
2983}
2984
2985#[cfg(test)]
2986mod test {
2987    use super::*;
2988    use crate::ast::Ident;
2989    use crate::lexer::Lexer;
2990    use crate::parser::parse;
2991
2992    #[test]
2993    fn build_type_env() {
2994        let text = r"
2995            (type UImm8 (primitive UImm8))
2996            (type A extern (enum (B (f1 u32) (f2 u32)) (C (f1 u32))))
2997        ";
2998        let ast = parse(Lexer::new(0, text).unwrap()).expect("should parse");
2999        let tyenv = TypeEnv::from_ast(&ast).expect("should not have type-definition errors");
3000
3001        let sym_a = tyenv
3002            .intern(&Ident("A".to_string(), Default::default()))
3003            .unwrap();
3004        let sym_b = tyenv
3005            .intern(&Ident("B".to_string(), Default::default()))
3006            .unwrap();
3007        let sym_c = tyenv
3008            .intern(&Ident("C".to_string(), Default::default()))
3009            .unwrap();
3010        let sym_a_b = tyenv
3011            .intern(&Ident("A.B".to_string(), Default::default()))
3012            .unwrap();
3013        let sym_a_c = tyenv
3014            .intern(&Ident("A.C".to_string(), Default::default()))
3015            .unwrap();
3016        let sym_uimm8 = tyenv
3017            .intern(&Ident("UImm8".to_string(), Default::default()))
3018            .unwrap();
3019        let sym_f1 = tyenv
3020            .intern(&Ident("f1".to_string(), Default::default()))
3021            .unwrap();
3022        let sym_f2 = tyenv
3023            .intern(&Ident("f2".to_string(), Default::default()))
3024            .unwrap();
3025
3026        assert_eq!(tyenv.type_map.get(&sym_uimm8).unwrap(), &TypeId(13));
3027        assert_eq!(tyenv.type_map.get(&sym_a).unwrap(), &TypeId(14));
3028
3029        let expected_types = vec![
3030            Type::Primitive(
3031                TypeId(13),
3032                sym_uimm8,
3033                Pos {
3034                    file: 0,
3035                    offset: 19,
3036                },
3037            ),
3038            Type::Enum {
3039                name: sym_a,
3040                id: TypeId(14),
3041                is_extern: true,
3042                is_nodebug: false,
3043                variants: vec![
3044                    Variant {
3045                        name: sym_b,
3046                        fullname: sym_a_b,
3047                        id: VariantId(0),
3048                        fields: Fields::Struct(StructFields {
3049                            fields: vec![
3050                                StructField {
3051                                    name: sym_f1,
3052                                    id: FieldId(0),
3053                                    ty: TypeId::U32,
3054                                },
3055                                StructField {
3056                                    name: sym_f2,
3057                                    id: FieldId(1),
3058                                    ty: TypeId::U32,
3059                                },
3060                            ],
3061                        }),
3062                        pos: Pos {
3063                            file: 0,
3064                            offset: 77,
3065                        },
3066                    },
3067                    Variant {
3068                        name: sym_c,
3069                        fullname: sym_a_c,
3070                        id: VariantId(1),
3071                        fields: Fields::Struct(StructFields {
3072                            fields: vec![StructField {
3073                                name: sym_f1,
3074                                id: FieldId(0),
3075                                ty: TypeId::U32,
3076                            }],
3077                        }),
3078                        pos: Pos {
3079                            file: 0,
3080                            offset: 99,
3081                        },
3082                    },
3083                ],
3084                pos: Pos {
3085                    file: 0,
3086                    offset: 62,
3087                },
3088            },
3089        ];
3090
3091        assert_eq!(
3092            tyenv.types.len(),
3093            expected_types.len() + BuiltinType::ALL.len()
3094        );
3095        for (i, (actual, expected)) in tyenv
3096            .types
3097            .iter()
3098            .skip(BuiltinType::ALL.len())
3099            .zip(&expected_types)
3100            .enumerate()
3101        {
3102            assert_eq!(expected, actual, "`{i}`th type is not equal!");
3103        }
3104    }
3105}