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