nickel_lang_core/
typ.rs

1//! Nickel static types.
2//!
3//! The type system of Nickel is comprised of primitive types, arrays, records, functions, and
4//! opaque types (contracts). This is a structural type system with row polymorphism for both
5//! records and enums.
6//!
7//! ## Record types (rows)
8//!
9//! A row type for a record is represented as a linked list of pairs `(id, type)` indicating the
10//! name and the type of each field. Row-polymorphism means that the tail of this list can be a
11//! type variable which can be abstracted over, leaving the row open for future extension. A simple
12//! illustration is record field access:
13//!
14//! ```nickel
15//! let f : forall a. { some_field : Number; a} -> Number =
16//!   fun record => record.some_field
17//! ```
18//!
19//! The type `{ some_field: Number; a }` indicates that an argument to this function must have at
20//! least the field `some_field` of type `Number`, but may contain other fields (or not).
21//!
22//! ## Dictionaries
23//!
24//! A dictionary type `{ _ : Type }` represents a record whose fields all have the type `Type`. The
25//! count and the name of the fields aren't constrained. Dictionaries can be mapped over, extended,
26//! shrinked and accessed in a type-safe manner.
27//!
28//! # Enum types
29//!
30//! An enum type is also a row type where each element is a tag, such as `[| 'foo, 'bar, 'baz |]`.
31//! This type represent values that can be either `'foo`, `'bar` or `'baz`. Enums support row
32//! polymorphism as well.
33//!
34//! # Contracts
35//!
36//! To each type corresponds a contract, which is equivalent to a Nickel function which checks at
37//! runtime that its argument is of the given type. Contract checks are introduced by a contract
38//! annotation or propagated via merging. They ensure sane interaction between typed and untyped
39//! parts.
40//!
41//! Conversely, any Nickel term seen as a contract corresponds to a type, which is opaque and can
42//! only be equated with itself.
43use crate::{
44    environment::Environment,
45    error::{EvalError, ParseError, ParseErrors, TypecheckError},
46    identifier::{Ident, LocIdent},
47    impl_display_from_pretty,
48    label::Polarity,
49    metrics::increment,
50    mk_app, mk_fun,
51    position::TermPos,
52    pretty::PrettyPrintCap,
53    stdlib::internals,
54    term::pattern::compile::Compile,
55    term::{
56        array::Array, make as mk_term, record::RecordData, string::NickelString, IndexMap,
57        MatchBranch, MatchData, RichTerm, Term,
58    },
59    traverse::*,
60};
61
62use std::{collections::HashSet, convert::Infallible};
63
64/// A record row, mapping an identifier to a type. A record type is a dictionary mapping
65/// identifiers to Nickel type. Record types are represented as sequences of `RecordRowF`, ending
66/// potentially with a type variable or `Dyn` in tail position.
67///
68/// # Type parameters
69///
70/// As other types with the `F` suffix, this type is parametrized by one or more recursive
71/// unfoldings (here, `Ty` for `TypeF`). See [`TypeF`] for more details.
72#[derive(Clone, PartialEq, Eq, Debug)]
73pub struct RecordRowF<Ty> {
74    pub id: LocIdent,
75    pub typ: Ty,
76}
77
78/// An enum row, mapping an identifier to an optional type. If the associated type is `None`, this
79/// represent a bare (unapplied) enum tag such as `'foo`. If the enum is applied (a variant), the
80/// type is store in `typ`. An enum type is a set of enum rows, represented as a sequence of
81/// `EnumRow`s, ending potentially with a type variable tail position.
82///
83/// # Type parameters
84///
85/// As other types with the `F` suffix, this type is parametrized by one or more recursive
86/// unfoldings (here, `Ty` for `TypeF`). See [`TypeF`] for more details.
87#[derive(Clone, PartialEq, Eq, Debug)]
88pub struct EnumRowF<Ty> {
89    pub id: LocIdent,
90    pub typ: Option<Ty>,
91}
92
93/// Generic sequence of record rows potentially with a type variable or `Dyn` in tail position.
94///
95/// As other types with the `F` suffix, this type is parametrized by one or more recursive
96/// unfoldings. See [`TypeF`] for more details.
97///
98/// # Type parameters
99///
100/// - `Ty` is the recursive unfolding of a Nickel type stored inside one row. In practice, a
101///   wrapper around an instantiation of `TypeF`.
102/// - `RRows` is the recursive unfolding of record rows (the tail of this row sequence). In
103///   practice, a wrapper around an instantiation of `RecordRowsF`.
104#[derive(Clone, PartialEq, Eq, Debug)]
105pub enum RecordRowsF<Ty, RRows> {
106    Empty,
107    Extend { row: RecordRowF<Ty>, tail: RRows },
108    TailVar(LocIdent),
109    TailDyn,
110}
111
112/// Generic sequence of enum rows potentially with a type variable in tail position.
113///
114/// As other types with the `F` suffix, this type is parametrized by one or more recursive
115/// unfoldings. See [`TypeF`] for more details.
116///
117/// # Type parameters
118///
119/// - `ERows` is the recursive unfolding of enum rows (the tail of this row sequence). In practice,
120///   a wrapper around `EnumRowsF`.
121#[derive(Clone, PartialEq, Eq, Debug)]
122pub enum EnumRowsF<Ty, ERows> {
123    Empty,
124    Extend { row: EnumRowF<Ty>, tail: ERows },
125    TailVar(LocIdent),
126}
127
128/// The kind of a quantified type variable.
129///
130/// Nickel uses several forms of polymorphism. A type variable can be substituted for a type, as in
131/// `id : forall a. a -> a`, for record rows as in `access_foo : forall a . {foo : Number; a} ->
132/// Number}`, or for enum rows. This information is implicit in the source syntax: we don't require
133/// users to write e.g. `forall a :: Type` or `forall a :: Rows`. But the kind of a variable is
134/// required for the typechecker. It is thus determined during parsing and stored as `VarKind` where
135/// type variables are introduced, that is, on forall quantifiers.
136#[derive(Clone, PartialEq, Eq, Debug, Default)]
137pub enum VarKind {
138    #[default]
139    Type,
140    /// `excluded` keeps track of which rows appear somewhere alongside the tail, and therefore
141    /// cannot appear in the tail. For instance `forall r. { ; r } -> { x : Number ; r }` assumes
142    EnumRows { excluded: HashSet<Ident> },
143    /// Same as for [Self::EnumRows].
144    RecordRows { excluded: HashSet<Ident> },
145}
146
147/// Equivalent to `std::mem::Discriminant<VarKind>`, but we can do things like match on it
148// TODO: this seems overly complicated, and it's anyways more space-efficient to store the
149// `excluded` information separately like we do in the `State` field constr. Probably we can store
150// it that way during parsing too.
151#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
152pub enum VarKindDiscriminant {
153    Type,
154    EnumRows,
155    RecordRows,
156}
157
158impl From<&VarKind> for VarKindDiscriminant {
159    fn from(vk: &VarKind) -> Self {
160        match vk {
161            VarKind::Type => VarKindDiscriminant::Type,
162            VarKind::EnumRows { .. } => VarKindDiscriminant::EnumRows,
163            VarKind::RecordRows { .. } => VarKindDiscriminant::RecordRows,
164        }
165    }
166}
167
168/// Flavour of a dictionary type. There are currently two way of writing a dictionary type-ish
169/// object: as a dictionary contract `{_ | T}` or as a dictionary type `{_ : T}`. Ideally, the
170/// former wouldn't even be a type but mostly syntactic sugar for a builtin contract application,
171/// or maybe a proper AST node.
172///
173/// However, the LSP needs to handle both dictionary types and contracts specifically in order to
174/// provide good completion. As we added dictionary contract just before 1.0 to fix a non trivial
175/// issue with respect to polymorphic contracts ([GitHub
176/// issue](https://github.com/tweag/nickel/issues/1228)), the solution to just tweak dictionary
177/// types to be able to hold both kinds - generating a different contract  - seemed to be the
178/// simplest to preserve the user experience (LSP, handling of dictionary when reporting a contract
179/// blame, etc.).
180///
181/// Dictionary contracts might get a proper AST node later on.
182#[derive(Clone, Debug, Copy, Eq, PartialEq)]
183pub enum DictTypeFlavour {
184    /// Dictionary type (`{_ : T}`)
185    Type,
186    /// Dictionary contract (`{_ | T}`)
187    Contract,
188}
189
190/// A Nickel type.
191///
192/// # Generic representation (functor)
193///
194/// A Nickel type is represented by a tree and is naturally defined in a recursive manner: for
195/// example, one would expect the constructor for function types (`Arrow`) to look like:
196///
197/// ```rust
198/// pub enum Type {
199///      Arrow(Box<Type>, Box<Type>),
200///      // ...
201/// }
202/// ```
203///
204/// However, `TypeF` is slightly different, in that it is parametrized by a generic type instead of
205/// using a concrete definition like `Box<Types>` (forget about rows for now):
206///
207/// ```rust
208/// pub enum TypeF<Ty /* , .. */> {
209///      Arrow(Ty, Ty),
210///      // ...
211/// }
212/// ```
213///
214/// `Ty` is also called a recursive unfolding throughout the documentation. By defining `struct
215/// Type(TypeF<Box<Types>>)`, we get back the original, natural definition.
216///
217/// ## Motivation 1: variation on `Types`
218///
219/// Having a generic definition makes it possible to easily create other types with the _same shape_
220/// as `Type` (seen as trees), but with enriched nodes. The typical use-case in Nickel is the
221/// variation on types used by the typechecker. During type inference, the typechecker operates on
222/// trees where each node can either be a concrete type, or a unification variable (a unknown type
223/// to be inferred). Instead of duplicating the whole definition of `Type` as well as all basic
224/// methods, we can simply have a different recursive definition:
225///
226/// ```
227/// # // phony declarations to make this example pass the tests
228/// # type VarId = ();
229/// # type TypeF<T> = T;
230///
231/// pub enum UnifType {
232///    UnifVar(VarId),
233///    Concrete(TypeF<Box<UnifType> /*, .. */>),
234///    // ..
235///  }
236/// ```
237///
238/// We get something that looks like normal Nickel types, except that each node can also be a
239/// unification variable as well.
240///
241/// ## Motivation 2: recursion schemes
242///
243/// This definition is actually in the style of recursion schemes. Pedantically, `TypeF` (hence the
244/// `F` suffix) is a functor, but the formal details aren't so important: keep in mind that the `F`
245/// suffix means that the recursive occurrences of subtrees (and enum rows and record rows as well)
246/// are replaced by generic parameters.
247///
248/// The usual motivation for recursion schemes is that they allow for elegant and simple definitions
249/// of recursive transformation over trees (here, `TypeF`, and more generally anything with an `F`
250/// suffix) in terms of simple appropriate chaining of `map` and folding/unfolding operations. A
251/// good example is the definition of [Type::traverse]. Although [crate::term::Term] isn't currently
252/// defined using functors per se, the way program transformations are written is in the same style
253/// as recursion schemes: we simply define the action of a transformation as a mapping on the
254/// current node, and let the traversal take care of the plumbing of recursion and reconstruction.
255///
256/// ## Type parameters
257///
258/// - `Ty`: the recursive unfolding of Nickel types
259/// - `RRows`: the recursive unfolding of record rows
260/// - `ERows`: the recursive unfolding of enum rows
261/// - `Te`: the type of a term (used to store contracts)
262#[derive(Clone, PartialEq, Eq, Debug)]
263pub enum TypeF<Ty, RRows, ERows, Te> {
264    /// The dynamic type, or unitype. Assigned to values whose actual type is not statically known
265    /// or checked.
266    Dyn,
267    /// A floating point number.
268    Number,
269    /// A boolean.
270    Bool,
271    /// A string literal.
272    String,
273    /// A symbol.
274    ///
275    /// See [`crate::term::Term::Sealed`].
276    Symbol,
277    /// The type of `Term::ForeignId`.
278    ForeignId,
279    /// A type created from a user-defined contract.
280    Contract(Te),
281    /// A function.
282    Arrow(Ty, Ty),
283    /// A type variable.
284    Var(Ident),
285    /// A forall binder.
286    Forall {
287        var: LocIdent,
288        var_kind: VarKind,
289        body: Ty,
290    },
291
292    /// An enum type, composed of a sequence of enum rows.
293    Enum(ERows),
294    /// A record type, composed of a sequence of record rows.
295    Record(RRows),
296    /// A dictionary type.
297    Dict {
298        type_fields: Ty,
299        flavour: DictTypeFlavour,
300    },
301    /// A parametrized array.
302    Array(Ty),
303    /// A type wildcard, wrapping an ID unique within a given file.
304    Wildcard(usize),
305}
306
307// Concrete, recursive definition of Nickel types from the generic `XxxF` definitions. This is
308// "tying" the knot. We have to put `Box` in the appropriate positions (otherwise, Rust will
309// complain that the type has an infinite size), but also avoid putting in more than necessary.
310//
311// For example, `RecordRows` contains a `RecordRow`. The latter doesn't need to be boxed, because a
312// `RecordRow` itself potentially contains occurrences of `Type` and `RecordRows`, which need to
313// be boxed. Hence, we don't need to additionally box `RecordRow`.
314
315/// Concrete, recursive definition for an enum row.
316pub type EnumRow = EnumRowF<Box<Type>>;
317/// Concrete, recursive definition for enum rows.
318#[derive(Clone, PartialEq, Debug)]
319pub struct EnumRows(pub EnumRowsF<Box<Type>, Box<EnumRows>>);
320/// Concrete, recursive definition for a record row.
321pub type RecordRow = RecordRowF<Box<Type>>;
322#[derive(Clone, PartialEq, Debug)]
323/// Concrete, recursive definition for record rows.
324pub struct RecordRows(pub RecordRowsF<Box<Type>, Box<RecordRows>>);
325
326/// Concrete, recursive type for a Nickel type.
327#[derive(Clone, PartialEq, Debug)]
328pub struct Type {
329    pub typ: TypeF<Box<Type>, RecordRows, EnumRows, RichTerm>,
330    pub pos: TermPos,
331}
332
333impl<Ty, RRows> RecordRowsF<Ty, RRows> {
334    /// Map functions over the children nodes of record rows, when seen as a tree. The mutable
335    /// state ( `S`) is threaded through the calls to the mapped functions. Functions are fallible
336    /// and may return an error `E`, which causes `try_map_state` to return early with the same
337    /// error.
338    ///
339    /// If we put aside the state and the error (see [RecordRowsF::map), this function makes
340    /// `RecordRowsF` a functor (of arity 2). As hinted by the type signature, this function just
341    /// maps on "one-level" of recursion, so to speak. Take the instantiated version `RecordRows`,
342    /// and record rows of the form `{foo : T, bar: U, baz: V}`. Then, calling `try_map_state(f_ty,
343    /// f_rrows, state)` on these rows will map `f_ty` onto `T` and `f_rrows` onto `{bar : U, baz:
344    /// V}`.
345    ///
346    /// Note that `f_ty` isn't mapped onto `U` and `V` recursively: map isn't a recursive
347    /// operation. It's however a building block to express recursive operations: as an example,
348    /// see [RecordRows::traverse].
349    pub fn try_map_state<TyO, RRowsO, FTy, FRRows, S, E>(
350        self,
351        mut f_ty: FTy,
352        mut f_rrows: FRRows,
353        state: &mut S,
354    ) -> Result<RecordRowsF<TyO, RRowsO>, E>
355    where
356        FTy: FnMut(Ty, &mut S) -> Result<TyO, E>,
357        FRRows: FnMut(RRows, &mut S) -> Result<RRowsO, E>,
358    {
359        match self {
360            RecordRowsF::Empty => Ok(RecordRowsF::Empty),
361            RecordRowsF::Extend {
362                row: RecordRowF { id, typ },
363                tail,
364            } => Ok(RecordRowsF::Extend {
365                row: RecordRowF {
366                    id,
367                    typ: f_ty(typ, state)?,
368                },
369                tail: f_rrows(tail, state)?,
370            }),
371            RecordRowsF::TailDyn => Ok(RecordRowsF::TailDyn),
372            RecordRowsF::TailVar(id) => Ok(RecordRowsF::TailVar(id)),
373        }
374    }
375
376    /// Variant of `try_map_state` without threaded state.
377    pub fn try_map<TyO, RRowsO, FTy, FRRows, E>(
378        self,
379        mut f_ty: FTy,
380        mut f_rrows: FRRows,
381    ) -> Result<RecordRowsF<TyO, RRowsO>, E>
382    where
383        FTy: FnMut(Ty) -> Result<TyO, E>,
384        FRRows: FnMut(RRows) -> Result<RRowsO, E>,
385    {
386        let f_ty_lifted = |rrow: Ty, _: &mut ()| -> Result<TyO, E> { f_ty(rrow) };
387        let f_rrows_lifted = |rrows: RRows, _: &mut ()| -> Result<RRowsO, E> { f_rrows(rrows) };
388
389        self.try_map_state(f_ty_lifted, f_rrows_lifted, &mut ())
390    }
391
392    /// Variant of `try_map_state` with infallible functions.
393    pub fn map_state<TyO, RRowsO, FTy, FRRows, S>(
394        self,
395        mut f_ty: FTy,
396        mut f_rrows: FRRows,
397        state: &mut S,
398    ) -> RecordRowsF<TyO, RRowsO>
399    where
400        FTy: FnMut(Ty, &mut S) -> TyO,
401        FRRows: FnMut(RRows, &mut S) -> RRowsO,
402    {
403        let f_ty_lifted = |rrow: Ty, state: &mut S| -> Result<TyO, ()> { Ok(f_ty(rrow, state)) };
404        let f_rrows_lifted =
405            |rrows: RRows, state: &mut S| -> Result<RRowsO, ()> { Ok(f_rrows(rrows, state)) };
406
407        self.try_map_state(f_ty_lifted, f_rrows_lifted, state)
408            .unwrap()
409    }
410
411    /// Variant of `try_map_state` without threaded state and with infallible functions.
412    pub fn map<TyO, RRowsO, FTy, FRRows>(
413        self,
414        mut f_ty: FTy,
415        mut f_rrows: FRRows,
416    ) -> RecordRowsF<TyO, RRowsO>
417    where
418        FTy: FnMut(Ty) -> TyO,
419        FRRows: FnMut(RRows) -> RRowsO,
420    {
421        let f_ty_lifted = |rrow: Ty| -> Result<TyO, Infallible> { Ok(f_ty(rrow)) };
422        let f_rrows_lifted = |rrows: RRows| -> Result<RRowsO, Infallible> { Ok(f_rrows(rrows)) };
423        self.try_map(f_ty_lifted, f_rrows_lifted).unwrap()
424    }
425}
426
427impl<Ty, ERows> EnumRowsF<Ty, ERows> {
428    /// Map functions over the tail of enum rows. The mutable state ( `S`) is threaded through the
429    /// calls to the mapped function. The function is fallible and may return an error `E`, which
430    /// causes `try_map_state` to return early with the same error.
431    ///
432    /// If we put aside the state and the error (see [EnumRowsF::map), this function makes
433    /// `EnumRowsF` a functor. As hinted by the type signature, this function just maps on
434    /// "one-level" of recursion, so to speak. Take the instantiated version `EnumRows`, and
435    /// enum rows of the form ``[| 'foo, 'bar, 'baz |]``. Then, calling `try_map_state(f_erows,
436    /// state)` on these rows will map `f_erows` onto ``[| 'bar, 'baz |]``.
437    ///
438    /// Note that `f_erows` is just mapped once. Map isn't a recursive operation. It's however a
439    /// building block to express recursive operations: as an example, see [RecordRows::traverse].
440    pub fn try_map_state<TyO, ERowsO, FTy, FERows, S, E>(
441        self,
442        mut f_ty: FTy,
443        f_erows: FERows,
444        state: &mut S,
445    ) -> Result<EnumRowsF<TyO, ERowsO>, E>
446    where
447        FTy: FnMut(Ty, &mut S) -> Result<TyO, E>,
448        FERows: FnOnce(ERows, &mut S) -> Result<ERowsO, E>,
449    {
450        match self {
451            EnumRowsF::Empty => Ok(EnumRowsF::Empty),
452            EnumRowsF::Extend {
453                row: EnumRowF { id, typ },
454                tail,
455            } => Ok(EnumRowsF::Extend {
456                row: EnumRowF {
457                    id,
458                    typ: typ.map(|ty| f_ty(ty, state)).transpose()?,
459                },
460                tail: f_erows(tail, state)?,
461            }),
462            EnumRowsF::TailVar(id) => Ok(EnumRowsF::TailVar(id)),
463        }
464    }
465
466    /// Variant of `try_map_state` without threaded state.
467    pub fn try_map<TyO, ERowsO, FTy, FERows, E>(
468        self,
469        mut f_ty: FTy,
470        mut f_erows: FERows,
471    ) -> Result<EnumRowsF<TyO, ERowsO>, E>
472    where
473        FTy: FnMut(Ty) -> Result<TyO, E>,
474        FERows: FnMut(ERows) -> Result<ERowsO, E>,
475    {
476        let f_ty_lifted = |erow: Ty, _: &mut ()| -> Result<TyO, E> { f_ty(erow) };
477        let f_erows_lifted = |erows: ERows, _: &mut ()| -> Result<ERowsO, E> { f_erows(erows) };
478
479        self.try_map_state(f_ty_lifted, f_erows_lifted, &mut ())
480    }
481
482    /// Variant of `try_map_state` with infallible functions.
483    pub fn map_state<TyO, ERowsO, FTy, FERows, S>(
484        self,
485        mut f_ty: FTy,
486        mut f_erows: FERows,
487        state: &mut S,
488    ) -> EnumRowsF<TyO, ERowsO>
489    where
490        FTy: FnMut(Ty, &mut S) -> TyO,
491        FERows: FnMut(ERows, &mut S) -> ERowsO,
492    {
493        let f_ty_lifted = |erow: Ty, state: &mut S| -> Result<TyO, ()> { Ok(f_ty(erow, state)) };
494        let f_erows_lifted =
495            |erows: ERows, state: &mut S| -> Result<ERowsO, ()> { Ok(f_erows(erows, state)) };
496        self.try_map_state(f_ty_lifted, f_erows_lifted, state)
497            .unwrap()
498    }
499
500    /// Variant of `try_map_state` without threaded state and with infallible functions.
501    pub fn map<TyO, ERowsO, FTy, FERows>(
502        self,
503        mut f_ty: FTy,
504        mut f_erows: FERows,
505    ) -> EnumRowsF<TyO, ERowsO>
506    where
507        FTy: FnMut(Ty) -> TyO,
508        FERows: FnMut(ERows) -> ERowsO,
509    {
510        let f_ty_lifted = |erow: Ty| -> Result<TyO, Infallible> { Ok(f_ty(erow)) };
511        let f_erows_lifted = |erows: ERows| -> Result<ERowsO, Infallible> { Ok(f_erows(erows)) };
512
513        self.try_map(f_ty_lifted, f_erows_lifted).unwrap()
514    }
515}
516
517impl<Ty, RRows, ERows, Te> TypeF<Ty, RRows, ERows, Te> {
518    /// Map functions over the children nodes of a type, when seen as a tree. The mutable state (
519    /// `S`) is threaded through the calls to the mapped functions. Functions are fallible and may
520    /// return an error `E`, which causes `try_map_state` to return early with the same error.
521    ///
522    /// If we put aside the state and the error (see [RecordRowsF::map), this function makes
523    /// `TypeF` a functor (of arity 3). As hinted by the type signature, this function just
524    /// maps on "one-level" of recursion, so to speak.
525    ///
526    /// Take the instantiated version `Type`, and a type of the form `(Dyn -> Dyn) -> (Number ->
527    /// Dyn)`. Then, calling `try_map_state(f_ty, ..)` on this type rows will map `f_ty` onto `(Dyn
528    /// -> Dyn)` and `Number -> Dyn` because they are direct children of the root `Arrow` node.
529    ///
530    /// Note that `f_ty` isn't mapped onto `Dyn` and `Number` recursively: map isn't a recursive
531    /// operation. It's however a building block to express recursive operations: as an example,
532    /// see [RecordRows::traverse].
533    ///
534    /// Since `TypeF` may contain record rows and enum rows as well, `f_rrows` and `f_erows` are
535    /// required to know how to map on record and enum types respectively.
536    pub fn try_map_state<TyO, RRowsO, ERowsO, TeO, FTy, FRRows, FERows, FTe, S, E>(
537        self,
538        mut f: FTy,
539        mut f_rrows: FRRows,
540        mut f_erows: FERows,
541        mut f_ctr: FTe,
542        state: &mut S,
543    ) -> Result<TypeF<TyO, RRowsO, ERowsO, TeO>, E>
544    where
545        FTy: FnMut(Ty, &mut S) -> Result<TyO, E>,
546        FRRows: FnMut(RRows, &mut S) -> Result<RRowsO, E>,
547        FERows: FnMut(ERows, &mut S) -> Result<ERowsO, E>,
548        FTe: FnMut(Te, &mut S) -> Result<TeO, E>,
549    {
550        match self {
551            TypeF::Dyn => Ok(TypeF::Dyn),
552            TypeF::Number => Ok(TypeF::Number),
553            TypeF::Bool => Ok(TypeF::Bool),
554            TypeF::String => Ok(TypeF::String),
555            TypeF::ForeignId => Ok(TypeF::ForeignId),
556            TypeF::Symbol => Ok(TypeF::Symbol),
557            TypeF::Contract(t) => Ok(TypeF::Contract(f_ctr(t, state)?)),
558            TypeF::Arrow(dom, codom) => Ok(TypeF::Arrow(f(dom, state)?, f(codom, state)?)),
559            TypeF::Var(i) => Ok(TypeF::Var(i)),
560            TypeF::Forall {
561                var,
562                var_kind,
563                body,
564            } => Ok(TypeF::Forall {
565                var,
566                var_kind,
567                body: f(body, state)?,
568            }),
569            TypeF::Enum(erows) => Ok(TypeF::Enum(f_erows(erows, state)?)),
570            TypeF::Record(rrows) => Ok(TypeF::Record(f_rrows(rrows, state)?)),
571            TypeF::Dict {
572                type_fields,
573                flavour: attrs,
574            } => Ok(TypeF::Dict {
575                type_fields: f(type_fields, state)?,
576                flavour: attrs,
577            }),
578            TypeF::Array(t) => Ok(TypeF::Array(f(t, state)?)),
579            TypeF::Wildcard(i) => Ok(TypeF::Wildcard(i)),
580        }
581    }
582
583    /// Variant of `try_map_state` without threaded state.
584    pub fn try_map<TyO, RRowsO, ERowsO, TeO, FTy, FRRows, FERows, FTe, E>(
585        self,
586        mut f: FTy,
587        mut f_rrows: FRRows,
588        mut f_erows: FERows,
589        mut f_ctr: FTe,
590    ) -> Result<TypeF<TyO, RRowsO, ERowsO, TeO>, E>
591    where
592        FTy: FnMut(Ty) -> Result<TyO, E>,
593        FRRows: FnMut(RRows) -> Result<RRowsO, E>,
594        FERows: FnMut(ERows) -> Result<ERowsO, E>,
595        FTe: FnMut(Te) -> Result<TeO, E>,
596    {
597        let f_lifted = |ty: Ty, _: &mut ()| -> Result<TyO, E> { f(ty) };
598        let f_rrows_lifted = |rrows: RRows, _: &mut ()| -> Result<RRowsO, E> { f_rrows(rrows) };
599        let f_erows_lifted = |erows: ERows, _: &mut ()| -> Result<ERowsO, E> { f_erows(erows) };
600        let f_ctr_lifted = |ctr: Te, _: &mut ()| -> Result<TeO, E> { f_ctr(ctr) };
601
602        self.try_map_state(
603            f_lifted,
604            f_rrows_lifted,
605            f_erows_lifted,
606            f_ctr_lifted,
607            &mut (),
608        )
609    }
610
611    /// Variant of `try_map_state` with infallible functions.
612    pub fn map_state<TyO, RRowsO, ERowsO, TeO, FTy, FRRows, FERows, FTe, S>(
613        self,
614        mut f: FTy,
615        mut f_rrows: FRRows,
616        mut f_erows: FERows,
617        mut f_ctr: FTe,
618        state: &mut S,
619    ) -> TypeF<TyO, RRowsO, ERowsO, TeO>
620    where
621        FTy: FnMut(Ty, &mut S) -> TyO,
622        FRRows: FnMut(RRows, &mut S) -> RRowsO,
623        FERows: FnMut(ERows, &mut S) -> ERowsO,
624        FTe: FnMut(Te, &mut S) -> TeO,
625    {
626        let f_lifted = |ty: Ty, state: &mut S| -> Result<TyO, Infallible> { Ok(f(ty, state)) };
627        let f_rrows_lifted = |rrows: RRows, state: &mut S| -> Result<RRowsO, Infallible> {
628            Ok(f_rrows(rrows, state))
629        };
630        let f_erows_lifted = |erows: ERows, state: &mut S| -> Result<ERowsO, Infallible> {
631            Ok(f_erows(erows, state))
632        };
633        let f_ctr_lifted =
634            |ctr: Te, state: &mut S| -> Result<TeO, Infallible> { Ok(f_ctr(ctr, state)) };
635
636        self.try_map_state(
637            f_lifted,
638            f_rrows_lifted,
639            f_erows_lifted,
640            f_ctr_lifted,
641            state,
642        )
643        .unwrap()
644    }
645
646    /// Variant of `try_map_state` without threaded state and with infallible functions.
647    pub fn map<TyO, RRowsO, ERowsO, TeO, FTy, FRRows, FERows, FTe>(
648        self,
649        mut f: FTy,
650        mut f_rrows: FRRows,
651        mut f_erows: FERows,
652        mut f_ctr: FTe,
653    ) -> TypeF<TyO, RRowsO, ERowsO, TeO>
654    where
655        FTy: FnMut(Ty) -> TyO,
656        FRRows: FnMut(RRows) -> RRowsO,
657        FERows: FnMut(ERows) -> ERowsO,
658        FTe: FnMut(Te) -> TeO,
659    {
660        let f_lifted = |ty: Ty, _: &mut ()| -> TyO { f(ty) };
661        let f_rrows_lifted = |rrows: RRows, _: &mut ()| -> RRowsO { f_rrows(rrows) };
662        let f_erows_lifted = |erows: ERows, _: &mut ()| -> ERowsO { f_erows(erows) };
663        let f_ctr_lifted = |ctr: Te, _: &mut ()| -> TeO { f_ctr(ctr) };
664
665        self.map_state(
666            f_lifted,
667            f_rrows_lifted,
668            f_erows_lifted,
669            f_ctr_lifted,
670            &mut (),
671        )
672    }
673
674    pub fn is_wildcard(&self) -> bool {
675        matches!(self, TypeF::Wildcard(_))
676    }
677
678    pub fn is_contract(&self) -> bool {
679        matches!(self, TypeF::Contract(_))
680    }
681}
682
683impl Traverse<Type> for RecordRows {
684    fn traverse<F, E>(self, f: &mut F, order: TraverseOrder) -> Result<RecordRows, E>
685    where
686        F: FnMut(Type) -> Result<Type, E>,
687    {
688        // traverse keeps track of state in the FnMut function. try_map_state
689        // keeps track of it in a separate state variable. we can pass the
690        // former into the latter by treating the function itself as the state
691        let rows = self.0.try_map_state(
692            |ty, f| Ok(Box::new(ty.traverse(f, order)?)),
693            |rrows, f| Ok(Box::new(rrows.traverse(f, order)?)),
694            f,
695        )?;
696
697        Ok(RecordRows(rows))
698    }
699
700    fn traverse_ref<S, U>(
701        &self,
702        f: &mut dyn FnMut(&Type, &S) -> TraverseControl<S, U>,
703        state: &S,
704    ) -> Option<U> {
705        match &self.0 {
706            RecordRowsF::Extend { row, tail } => row
707                .typ
708                .traverse_ref(f, state)
709                .or_else(|| tail.traverse_ref(f, state)),
710            _ => None,
711        }
712    }
713}
714
715impl Traverse<Type> for EnumRows {
716    fn traverse<F, E>(self, f: &mut F, order: TraverseOrder) -> Result<EnumRows, E>
717    where
718        F: FnMut(Type) -> Result<Type, E>,
719    {
720        // traverse keeps track of state in the FnMut function. try_map_state
721        // keeps track of it in a separate state variable. we can pass the
722        // former into the latter by treating the function itself as the state
723        let rows = self.0.try_map_state(
724            |ty, f| Ok(Box::new(ty.traverse(f, order)?)),
725            |rrows, f| Ok(Box::new(rrows.traverse(f, order)?)),
726            f,
727        )?;
728
729        Ok(EnumRows(rows))
730    }
731
732    fn traverse_ref<S, U>(
733        &self,
734        f: &mut dyn FnMut(&Type, &S) -> TraverseControl<S, U>,
735        state: &S,
736    ) -> Option<U> {
737        match &self.0 {
738            EnumRowsF::Extend { row, tail } => row
739                .typ
740                .as_ref()
741                .and_then(|ty| ty.traverse_ref(f, state))
742                .or_else(|| tail.traverse_ref(f, state)),
743            _ => None,
744        }
745    }
746}
747
748#[derive(Clone, Debug)]
749pub struct UnboundTypeVariableError(pub LocIdent);
750
751impl From<UnboundTypeVariableError> for EvalError {
752    fn from(err: UnboundTypeVariableError) -> Self {
753        let UnboundTypeVariableError(id) = err;
754        let pos = id.pos;
755        EvalError::UnboundIdentifier(id, pos)
756    }
757}
758
759impl From<UnboundTypeVariableError> for TypecheckError {
760    fn from(err: UnboundTypeVariableError) -> Self {
761        use crate::{bytecode::ast::alloc::AstAlloc, error::TypecheckErrorData};
762
763        TypecheckError::new(AstAlloc::new(), |_alloc| {
764            TypecheckErrorData::UnboundTypeVariable(err.0)
765        })
766    }
767}
768
769impl From<UnboundTypeVariableError> for ParseError {
770    fn from(err: UnboundTypeVariableError) -> Self {
771        ParseError::UnboundTypeVariables(vec![err.0])
772    }
773}
774
775impl From<UnboundTypeVariableError> for ParseErrors {
776    fn from(err: UnboundTypeVariableError) -> Self {
777        ParseErrors::from(ParseError::from(err))
778    }
779}
780
781pub struct RecordRowsIterator<'a, Ty, RRows> {
782    pub(crate) rrows: Option<&'a RRows>,
783    pub(crate) ty: std::marker::PhantomData<Ty>,
784}
785
786pub enum RecordRowsIteratorItem<'a, Ty> {
787    TailDyn,
788    TailVar(&'a LocIdent),
789    Row(RecordRowF<&'a Ty>),
790}
791
792impl<'a> Iterator for RecordRowsIterator<'a, Type, RecordRows> {
793    type Item = RecordRowsIteratorItem<'a, Type>;
794
795    fn next(&mut self) -> Option<Self::Item> {
796        self.rrows.and_then(|next| match next.0 {
797            RecordRowsF::Empty => {
798                self.rrows = None;
799                None
800            }
801            RecordRowsF::TailDyn => {
802                self.rrows = None;
803                Some(RecordRowsIteratorItem::TailDyn)
804            }
805            RecordRowsF::TailVar(ref id) => {
806                self.rrows = None;
807                Some(RecordRowsIteratorItem::TailVar(id))
808            }
809            RecordRowsF::Extend { ref row, ref tail } => {
810                self.rrows = Some(tail);
811                Some(RecordRowsIteratorItem::Row(RecordRowF {
812                    id: row.id,
813                    typ: row.typ.as_ref(),
814                }))
815            }
816        })
817    }
818}
819
820pub struct EnumRowsIterator<'a, Ty, ERows> {
821    pub(crate) erows: Option<&'a ERows>,
822    pub(crate) ty: std::marker::PhantomData<Ty>,
823}
824
825pub enum EnumRowsIteratorItem<'a, Ty> {
826    TailVar(&'a LocIdent),
827    Row(EnumRowF<&'a Ty>),
828}
829
830impl<'a> Iterator for EnumRowsIterator<'a, Type, EnumRows> {
831    type Item = EnumRowsIteratorItem<'a, Type>;
832
833    fn next(&mut self) -> Option<Self::Item> {
834        self.erows.and_then(|next| match next.0 {
835            EnumRowsF::Empty => {
836                self.erows = None;
837                None
838            }
839            EnumRowsF::TailVar(ref id) => {
840                self.erows = None;
841                Some(EnumRowsIteratorItem::TailVar(id))
842            }
843            EnumRowsF::Extend { ref row, ref tail } => {
844                self.erows = Some(tail);
845                Some(EnumRowsIteratorItem::Row(EnumRowF {
846                    id: row.id,
847                    typ: row.typ.as_ref().map(AsRef::as_ref),
848                }))
849            }
850        })
851    }
852}
853
854trait Subcontract {
855    /// Return the contract corresponding to a type component of a larger type.
856    ///
857    /// # Arguments
858    ///
859    /// - `vars` is an environment mapping type variables to contracts. Type variables are
860    ///   introduced locally when opening a `forall`. Note that we don't need to keep separate
861    ///   environments for different kind of type variables, as by shadowing, one name can only
862    ///   refer to one type of variable at any given time.
863    /// - `pol` is the current polarity, which is toggled when generating a contract for the
864    ///   argument of an arrow type (see [`crate::label::Label`]).
865    /// - `sy` is a counter used to generate fresh symbols for `forall` contracts (see
866    ///   [`crate::term::Term::Sealed`]).
867    fn subcontract(
868        &self,
869        vars: Environment<Ident, RichTerm>,
870        pol: Polarity,
871        sy: &mut i32,
872    ) -> Result<RichTerm, UnboundTypeVariableError>;
873}
874
875/// Retrieve the contract corresponding to a type variable occurrence in a type as a `RichTerm`.
876/// Helper used by the `subcontract` functions.
877fn get_var_contract(
878    vars: &Environment<Ident, RichTerm>,
879    sym: Ident,
880    pos: TermPos,
881) -> Result<RichTerm, UnboundTypeVariableError> {
882    Ok(vars
883        .get(&sym)
884        .ok_or(UnboundTypeVariableError(LocIdent::from(sym).with_pos(pos)))?
885        .clone())
886}
887
888impl Subcontract for Type {
889    fn subcontract(
890        &self,
891        mut vars: Environment<Ident, RichTerm>,
892        pol: Polarity,
893        sy: &mut i32,
894    ) -> Result<RichTerm, UnboundTypeVariableError> {
895        let ctr = match self.typ {
896            TypeF::Dyn => internals::dynamic(),
897            TypeF::Number => internals::num(),
898            TypeF::Bool => internals::bool(),
899            TypeF::String => internals::string(),
900            TypeF::ForeignId => internals::foreign_id(),
901            // Array Dyn is specialized to array_dyn, which is constant time
902            TypeF::Array(ref ty) if matches!(ty.typ, TypeF::Dyn) => internals::array_dyn(),
903            TypeF::Array(ref ty) => mk_app!(internals::array(), ty.subcontract(vars, pol, sy)?),
904            TypeF::Symbol => panic!("unexpected Symbol type during contract elaboration"),
905            // Similarly, any variant of `A -> B` where either `A` or `B` is `Dyn` get specialized
906            // to the corresponding builtin contract.
907            TypeF::Arrow(ref s, ref t) if matches!((&s.typ, &t.typ), (TypeF::Dyn, TypeF::Dyn)) => {
908                internals::func_dyn()
909            }
910            TypeF::Arrow(ref s, ref t) if matches!(s.typ, TypeF::Dyn) => {
911                mk_app!(internals::func_codom(), t.subcontract(vars, pol, sy)?)
912            }
913            TypeF::Arrow(ref s, ref t) if matches!(t.typ, TypeF::Dyn) => {
914                mk_app!(
915                    internals::func_dom(),
916                    s.subcontract(vars.clone(), pol.flip(), sy)?
917                )
918            }
919            TypeF::Arrow(ref s, ref t) => mk_app!(
920                internals::func(),
921                s.subcontract(vars.clone(), pol.flip(), sy)?,
922                t.subcontract(vars, pol, sy)?
923            ),
924            // Note that we do an early return here.
925            //
926            // All builtin contracts needs an additional wrapping as a `CustomContract`: they're
927            // written as a custom contract but they miss the `%contract/custom%` (mostly because
928            // it's nicer to do it just once at the end than littering the internals module with
929            // `%contract/custom%` applications).
930            //
931            // However, in the case of a contract embedded in a type, we don't want to do the
932            // additional wrapping, as `t` should already be a fully constructed contract.
933            TypeF::Contract(ref t) => return Ok(t.clone()),
934            TypeF::Var(id) => get_var_contract(&vars, id, self.pos)?,
935            TypeF::Forall {
936                ref var,
937                ref body,
938                ref var_kind,
939            } => {
940                let sealing_key = Term::SealingKey(*sy);
941                let contract = match var_kind {
942                    VarKind::Type => mk_app!(internals::forall_var(), sealing_key.clone()),
943                    // For now, the enum contract doesn't enforce parametricity: see the
944                    // implementation of `forall_enum_tail` inside the internal module for more
945                    // details.
946                    VarKind::EnumRows { .. } => internals::forall_enum_tail(),
947                    VarKind::RecordRows { excluded } => {
948                        let excluded_ncl: RichTerm = Term::Array(
949                            Array::from_iter(
950                                excluded
951                                    .iter()
952                                    .map(|id| Term::Str(NickelString::from(*id)).into()),
953                            ),
954                            Default::default(),
955                        )
956                        .into();
957
958                        mk_app!(
959                            internals::forall_record_tail(),
960                            sealing_key.clone(),
961                            excluded_ncl
962                        )
963                    }
964                };
965                vars.insert(var.ident(), contract);
966
967                *sy += 1;
968                mk_app!(
969                    internals::forall(),
970                    sealing_key,
971                    Term::from(pol),
972                    body.subcontract(vars, pol, sy)?
973                )
974            }
975            TypeF::Enum(ref erows) => erows.subcontract(vars, pol, sy)?,
976            TypeF::Record(ref rrows) => rrows.subcontract(vars, pol, sy)?,
977            // `{_: Dyn}` and `{_ | Dyn}` are equivalent, and both specialied to the constant-time
978            // `dict_dyn`.
979            TypeF::Dict {
980                ref type_fields,
981                flavour: _,
982            } if matches!(type_fields.typ, TypeF::Dyn) => internals::dict_dyn(),
983            TypeF::Dict {
984                ref type_fields,
985                flavour: DictTypeFlavour::Contract,
986            } => {
987                mk_app!(
988                    internals::dict_contract(),
989                    type_fields.subcontract(vars, pol, sy)?
990                )
991            }
992            TypeF::Dict {
993                ref type_fields,
994                flavour: DictTypeFlavour::Type,
995            } => {
996                mk_app!(
997                    internals::dict_type(),
998                    type_fields.subcontract(vars, pol, sy)?
999                )
1000            }
1001            TypeF::Wildcard(_) => internals::dynamic(),
1002        };
1003
1004        Ok(mk_term::custom_contract(ctr))
1005    }
1006}
1007
1008impl EnumRows {
1009    /// Find the row with the given identifier in the enum type. Return `None` if there is no such
1010    /// row.
1011    pub fn find_row(&self, id: Ident) -> Option<EnumRow> {
1012        self.iter().find_map(|row_item| match row_item {
1013            EnumRowsIteratorItem::Row(row) if row.id.ident() == id => Some(EnumRow {
1014                id: row.id,
1015                typ: row.typ.cloned().map(Box::new),
1016            }),
1017            _ => None,
1018        })
1019    }
1020
1021    pub fn iter(&self) -> EnumRowsIterator<Type, EnumRows> {
1022        EnumRowsIterator {
1023            erows: Some(self),
1024            ty: std::marker::PhantomData,
1025        }
1026    }
1027
1028    /// Simplify enum rows for contract generation when the rows are part of a static type
1029    /// annotation. See [Type::contract_static].
1030    ///
1031    /// The following simplification are applied:
1032    ///
1033    /// - the type of the argument of each enum variant is simplified as well
1034    /// - if the polarity is positive and the rows are composed entirely of enum tags and enum
1035    ///   variants whose argument's simplified type is `Dyn`, the entire rows are elided by returning
1036    ///   `None`
1037    /// - a tail variable in tail position is currently left unchanged, because it doesn't give
1038    ///   rise to any sealing at runtime currently (see documentation of `$forall_enum_tail` in the
1039    ///   internals module of the stdlib)
1040    fn simplify(
1041        self,
1042        contract_env: &mut Environment<Ident, RichTerm>,
1043        simplify_vars: SimplifyVars,
1044        polarity: Polarity,
1045    ) -> Option<Self> {
1046        // Actual simplification logic. We can elide the initial enum row type if **each**
1047        // individual row can be elided and this enum has an empty tail. Thus, when making
1048        // recursive calls, we can't just return `None` when some subrows can be elided, as we
1049        // might still need them. Instead, `do_simplify` returns a tuple of the simplified rows and
1050        // a boolean indicating if this part can be elided.
1051        fn do_simplify(
1052            rows: EnumRows,
1053            contract_env: &mut Environment<Ident, RichTerm>,
1054            simplify_vars: SimplifyVars,
1055            polarity: Polarity,
1056        ) -> (EnumRows, bool) {
1057            match rows {
1058                EnumRows(EnumRowsF::Empty) => (EnumRows(EnumRowsF::Empty), true),
1059                // Currently, we could actually return `true` here, because the tail contract for
1060                // enums doesn't do anything (see documentation of `$forall_enum_tail` in the
1061                // `internals` module of the stdlib). However, if we ever fix this shortcoming of
1062                // enum tail contracts and actually enforce parametricity, returning `true` would
1063                // become incorrect. So let's be future proof.
1064                EnumRows(EnumRowsF::TailVar(id)) => (EnumRows(EnumRowsF::TailVar(id)), false),
1065                EnumRows(EnumRowsF::Extend { row, tail }) => {
1066                    let (tail, mut elide) =
1067                        do_simplify(*tail, contract_env, simplify_vars.clone(), polarity);
1068
1069                    let typ = row.typ.map(|mut typ| {
1070                        *typ = typ.simplify(contract_env, simplify_vars, polarity);
1071                        elide = elide && matches!(typ.typ, TypeF::Dyn);
1072                        typ
1073                    });
1074
1075                    let row = EnumRowF { id: row.id, typ };
1076
1077                    (
1078                        EnumRows(EnumRowsF::Extend {
1079                            row,
1080                            tail: Box::new(tail),
1081                        }),
1082                        elide,
1083                    )
1084                }
1085            }
1086        }
1087
1088        let (result, elide) = do_simplify(self, contract_env, simplify_vars, polarity);
1089
1090        if matches!((elide, polarity), (true, Polarity::Positive)) {
1091            None
1092        } else {
1093            Some(result)
1094        }
1095    }
1096}
1097
1098impl Subcontract for EnumRows {
1099    fn subcontract(
1100        &self,
1101        vars: Environment<Ident, RichTerm>,
1102        pol: Polarity,
1103        sy: &mut i32,
1104    ) -> Result<RichTerm, UnboundTypeVariableError> {
1105        use crate::term::{
1106            pattern::{EnumPattern, Pattern, PatternData},
1107            BinaryOp,
1108        };
1109
1110        let mut branches = Vec::new();
1111        let mut tail_var = None;
1112
1113        let value_arg = LocIdent::fresh();
1114        let label_arg = LocIdent::fresh();
1115        // We don't need to generate a different fresh variable for each match branch, as they have
1116        // their own scope, so we use the same name instead.
1117        let variant_arg = LocIdent::fresh();
1118
1119        // We build a match where each row corresponds to a branch, such that:
1120        //
1121        // - if the row is a simple enum tag, we just return the original contract argument
1122        // - if the row is an enum variant, we extract the argument and apply the corresponding
1123        //   contract to it
1124        //
1125        // For the default branch, depending on the tail:
1126        //
1127        // - if the tail is an enum type variable, we perform the required sealing/unsealing
1128        // - otherwise, if the enum type is closed, we add a default case which blames
1129        //
1130        // For example, for an enum type [| 'foo, 'bar, 'Baz T |], the function looks like:
1131        //
1132        // ```
1133        // fun label value =>
1134        //   value |> match {
1135        //     'foo => 'Ok x,
1136        //     'bar => 'Ok x,
1137        //     'Baz variant_arg => 'Ok ('Baz (%apply_contract% T label_arg variant_arg)),
1138        //     _ => $enum_fail l
1139        //   }
1140        // ```
1141        for row in self.iter() {
1142            match row {
1143                EnumRowsIteratorItem::Row(row) => {
1144                    let arg_pattern = row.typ.as_ref().map(|_| {
1145                        Box::new(Pattern {
1146                            data: PatternData::Any(variant_arg),
1147                            alias: None,
1148                            pos: TermPos::None,
1149                        })
1150                    });
1151
1152                    let body = if let Some(ty) = row.typ.as_ref() {
1153                        // 'Tag (%apply_contract% T label_arg variant_arg)
1154                        let arg = mk_app!(
1155                            mk_term::op2(
1156                                BinaryOp::ContractApply,
1157                                ty.subcontract(vars.clone(), pol, sy)?,
1158                                mk_term::var(label_arg)
1159                            ),
1160                            mk_term::var(variant_arg)
1161                        );
1162
1163                        mk_term::enum_variant(row.id, arg)
1164                    } else {
1165                        mk_term::var(value_arg)
1166                    };
1167
1168                    let body = mk_term::enum_variant("Ok", body);
1169
1170                    let pattern = Pattern {
1171                        data: PatternData::Enum(EnumPattern {
1172                            tag: row.id,
1173                            pattern: arg_pattern,
1174                            pos: row.id.pos,
1175                        }),
1176                        alias: None,
1177                        pos: row.id.pos,
1178                    };
1179
1180                    branches.push(MatchBranch {
1181                        pattern,
1182                        guard: None,
1183                        body,
1184                    });
1185                }
1186                EnumRowsIteratorItem::TailVar(var) => {
1187                    tail_var = Some(var);
1188                }
1189            }
1190        }
1191
1192        let (default, default_pos) = if let Some(var) = tail_var {
1193            (
1194                mk_app!(
1195                    mk_term::op2(
1196                        BinaryOp::ContractApply,
1197                        get_var_contract(&vars, var.ident(), var.pos)?,
1198                        mk_term::var(label_arg)
1199                    ),
1200                    mk_term::var(value_arg)
1201                ),
1202                var.pos,
1203            )
1204        } else {
1205            (
1206                mk_app!(internals::enum_fail(), mk_term::var(label_arg)),
1207                TermPos::None,
1208            )
1209        };
1210
1211        branches.push(MatchBranch {
1212            pattern: Pattern {
1213                data: PatternData::Wildcard,
1214                alias: None,
1215                pos: default_pos,
1216            },
1217            guard: None,
1218            body: default,
1219        });
1220
1221        // We pre-compile the match expression, so that it's not compiled again and again at each
1222        // application of the contract.
1223        let match_expr = MatchData { branches }.compile(mk_term::var(value_arg), TermPos::None);
1224
1225        let case = mk_fun!(label_arg, value_arg, match_expr);
1226        Ok(mk_app!(internals::enumeration(), case))
1227    }
1228}
1229
1230impl RecordRows {
1231    /// Find a nested binding in a record row type. The nested field is given as a list of
1232    /// successive fields, that is, as a path. Return `None` if there is no such binding.
1233    ///
1234    /// # Example
1235    ///
1236    /// - self: ` {a : {b : Number }}`
1237    /// - path: `["a", "b"]`
1238    /// - result: `Some(Number)`
1239    pub fn find_path(&self, path: &[Ident]) -> Option<RecordRow> {
1240        if path.is_empty() {
1241            return None;
1242        }
1243
1244        // While going through the record rows, we use this helper for recursion instead of
1245        // `find_path`, to avoid cloning a lot of intermediate rows, and rather only clone the
1246        // final one to return.
1247        fn find_path_ref<'a>(
1248            rrows: &'a RecordRows,
1249            path: &[Ident],
1250        ) -> Option<RecordRowF<&'a Type>> {
1251            let next = rrows.iter().find_map(|item| match item {
1252                RecordRowsIteratorItem::Row(row) if row.id.ident() == path[0] => Some(row.clone()),
1253                _ => None,
1254            });
1255
1256            if path.len() == 1 {
1257                next
1258            } else {
1259                match next.map(|row| &row.typ.typ) {
1260                    Some(TypeF::Record(rrows)) => find_path_ref(rrows, &path[1..]),
1261                    _ => None,
1262                }
1263            }
1264        }
1265
1266        find_path_ref(self, path).map(|row| RecordRow {
1267            id: row.id,
1268            typ: Box::new(row.typ.clone()),
1269        })
1270    }
1271
1272    /// Find the row with the given identifier in the record type. Return `None` if there is no such
1273    /// row.
1274    ///
1275    /// Equivalent to `find_path(&[id])`.
1276    pub fn find_row(&self, id: Ident) -> Option<RecordRow> {
1277        self.find_path(&[id])
1278    }
1279
1280    pub fn iter(&self) -> RecordRowsIterator<Type, RecordRows> {
1281        RecordRowsIterator {
1282            rrows: Some(self),
1283            ty: std::marker::PhantomData,
1284        }
1285    }
1286
1287    /// Simplify record rows for contract generation when the rows are part of a static type
1288    /// annotation. See [Type::contract_static].
1289    ///
1290    /// The following simplifications are applied:
1291    ///
1292    /// - the type of each field is simplified
1293    /// - if the polarity is positive and the tail is known to be simplified to `Dyn`, any field
1294    ///   whose simplified type is `Dyn` is entirely elided from the final result. That is, `{foo :
1295    ///   Number, bar : Number -> Number}` in positive position is simplified to `{bar : Number ->
1296    ///   Dyn; Dyn}`. `{foo : Number, bar : Dyn}` is simplified to `{; Dyn}` which is simplified
1297    ///   further to `Dyn` by [Type::simplify].
1298    ///
1299    ///   We can't do that when the tail can't be simplified to `Dyn` (this is the case when the
1300    ///   tail variable has been introduced by a forall in negative position).
1301    /// - The case of a tail variable is a bit more complex and is detailed below.
1302    ///
1303    /// # Simplification of tail variable
1304    ///
1305    /// The following paragraphs distinguish between a tail variable (and thus an enclosing record
1306    /// type) in positive position and a tail variable in negative position (the polarity of the
1307    /// introducing forall is yet another dimension, covered in each paragraph).
1308    ///
1309    /// ## Negative polarity
1310    ///
1311    /// The simplification of record row variables is made harder by the presence of excluded
1312    /// fields. It's tempting to replace all `r` in tail position by `Dyn` if the introducing
1313    /// `forall r` was in positive position, as we do for regular type variables, but it's
1314    /// unfortunately not sound. For example:
1315    ///
1316    /// ```nickel
1317    /// inject_meta: forall r. {; r} -> {meta : String; r}
1318    /// ```
1319    ///
1320    /// In this case, the `forall r` is in positive position, but the `r` in `{; r}` in negative
1321    /// position can't be entirely elided, because it checks that `meta` isn't already present in
1322    /// the original argument. In particular, the original contract rejects `inject_meta {meta =
1323    /// "hello"}`, but blindly simplifying the type of `inject_meta` to `{; Dyn} -> {; Dyn}`
1324    /// would allow this argument.
1325    ///
1326    /// We can simplify it to something like `{; $forall_record_tail_excluded_only ["meta"]} -> {;
1327    /// Dyn}` (further simplified to `{; $forall_record_tail_excluded_only ["meta"]} -> Dyn` by
1328    /// `Type::simplify`). `$forall_record_tail_excluded_only` is a specialized version of the
1329    /// record tail contract `$forall_record_tail$ which doesn't seal but still check for the
1330    /// absence of excluded fields.
1331    ///
1332    /// Note that we can't actually put an arbitrary contract in the tail of a record type. This is
1333    /// why the `simplify()` methods take a mutable reference to an environment `contract_env`
1334    /// which will be passed to the contract generation methods. What we do in practice is to bind
1335    /// a fresh record row variable to `$forall_record_tail_excluded_only ["meta"]` in this
1336    /// environment and we substitute the tail var for this fresh variable.
1337    ///
1338    /// On the other hand, in
1339    ///
1340    /// ```nickel
1341    /// update_meta: forall r. {meta : String; r} -> {meta : String; r}
1342    /// ```
1343    ///
1344    /// The contract can be simplified to `{meta : String; Dyn} -> Dyn`. To detect this case, we
1345    /// record the set of fields that are present in the current rows and check if this set is
1346    /// equal to the excluded fields for this row variable (it's always a subset of the excluded
1347    /// fields by definition of row constraints), or if there are some excluded fields left to
1348    /// check.
1349    ///
1350    /// ## Positive polarity
1351    ///
1352    /// In a positive position, we can replace the tail with `Dyn` if the tail isn't a variable
1353    /// introduced in negative position, because a typed term can never violate row constraints.
1354    ///
1355    /// For a variable introduced by a forall in negative position, we need to keep the unsealing
1356    /// operation to make sure the corresponding forall type doesn't blame. But the unsealing
1357    /// operation needs prior sealing to work out, so we need to keep the tail even for record
1358    /// types in positive position. What's more, _we can't elide any field in this case_. Indeed,
1359    /// eliding fields can change what goes in the tail, and can only be done when the tail is
1360    /// ensured to be `Dyn`.
1361    fn simplify(
1362        self,
1363        contract_env: &mut Environment<Ident, RichTerm>,
1364        simplify_vars: SimplifyVars,
1365        polarity: Polarity,
1366    ) -> Self {
1367        // This helper does a first traversal of record rows in order to peek the tail and check if
1368        // it's a variable introduced by a forall in negative position. In this case, the second
1369        // component of the result will be `false` and we can't elide any field during the actual
1370        // simplification.
1371        //
1372        // As we will need the set of all fields later, we build during this first traversal as well.
1373        fn peek_tail(rrows: &RecordRows, simplify_vars: &SimplifyVars) -> (HashSet<Ident>, bool) {
1374            let mut fields = HashSet::new();
1375            let mut can_elide = true;
1376
1377            for row in rrows.iter() {
1378                match row {
1379                    RecordRowsIteratorItem::Row(row) => {
1380                        fields.insert(row.id.ident());
1381                    }
1382                    RecordRowsIteratorItem::TailVar(id)
1383                        if simplify_vars.rrows_vars_elide.get(&id.ident()).is_none() =>
1384                    {
1385                        can_elide = false;
1386                    }
1387                    _ => {}
1388                }
1389            }
1390
1391            (fields, can_elide)
1392        }
1393
1394        // Actual simplification logic, which additionally remembers if a field has been elided and
1395        // if we thus need to change the tail to `Dyn` to account for the elision.
1396        fn do_simplify(
1397            rrows: RecordRows,
1398            contract_env: &mut Environment<Ident, RichTerm>,
1399            simplify_vars: SimplifyVars,
1400            polarity: Polarity,
1401            fields: &HashSet<Ident>,
1402            can_elide: bool,
1403        ) -> RecordRows {
1404            let rrows = match rrows.0 {
1405                // Because we may have elided some fields, we always make this record type open
1406                // when in positive position, which never hurts
1407                RecordRowsF::Empty if matches!(polarity, Polarity::Positive) => {
1408                    RecordRowsF::TailDyn
1409                }
1410                RecordRowsF::Empty => RecordRowsF::Empty,
1411                RecordRowsF::Extend { row, tail } => {
1412                    let typ_simplified =
1413                        row.typ
1414                            .simplify(contract_env, simplify_vars.clone(), polarity);
1415                    // If we are in a positive position, the simplified type of the field is `Dyn`,
1416                    // and we can elide fields given the current tail of these rows, we get rid of
1417                    // the whole field.
1418                    let elide_field = matches!(
1419                        (&typ_simplified.typ, polarity),
1420                        (TypeF::Dyn, Polarity::Positive)
1421                    ) && can_elide;
1422
1423                    let row = RecordRow {
1424                        id: row.id,
1425                        typ: Box::new(typ_simplified),
1426                    };
1427
1428                    let tail = Box::new(do_simplify(
1429                        *tail,
1430                        contract_env,
1431                        simplify_vars,
1432                        polarity,
1433                        fields,
1434                        can_elide,
1435                    ));
1436
1437                    if elide_field {
1438                        tail.0
1439                    } else {
1440                        RecordRowsF::Extend { row, tail }
1441                    }
1442                }
1443                RecordRowsF::TailDyn => RecordRowsF::TailDyn,
1444                RecordRowsF::TailVar(id) => {
1445                    let excluded = simplify_vars.rrows_vars_elide.get(&id.ident());
1446
1447                    match (excluded, polarity) {
1448                        // If the variable was introduced in positive position, it won't cause any
1449                        // (un)sealing. So if we are in positive position as well, we can just get
1450                        // rid of the tail
1451                        (Some(_), Polarity::Positive) => RecordRowsF::TailDyn,
1452                        // If the variable was introduced in negative position, we can't elide the
1453                        // unsealing contracts which are in negative position as well, and thus
1454                        // neither elide the corresponding sealing contract.
1455                        (None, Polarity::Positive | Polarity::Negative) => RecordRowsF::TailVar(id),
1456                        // If the variable was introduced by a forall in positive position and we
1457                        // are in a negative position, we can do some simplifying and at least get
1458                        // rid of the sealing operation, and sometimes of the whole tail contract.
1459                        (Some(excluded), Polarity::Negative) => {
1460                            // We don't have to check for the absence of fields that appear in this row
1461                            // type
1462                            let excluded = excluded - fields;
1463
1464                            // If all the excluded fields are listed in the record type, the
1465                            // contract will never blame on the presence of an excluded field, so
1466                            // we can get rid of the tail altogether
1467                            if excluded.is_empty() {
1468                                RecordRowsF::TailDyn
1469                            }
1470                            // Otherwise, we need to check for the absence of the remaining excluded
1471                            // fields with a specialized tail contract which doesn't seal the tail.
1472                            else {
1473                                let fresh_var = LocIdent::fresh();
1474
1475                                let excluded_ncl: RichTerm = Term::Array(
1476                                    Array::from_iter(
1477                                        excluded
1478                                            .iter()
1479                                            .map(|id| Term::Str(NickelString::from(*id)).into()),
1480                                    ),
1481                                    Default::default(),
1482                                )
1483                                .into();
1484
1485                                contract_env.insert(
1486                                    fresh_var.ident(),
1487                                    mk_app!(
1488                                        internals::forall_record_tail_excluded_only(),
1489                                        excluded_ncl
1490                                    ),
1491                                );
1492
1493                                RecordRowsF::TailVar(fresh_var)
1494                            }
1495                        }
1496                    }
1497                }
1498            };
1499
1500            RecordRows(rrows)
1501        }
1502
1503        let (fields, can_elide) = peek_tail(&self, &simplify_vars);
1504        do_simplify(
1505            self,
1506            contract_env,
1507            simplify_vars,
1508            polarity,
1509            &fields,
1510            can_elide,
1511        )
1512    }
1513}
1514
1515impl Subcontract for RecordRows {
1516    fn subcontract(
1517        &self,
1518        vars: Environment<Ident, RichTerm>,
1519        pol: Polarity,
1520        sy: &mut i32,
1521    ) -> Result<RichTerm, UnboundTypeVariableError> {
1522        // We begin by building a record whose arguments are contracts
1523        // derived from the types of the statically known fields.
1524        let mut rrows = self;
1525        let mut fcs = IndexMap::new();
1526
1527        while let RecordRowsF::Extend {
1528            row: RecordRowF { id, typ: ty },
1529            tail,
1530        } = &rrows.0
1531        {
1532            fcs.insert(*id, ty.subcontract(vars.clone(), pol, sy)?);
1533            rrows = tail
1534        }
1535
1536        let has_tail = !matches!(&rrows.0, RecordRowsF::Empty);
1537        // Now that we've dealt with the row extends, we just need to
1538        // work out the tail.
1539        let tail = match &rrows.0 {
1540            RecordRowsF::Empty => internals::empty_tail(),
1541            RecordRowsF::TailDyn => internals::dyn_tail(),
1542            RecordRowsF::TailVar(id) => get_var_contract(&vars, id.ident(), id.pos)?,
1543            // Safety: the while above excludes that `tail` can have the form `Extend`.
1544            RecordRowsF::Extend { .. } => unreachable!(),
1545        };
1546
1547        let rec = RichTerm::from(Term::Record(RecordData::with_field_values(fcs)));
1548
1549        Ok(mk_app!(
1550            internals::record_type(),
1551            rec,
1552            tail,
1553            Term::Bool(has_tail)
1554        ))
1555    }
1556}
1557
1558impl From<TypeF<Box<Type>, RecordRows, EnumRows, RichTerm>> for Type {
1559    fn from(typ: TypeF<Box<Type>, RecordRows, EnumRows, RichTerm>) -> Self {
1560        Type {
1561            typ,
1562            pos: TermPos::None,
1563        }
1564    }
1565}
1566
1567impl Type {
1568    /// Creates a `Type` with the specified position
1569    pub fn with_pos(self, pos: TermPos) -> Type {
1570        Type { pos, ..self }
1571    }
1572
1573    /// Returns the same type with the position cleared (set to `None`).
1574    ///
1575    /// This is currently only used in test code, but because it's used from integration
1576    /// tests we cannot hide it behind cfg(test).
1577    pub fn without_pos(self) -> Type {
1578        self.traverse(
1579            &mut |t: Type| {
1580                Ok::<_, Infallible>(Type {
1581                    pos: TermPos::None,
1582                    ..t
1583                })
1584            },
1585            TraverseOrder::BottomUp,
1586        )
1587        .unwrap()
1588        .traverse(
1589            &mut |t: RichTerm| {
1590                Ok::<_, Infallible>(RichTerm {
1591                    pos: TermPos::None,
1592                    ..t
1593                })
1594            },
1595            TraverseOrder::BottomUp,
1596        )
1597        .unwrap()
1598    }
1599
1600    /// Variant of [Self::contract] that returns the contract corresponding to a type which appears
1601    /// in a static type annotation. [Self::contract_static] uses the fact that the checked term
1602    /// has been typechecked to optimize the generated contract thanks to the guarantee of static
1603    /// typing.
1604    pub fn contract_static(self) -> Result<RichTerm, UnboundTypeVariableError> {
1605        let mut sy = 0;
1606        let mut contract_env = Environment::new();
1607
1608        self.simplify(&mut contract_env, SimplifyVars::new(), Polarity::Positive)
1609            .subcontract(contract_env, Polarity::Positive, &mut sy)
1610    }
1611
1612    /// Return the contract corresponding to a type. Said contract must then be applied using the
1613    /// `ApplyContract` primitive operation.
1614    pub fn contract(&self) -> Result<RichTerm, UnboundTypeVariableError> {
1615        increment!(format!("gen_contract:{}", self.pretty_print_cap(40)));
1616
1617        let mut sy = 0;
1618
1619        self.subcontract(Environment::new(), Polarity::Positive, &mut sy)
1620    }
1621
1622    /// Returns true if this type is a function type (including a polymorphic one), false
1623    /// otherwise.
1624    pub fn is_function_type(&self) -> bool {
1625        match &self.typ {
1626            TypeF::Forall { body, .. } => body.is_function_type(),
1627            TypeF::Arrow(..) => true,
1628            _ => false,
1629        }
1630    }
1631
1632    /// Determine if a type is an atom, that is a either a primitive type (`Dyn`, `Number`, etc.) or
1633    /// a type delimited by specific markers (such as a row type). Used in formatting to decide if
1634    /// parentheses need to be inserted during pretty pretting.
1635    pub fn fmt_is_atom(&self) -> bool {
1636        match &self.typ {
1637            TypeF::Dyn
1638            | TypeF::Number
1639            | TypeF::Bool
1640            | TypeF::String
1641            | TypeF::Var(_)
1642            | TypeF::Record(_)
1643            | TypeF::Enum(_) => true,
1644            TypeF::Contract(rt) if rt.as_ref().is_atom() => true,
1645            _ => false,
1646        }
1647    }
1648
1649    /// Searches for a `TypeF::Contract`. If one is found, returns the term it contains.
1650    pub fn find_contract(&self) -> Option<RichTerm> {
1651        self.find_map(|ty: &Type| match &ty.typ {
1652            TypeF::Contract(f) => Some(f.clone()),
1653            _ => None,
1654        })
1655    }
1656
1657    /// Static typing guarantees make some of the contract checks useless, assuming that blame
1658    /// safety holds. This function simplifies `self` for contract generation, assuming it is part
1659    /// of a static type annotation, by eliding some of these useless subcontracts.
1660    ///
1661    /// # Simplifications
1662    ///
1663    /// - `forall`s of a type variable in positive positions are removed, and the corresponding type
1664    ///   variable is substituted for a `Dyn` contract. In consequence, [Self::contract()] will generate
1665    ///   simplified contracts as well. For example, `forall a. Array a -> a` becomes `Array Dyn -> Dyn`,
1666    ///   and `Array Dyn` will then be specialized to `$array_dyn` which has a constant-time overhead
1667    ///   (while `Array a` is linear in the size of the array). The final contract will just check that
1668    ///   the argument is an array.
1669    /// - `forall`s of a row variable (either record or enum) are removed as well. The substitution of
1670    ///   the corresponding row variable is a bit more complex than in the type variable case and depends
1671    ///   on the situation. See [RecordRows::simplify] and [EnumRows::simplify] for more details.
1672    /// - `forall`s in negative position are left unchanged.
1673    /// - All positive occurrences of type constructors that aren't a function type are recursively
1674    ///   simplified. If they are simplified to a trivial type, such as `{; Dyn}` for a record or `Array
1675    ///   Dyn` for an array, they are further simplified to `Dyn`.
1676    ///   are turned to `Dyn` contracts. However, we must be careful here: type constructors might
1677    ///   have function types somewhere inside, as in `{foo : Number, bar : Array (String ->
1678    ///   String) }`. In this case, we can elide `foo`, but not `bar`.
1679    fn simplify(
1680        self,
1681        contract_env: &mut Environment<Ident, RichTerm>,
1682        mut simplify_vars: SimplifyVars,
1683        polarity: Polarity,
1684    ) -> Self {
1685        let mut pos = self.pos;
1686
1687        let simplified = match self.typ {
1688            TypeF::Arrow(dom, codom) => TypeF::Arrow(
1689                Box::new(dom.simplify(contract_env, simplify_vars.clone(), polarity.flip())),
1690                Box::new(codom.simplify(contract_env, simplify_vars, polarity)),
1691            ),
1692            TypeF::Forall {
1693                var,
1694                var_kind: VarKind::Type,
1695                body,
1696            } if polarity == Polarity::Positive => {
1697                // The case of type variables is the simplest: we just replace all of them by
1698                // `Dyn`. We don't bother messing with the contract environment and only register
1699                // them in `simplify_vars`. Subsequent calls to `simplify` will check the register
1700                // and replace the variable by `Dyn` when appropriate.
1701                simplify_vars.type_vars_elide.insert(var.ident(), ());
1702
1703                let result = body.simplify(contract_env, simplify_vars, polarity);
1704                // we keep the position of the body, not the one of the forall
1705                pos = result.pos;
1706                result.typ
1707            }
1708            TypeF::Forall {
1709                var,
1710                var_kind: VarKind::RecordRows { excluded },
1711                body,
1712            } if polarity == Polarity::Positive => {
1713                simplify_vars.rrows_vars_elide.insert(var.ident(), excluded);
1714
1715                let result = body.simplify(contract_env, simplify_vars, polarity);
1716                // we keep the position of the body, not the one of the forall
1717                pos = result.pos;
1718                result.typ
1719            }
1720            // For enum rows in negative position, we don't any simplification for now because
1721            // parametricity isn't enforced. Polymorphic enum row contracts are mostly doing
1722            // nothing, and it's fine to keep them as they are. See the documentation of
1723            // `$forall_enum_tail` for more details. Even if we do enforce parametricity in the
1724            // future, we will be missing an optimization opportunity here but we won't introduce
1725            // any unsoundness.
1726            //
1727            // We don't elide foralls in negative position either.
1728            //
1729            // In both cases, we still simplify the body of the forall.
1730            TypeF::Forall {
1731                var,
1732                var_kind,
1733                body,
1734            } => TypeF::Forall {
1735                var,
1736                var_kind,
1737                body: Box::new(body.simplify(contract_env, simplify_vars, polarity)),
1738            },
1739            TypeF::Var(id) if simplify_vars.type_vars_elide.get(&id).is_some() => TypeF::Dyn,
1740            // Any ground type in positive position can be entirely elided
1741            TypeF::Number | TypeF::String | TypeF::Bool | TypeF::Symbol | TypeF::ForeignId
1742                if matches!(polarity, Polarity::Positive) =>
1743            {
1744                TypeF::Dyn
1745            }
1746            // We need to recurse into type constructors, which could contain arrow types inside.
1747            TypeF::Record(rrows) => {
1748                let rrows_simplified = rrows.simplify(contract_env, simplify_vars, polarity);
1749
1750                // [^simplify-type-constructor-positive]: if we are in positive position, and the
1751                // record type is entirely elided (which means simplified to `{; Dyn}`) or is
1752                // empty, we can simplify it further to `Dyn`. This works for other type
1753                // constructors as well (arrays, dictionaries, etc.): if there's no negative type
1754                // or polymorphic tail somewhere inside, we can get rid of the whole thing.
1755                if matches!(
1756                    (&rrows_simplified, polarity),
1757                    (
1758                        RecordRows(RecordRowsF::TailDyn) | RecordRows(RecordRowsF::Empty),
1759                        Polarity::Positive
1760                    )
1761                ) {
1762                    TypeF::Dyn
1763                } else {
1764                    TypeF::Record(rrows_simplified)
1765                }
1766            }
1767            TypeF::Enum(erows) => {
1768                let erows = erows.simplify(contract_env, simplify_vars, polarity);
1769
1770                // See [^simplify-type-constructor-positive]
1771                erows.map(TypeF::Enum).unwrap_or(TypeF::Dyn)
1772            }
1773            TypeF::Dict {
1774                type_fields,
1775                flavour,
1776            } => {
1777                let type_fields =
1778                    Box::new(type_fields.simplify(contract_env, simplify_vars, polarity));
1779
1780                // See [^simplify-type-constructor-positive]
1781                if matches!(
1782                    (&type_fields.typ, polarity),
1783                    (TypeF::Dyn, Polarity::Positive)
1784                ) {
1785                    TypeF::Dyn
1786                } else {
1787                    TypeF::Dict {
1788                        type_fields,
1789                        flavour,
1790                    }
1791                }
1792            }
1793            TypeF::Array(t) => {
1794                let type_elts = t.simplify(contract_env, simplify_vars, polarity);
1795
1796                // See [^simplify-type-constructor-positive]
1797                if matches!((&type_elts.typ, polarity), (TypeF::Dyn, Polarity::Positive)) {
1798                    TypeF::Dyn
1799                } else {
1800                    TypeF::Array(Box::new(type_elts))
1801                }
1802            }
1803            // All the remaining cases are ground types that don't contain subtypes, or they are
1804            // type variables that can't be elided. We leave them unchanged
1805            t => t,
1806        };
1807
1808        Type {
1809            typ: simplified,
1810            pos,
1811        }
1812    }
1813}
1814
1815impl Traverse<Type> for Type {
1816    fn traverse<F, E>(self, f: &mut F, order: TraverseOrder) -> Result<Self, E>
1817    where
1818        F: FnMut(Type) -> Result<Type, E>,
1819    {
1820        let pre_map = match order {
1821            TraverseOrder::TopDown => f(self)?,
1822            TraverseOrder::BottomUp => self,
1823        };
1824
1825        // traverse keeps track of state in the FnMut function. try_map_state
1826        // keeps track of it in a separate state variable. we can pass the
1827        // former into the latter by treating the function itself as the state
1828        let typ = pre_map.typ.try_map_state(
1829            |ty, f| Ok(Box::new(ty.traverse(f, order)?)),
1830            |rrows, f| rrows.traverse(f, order),
1831            |erows, f| erows.traverse(f, order),
1832            |ctr, _| Ok(ctr),
1833            f,
1834        )?;
1835
1836        let post_map = Type { typ, ..pre_map };
1837
1838        match order {
1839            TraverseOrder::TopDown => Ok(post_map),
1840            TraverseOrder::BottomUp => f(post_map),
1841        }
1842    }
1843
1844    fn traverse_ref<S, U>(
1845        &self,
1846        f: &mut dyn FnMut(&Type, &S) -> TraverseControl<S, U>,
1847        state: &S,
1848    ) -> Option<U> {
1849        let child_state = match f(self, state) {
1850            TraverseControl::Continue => None,
1851            TraverseControl::ContinueWithScope(s) => Some(s),
1852            TraverseControl::SkipBranch => {
1853                return None;
1854            }
1855            TraverseControl::Return(ret) => {
1856                return Some(ret);
1857            }
1858        };
1859        let state = child_state.as_ref().unwrap_or(state);
1860
1861        match &self.typ {
1862            TypeF::Dyn
1863            | TypeF::Number
1864            | TypeF::Bool
1865            | TypeF::String
1866            | TypeF::ForeignId
1867            | TypeF::Symbol
1868            | TypeF::Var(_)
1869            | TypeF::Wildcard(_) => None,
1870            TypeF::Contract(rt) => rt.traverse_ref(f, state),
1871            TypeF::Arrow(t1, t2) => t1
1872                .traverse_ref(f, state)
1873                .or_else(|| t2.traverse_ref(f, state)),
1874            TypeF::Forall { body: t, .. }
1875            | TypeF::Dict { type_fields: t, .. }
1876            | TypeF::Array(t) => t.traverse_ref(f, state),
1877            TypeF::Record(rrows) => rrows.traverse_ref(f, state),
1878            TypeF::Enum(erows) => erows.traverse_ref(f, state),
1879        }
1880    }
1881}
1882
1883impl Traverse<RichTerm> for Type {
1884    fn traverse<F, E>(self, f: &mut F, order: TraverseOrder) -> Result<Self, E>
1885    where
1886        F: FnMut(RichTerm) -> Result<RichTerm, E>,
1887    {
1888        self.traverse(
1889            &mut |ty: Type| match ty.typ {
1890                TypeF::Contract(t) => t
1891                    .traverse(f, order)
1892                    .map(|t| Type::from(TypeF::Contract(t)).with_pos(ty.pos)),
1893                _ => Ok(ty),
1894            },
1895            order,
1896        )
1897    }
1898
1899    fn traverse_ref<S, U>(
1900        &self,
1901        f: &mut dyn FnMut(&RichTerm, &S) -> TraverseControl<S, U>,
1902        state: &S,
1903    ) -> Option<U> {
1904        self.traverse_ref(
1905            &mut |ty: &Type, s: &S| match &ty.typ {
1906                TypeF::Contract(t) => {
1907                    if let Some(ret) = t.traverse_ref(f, s) {
1908                        TraverseControl::Return(ret)
1909                    } else {
1910                        TraverseControl::SkipBranch
1911                    }
1912                }
1913                _ => TraverseControl::Continue,
1914            },
1915            state,
1916        )
1917    }
1918}
1919
1920/// Some context storing various type variables that can be simplified during static contract
1921/// simplification (see the various `simplify()` methods).
1922#[derive(Clone, Debug, Default)]
1923struct SimplifyVars {
1924    /// Environment used as a persistent HashSet to record type variables that can be elided
1925    /// (replaced by `Dyn`) because they were introduced by a forall in positive position.
1926    type_vars_elide: Environment<Ident, ()>,
1927    /// Record record rows variables that were introduced by a forall in positive position and
1928    /// their corresponding `excluded` field. They will be substituted for different simplified
1929    /// contracts depending on where the variable appears and the surrounding record.
1930    rrows_vars_elide: Environment<Ident, HashSet<Ident>>,
1931}
1932
1933impl SimplifyVars {
1934    fn new() -> Self {
1935        Self::default()
1936    }
1937}
1938
1939impl_display_from_pretty!(Type);
1940impl_display_from_pretty!(EnumRow);
1941impl_display_from_pretty!(EnumRows);
1942impl_display_from_pretty!(RecordRow);
1943impl_display_from_pretty!(RecordRows);
1944
1945impl PrettyPrintCap for Type {}
1946
1947#[cfg(test)]
1948mod tests {
1949    use super::*;
1950    use crate::parser::{grammar::FixedTypeParser, lexer::Lexer, ErrorTolerantParserCompat};
1951
1952    /// Parse a type represented as a string.
1953    fn parse_type(s: &str) -> Type {
1954        use crate::files::Files;
1955        let id = Files::new().add("<test>", s);
1956
1957        FixedTypeParser::new()
1958            .parse_strict_compat(id, Lexer::new(s))
1959            .unwrap()
1960    }
1961
1962    /// Parse a type, simplify it and assert that the result corresponds to the second argument
1963    /// (which is parsed and printed again to eliminate formatting differences). This function also
1964    /// checks that the contract generation from the simplified version works without an unbound
1965    /// type variable error.
1966    #[track_caller]
1967    fn assert_simplifies_to(orig: &str, target: &str) {
1968        let parsed = parse_type(orig);
1969
1970        parsed.clone().contract_static().unwrap();
1971
1972        let simplified = parsed.simplify(
1973            &mut Environment::new(),
1974            SimplifyVars::new(),
1975            Polarity::Positive,
1976        );
1977        let target_typ = parse_type(target);
1978
1979        assert_eq!(format!("{simplified}"), format!("{target_typ}"));
1980    }
1981
1982    #[test]
1983    fn simplify() {
1984        assert_simplifies_to("forall a. a -> (a -> a) -> a", "Dyn -> (Dyn -> Dyn) -> Dyn");
1985        assert_simplifies_to(
1986            "forall b. (forall a. a -> a) -> b",
1987            "(forall a. a -> a) -> Dyn",
1988        );
1989
1990        // Big but entirely positive type
1991        assert_simplifies_to(
1992            "{foo : Array {bar : String, baz : Number}, qux: [| 'Foo, 'Bar, 'Baz Dyn |], pweep: {single : Array Bool}}",
1993            "Dyn"
1994        );
1995        // Mixed type with arrows inside the return value
1996        assert_simplifies_to(
1997            "Array Number -> Array {foo : Number,  bar : String -> String}",
1998            "Array Number -> Array {bar: String -> Dyn; Dyn}",
1999        );
2000        // Enum with arrows inside
2001        assert_simplifies_to(
2002            "{single : Array [| 'Foo, 'Bar, 'Baz (Dyn -> Dyn) |] }",
2003            "{single : Array [| 'Foo, 'Bar, 'Baz (Dyn -> Dyn) |]; Dyn}",
2004        );
2005
2006        // Polymorphic rows, case without excluded fields to check
2007        assert_simplifies_to(
2008            "forall r. {meta : String, doc : String; r} -> {meta : String; r}",
2009            "{meta : String, doc : String; Dyn} -> Dyn",
2010        );
2011        // Polymorphic rows in negative position should prevent row elision in positive position
2012        assert_simplifies_to(
2013            "(forall r. {meta : String, count: Number; r} -> {; r}) -> {meta : String, count: Number}",
2014            "(forall r. {meta : Dyn, count : Dyn; r} -> {; r}) -> Dyn",
2015        );
2016    }
2017
2018    #[test]
2019    fn simplify_dont_elide_record() {
2020        use regex::Regex;
2021        // The simplified version of the type should contain a generated variable here. We can't
2022        // represent a generated variable in normal Nickel syntax, so instead of parsing the
2023        // expected result and print it again to eliminate formatting details as in
2024        // `assert_simplifies_to`, we will print the simplified version and do a direct string
2025        // comparison on the result.
2026        let orig = "forall r. {x: Number, y: String; r} -> {z: Array Bool; r}";
2027
2028        let mut contract_env = Environment::new();
2029        let simplified = parse_type(orig)
2030            .simplify(&mut contract_env, SimplifyVars::new(), Polarity::Positive)
2031            .to_string();
2032
2033        // Note: because we match on strings directly, we must get the formatting right.
2034        let expected = Regex::new(r"\{ x : Number, y : String; (%\d+) \} -> Dyn").unwrap();
2035        let caps = expected.captures(&simplified).unwrap();
2036
2037        // We should have the generated variable in the contract environment bound to
2038        // `$forall_enum_tail_excluded_only ["z"]`. Indeed, `x`, `y` and `z` are excluded fields
2039        // for the row variable `r`. Because `forall r` is in positive position, we remove the
2040        // forall. `{x: Number, y: String; r}` is in negative position, so we still need to check
2041        // for excluded fields, but `x` and `y` are already taken care of in the body of the record
2042        // type. It only remains to check that `z` isn't present in the input record.
2043        let expected_var = Ident::from(caps.get(1).unwrap().as_str());
2044
2045        assert_eq!(
2046            contract_env.get(&expected_var).unwrap().to_string(),
2047            "$forall_record_tail_excluded_only [ \"z\" ]"
2048        );
2049    }
2050}