diff options
author | Nadrieril | 2019-05-06 22:09:55 +0200 |
---|---|---|
committer | Nadrieril | 2019-05-06 22:09:55 +0200 |
commit | 423fdeebe9247b16744fae4b50df415bbd08be04 (patch) | |
tree | f2f16407d7e365e6fecee400a1959ca08b2a5156 /dhall/src/phase | |
parent | 2075cba6d883278a534afd2d8fe8f0a5e9b2f0d0 (diff) |
Reorganize dhall into a phase structure
Diffstat (limited to 'dhall/src/phase')
-rw-r--r-- | dhall/src/phase/binary.rs | 363 | ||||
-rw-r--r-- | dhall/src/phase/mod.rs | 389 | ||||
-rw-r--r-- | dhall/src/phase/normalize.rs | 1888 | ||||
-rw-r--r-- | dhall/src/phase/parse.rs | 38 | ||||
-rw-r--r-- | dhall/src/phase/resolve.rs | 139 | ||||
-rw-r--r-- | dhall/src/phase/typecheck.rs | 1276 |
6 files changed, 4093 insertions, 0 deletions
diff --git a/dhall/src/phase/binary.rs b/dhall/src/phase/binary.rs new file mode 100644 index 0000000..9c31d4c --- /dev/null +++ b/dhall/src/phase/binary.rs @@ -0,0 +1,363 @@ +use dhall_syntax::*; +use itertools::*; +use serde_cbor::value::value as cbor; + +type ParsedExpr = SubExpr<X, Import>; + +#[derive(Debug)] +pub enum DecodeError { + CBORError(serde_cbor::error::Error), + WrongFormatError(String), +} + +pub fn decode(data: &[u8]) -> Result<ParsedExpr, DecodeError> { + match serde_cbor::de::from_slice(data) { + Ok(v) => cbor_value_to_dhall(&v), + Err(e) => Err(DecodeError::CBORError(e)), + } +} + +fn cbor_value_to_dhall(data: &cbor::Value) -> Result<ParsedExpr, DecodeError> { + use cbor::Value::*; + use dhall_syntax::{BinOp, Builtin, Const}; + use ExprF::*; + Ok(rc(match data { + String(s) => match Builtin::parse(s) { + Some(b) => ExprF::Builtin(b), + None => match s.as_str() { + "True" => BoolLit(true), + "False" => BoolLit(false), + "Type" => Const(Const::Type), + "Kind" => Const(Const::Kind), + "Sort" => Const(Const::Sort), + _ => Err(DecodeError::WrongFormatError("builtin".to_owned()))?, + }, + }, + U64(n) => Var(V(Label::from("_"), *n as usize)), + F64(x) => DoubleLit((*x).into()), + Bool(b) => BoolLit(*b), + Array(vec) => match vec.as_slice() { + [String(l), U64(n)] => { + let l = Label::from(l.as_str()); + Var(V(l, *n as usize)) + } + [U64(0), f, args..] => { + let mut f = cbor_value_to_dhall(&f)?; + for a in args { + let a = cbor_value_to_dhall(&a)?; + f = rc(App(f, a)) + } + return Ok(f); + } + [U64(1), x, y] => { + let x = cbor_value_to_dhall(&x)?; + let y = cbor_value_to_dhall(&y)?; + Lam(Label::from("_"), x, y) + } + [U64(1), String(l), x, y] => { + let x = cbor_value_to_dhall(&x)?; + let y = cbor_value_to_dhall(&y)?; + let l = Label::from(l.as_str()); + Lam(l, x, y) + } + [U64(2), x, y] => { + let x = cbor_value_to_dhall(&x)?; + let y = cbor_value_to_dhall(&y)?; + Pi(Label::from("_"), x, y) + } + [U64(2), String(l), x, y] => { + let x = cbor_value_to_dhall(&x)?; + let y = cbor_value_to_dhall(&y)?; + let l = Label::from(l.as_str()); + Pi(l, x, y) + } + [U64(3), U64(n), x, y] => { + let x = cbor_value_to_dhall(&x)?; + let y = cbor_value_to_dhall(&y)?; + use BinOp::*; + let op = match n { + 0 => BoolOr, + 1 => BoolAnd, + 2 => BoolEQ, + 3 => BoolNE, + 4 => NaturalPlus, + 5 => NaturalTimes, + 6 => TextAppend, + 7 => ListAppend, + 8 => Combine, + 9 => Prefer, + 10 => CombineTypes, + 11 => ImportAlt, + _ => { + Err(DecodeError::WrongFormatError("binop".to_owned()))? + } + }; + BinOp(op, x, y) + } + [U64(4), t] => { + let t = cbor_value_to_dhall(&t)?; + EmptyListLit(t) + } + [U64(4), Null, rest..] => { + let rest = rest + .iter() + .map(cbor_value_to_dhall) + .collect::<Result<Vec<_>, _>>()?; + NEListLit(rest) + } + [U64(5), t] => { + let t = cbor_value_to_dhall(&t)?; + OldOptionalLit(None, t) + } + [U64(5), Null, x] => { + let x = cbor_value_to_dhall(&x)?; + SomeLit(x) + } + [U64(5), t, x] => { + let x = cbor_value_to_dhall(&x)?; + let t = cbor_value_to_dhall(&t)?; + OldOptionalLit(Some(x), t) + } + [U64(6), x, y] => { + let x = cbor_value_to_dhall(&x)?; + let y = cbor_value_to_dhall(&y)?; + Merge(x, y, None) + } + [U64(6), x, y, z] => { + let x = cbor_value_to_dhall(&x)?; + let y = cbor_value_to_dhall(&y)?; + let z = cbor_value_to_dhall(&z)?; + Merge(x, y, Some(z)) + } + [U64(7), Object(map)] => { + let map = cbor_map_to_dhall_map(map)?; + RecordType(map) + } + [U64(8), Object(map)] => { + let map = cbor_map_to_dhall_map(map)?; + RecordLit(map) + } + [U64(9), x, String(l)] => { + let x = cbor_value_to_dhall(&x)?; + let l = Label::from(l.as_str()); + Field(x, l) + } + [U64(11), Object(map)] => { + let map = cbor_map_to_dhall_opt_map(map)?; + UnionType(map) + } + [U64(12), String(l), x, Object(map)] => { + let map = cbor_map_to_dhall_opt_map(map)?; + let x = cbor_value_to_dhall(&x)?; + let l = Label::from(l.as_str()); + UnionLit(l, x, map) + } + [U64(14), x, y, z] => { + let x = cbor_value_to_dhall(&x)?; + let y = cbor_value_to_dhall(&y)?; + let z = cbor_value_to_dhall(&z)?; + BoolIf(x, y, z) + } + [U64(15), U64(x)] => NaturalLit(*x as Natural), + [U64(16), U64(x)] => IntegerLit(*x as Integer), + [U64(16), I64(x)] => IntegerLit(*x as Integer), + [U64(18), String(first), rest..] => { + TextLit(InterpolatedText::from(( + first.clone(), + rest.iter() + .tuples() + .map(|(x, y)| { + let x = cbor_value_to_dhall(&x)?; + let y = match y { + String(s) => s.clone(), + _ => Err(DecodeError::WrongFormatError( + "text".to_owned(), + ))?, + }; + Ok((x, y)) + }) + .collect::<Result<_, _>>()?, + ))) + } + [U64(24), hash, U64(mode), U64(scheme), rest..] => { + let mode = match mode { + 1 => ImportMode::RawText, + _ => ImportMode::Code, + }; + let hash = match hash { + Null => None, + Array(vec) => match vec.as_slice() { + [String(protocol), String(hash)] => Some(Hash { + protocol: protocol.clone(), + hash: hash.clone(), + }), + _ => Err(DecodeError::WrongFormatError( + "import/hash".to_owned(), + ))?, + }, + _ => Err(DecodeError::WrongFormatError( + "import/hash".to_owned(), + ))?, + }; + let mut rest = rest.iter(); + let location = match scheme { + 0 | 1 => { + let scheme = match scheme { + 0 => Scheme::HTTP, + _ => Scheme::HTTPS, + }; + let headers = match rest.next() { + Some(Null) => None, + Some(x) => { + match cbor_value_to_dhall(&x)?.as_ref() { + Embed(import) => Some(Box::new( + import.location_hashed.clone(), + )), + _ => Err(DecodeError::WrongFormatError( + "import/remote/headers".to_owned(), + ))?, + } + } + _ => Err(DecodeError::WrongFormatError( + "import/remote/headers".to_owned(), + ))?, + }; + let authority = match rest.next() { + Some(String(s)) => s.to_owned(), + _ => Err(DecodeError::WrongFormatError( + "import/remote/authority".to_owned(), + ))?, + }; + let query = match rest.next_back() { + Some(Null) => None, + Some(String(s)) => Some(s.to_owned()), + _ => Err(DecodeError::WrongFormatError( + "import/remote/query".to_owned(), + ))?, + }; + let path = rest + .map(|s| { + s.as_string().ok_or_else(|| { + DecodeError::WrongFormatError( + "import/remote/path".to_owned(), + ) + }) + }) + .collect::<Result<_, _>>()?; + ImportLocation::Remote(URL { + scheme, + authority, + path, + query, + headers, + }) + } + 2 | 3 | 4 | 5 => { + let prefix = match scheme { + 2 => FilePrefix::Absolute, + 3 => FilePrefix::Here, + 4 => FilePrefix::Parent, + 5 => FilePrefix::Home, + _ => Err(DecodeError::WrongFormatError( + "import/local/prefix".to_owned(), + ))?, + }; + let path = rest + .map(|s| { + s.as_string().ok_or_else(|| { + DecodeError::WrongFormatError( + "import/local/path".to_owned(), + ) + }) + }) + .collect::<Result<_, _>>()?; + ImportLocation::Local(prefix, path) + } + 6 => { + let env = match rest.next() { + Some(String(s)) => s.to_owned(), + _ => Err(DecodeError::WrongFormatError( + "import/env".to_owned(), + ))?, + }; + ImportLocation::Env(env) + } + 7 => ImportLocation::Missing, + _ => Err(DecodeError::WrongFormatError( + "import/type".to_owned(), + ))?, + }; + Embed(Import { + mode, + location_hashed: ImportHashed { hash, location }, + }) + } + [U64(25), bindings..] => { + let mut tuples = bindings.iter().tuples(); + let bindings = (&mut tuples) + .map(|(x, t, v)| { + let x = x.as_string().ok_or_else(|| { + DecodeError::WrongFormatError( + "let/label".to_owned(), + ) + })?; + let x = Label::from(x.as_str()); + let t = match t { + Null => None, + t => Some(cbor_value_to_dhall(&t)?), + }; + let v = cbor_value_to_dhall(&v)?; + Ok((x, t, v)) + }) + .collect::<Result<Vec<_>, _>>()?; + let expr = tuples.into_buffer().next().ok_or_else(|| { + DecodeError::WrongFormatError("let/expr".to_owned()) + })?; + let expr = cbor_value_to_dhall(expr)?; + return Ok(bindings + .into_iter() + .rev() + .fold(expr, |acc, (x, t, v)| rc(Let(x, t, v, acc)))); + } + [U64(26), x, y] => { + let x = cbor_value_to_dhall(&x)?; + let y = cbor_value_to_dhall(&y)?; + Annot(x, y) + } + _ => Err(DecodeError::WrongFormatError(format!("{:?}", data)))?, + }, + _ => Err(DecodeError::WrongFormatError(format!("{:?}", data)))?, + })) +} + +fn cbor_map_to_dhall_map( + map: &std::collections::BTreeMap<cbor::ObjectKey, cbor::Value>, +) -> Result<std::collections::BTreeMap<Label, ParsedExpr>, DecodeError> { + map.iter() + .map(|(k, v)| -> Result<(_, _), _> { + let k = k.as_string().ok_or_else(|| { + DecodeError::WrongFormatError("map/key".to_owned()) + })?; + let v = cbor_value_to_dhall(v)?; + Ok((Label::from(k.as_ref()), v)) + }) + .collect::<Result<_, _>>() +} + +fn cbor_map_to_dhall_opt_map( + map: &std::collections::BTreeMap<cbor::ObjectKey, cbor::Value>, +) -> Result<std::collections::BTreeMap<Label, Option<ParsedExpr>>, DecodeError> +{ + map.iter() + .map(|(k, v)| -> Result<(_, _), _> { + let k = k.as_string().ok_or_else(|| { + DecodeError::WrongFormatError("map/key".to_owned()) + })?; + let v = match v { + cbor::Value::Null => None, + _ => Some(cbor_value_to_dhall(v)?), + }; + Ok((Label::from(k.as_ref()), v)) + }) + .collect::<Result<_, _>>() +} diff --git a/dhall/src/phase/mod.rs b/dhall/src/phase/mod.rs new file mode 100644 index 0000000..0b6eca6 --- /dev/null +++ b/dhall/src/phase/mod.rs @@ -0,0 +1,389 @@ +use std::borrow::Cow; +use std::fmt::Display; +use std::path::Path; + +use dhall_syntax::{Const, Import, Span, SubExpr, X}; + +use crate::error::Error; + +use normalize::{AlphaVar, Thunk, Value}; +use resolve::{ImportError, ImportRoot}; +use typecheck::{ + const_to_typed, type_of_const, TypeError, TypeMessage, TypecheckContext, +}; + +pub(crate) mod binary; +pub(crate) mod normalize; +pub(crate) mod parse; +pub(crate) mod resolve; +pub(crate) mod typecheck; + +pub(crate) type ParsedSubExpr = SubExpr<Span, Import>; +pub(crate) type ResolvedSubExpr = SubExpr<Span, Normalized>; +pub(crate) type NormalizedSubExpr = SubExpr<X, X>; + +#[derive(Debug, Clone)] +pub(crate) struct Parsed(pub(crate) ParsedSubExpr, pub(crate) ImportRoot); + +/// An expression where all imports have been resolved +#[derive(Debug, Clone)] +pub(crate) struct Resolved(pub(crate) ResolvedSubExpr); + +/// A typed expression +#[derive(Debug, Clone)] +pub(crate) enum Typed { + // The `Sort` higher-kinded type; it doesn't have a type + Sort, + // Any other value, along with (optionally) its type + Value(Thunk, Option<Type>), +} + +/// A normalized expression. +/// +/// Invariant: the contained Typed expression must be in normal form, +#[derive(Debug, Clone)] +pub(crate) struct Normalized(pub(crate) Typed); + +/// A Dhall expression representing a simple type. +/// +/// This captures what is usually simply called a "type", like +/// `Bool`, `{ x: Integer }` or `Natural -> Text`. +/// +/// For a more general notion of "type", see [Type]. +#[derive(Debug, Clone)] +pub struct SimpleType(pub(crate) NormalizedSubExpr); + +/// A Dhall expression representing a (possibly higher-kinded) type. +/// +/// This includes [SimpleType]s but also higher-kinded expressions like +/// `Type`, `Kind` and `{ x: Type }`. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Type(pub(crate) TypeInternal); + +#[derive(Debug, Clone)] +pub(crate) enum TypeInternal { + Const(Const), + /// This must not contain a Const value. + Typed(Box<Typed>), +} + +impl Parsed { + pub fn parse_file(f: &Path) -> Result<Parsed, Error> { + parse::parse_file(f) + } + + pub fn parse_str(s: &str) -> Result<Parsed, Error> { + parse::parse_str(s) + } + + #[allow(dead_code)] + pub fn parse_binary_file(f: &Path) -> Result<Parsed, Error> { + parse::parse_binary_file(f) + } + + pub fn resolve(self) -> Result<Resolved, ImportError> { + resolve::resolve(self) + } + + #[allow(dead_code)] + pub fn skip_resolve(self) -> Result<Resolved, ImportError> { + resolve::skip_resolve_expr(self) + } +} + +impl Resolved { + pub fn typecheck(self) -> Result<Typed, TypeError> { + typecheck::typecheck(self) + } + pub fn typecheck_with(self, ty: &Type) -> Result<Typed, TypeError> { + typecheck::typecheck_with(self, ty) + } + /// Pretends this expression has been typechecked. Use with care. + #[allow(dead_code)] + pub fn skip_typecheck(self) -> Typed { + typecheck::skip_typecheck(self) + } +} + +impl Typed { + /// Reduce an expression to its normal form, performing beta reduction + /// + /// `normalize` does not type-check the expression. You may want to type-check + /// expressions before normalizing them since normalization can convert an + /// ill-typed expression into a well-typed expression. + /// + /// However, `normalize` will not fail if the expression is ill-typed and will + /// leave ill-typed sub-expressions unevaluated. + pub fn normalize(self) -> Normalized { + match &self { + Typed::Sort => {} + Typed::Value(thunk, _) => { + thunk.normalize_nf(); + } + } + Normalized(self) + } + + pub(crate) fn from_thunk_and_type(th: Thunk, t: Type) -> Self { + Typed::Value(th, Some(t)) + } + pub(crate) fn from_thunk_untyped(th: Thunk) -> Self { + Typed::Value(th, None) + } + + // TODO: Avoid cloning if possible + pub(crate) fn to_value(&self) -> Value { + match self { + Typed::Value(th, _) => th.to_value(), + Typed::Sort => Value::Const(Const::Sort), + } + } + pub(crate) fn to_expr(&self) -> NormalizedSubExpr { + self.to_value().normalize_to_expr() + } + pub(crate) fn to_expr_alpha(&self) -> NormalizedSubExpr { + self.to_value().normalize_to_expr_maybe_alpha(true) + } + pub(crate) fn to_thunk(&self) -> Thunk { + match self { + Typed::Value(th, _) => th.clone(), + Typed::Sort => Thunk::from_value(Value::Const(Const::Sort)), + } + } + pub(crate) fn to_type(&self) -> Type { + match self { + Typed::Sort => Type(TypeInternal::Const(Const::Sort)), + Typed::Value(th, _) => match &*th.as_value() { + Value::Const(c) => Type(TypeInternal::Const(*c)), + _ => Type(TypeInternal::Typed(Box::new(self.clone()))), + }, + } + } + + pub(crate) fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> { + match self { + Typed::Value(_, Some(t)) => Ok(Cow::Borrowed(t)), + Typed::Value(_, None) => Err(TypeError::new( + &TypecheckContext::new(), + TypeMessage::Untyped, + )), + Typed::Sort => { + Err(TypeError::new(&TypecheckContext::new(), TypeMessage::Sort)) + } + } + } + + pub(crate) fn const_sort() -> Self { + Typed::Sort + } + + pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self { + match self { + Typed::Value(th, t) => Typed::Value( + th.shift(delta, var), + t.as_ref().map(|x| x.shift(delta, var)), + ), + Typed::Sort => Typed::Sort, + } + } + + pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self { + match self { + Typed::Value(th, t) => Typed::Value( + th.subst_shift(var, val), + t.as_ref().map(|x| x.subst_shift(var, val)), + ), + Typed::Sort => Typed::Sort, + } + } +} + +impl Type { + pub(crate) fn to_normalized(&self) -> Normalized { + self.0.to_normalized() + } + pub(crate) fn to_expr(&self) -> NormalizedSubExpr { + self.0.to_expr() + } + pub(crate) fn to_value(&self) -> Value { + self.0.to_value() + } + pub(crate) fn as_const(&self) -> Option<Const> { + self.0.as_const() + } + pub(crate) fn internal_whnf(&self) -> Option<Value> { + self.0.whnf() + } + pub(crate) fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> { + self.0.get_type() + } + + pub(crate) fn const_sort() -> Self { + Type(TypeInternal::Const(Const::Sort)) + } + pub(crate) fn const_kind() -> Self { + Type(TypeInternal::Const(Const::Kind)) + } + pub(crate) fn const_type() -> Self { + Type(TypeInternal::Const(Const::Type)) + } + + pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self { + Type(self.0.shift(delta, var)) + } + pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self { + Type(self.0.subst_shift(var, val)) + } +} + +impl TypeInternal { + pub(crate) fn to_typed(&self) -> Typed { + match self { + TypeInternal::Typed(e) => e.as_ref().clone(), + TypeInternal::Const(c) => const_to_typed(*c), + } + } + pub(crate) fn to_normalized(&self) -> Normalized { + self.to_typed().normalize() + } + pub(crate) fn to_expr(&self) -> NormalizedSubExpr { + self.to_normalized().to_expr() + } + pub(crate) fn to_value(&self) -> Value { + self.to_typed().to_value() + } + pub(crate) fn get_type(&self) -> Result<Cow<'_, Type>, TypeError> { + Ok(match self { + TypeInternal::Typed(e) => e.get_type()?, + TypeInternal::Const(c) => Cow::Owned(type_of_const(*c)?), + }) + } + pub(crate) fn as_const(&self) -> Option<Const> { + match self { + TypeInternal::Const(c) => Some(*c), + _ => None, + } + } + pub(crate) fn whnf(&self) -> Option<Value> { + match self { + TypeInternal::Typed(e) => Some(e.to_value()), + _ => None, + } + } + pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self { + use TypeInternal::*; + match self { + Typed(e) => Typed(Box::new(e.shift(delta, var))), + Const(c) => Const(*c), + } + } + pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self { + use TypeInternal::*; + match self { + Typed(e) => Typed(Box::new(e.subst_shift(var, val))), + Const(c) => Const(*c), + } + } +} + +impl Normalized { + pub(crate) fn from_thunk_and_type(th: Thunk, t: Type) -> Self { + Normalized(Typed::from_thunk_and_type(th, t)) + } + + pub(crate) fn to_expr(&self) -> NormalizedSubExpr { + self.0.to_expr() + } + #[allow(dead_code)] + pub(crate) fn to_expr_alpha(&self) -> NormalizedSubExpr { + self.0.to_expr_alpha() + } + pub(crate) fn to_value(&self) -> Value { + self.0.to_value() + } + pub(crate) fn to_thunk(&self) -> Thunk { + self.0.to_thunk() + } + pub(crate) fn to_type(self) -> Type { + self.0.to_type() + } + pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self { + Normalized(self.0.shift(delta, var)) + } +} + +macro_rules! derive_traits_for_wrapper_struct { + ($ty:ident) => { + impl std::cmp::PartialEq for $ty { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } + } + + impl std::cmp::Eq for $ty {} + + impl std::fmt::Display for $ty { + fn fmt( + &self, + f: &mut std::fmt::Formatter, + ) -> Result<(), std::fmt::Error> { + self.0.fmt(f) + } + } + }; +} + +derive_traits_for_wrapper_struct!(Parsed); +derive_traits_for_wrapper_struct!(Resolved); +derive_traits_for_wrapper_struct!(Normalized); +derive_traits_for_wrapper_struct!(SimpleType); + +impl Eq for Typed {} +impl PartialEq for Typed { + fn eq(&self, other: &Self) -> bool { + self.to_value() == other.to_value() + } +} + +impl Eq for TypeInternal {} +impl PartialEq for TypeInternal { + fn eq(&self, other: &Self) -> bool { + self.to_normalized() == other.to_normalized() + } +} + +impl Display for Typed { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + self.to_expr().fmt(f) + } +} + +impl Display for Type { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + self.to_normalized().fmt(f) + } +} + +// Exposed for the macros +#[doc(hidden)] +impl From<SimpleType> for NormalizedSubExpr { + fn from(x: SimpleType) -> NormalizedSubExpr { + x.0 + } +} + +// Exposed for the macros +#[doc(hidden)] +impl From<NormalizedSubExpr> for SimpleType { + fn from(x: NormalizedSubExpr) -> SimpleType { + SimpleType(x) + } +} + +// Exposed for the macros +#[doc(hidden)] +impl From<Normalized> for Typed { + fn from(x: Normalized) -> Typed { + x.0 + } +} diff --git a/dhall/src/phase/normalize.rs b/dhall/src/phase/normalize.rs new file mode 100644 index 0000000..2340bbd --- /dev/null +++ b/dhall/src/phase/normalize.rs @@ -0,0 +1,1888 @@ +#![allow(non_snake_case)] +use std::collections::BTreeMap; +use std::rc::Rc; + +use dhall_proc_macros as dhall; +use dhall_syntax::context::Context; +use dhall_syntax::{ + rc, BinOp, Builtin, Const, ExprF, Integer, InterpolatedTextContents, Label, + Natural, V, X, +}; + +use crate::phase::{NormalizedSubExpr, ResolvedSubExpr, Type, Typed}; + +type InputSubExpr = ResolvedSubExpr; +type OutputSubExpr = NormalizedSubExpr; + +/// Stores a pair of variables: a normal one and if relevant one +/// that corresponds to the alpha-normalized version of the first one. +#[derive(Debug, Clone, Eq)] +pub(crate) struct AlphaVar { + normal: V<Label>, + alpha: Option<V<()>>, +} + +impl AlphaVar { + pub(crate) fn to_var(&self, alpha: bool) -> V<Label> { + match (alpha, &self.alpha) { + (true, Some(x)) => V("_".into(), x.1), + _ => self.normal.clone(), + } + } + pub(crate) fn from_var(normal: V<Label>) -> Self { + AlphaVar { + normal, + alpha: None, + } + } + pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self { + AlphaVar { + normal: self.normal.shift(delta, &var.normal), + alpha: match (&self.alpha, &var.alpha) { + (Some(x), Some(v)) => Some(x.shift(delta, v)), + _ => None, + }, + } + } +} + +/// Equality is up to alpha-equivalence. +impl std::cmp::PartialEq for AlphaVar { + fn eq(&self, other: &Self) -> bool { + match (&self.alpha, &other.alpha) { + (Some(x), Some(y)) => x == y, + (None, None) => self.normal == other.normal, + _ => false, + } + } +} + +impl From<Label> for AlphaVar { + fn from(x: Label) -> AlphaVar { + AlphaVar { + normal: V(x, 0), + alpha: Some(V((), 0)), + } + } +} +impl<'a> From<&'a Label> for AlphaVar { + fn from(x: &'a Label) -> AlphaVar { + x.clone().into() + } +} +impl From<AlphaLabel> for AlphaVar { + fn from(x: AlphaLabel) -> AlphaVar { + let l: Label = x.into(); + l.into() + } +} +impl<'a> From<&'a AlphaLabel> for AlphaVar { + fn from(x: &'a AlphaLabel) -> AlphaVar { + x.clone().into() + } +} + +// Exactly like a Label, but equality returns always true. +// This is so that Value equality is exactly alpha-equivalence. +#[derive(Debug, Clone, Eq)] +pub(crate) struct AlphaLabel(Label); + +impl AlphaLabel { + fn to_label_maybe_alpha(&self, alpha: bool) -> Label { + if alpha { + "_".into() + } else { + self.to_label() + } + } + fn to_label(&self) -> Label { + self.clone().into() + } +} + +impl std::cmp::PartialEq for AlphaLabel { + fn eq(&self, _other: &Self) -> bool { + true + } +} + +impl From<Label> for AlphaLabel { + fn from(x: Label) -> AlphaLabel { + AlphaLabel(x) + } +} +impl From<AlphaLabel> for Label { + fn from(x: AlphaLabel) -> Label { + x.0 + } +} + +#[derive(Debug, Clone)] +enum EnvItem { + Thunk(Thunk), + Skip(AlphaVar), +} + +impl EnvItem { + fn shift(&self, delta: isize, var: &AlphaVar) -> Self { + use EnvItem::*; + match self { + Thunk(e) => Thunk(e.shift(delta, var)), + Skip(v) => Skip(v.shift(delta, var)), + } + } + + pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self { + match self { + EnvItem::Thunk(e) => EnvItem::Thunk(e.subst_shift(var, val)), + EnvItem::Skip(v) if v == var => EnvItem::Thunk(val.to_thunk()), + EnvItem::Skip(v) => EnvItem::Skip(v.shift(-1, var)), + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct NormalizationContext(Rc<Context<Label, EnvItem>>); + +impl NormalizationContext { + pub(crate) fn new() -> Self { + NormalizationContext(Rc::new(Context::new())) + } + fn skip(&self, x: &Label) -> Self { + NormalizationContext(Rc::new( + self.0 + .map(|_, e| e.shift(1, &x.into())) + .insert(x.clone(), EnvItem::Skip(x.into())), + )) + } + fn lookup(&self, var: &V<Label>) -> Value { + let V(x, n) = var; + match self.0.lookup(x, *n) { + Some(EnvItem::Thunk(t)) => t.to_value(), + Some(EnvItem::Skip(newvar)) => Value::Var(newvar.clone()), + // Free variable + None => Value::Var(AlphaVar::from_var(var.clone())), + } + } + pub(crate) fn from_typecheck_ctx( + tc_ctx: &crate::phase::typecheck::TypecheckContext, + ) -> Self { + use crate::phase::typecheck::EnvItem::*; + let mut ctx = Context::new(); + for (k, vs) in tc_ctx.0.iter_keys() { + for v in vs.iter() { + let new_item = match v { + Type(var, _) => EnvItem::Skip(var.clone()), + Value(e) => EnvItem::Thunk(e.to_thunk()), + }; + ctx = ctx.insert(k.clone(), new_item); + } + } + NormalizationContext(Rc::new(ctx)) + } + + fn shift(&self, delta: isize, var: &AlphaVar) -> Self { + NormalizationContext(Rc::new(self.0.map(|_, e| e.shift(delta, var)))) + } + + fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self { + NormalizationContext(Rc::new( + self.0.map(|_, e| e.subst_shift(var, val)), + )) + } +} + +/// A semantic value. +/// Equality is up to alpha-equivalence (renaming of bound variables). +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) enum Value { + /// Closures + Lam(AlphaLabel, Thunk, Thunk), + AppliedBuiltin(Builtin, Vec<Thunk>), + /// `λ(x: a) -> Some x` + OptionalSomeClosure(TypeThunk), + /// `λ(x : a) -> λ(xs : List a) -> [ x ] # xs` + /// `λ(xs : List a) -> [ x ] # xs` + ListConsClosure(TypeThunk, Option<Thunk>), + /// `λ(x : Natural) -> x + 1` + NaturalSuccClosure, + Pi(AlphaLabel, TypeThunk, TypeThunk), + + Var(AlphaVar), + Const(Const), + BoolLit(bool), + NaturalLit(Natural), + IntegerLit(Integer), + EmptyOptionalLit(TypeThunk), + NEOptionalLit(Thunk), + EmptyListLit(TypeThunk), + NEListLit(Vec<Thunk>), + RecordLit(BTreeMap<Label, Thunk>), + RecordType(BTreeMap<Label, TypeThunk>), + UnionType(BTreeMap<Label, Option<TypeThunk>>), + UnionConstructor(Label, BTreeMap<Label, Option<TypeThunk>>), + UnionLit(Label, Thunk, BTreeMap<Label, Option<TypeThunk>>), + TextLit(Vec<InterpolatedTextContents<Thunk>>), + /// Invariant: This must not contain a value captured by one of the variants above. + PartialExpr(ExprF<Thunk, Label, X>), +} + +impl Value { + pub(crate) fn into_thunk(self) -> Thunk { + Thunk::from_value(self) + } + + /// Convert the value to a fully normalized syntactic expression + pub(crate) fn normalize_to_expr(&self) -> OutputSubExpr { + self.normalize_to_expr_maybe_alpha(false) + } + /// Convert the value to a fully normalized syntactic expression. Also alpha-normalize + /// if alpha is `true` + pub(crate) fn normalize_to_expr_maybe_alpha( + &self, + alpha: bool, + ) -> OutputSubExpr { + match self { + Value::Lam(x, t, e) => rc(ExprF::Lam( + x.to_label_maybe_alpha(alpha), + t.normalize_to_expr_maybe_alpha(alpha), + e.normalize_to_expr_maybe_alpha(alpha), + )), + Value::AppliedBuiltin(b, args) => { + let mut e = rc(ExprF::Builtin(*b)); + for v in args { + e = rc(ExprF::App( + e, + v.normalize_to_expr_maybe_alpha(alpha), + )); + } + e + } + Value::OptionalSomeClosure(n) => { + let a = n.normalize_to_expr_maybe_alpha(alpha); + dhall::subexpr!(λ(x: a) -> Some x) + } + Value::ListConsClosure(a, None) => { + // Avoid accidental capture of the new `x` variable + let a1 = a.shift(1, &Label::from("x").into()); + let a1 = a1.normalize_to_expr_maybe_alpha(alpha); + let a = a.normalize_to_expr_maybe_alpha(alpha); + dhall::subexpr!(λ(x : a) -> λ(xs : List a1) -> [ x ] # xs) + } + Value::ListConsClosure(n, Some(v)) => { + // Avoid accidental capture of the new `xs` variable + let v = v.shift(1, &Label::from("xs").into()); + let v = v.normalize_to_expr_maybe_alpha(alpha); + let a = n.normalize_to_expr_maybe_alpha(alpha); + dhall::subexpr!(λ(xs : List a) -> [ v ] # xs) + } + Value::NaturalSuccClosure => { + dhall::subexpr!(λ(x : Natural) -> x + 1) + } + Value::Pi(x, t, e) => rc(ExprF::Pi( + x.to_label_maybe_alpha(alpha), + t.normalize_to_expr_maybe_alpha(alpha), + e.normalize_to_expr_maybe_alpha(alpha), + )), + Value::Var(v) => rc(ExprF::Var(v.to_var(alpha))), + Value::Const(c) => rc(ExprF::Const(*c)), + Value::BoolLit(b) => rc(ExprF::BoolLit(*b)), + Value::NaturalLit(n) => rc(ExprF::NaturalLit(*n)), + Value::IntegerLit(n) => rc(ExprF::IntegerLit(*n)), + Value::EmptyOptionalLit(n) => rc(ExprF::App( + rc(ExprF::Builtin(Builtin::OptionalNone)), + n.normalize_to_expr_maybe_alpha(alpha), + )), + Value::NEOptionalLit(n) => { + rc(ExprF::SomeLit(n.normalize_to_expr_maybe_alpha(alpha))) + } + Value::EmptyListLit(n) => { + rc(ExprF::EmptyListLit(n.normalize_to_expr_maybe_alpha(alpha))) + } + Value::NEListLit(elts) => rc(ExprF::NEListLit( + elts.into_iter() + .map(|n| n.normalize_to_expr_maybe_alpha(alpha)) + .collect(), + )), + Value::RecordLit(kvs) => rc(ExprF::RecordLit( + kvs.iter() + .map(|(k, v)| { + (k.clone(), v.normalize_to_expr_maybe_alpha(alpha)) + }) + .collect(), + )), + Value::RecordType(kts) => rc(ExprF::RecordType( + kts.iter() + .map(|(k, v)| { + (k.clone(), v.normalize_to_expr_maybe_alpha(alpha)) + }) + .collect(), + )), + Value::UnionType(kts) => rc(ExprF::UnionType( + kts.iter() + .map(|(k, v)| { + ( + k.clone(), + v.as_ref().map(|v| { + v.normalize_to_expr_maybe_alpha(alpha) + }), + ) + }) + .collect(), + )), + Value::UnionConstructor(l, kts) => { + let kts = kts + .iter() + .map(|(k, v)| { + ( + k.clone(), + v.as_ref().map(|v| { + v.normalize_to_expr_maybe_alpha(alpha) + }), + ) + }) + .collect(); + rc(ExprF::Field(rc(ExprF::UnionType(kts)), l.clone())) + } + Value::UnionLit(l, v, kts) => rc(ExprF::UnionLit( + l.clone(), + v.normalize_to_expr_maybe_alpha(alpha), + kts.iter() + .map(|(k, v)| { + ( + k.clone(), + v.as_ref().map(|v| { + v.normalize_to_expr_maybe_alpha(alpha) + }), + ) + }) + .collect(), + )), + Value::TextLit(elts) => { + use InterpolatedTextContents::{Expr, Text}; + rc(ExprF::TextLit( + elts.iter() + .map(|contents| match contents { + Expr(e) => { + Expr(e.normalize_to_expr_maybe_alpha(alpha)) + } + Text(s) => Text(s.clone()), + }) + .collect(), + )) + } + Value::PartialExpr(e) => { + rc(e.map_ref_simple(|v| v.normalize_to_expr_maybe_alpha(alpha))) + } + } + } + + // Deprecated + fn normalize(&self) -> Value { + let mut v = self.clone(); + v.normalize_mut(); + v + } + + pub(crate) fn normalize_mut(&mut self) { + match self { + Value::NaturalSuccClosure + | Value::Var(_) + | Value::Const(_) + | Value::BoolLit(_) + | Value::NaturalLit(_) + | Value::IntegerLit(_) => {} + + Value::EmptyOptionalLit(tth) + | Value::OptionalSomeClosure(tth) + | Value::EmptyListLit(tth) => { + tth.normalize_mut(); + } + + Value::NEOptionalLit(th) => { + th.normalize_mut(); + } + Value::Lam(_, t, e) => { + t.normalize_mut(); + e.normalize_mut(); + } + Value::Pi(_, t, e) => { + t.normalize_mut(); + e.normalize_mut(); + } + Value::AppliedBuiltin(_, args) => { + for x in args.iter_mut() { + x.normalize_mut(); + } + } + Value::ListConsClosure(t, v) => { + t.normalize_mut(); + for x in v.iter_mut() { + x.normalize_mut(); + } + } + Value::NEListLit(elts) => { + for x in elts.iter_mut() { + x.normalize_mut(); + } + } + Value::RecordLit(kvs) => { + for x in kvs.values_mut() { + x.normalize_mut(); + } + } + Value::RecordType(kvs) => { + for x in kvs.values_mut() { + x.normalize_mut(); + } + } + Value::UnionType(kts) | Value::UnionConstructor(_, kts) => { + for x in kts.values_mut().flat_map(|opt| opt) { + x.normalize_mut(); + } + } + Value::UnionLit(_, v, kts) => { + v.normalize_mut(); + for x in kts.values_mut().flat_map(|opt| opt) { + x.normalize_mut(); + } + } + Value::TextLit(elts) => { + for x in elts.iter_mut() { + use InterpolatedTextContents::{Expr, Text}; + match x { + Expr(n) => n.normalize_mut(), + Text(_) => {} + } + } + } + Value::PartialExpr(e) => { + // TODO: need map_mut_simple + e.map_ref_simple(|v| { + v.normalize_nf(); + }); + } + } + } + + /// Apply to a value + pub(crate) fn app(self, val: Value) -> Value { + self.app_val(val) + } + + /// Apply to a value + pub(crate) fn app_val(self, val: Value) -> Value { + self.app_thunk(val.into_thunk()) + } + + /// Apply to a thunk + pub(crate) fn app_thunk(self, th: Thunk) -> Value { + Thunk::from_value(self).app_thunk(th) + } + + pub(crate) fn from_builtin(b: Builtin) -> Value { + Value::AppliedBuiltin(b, vec![]) + } + + fn shift(&self, delta: isize, var: &AlphaVar) -> Self { + match self { + Value::Lam(x, t, e) => Value::Lam( + x.clone(), + t.shift(delta, var), + e.shift(delta, &var.shift(1, &x.into())), + ), + Value::AppliedBuiltin(b, args) => Value::AppliedBuiltin( + *b, + args.iter().map(|v| v.shift(delta, var)).collect(), + ), + Value::NaturalSuccClosure => Value::NaturalSuccClosure, + Value::OptionalSomeClosure(tth) => { + Value::OptionalSomeClosure(tth.shift(delta, var)) + } + Value::ListConsClosure(t, v) => Value::ListConsClosure( + t.shift(delta, var), + v.as_ref().map(|v| v.shift(delta, var)), + ), + Value::Pi(x, t, e) => Value::Pi( + x.clone(), + t.shift(delta, var), + e.shift(delta, &var.shift(1, &x.into())), + ), + Value::Var(v) => Value::Var(v.shift(delta, var)), + Value::Const(c) => Value::Const(*c), + Value::BoolLit(b) => Value::BoolLit(*b), + Value::NaturalLit(n) => Value::NaturalLit(*n), + Value::IntegerLit(n) => Value::IntegerLit(*n), + Value::EmptyOptionalLit(tth) => { + Value::EmptyOptionalLit(tth.shift(delta, var)) + } + Value::NEOptionalLit(th) => { + Value::NEOptionalLit(th.shift(delta, var)) + } + Value::EmptyListLit(tth) => { + Value::EmptyListLit(tth.shift(delta, var)) + } + Value::NEListLit(elts) => Value::NEListLit( + elts.iter().map(|v| v.shift(delta, var)).collect(), + ), + Value::RecordLit(kvs) => Value::RecordLit( + kvs.iter() + .map(|(k, v)| (k.clone(), v.shift(delta, var))) + .collect(), + ), + Value::RecordType(kvs) => Value::RecordType( + kvs.iter() + .map(|(k, v)| (k.clone(), v.shift(delta, var))) + .collect(), + ), + Value::UnionType(kts) => Value::UnionType( + kts.iter() + .map(|(k, v)| { + (k.clone(), v.as_ref().map(|v| v.shift(delta, var))) + }) + .collect(), + ), + Value::UnionConstructor(x, kts) => Value::UnionConstructor( + x.clone(), + kts.iter() + .map(|(k, v)| { + (k.clone(), v.as_ref().map(|v| v.shift(delta, var))) + }) + .collect(), + ), + Value::UnionLit(x, v, kts) => Value::UnionLit( + x.clone(), + v.shift(delta, var), + kts.iter() + .map(|(k, v)| { + (k.clone(), v.as_ref().map(|v| v.shift(delta, var))) + }) + .collect(), + ), + Value::TextLit(elts) => Value::TextLit( + elts.iter() + .map(|contents| { + use InterpolatedTextContents::{Expr, Text}; + match contents { + Expr(th) => Expr(th.shift(delta, var)), + Text(s) => Text(s.clone()), + } + }) + .collect(), + ), + Value::PartialExpr(e) => { + Value::PartialExpr(e.map_ref_with_special_handling_of_binders( + |v| v.shift(delta, var), + |x, v| v.shift(delta, &var.shift(1, &x.into())), + X::clone, + Label::clone, + )) + } + } + } + + pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self { + match self { + // Retry normalizing since substituting may allow progress + Value::AppliedBuiltin(b, args) => apply_builtin( + *b, + args.iter().map(|v| v.subst_shift(var, val)).collect(), + ), + // Retry normalizing since substituting may allow progress + Value::PartialExpr(e) => { + normalize_one_layer(e.map_ref_with_special_handling_of_binders( + |v| v.subst_shift(var, val), + |x, v| { + v.subst_shift( + &var.shift(1, &x.into()), + &val.shift(1, &x.into()), + ) + }, + X::clone, + Label::clone, + )) + } + // Retry normalizing since substituting may allow progress + Value::TextLit(elts) => { + Value::TextLit(squash_textlit(elts.iter().map(|contents| { + use InterpolatedTextContents::{Expr, Text}; + match contents { + Expr(th) => Expr(th.subst_shift(var, val)), + Text(s) => Text(s.clone()), + } + }))) + } + Value::Lam(x, t, e) => Value::Lam( + x.clone(), + t.subst_shift(var, val), + e.subst_shift( + &var.shift(1, &x.into()), + &val.shift(1, &x.into()), + ), + ), + Value::NaturalSuccClosure => Value::NaturalSuccClosure, + Value::OptionalSomeClosure(tth) => { + Value::OptionalSomeClosure(tth.subst_shift(var, val)) + } + Value::ListConsClosure(t, v) => Value::ListConsClosure( + t.subst_shift(var, val), + v.as_ref().map(|v| v.subst_shift(var, val)), + ), + Value::Pi(x, t, e) => Value::Pi( + x.clone(), + t.subst_shift(var, val), + e.subst_shift( + &var.shift(1, &x.into()), + &val.shift(1, &x.into()), + ), + ), + Value::Var(v) if v == var => val.to_value().clone(), + Value::Var(v) => Value::Var(v.shift(-1, var)), + Value::Const(c) => Value::Const(*c), + Value::BoolLit(b) => Value::BoolLit(*b), + Value::NaturalLit(n) => Value::NaturalLit(*n), + Value::IntegerLit(n) => Value::IntegerLit(*n), + Value::EmptyOptionalLit(tth) => { + Value::EmptyOptionalLit(tth.subst_shift(var, val)) + } + Value::NEOptionalLit(th) => { + Value::NEOptionalLit(th.subst_shift(var, val)) + } + Value::EmptyListLit(tth) => { + Value::EmptyListLit(tth.subst_shift(var, val)) + } + Value::NEListLit(elts) => Value::NEListLit( + elts.iter().map(|v| v.subst_shift(var, val)).collect(), + ), + Value::RecordLit(kvs) => Value::RecordLit( + kvs.iter() + .map(|(k, v)| (k.clone(), v.subst_shift(var, val))) + .collect(), + ), + Value::RecordType(kvs) => Value::RecordType( + kvs.iter() + .map(|(k, v)| (k.clone(), v.subst_shift(var, val))) + .collect(), + ), + Value::UnionType(kts) => Value::UnionType( + kts.iter() + .map(|(k, v)| { + (k.clone(), v.as_ref().map(|v| v.subst_shift(var, val))) + }) + .collect(), + ), + Value::UnionConstructor(x, kts) => Value::UnionConstructor( + x.clone(), + kts.iter() + .map(|(k, v)| { + (k.clone(), v.as_ref().map(|v| v.subst_shift(var, val))) + }) + .collect(), + ), + Value::UnionLit(x, v, kts) => Value::UnionLit( + x.clone(), + v.subst_shift(var, val), + kts.iter() + .map(|(k, v)| { + (k.clone(), v.as_ref().map(|v| v.subst_shift(var, val))) + }) + .collect(), + ), + } + } +} + +mod thunk { + use super::{ + apply_any, normalize_whnf, AlphaVar, InputSubExpr, + NormalizationContext, OutputSubExpr, Value, + }; + use crate::phase::Typed; + use std::cell::{Ref, RefCell}; + use std::rc::Rc; + + #[derive(Debug, Clone, Copy)] + enum Marker { + /// Weak Head Normal Form, i.e. subexpressions may not be normalized + WHNF, + /// Normal form, i.e. completely normalized + NF, + } + use Marker::{NF, WHNF}; + + #[derive(Debug)] + enum ThunkInternal { + /// Non-normalized value alongside a normalization context + Unnormalized(NormalizationContext, InputSubExpr), + /// Partially normalized value. + /// Invariant: if the marker is `NF`, the value must be fully normalized + Value(Marker, Value), + } + + /// Stores a possibl unevaluated value. Uses RefCell to ensure that + /// the value gets normalized at most once. + #[derive(Debug, Clone)] + pub struct Thunk(Rc<RefCell<ThunkInternal>>); + + impl ThunkInternal { + fn into_thunk(self) -> Thunk { + Thunk(Rc::new(RefCell::new(self))) + } + + fn normalize_whnf(&mut self) { + match self { + ThunkInternal::Unnormalized(ctx, e) => { + *self = ThunkInternal::Value( + WHNF, + normalize_whnf(ctx.clone(), e.clone()), + ) + } + // Already at least in WHNF + ThunkInternal::Value(_, _) => {} + } + } + + fn normalize_nf(&mut self) { + match self { + ThunkInternal::Unnormalized(_, _) => { + self.normalize_whnf(); + self.normalize_nf(); + } + ThunkInternal::Value(m @ WHNF, v) => { + v.normalize_mut(); + *m = NF; + } + // Already in NF + ThunkInternal::Value(NF, _) => {} + } + } + + // Always use normalize_whnf before + fn as_whnf(&self) -> &Value { + match self { + ThunkInternal::Unnormalized(_, _) => unreachable!(), + ThunkInternal::Value(_, v) => v, + } + } + + // Always use normalize_nf before + fn as_nf(&self) -> &Value { + match self { + ThunkInternal::Unnormalized(_, _) => unreachable!(), + ThunkInternal::Value(WHNF, _) => unreachable!(), + ThunkInternal::Value(NF, v) => v, + } + } + + fn shift(&self, delta: isize, var: &AlphaVar) -> Self { + match self { + ThunkInternal::Unnormalized(ctx, e) => { + ThunkInternal::Unnormalized( + ctx.shift(delta, var), + e.clone(), + ) + } + ThunkInternal::Value(m, v) => { + ThunkInternal::Value(*m, v.shift(delta, var)) + } + } + } + + fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self { + match self { + ThunkInternal::Unnormalized(ctx, e) => { + ThunkInternal::Unnormalized( + ctx.subst_shift(var, val), + e.clone(), + ) + } + ThunkInternal::Value(_, v) => { + // The resulting value may not stay in normal form after substitution + ThunkInternal::Value(WHNF, v.subst_shift(var, val)) + } + } + } + } + + impl Thunk { + pub(crate) fn new(ctx: NormalizationContext, e: InputSubExpr) -> Thunk { + ThunkInternal::Unnormalized(ctx, e).into_thunk() + } + + pub(crate) fn from_value(v: Value) -> Thunk { + ThunkInternal::Value(WHNF, v).into_thunk() + } + + pub(crate) fn from_normalized_expr(e: OutputSubExpr) -> Thunk { + Thunk::new( + NormalizationContext::new(), + e.embed_absurd().note_absurd(), + ) + } + + // Normalizes contents to normal form; faster than `normalize_nf` if + // no one else shares this thunk + pub(crate) fn normalize_mut(&mut self) { + match Rc::get_mut(&mut self.0) { + // Mutate directly if sole owner + Some(refcell) => RefCell::get_mut(refcell).normalize_nf(), + // Otherwise mutate through the refcell + None => self.0.borrow_mut().normalize_nf(), + } + } + + fn do_normalize_whnf(&self) { + let borrow = self.0.borrow(); + match &*borrow { + ThunkInternal::Unnormalized(_, _) => { + drop(borrow); + self.0.borrow_mut().normalize_whnf(); + } + // Already at least in WHNF + ThunkInternal::Value(_, _) => {} + } + } + + fn do_normalize_nf(&self) { + let borrow = self.0.borrow(); + match &*borrow { + ThunkInternal::Unnormalized(_, _) + | ThunkInternal::Value(WHNF, _) => { + drop(borrow); + self.0.borrow_mut().normalize_nf(); + } + // Already in NF + ThunkInternal::Value(NF, _) => {} + } + } + + // WARNING: avoid normalizing any thunk while holding on to this ref + // or you could run into BorrowMut panics + pub(crate) fn normalize_nf(&self) -> Ref<Value> { + self.do_normalize_nf(); + Ref::map(self.0.borrow(), ThunkInternal::as_nf) + } + + // WARNING: avoid normalizing any thunk while holding on to this ref + // or you could run into BorrowMut panics + pub(crate) fn as_value(&self) -> Ref<Value> { + self.do_normalize_whnf(); + Ref::map(self.0.borrow(), ThunkInternal::as_whnf) + } + + pub(crate) fn to_value(&self) -> Value { + self.as_value().clone() + } + + pub(crate) fn normalize_to_expr(&self) -> OutputSubExpr { + self.normalize_to_expr_maybe_alpha(false) + } + + pub(crate) fn normalize_to_expr_maybe_alpha( + &self, + alpha: bool, + ) -> OutputSubExpr { + self.normalize_nf().normalize_to_expr_maybe_alpha(alpha) + } + + pub(crate) fn app_val(&self, val: Value) -> Value { + self.app_thunk(val.into_thunk()) + } + + pub(crate) fn app_thunk(&self, th: Thunk) -> Value { + apply_any(self.clone(), th) + } + + pub(crate) fn shift(&self, delta: isize, var: &AlphaVar) -> Self { + self.0.borrow().shift(delta, var).into_thunk() + } + + pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self { + self.0.borrow().subst_shift(var, val).into_thunk() + } + } + + impl std::cmp::PartialEq for Thunk { + fn eq(&self, other: &Self) -> bool { + &*self.as_value() == &*other.as_value() + } + } + impl std::cmp::Eq for Thunk {} +} + +pub(crate) use thunk::Thunk; + +/// A thunk in type position. +#[derive(Debug, Clone)] +pub(crate) enum TypeThunk { + Thunk(Thunk), + Type(Type), +} + +impl TypeThunk { + fn from_value(v: Value) -> TypeThunk { + TypeThunk::from_thunk(Thunk::from_value(v)) + } + + fn from_thunk(th: Thunk) -> TypeThunk { + TypeThunk::Thunk(th) + } + + pub(crate) fn from_type(t: Type) -> TypeThunk { + TypeThunk::Type(t) + } + + fn normalize_mut(&mut self) { + match self { + TypeThunk::Thunk(th) => th.normalize_mut(), + TypeThunk::Type(_) => {} + } + } + + pub(crate) fn normalize_nf(&self) -> Value { + match self { + TypeThunk::Thunk(th) => th.normalize_nf().clone(), + TypeThunk::Type(t) => t.to_value().normalize(), + } + } + + pub(crate) fn to_value(&self) -> Value { + match self { + TypeThunk::Thunk(th) => th.to_value(), + TypeThunk::Type(t) => t.to_value(), + } + } + + pub(crate) fn normalize_to_expr_maybe_alpha( + &self, + alpha: bool, + ) -> OutputSubExpr { + self.normalize_nf().normalize_to_expr_maybe_alpha(alpha) + } + + fn shift(&self, delta: isize, var: &AlphaVar) -> Self { + match self { + TypeThunk::Thunk(th) => TypeThunk::Thunk(th.shift(delta, var)), + TypeThunk::Type(t) => TypeThunk::Type(t.shift(delta, var)), + } + } + + pub(crate) fn subst_shift(&self, var: &AlphaVar, val: &Typed) -> Self { + match self { + TypeThunk::Thunk(th) => TypeThunk::Thunk(th.subst_shift(var, val)), + TypeThunk::Type(t) => TypeThunk::Type(t.subst_shift(var, val)), + } + } +} + +impl std::cmp::PartialEq for TypeThunk { + fn eq(&self, other: &Self) -> bool { + self.to_value() == other.to_value() + } +} +impl std::cmp::Eq for TypeThunk {} + +fn apply_builtin(b: Builtin, args: Vec<Thunk>) -> Value { + use dhall_syntax::Builtin::*; + use Value::*; + + // Return Ok((unconsumed args, returned value)), or Err(()) if value could not be produced. + let ret = match (b, args.as_slice()) { + (OptionalNone, [t, r..]) => { + Ok((r, EmptyOptionalLit(TypeThunk::from_thunk(t.clone())))) + } + (NaturalIsZero, [n, r..]) => match &*n.as_value() { + NaturalLit(n) => Ok((r, BoolLit(*n == 0))), + _ => Err(()), + }, + (NaturalEven, [n, r..]) => match &*n.as_value() { + NaturalLit(n) => Ok((r, BoolLit(*n % 2 == 0))), + _ => Err(()), + }, + (NaturalOdd, [n, r..]) => match &*n.as_value() { + NaturalLit(n) => Ok((r, BoolLit(*n % 2 != 0))), + _ => Err(()), + }, + (NaturalToInteger, [n, r..]) => match &*n.as_value() { + NaturalLit(n) => Ok((r, IntegerLit(*n as isize))), + _ => Err(()), + }, + (NaturalShow, [n, r..]) => match &*n.as_value() { + NaturalLit(n) => Ok(( + r, + TextLit(vec![InterpolatedTextContents::Text(n.to_string())]), + )), + _ => Err(()), + }, + (ListLength, [_, l, r..]) => match &*l.as_value() { + EmptyListLit(_) => Ok((r, NaturalLit(0))), + NEListLit(xs) => Ok((r, NaturalLit(xs.len()))), + _ => Err(()), + }, + (ListHead, [_, l, r..]) => match &*l.as_value() { + EmptyListLit(n) => Ok((r, EmptyOptionalLit(n.clone()))), + NEListLit(xs) => { + Ok((r, NEOptionalLit(xs.iter().next().unwrap().clone()))) + } + _ => Err(()), + }, + (ListLast, [_, l, r..]) => match &*l.as_value() { + EmptyListLit(n) => Ok((r, EmptyOptionalLit(n.clone()))), + NEListLit(xs) => { + Ok((r, NEOptionalLit(xs.iter().rev().next().unwrap().clone()))) + } + _ => Err(()), + }, + (ListReverse, [_, l, r..]) => match &*l.as_value() { + EmptyListLit(n) => Ok((r, EmptyListLit(n.clone()))), + NEListLit(xs) => { + Ok((r, NEListLit(xs.iter().rev().cloned().collect()))) + } + _ => Err(()), + }, + (ListIndexed, [_, l, r..]) => match &*l.as_value() { + EmptyListLit(t) => { + let mut kts = BTreeMap::new(); + kts.insert( + "index".into(), + TypeThunk::from_value(Value::from_builtin(Natural)), + ); + kts.insert("value".into(), t.clone()); + Ok((r, EmptyListLit(TypeThunk::from_value(RecordType(kts))))) + } + NEListLit(xs) => { + let xs = xs + .iter() + .enumerate() + .map(|(i, e)| { + let i = NaturalLit(i); + let mut kvs = BTreeMap::new(); + kvs.insert("index".into(), Thunk::from_value(i)); + kvs.insert("value".into(), e.clone()); + Thunk::from_value(RecordLit(kvs)) + }) + .collect(); + Ok((r, NEListLit(xs))) + } + _ => Err(()), + }, + (ListBuild, [t, f, r..]) => match &*f.as_value() { + // fold/build fusion + Value::AppliedBuiltin(ListFold, args) => { + if args.len() >= 2 { + Ok((r, args[1].to_value())) + } else { + // Do we really need to handle this case ? + unimplemented!() + } + } + _ => Ok(( + r, + f.app_val(Value::from_builtin(List).app_thunk(t.clone())) + .app_val(ListConsClosure( + TypeThunk::from_thunk(t.clone()), + None, + )) + .app_val(EmptyListLit(TypeThunk::from_thunk(t.clone()))), + )), + }, + (ListFold, [_, l, _, cons, nil, r..]) => match &*l.as_value() { + EmptyListLit(_) => Ok((r, nil.to_value())), + NEListLit(xs) => { + let mut v = nil.clone(); + for x in xs.iter().rev() { + v = cons + .clone() + .app_thunk(x.clone()) + .app_thunk(v) + .into_thunk(); + } + Ok((r, v.to_value())) + } + _ => Err(()), + }, + (OptionalBuild, [t, f, r..]) => match &*f.as_value() { + // fold/build fusion + Value::AppliedBuiltin(OptionalFold, args) => { + if args.len() >= 2 { + Ok((r, args[1].to_value())) + } else { + // Do we really need to handle this case ? + unimplemented!() + } + } + _ => Ok(( + r, + f.app_val(Value::from_builtin(Optional).app_thunk(t.clone())) + .app_val(OptionalSomeClosure(TypeThunk::from_thunk( + t.clone(), + ))) + .app_val(EmptyOptionalLit(TypeThunk::from_thunk( + t.clone(), + ))), + )), + }, + (OptionalFold, [_, v, _, just, nothing, r..]) => match &*v.as_value() { + EmptyOptionalLit(_) => Ok((r, nothing.to_value())), + NEOptionalLit(x) => Ok((r, just.app_thunk(x.clone()))), + _ => Err(()), + }, + (NaturalBuild, [f, r..]) => match &*f.as_value() { + // fold/build fusion + Value::AppliedBuiltin(NaturalFold, args) => { + if args.len() >= 1 { + Ok((r, args[0].to_value())) + } else { + // Do we really need to handle this case ? + unimplemented!() + } + } + _ => Ok(( + r, + f.app_val(Value::from_builtin(Natural)) + .app_val(NaturalSuccClosure) + .app_val(NaturalLit(0)), + )), + }, + (NaturalFold, [n, t, succ, zero, r..]) => match &*n.as_value() { + NaturalLit(0) => Ok((r, zero.to_value())), + NaturalLit(n) => { + let fold = Value::from_builtin(NaturalFold) + .app(NaturalLit(n - 1)) + .app_thunk(t.clone()) + .app_thunk(succ.clone()) + .app_thunk(zero.clone()); + Ok((r, succ.app_val(fold))) + } + _ => Err(()), + }, + _ => Err(()), + }; + match ret { + Ok((unconsumed_args, mut v)) => { + let n_consumed_args = args.len() - unconsumed_args.len(); + for x in args.into_iter().skip(n_consumed_args) { + v = v.app_thunk(x); + } + v + } + Err(()) => AppliedBuiltin(b, args), + } +} + +fn apply_any(f: Thunk, a: Thunk) -> Value { + let fallback = |f: Thunk, a: Thunk| Value::PartialExpr(ExprF::App(f, a)); + + let f_borrow = f.as_value(); + match &*f_borrow { + Value::Lam(x, _, e) => { + let val = Typed::from_thunk_untyped(a); + e.subst_shift(&x.into(), &val).to_value() + } + Value::AppliedBuiltin(b, args) => { + use std::iter::once; + let args = args.iter().cloned().chain(once(a.clone())).collect(); + apply_builtin(*b, args) + } + Value::OptionalSomeClosure(_) => Value::NEOptionalLit(a), + Value::ListConsClosure(t, None) => { + Value::ListConsClosure(t.clone(), Some(a)) + } + Value::ListConsClosure(_, Some(x)) => { + let a_borrow = a.as_value(); + match &*a_borrow { + Value::EmptyListLit(_) => Value::NEListLit(vec![x.clone()]), + Value::NEListLit(xs) => { + use std::iter::once; + let xs = + once(x.clone()).chain(xs.iter().cloned()).collect(); + Value::NEListLit(xs) + } + _ => { + drop(f_borrow); + drop(a_borrow); + fallback(f, a) + } + } + } + Value::NaturalSuccClosure => { + let a_borrow = a.as_value(); + match &*a_borrow { + Value::NaturalLit(n) => Value::NaturalLit(n + 1), + _ => { + drop(f_borrow); + drop(a_borrow); + fallback(f, a) + } + } + } + Value::UnionConstructor(l, kts) => { + Value::UnionLit(l.clone(), a, kts.clone()) + } + _ => { + drop(f_borrow); + fallback(f, a) + } + } +} + +fn squash_textlit( + elts: impl Iterator<Item = InterpolatedTextContents<Thunk>>, +) -> Vec<InterpolatedTextContents<Thunk>> { + use std::mem::replace; + use InterpolatedTextContents::{Expr, Text}; + + fn inner( + elts: impl Iterator<Item = InterpolatedTextContents<Thunk>>, + crnt_str: &mut String, + ret: &mut Vec<InterpolatedTextContents<Thunk>>, + ) { + for contents in elts { + match contents { + Text(s) => crnt_str.push_str(&s), + Expr(e) => { + let e_borrow = e.as_value(); + match &*e_borrow { + Value::TextLit(elts2) => { + inner(elts2.iter().cloned(), crnt_str, ret) + } + _ => { + drop(e_borrow); + if !crnt_str.is_empty() { + ret.push(Text(replace(crnt_str, String::new()))) + } + ret.push(Expr(e.clone())) + } + } + } + } + } + } + + let mut crnt_str = String::new(); + let mut ret = Vec::new(); + inner(elts, &mut crnt_str, &mut ret); + if !crnt_str.is_empty() { + ret.push(Text(replace(&mut crnt_str, String::new()))) + } + ret +} + +/// Reduces the imput expression to a Value. Evaluates as little as possible. +fn normalize_whnf(ctx: NormalizationContext, expr: InputSubExpr) -> Value { + match expr.as_ref() { + ExprF::Embed(e) => return e.to_value(), + ExprF::Var(v) => return ctx.lookup(v), + _ => {} + } + + // Thunk subexpressions + let expr: ExprF<Thunk, Label, X> = + expr.as_ref().map_ref_with_special_handling_of_binders( + |e| Thunk::new(ctx.clone(), e.clone()), + |x, e| Thunk::new(ctx.skip(x), e.clone()), + |_| unreachable!(), + Label::clone, + ); + + normalize_one_layer(expr) +} + +fn normalize_one_layer(expr: ExprF<Thunk, Label, X>) -> Value { + use Value::{ + BoolLit, EmptyListLit, EmptyOptionalLit, IntegerLit, Lam, NEListLit, + NEOptionalLit, NaturalLit, Pi, RecordLit, RecordType, TextLit, + UnionConstructor, UnionLit, UnionType, + }; + + // Small helper enum to avoid repetition + enum Ret<'a> { + RetValue(Value), + RetThunk(Thunk), + RetThunkRef(&'a Thunk), + RetExpr(ExprF<Thunk, Label, X>), + } + use Ret::{RetExpr, RetThunk, RetThunkRef, RetValue}; + + let ret = match expr { + ExprF::Embed(_) => unreachable!(), + ExprF::Var(_) => unreachable!(), + ExprF::Annot(x, _) => RetThunk(x), + ExprF::Lam(x, t, e) => RetValue(Lam(x.into(), t, e)), + ExprF::Pi(x, t, e) => RetValue(Pi( + x.into(), + TypeThunk::from_thunk(t), + TypeThunk::from_thunk(e), + )), + ExprF::Let(x, _, v, b) => { + let v = Typed::from_thunk_untyped(v); + RetThunk(b.subst_shift(&x.into(), &v)) + } + ExprF::App(v, a) => RetValue(v.app_thunk(a)), + ExprF::Builtin(b) => RetValue(Value::from_builtin(b)), + ExprF::Const(c) => RetValue(Value::Const(c)), + ExprF::BoolLit(b) => RetValue(BoolLit(b)), + ExprF::NaturalLit(n) => RetValue(NaturalLit(n)), + ExprF::IntegerLit(n) => RetValue(IntegerLit(n)), + ExprF::DoubleLit(_) => RetExpr(expr), + ExprF::OldOptionalLit(None, t) => { + RetValue(EmptyOptionalLit(TypeThunk::from_thunk(t))) + } + ExprF::OldOptionalLit(Some(e), _) => RetValue(NEOptionalLit(e)), + ExprF::SomeLit(e) => RetValue(NEOptionalLit(e)), + ExprF::EmptyListLit(t) => { + RetValue(EmptyListLit(TypeThunk::from_thunk(t))) + } + ExprF::NEListLit(elts) => { + RetValue(NEListLit(elts.into_iter().collect())) + } + ExprF::RecordLit(kvs) => RetValue(RecordLit(kvs.into_iter().collect())), + ExprF::RecordType(kts) => RetValue(RecordType( + kts.into_iter() + .map(|(k, t)| (k, TypeThunk::from_thunk(t))) + .collect(), + )), + ExprF::UnionLit(l, x, kts) => RetValue(UnionLit( + l, + x, + kts.into_iter() + .map(|(k, t)| (k, t.map(|t| TypeThunk::from_thunk(t)))) + .collect(), + )), + ExprF::UnionType(kts) => RetValue(UnionType( + kts.into_iter() + .map(|(k, t)| (k, t.map(|t| TypeThunk::from_thunk(t)))) + .collect(), + )), + ExprF::TextLit(elts) => { + use InterpolatedTextContents::Expr; + let elts: Vec<_> = squash_textlit(elts.into_iter()); + // Simplify bare interpolation + if let [Expr(th)] = elts.as_slice() { + RetThunk(th.clone()) + } else { + RetValue(TextLit(elts)) + } + } + ExprF::BoolIf(ref b, ref e1, ref e2) => { + let b_borrow = b.as_value(); + match &*b_borrow { + BoolLit(true) => RetThunkRef(e1), + BoolLit(false) => RetThunkRef(e2), + _ => { + let e1_borrow = e1.as_value(); + let e2_borrow = e2.as_value(); + match (&*e1_borrow, &*e2_borrow) { + // Simplify `if b then True else False` + (BoolLit(true), BoolLit(false)) => RetThunkRef(b), + _ if e1 == e2 => RetThunkRef(e1), + _ => { + drop(b_borrow); + drop(e1_borrow); + drop(e2_borrow); + RetExpr(expr) + } + } + } + } + } + ExprF::BinOp(o, ref x, ref y) => { + use BinOp::{ + BoolAnd, BoolEQ, BoolNE, BoolOr, ListAppend, NaturalPlus, + NaturalTimes, TextAppend, + }; + let x_borrow = x.as_value(); + let y_borrow = y.as_value(); + match (o, &*x_borrow, &*y_borrow) { + (BoolAnd, BoolLit(true), _) => RetThunkRef(y), + (BoolAnd, _, BoolLit(true)) => RetThunkRef(x), + (BoolAnd, BoolLit(false), _) => RetValue(BoolLit(false)), + (BoolAnd, _, BoolLit(false)) => RetValue(BoolLit(false)), + (BoolAnd, _, _) if x == y => RetThunkRef(x), + (BoolOr, BoolLit(true), _) => RetValue(BoolLit(true)), + (BoolOr, _, BoolLit(true)) => RetValue(BoolLit(true)), + (BoolOr, BoolLit(false), _) => RetThunkRef(y), + (BoolOr, _, BoolLit(false)) => RetThunkRef(x), + (BoolOr, _, _) if x == y => RetThunkRef(x), + (BoolEQ, BoolLit(true), _) => RetThunkRef(y), + (BoolEQ, _, BoolLit(true)) => RetThunkRef(x), + (BoolEQ, BoolLit(x), BoolLit(y)) => RetValue(BoolLit(x == y)), + (BoolEQ, _, _) if x == y => RetValue(BoolLit(true)), + (BoolNE, BoolLit(false), _) => RetThunkRef(y), + (BoolNE, _, BoolLit(false)) => RetThunkRef(x), + (BoolNE, BoolLit(x), BoolLit(y)) => RetValue(BoolLit(x != y)), + (BoolNE, _, _) if x == y => RetValue(BoolLit(false)), + + (NaturalPlus, NaturalLit(0), _) => RetThunkRef(y), + (NaturalPlus, _, NaturalLit(0)) => RetThunkRef(x), + (NaturalPlus, NaturalLit(x), NaturalLit(y)) => { + RetValue(NaturalLit(x + y)) + } + (NaturalTimes, NaturalLit(0), _) => RetValue(NaturalLit(0)), + (NaturalTimes, _, NaturalLit(0)) => RetValue(NaturalLit(0)), + (NaturalTimes, NaturalLit(1), _) => RetThunkRef(y), + (NaturalTimes, _, NaturalLit(1)) => RetThunkRef(x), + (NaturalTimes, NaturalLit(x), NaturalLit(y)) => { + RetValue(NaturalLit(x * y)) + } + + (ListAppend, EmptyListLit(_), _) => RetThunkRef(y), + (ListAppend, _, EmptyListLit(_)) => RetThunkRef(x), + (ListAppend, NEListLit(xs), NEListLit(ys)) => RetValue( + NEListLit(xs.iter().chain(ys.iter()).cloned().collect()), + ), + + (TextAppend, TextLit(x), _) if x.is_empty() => RetThunkRef(y), + (TextAppend, _, TextLit(y)) if y.is_empty() => RetThunkRef(x), + (TextAppend, TextLit(x), TextLit(y)) => RetValue(TextLit( + squash_textlit(x.iter().chain(y.iter()).cloned()), + )), + (TextAppend, TextLit(x), _) => { + use std::iter::once; + let y = InterpolatedTextContents::Expr(y.clone()); + RetValue(TextLit(squash_textlit( + x.iter().cloned().chain(once(y)), + ))) + } + (TextAppend, _, TextLit(y)) => { + use std::iter::once; + let x = InterpolatedTextContents::Expr(x.clone()); + RetValue(TextLit(squash_textlit( + once(x).chain(y.iter().cloned()), + ))) + } + + _ => { + drop(x_borrow); + drop(y_borrow); + RetExpr(expr) + } + } + } + + ExprF::Projection(_, ls) if ls.is_empty() => { + RetValue(RecordLit(std::collections::BTreeMap::new())) + } + ExprF::Projection(ref v, ref ls) => { + let v_borrow = v.as_value(); + match &*v_borrow { + RecordLit(kvs) => RetValue(RecordLit( + ls.iter() + .filter_map(|l| { + kvs.get(l).map(|x| (l.clone(), x.clone())) + }) + .collect(), + )), + _ => { + drop(v_borrow); + RetExpr(expr) + } + } + } + ExprF::Field(ref v, ref l) => { + let v_borrow = v.as_value(); + match &*v_borrow { + RecordLit(kvs) => match kvs.get(l) { + Some(r) => RetThunk(r.clone()), + None => { + drop(v_borrow); + RetExpr(expr) + } + }, + UnionType(kts) => { + RetValue(UnionConstructor(l.clone(), kts.clone())) + } + _ => { + drop(v_borrow); + RetExpr(expr) + } + } + } + + ExprF::Merge(ref handlers, ref variant, _) => { + let handlers_borrow = handlers.as_value(); + let variant_borrow = variant.as_value(); + match (&*handlers_borrow, &*variant_borrow) { + (RecordLit(kvs), UnionConstructor(l, _)) => match kvs.get(l) { + Some(h) => RetThunk(h.clone()), + None => { + drop(handlers_borrow); + drop(variant_borrow); + RetExpr(expr) + } + }, + (RecordLit(kvs), UnionLit(l, v, _)) => match kvs.get(l) { + Some(h) => RetValue(h.app_thunk(v.clone())), + None => { + drop(handlers_borrow); + drop(variant_borrow); + RetExpr(expr) + } + }, + _ => { + drop(handlers_borrow); + drop(variant_borrow); + RetExpr(expr) + } + } + } + }; + + match ret { + RetValue(v) => v, + RetThunk(th) => th.to_value(), + RetThunkRef(th) => th.to_value(), + RetExpr(expr) => Value::PartialExpr(expr), + } +} + +#[cfg(test)] +mod spec_tests { + #![rustfmt::skip] + + macro_rules! norm { + ($name:ident, $path:expr) => { + make_spec_test!(Normalization, Success, $name, $path); + }; + } + + macro_rules! alpha_norm { + ($name:ident, $path:expr) => { + make_spec_test!(AlphaNormalization, Success, $name, $path); + }; + } + + norm!(success_haskell_tutorial_access_0, "haskell-tutorial/access/0"); + norm!(success_haskell_tutorial_access_1, "haskell-tutorial/access/1"); + // norm!(success_haskell_tutorial_combineTypes_0, "haskell-tutorial/combineTypes/0"); + // norm!(success_haskell_tutorial_combineTypes_1, "haskell-tutorial/combineTypes/1"); + // norm!(success_haskell_tutorial_prefer_0, "haskell-tutorial/prefer/0"); + norm!(success_haskell_tutorial_projection_0, "haskell-tutorial/projection/0"); + + norm!(success_prelude_Bool_and_0, "prelude/Bool/and/0"); + norm!(success_prelude_Bool_and_1, "prelude/Bool/and/1"); + norm!(success_prelude_Bool_build_0, "prelude/Bool/build/0"); + norm!(success_prelude_Bool_build_1, "prelude/Bool/build/1"); + norm!(success_prelude_Bool_even_0, "prelude/Bool/even/0"); + norm!(success_prelude_Bool_even_1, "prelude/Bool/even/1"); + norm!(success_prelude_Bool_even_2, "prelude/Bool/even/2"); + norm!(success_prelude_Bool_even_3, "prelude/Bool/even/3"); + norm!(success_prelude_Bool_fold_0, "prelude/Bool/fold/0"); + norm!(success_prelude_Bool_fold_1, "prelude/Bool/fold/1"); + norm!(success_prelude_Bool_not_0, "prelude/Bool/not/0"); + norm!(success_prelude_Bool_not_1, "prelude/Bool/not/1"); + norm!(success_prelude_Bool_odd_0, "prelude/Bool/odd/0"); + norm!(success_prelude_Bool_odd_1, "prelude/Bool/odd/1"); + norm!(success_prelude_Bool_odd_2, "prelude/Bool/odd/2"); + norm!(success_prelude_Bool_odd_3, "prelude/Bool/odd/3"); + norm!(success_prelude_Bool_or_0, "prelude/Bool/or/0"); + norm!(success_prelude_Bool_or_1, "prelude/Bool/or/1"); + norm!(success_prelude_Bool_show_0, "prelude/Bool/show/0"); + norm!(success_prelude_Bool_show_1, "prelude/Bool/show/1"); + // norm!(success_prelude_Double_show_0, "prelude/Double/show/0"); + // norm!(success_prelude_Double_show_1, "prelude/Double/show/1"); + // norm!(success_prelude_Integer_show_0, "prelude/Integer/show/0"); + // norm!(success_prelude_Integer_show_1, "prelude/Integer/show/1"); + // norm!(success_prelude_Integer_toDouble_0, "prelude/Integer/toDouble/0"); + // norm!(success_prelude_Integer_toDouble_1, "prelude/Integer/toDouble/1"); + norm!(success_prelude_List_all_0, "prelude/List/all/0"); + norm!(success_prelude_List_all_1, "prelude/List/all/1"); + norm!(success_prelude_List_any_0, "prelude/List/any/0"); + norm!(success_prelude_List_any_1, "prelude/List/any/1"); + norm!(success_prelude_List_build_0, "prelude/List/build/0"); + norm!(success_prelude_List_build_1, "prelude/List/build/1"); + norm!(success_prelude_List_concat_0, "prelude/List/concat/0"); + norm!(success_prelude_List_concat_1, "prelude/List/concat/1"); + norm!(success_prelude_List_concatMap_0, "prelude/List/concatMap/0"); + norm!(success_prelude_List_concatMap_1, "prelude/List/concatMap/1"); + norm!(success_prelude_List_filter_0, "prelude/List/filter/0"); + norm!(success_prelude_List_filter_1, "prelude/List/filter/1"); + norm!(success_prelude_List_fold_0, "prelude/List/fold/0"); + norm!(success_prelude_List_fold_1, "prelude/List/fold/1"); + norm!(success_prelude_List_fold_2, "prelude/List/fold/2"); + norm!(success_prelude_List_generate_0, "prelude/List/generate/0"); + norm!(success_prelude_List_generate_1, "prelude/List/generate/1"); + norm!(success_prelude_List_head_0, "prelude/List/head/0"); + norm!(success_prelude_List_head_1, "prelude/List/head/1"); + norm!(success_prelude_List_indexed_0, "prelude/List/indexed/0"); + norm!(success_prelude_List_indexed_1, "prelude/List/indexed/1"); + norm!(success_prelude_List_iterate_0, "prelude/List/iterate/0"); + norm!(success_prelude_List_iterate_1, "prelude/List/iterate/1"); + norm!(success_prelude_List_last_0, "prelude/List/last/0"); + norm!(success_prelude_List_last_1, "prelude/List/last/1"); + norm!(success_prelude_List_length_0, "prelude/List/length/0"); + norm!(success_prelude_List_length_1, "prelude/List/length/1"); + norm!(success_prelude_List_map_0, "prelude/List/map/0"); + norm!(success_prelude_List_map_1, "prelude/List/map/1"); + norm!(success_prelude_List_null_0, "prelude/List/null/0"); + norm!(success_prelude_List_null_1, "prelude/List/null/1"); + norm!(success_prelude_List_replicate_0, "prelude/List/replicate/0"); + norm!(success_prelude_List_replicate_1, "prelude/List/replicate/1"); + norm!(success_prelude_List_reverse_0, "prelude/List/reverse/0"); + norm!(success_prelude_List_reverse_1, "prelude/List/reverse/1"); + norm!(success_prelude_List_shifted_0, "prelude/List/shifted/0"); + norm!(success_prelude_List_shifted_1, "prelude/List/shifted/1"); + norm!(success_prelude_List_unzip_0, "prelude/List/unzip/0"); + norm!(success_prelude_List_unzip_1, "prelude/List/unzip/1"); + norm!(success_prelude_Natural_build_0, "prelude/Natural/build/0"); + norm!(success_prelude_Natural_build_1, "prelude/Natural/build/1"); + norm!(success_prelude_Natural_enumerate_0, "prelude/Natural/enumerate/0"); + norm!(success_prelude_Natural_enumerate_1, "prelude/Natural/enumerate/1"); + norm!(success_prelude_Natural_even_0, "prelude/Natural/even/0"); + norm!(success_prelude_Natural_even_1, "prelude/Natural/even/1"); + norm!(success_prelude_Natural_fold_0, "prelude/Natural/fold/0"); + norm!(success_prelude_Natural_fold_1, "prelude/Natural/fold/1"); + norm!(success_prelude_Natural_fold_2, "prelude/Natural/fold/2"); + norm!(success_prelude_Natural_isZero_0, "prelude/Natural/isZero/0"); + norm!(success_prelude_Natural_isZero_1, "prelude/Natural/isZero/1"); + norm!(success_prelude_Natural_odd_0, "prelude/Natural/odd/0"); + norm!(success_prelude_Natural_odd_1, "prelude/Natural/odd/1"); + norm!(success_prelude_Natural_product_0, "prelude/Natural/product/0"); + norm!(success_prelude_Natural_product_1, "prelude/Natural/product/1"); + norm!(success_prelude_Natural_show_0, "prelude/Natural/show/0"); + norm!(success_prelude_Natural_show_1, "prelude/Natural/show/1"); + norm!(success_prelude_Natural_sum_0, "prelude/Natural/sum/0"); + norm!(success_prelude_Natural_sum_1, "prelude/Natural/sum/1"); + // norm!(success_prelude_Natural_toDouble_0, "prelude/Natural/toDouble/0"); + // norm!(success_prelude_Natural_toDouble_1, "prelude/Natural/toDouble/1"); + norm!(success_prelude_Natural_toInteger_0, "prelude/Natural/toInteger/0"); + norm!(success_prelude_Natural_toInteger_1, "prelude/Natural/toInteger/1"); + norm!(success_prelude_Optional_all_0, "prelude/Optional/all/0"); + norm!(success_prelude_Optional_all_1, "prelude/Optional/all/1"); + norm!(success_prelude_Optional_any_0, "prelude/Optional/any/0"); + norm!(success_prelude_Optional_any_1, "prelude/Optional/any/1"); + norm!(success_prelude_Optional_build_0, "prelude/Optional/build/0"); + norm!(success_prelude_Optional_build_1, "prelude/Optional/build/1"); + norm!(success_prelude_Optional_concat_0, "prelude/Optional/concat/0"); + norm!(success_prelude_Optional_concat_1, "prelude/Optional/concat/1"); + norm!(success_prelude_Optional_concat_2, "prelude/Optional/concat/2"); + norm!(success_prelude_Optional_filter_0, "prelude/Optional/filter/0"); + norm!(success_prelude_Optional_filter_1, "prelude/Optional/filter/1"); + norm!(success_prelude_Optional_fold_0, "prelude/Optional/fold/0"); + norm!(success_prelude_Optional_fold_1, "prelude/Optional/fold/1"); + norm!(success_prelude_Optional_head_0, "prelude/Optional/head/0"); + norm!(success_prelude_Optional_head_1, "prelude/Optional/head/1"); + norm!(success_prelude_Optional_head_2, "prelude/Optional/head/2"); + norm!(success_prelude_Optional_last_0, "prelude/Optional/last/0"); + norm!(success_prelude_Optional_last_1, "prelude/Optional/last/1"); + norm!(success_prelude_Optional_last_2, "prelude/Optional/last/2"); + norm!(success_prelude_Optional_length_0, "prelude/Optional/length/0"); + norm!(success_prelude_Optional_length_1, "prelude/Optional/length/1"); + norm!(success_prelude_Optional_map_0, "prelude/Optional/map/0"); + norm!(success_prelude_Optional_map_1, "prelude/Optional/map/1"); + norm!(success_prelude_Optional_null_0, "prelude/Optional/null/0"); + norm!(success_prelude_Optional_null_1, "prelude/Optional/null/1"); + norm!(success_prelude_Optional_toList_0, "prelude/Optional/toList/0"); + norm!(success_prelude_Optional_toList_1, "prelude/Optional/toList/1"); + norm!(success_prelude_Optional_unzip_0, "prelude/Optional/unzip/0"); + norm!(success_prelude_Optional_unzip_1, "prelude/Optional/unzip/1"); + norm!(success_prelude_Text_concat_0, "prelude/Text/concat/0"); + norm!(success_prelude_Text_concat_1, "prelude/Text/concat/1"); + norm!(success_prelude_Text_concatMap_0, "prelude/Text/concatMap/0"); + norm!(success_prelude_Text_concatMap_1, "prelude/Text/concatMap/1"); + // norm!(success_prelude_Text_concatMapSep_0, "prelude/Text/concatMapSep/0"); + // norm!(success_prelude_Text_concatMapSep_1, "prelude/Text/concatMapSep/1"); + // norm!(success_prelude_Text_concatSep_0, "prelude/Text/concatSep/0"); + // norm!(success_prelude_Text_concatSep_1, "prelude/Text/concatSep/1"); + // norm!(success_prelude_Text_show_0, "prelude/Text/show/0"); + // norm!(success_prelude_Text_show_1, "prelude/Text/show/1"); + // norm!(success_remoteSystems, "remoteSystems"); + + // norm!(success_simple_doubleShow, "simple/doubleShow"); + norm!(success_simple_enum, "simple/enum"); + // norm!(success_simple_integerShow, "simple/integerShow"); + // norm!(success_simple_integerToDouble, "simple/integerToDouble"); + norm!(success_simple_letlet, "simple/letlet"); + norm!(success_simple_listBuild, "simple/listBuild"); + norm!(success_simple_multiLine, "simple/multiLine"); + norm!(success_simple_naturalBuild, "simple/naturalBuild"); + norm!(success_simple_naturalPlus, "simple/naturalPlus"); + norm!(success_simple_naturalShow, "simple/naturalShow"); + norm!(success_simple_naturalToInteger, "simple/naturalToInteger"); + norm!(success_simple_optionalBuild, "simple/optionalBuild"); + norm!(success_simple_optionalBuildFold, "simple/optionalBuildFold"); + norm!(success_simple_optionalFold, "simple/optionalFold"); + // norm!(success_simple_sortOperator, "simple/sortOperator"); + norm!(success_simplifications_and, "simplifications/and"); + norm!(success_simplifications_eq, "simplifications/eq"); + norm!(success_simplifications_ifThenElse, "simplifications/ifThenElse"); + norm!(success_simplifications_ne, "simplifications/ne"); + norm!(success_simplifications_or, "simplifications/or"); + + norm!(success_unit_Bool, "unit/Bool"); + norm!(success_unit_Double, "unit/Double"); + norm!(success_unit_DoubleLiteral, "unit/DoubleLiteral"); + norm!(success_unit_DoubleShow, "unit/DoubleShow"); + // norm!(success_unit_DoubleShowValue, "unit/DoubleShowValue"); + norm!(success_unit_EmptyAlternative, "unit/EmptyAlternative"); + norm!(success_unit_FunctionApplicationCapture, "unit/FunctionApplicationCapture"); + norm!(success_unit_FunctionApplicationNoSubstitute, "unit/FunctionApplicationNoSubstitute"); + norm!(success_unit_FunctionApplicationNormalizeArguments, "unit/FunctionApplicationNormalizeArguments"); + norm!(success_unit_FunctionApplicationSubstitute, "unit/FunctionApplicationSubstitute"); + norm!(success_unit_FunctionNormalizeArguments, "unit/FunctionNormalizeArguments"); + norm!(success_unit_FunctionTypeNormalizeArguments, "unit/FunctionTypeNormalizeArguments"); + norm!(success_unit_IfAlternativesIdentical, "unit/IfAlternativesIdentical"); + norm!(success_unit_IfFalse, "unit/IfFalse"); + norm!(success_unit_IfNormalizePredicateAndBranches, "unit/IfNormalizePredicateAndBranches"); + norm!(success_unit_IfTrivial, "unit/IfTrivial"); + norm!(success_unit_IfTrue, "unit/IfTrue"); + norm!(success_unit_Integer, "unit/Integer"); + norm!(success_unit_IntegerNegative, "unit/IntegerNegative"); + norm!(success_unit_IntegerPositive, "unit/IntegerPositive"); + // norm!(success_unit_IntegerShow_12, "unit/IntegerShow-12"); + // norm!(success_unit_IntegerShow12, "unit/IntegerShow12"); + norm!(success_unit_IntegerShow, "unit/IntegerShow"); + // norm!(success_unit_IntegerToDouble_12, "unit/IntegerToDouble-12"); + // norm!(success_unit_IntegerToDouble12, "unit/IntegerToDouble12"); + norm!(success_unit_IntegerToDouble, "unit/IntegerToDouble"); + norm!(success_unit_Kind, "unit/Kind"); + norm!(success_unit_Let, "unit/Let"); + norm!(success_unit_LetWithType, "unit/LetWithType"); + norm!(success_unit_List, "unit/List"); + norm!(success_unit_ListBuild, "unit/ListBuild"); + norm!(success_unit_ListBuildFoldFusion, "unit/ListBuildFoldFusion"); + norm!(success_unit_ListBuildImplementation, "unit/ListBuildImplementation"); + norm!(success_unit_ListFold, "unit/ListFold"); + norm!(success_unit_ListFoldEmpty, "unit/ListFoldEmpty"); + norm!(success_unit_ListFoldOne, "unit/ListFoldOne"); + norm!(success_unit_ListHead, "unit/ListHead"); + norm!(success_unit_ListHeadEmpty, "unit/ListHeadEmpty"); + norm!(success_unit_ListHeadOne, "unit/ListHeadOne"); + norm!(success_unit_ListIndexed, "unit/ListIndexed"); + norm!(success_unit_ListIndexedEmpty, "unit/ListIndexedEmpty"); + norm!(success_unit_ListIndexedOne, "unit/ListIndexedOne"); + norm!(success_unit_ListLast, "unit/ListLast"); + norm!(success_unit_ListLastEmpty, "unit/ListLastEmpty"); + norm!(success_unit_ListLastOne, "unit/ListLastOne"); + norm!(success_unit_ListLength, "unit/ListLength"); + norm!(success_unit_ListLengthEmpty, "unit/ListLengthEmpty"); + norm!(success_unit_ListLengthOne, "unit/ListLengthOne"); + norm!(success_unit_ListNormalizeElements, "unit/ListNormalizeElements"); + norm!(success_unit_ListNormalizeTypeAnnotation, "unit/ListNormalizeTypeAnnotation"); + norm!(success_unit_ListReverse, "unit/ListReverse"); + norm!(success_unit_ListReverseEmpty, "unit/ListReverseEmpty"); + norm!(success_unit_ListReverseTwo, "unit/ListReverseTwo"); + norm!(success_unit_Merge, "unit/Merge"); + norm!(success_unit_MergeEmptyAlternative, "unit/MergeEmptyAlternative"); + norm!(success_unit_MergeNormalizeArguments, "unit/MergeNormalizeArguments"); + norm!(success_unit_MergeWithType, "unit/MergeWithType"); + norm!(success_unit_MergeWithTypeNormalizeArguments, "unit/MergeWithTypeNormalizeArguments"); + norm!(success_unit_Natural, "unit/Natural"); + norm!(success_unit_NaturalBuild, "unit/NaturalBuild"); + norm!(success_unit_NaturalBuildFoldFusion, "unit/NaturalBuildFoldFusion"); + norm!(success_unit_NaturalBuildImplementation, "unit/NaturalBuildImplementation"); + norm!(success_unit_NaturalEven, "unit/NaturalEven"); + norm!(success_unit_NaturalEvenOne, "unit/NaturalEvenOne"); + norm!(success_unit_NaturalEvenZero, "unit/NaturalEvenZero"); + norm!(success_unit_NaturalFold, "unit/NaturalFold"); + norm!(success_unit_NaturalFoldOne, "unit/NaturalFoldOne"); + norm!(success_unit_NaturalFoldZero, "unit/NaturalFoldZero"); + norm!(success_unit_NaturalIsZero, "unit/NaturalIsZero"); + norm!(success_unit_NaturalIsZeroOne, "unit/NaturalIsZeroOne"); + norm!(success_unit_NaturalIsZeroZero, "unit/NaturalIsZeroZero"); + norm!(success_unit_NaturalLiteral, "unit/NaturalLiteral"); + norm!(success_unit_NaturalOdd, "unit/NaturalOdd"); + norm!(success_unit_NaturalOddOne, "unit/NaturalOddOne"); + norm!(success_unit_NaturalOddZero, "unit/NaturalOddZero"); + norm!(success_unit_NaturalShow, "unit/NaturalShow"); + norm!(success_unit_NaturalShowOne, "unit/NaturalShowOne"); + norm!(success_unit_NaturalToInteger, "unit/NaturalToInteger"); + norm!(success_unit_NaturalToIntegerOne, "unit/NaturalToIntegerOne"); + norm!(success_unit_None, "unit/None"); + norm!(success_unit_NoneNatural, "unit/NoneNatural"); + norm!(success_unit_OperatorAndEquivalentArguments, "unit/OperatorAndEquivalentArguments"); + norm!(success_unit_OperatorAndLhsFalse, "unit/OperatorAndLhsFalse"); + norm!(success_unit_OperatorAndLhsTrue, "unit/OperatorAndLhsTrue"); + norm!(success_unit_OperatorAndNormalizeArguments, "unit/OperatorAndNormalizeArguments"); + norm!(success_unit_OperatorAndRhsFalse, "unit/OperatorAndRhsFalse"); + norm!(success_unit_OperatorAndRhsTrue, "unit/OperatorAndRhsTrue"); + norm!(success_unit_OperatorEqualEquivalentArguments, "unit/OperatorEqualEquivalentArguments"); + norm!(success_unit_OperatorEqualLhsTrue, "unit/OperatorEqualLhsTrue"); + norm!(success_unit_OperatorEqualNormalizeArguments, "unit/OperatorEqualNormalizeArguments"); + norm!(success_unit_OperatorEqualRhsTrue, "unit/OperatorEqualRhsTrue"); + norm!(success_unit_OperatorListConcatenateLhsEmpty, "unit/OperatorListConcatenateLhsEmpty"); + norm!(success_unit_OperatorListConcatenateListList, "unit/OperatorListConcatenateListList"); + norm!(success_unit_OperatorListConcatenateNormalizeArguments, "unit/OperatorListConcatenateNormalizeArguments"); + norm!(success_unit_OperatorListConcatenateRhsEmpty, "unit/OperatorListConcatenateRhsEmpty"); + norm!(success_unit_OperatorNotEqualEquivalentArguments, "unit/OperatorNotEqualEquivalentArguments"); + norm!(success_unit_OperatorNotEqualLhsFalse, "unit/OperatorNotEqualLhsFalse"); + norm!(success_unit_OperatorNotEqualNormalizeArguments, "unit/OperatorNotEqualNormalizeArguments"); + norm!(success_unit_OperatorNotEqualRhsFalse, "unit/OperatorNotEqualRhsFalse"); + norm!(success_unit_OperatorOrEquivalentArguments, "unit/OperatorOrEquivalentArguments"); + norm!(success_unit_OperatorOrLhsFalse, "unit/OperatorOrLhsFalse"); + norm!(success_unit_OperatorOrLhsTrue, "unit/OperatorOrLhsTrue"); + norm!(success_unit_OperatorOrNormalizeArguments, "unit/OperatorOrNormalizeArguments"); + norm!(success_unit_OperatorOrRhsFalse, "unit/OperatorOrRhsFalse"); + norm!(success_unit_OperatorOrRhsTrue, "unit/OperatorOrRhsTrue"); + norm!(success_unit_OperatorPlusLhsZero, "unit/OperatorPlusLhsZero"); + norm!(success_unit_OperatorPlusNormalizeArguments, "unit/OperatorPlusNormalizeArguments"); + norm!(success_unit_OperatorPlusOneAndOne, "unit/OperatorPlusOneAndOne"); + norm!(success_unit_OperatorPlusRhsZero, "unit/OperatorPlusRhsZero"); + norm!(success_unit_OperatorTextConcatenateLhsEmpty, "unit/OperatorTextConcatenateLhsEmpty"); + norm!(success_unit_OperatorTextConcatenateLhsNonEmpty, "unit/OperatorTextConcatenateLhsNonEmpty"); + norm!(success_unit_OperatorTextConcatenateTextText, "unit/OperatorTextConcatenateTextText"); + norm!(success_unit_OperatorTimesLhsOne, "unit/OperatorTimesLhsOne"); + norm!(success_unit_OperatorTimesLhsZero, "unit/OperatorTimesLhsZero"); + norm!(success_unit_OperatorTimesNormalizeArguments, "unit/OperatorTimesNormalizeArguments"); + norm!(success_unit_OperatorTimesRhsOne, "unit/OperatorTimesRhsOne"); + norm!(success_unit_OperatorTimesRhsZero, "unit/OperatorTimesRhsZero"); + norm!(success_unit_OperatorTimesTwoAndTwo, "unit/OperatorTimesTwoAndTwo"); + norm!(success_unit_Optional, "unit/Optional"); + norm!(success_unit_OptionalBuild, "unit/OptionalBuild"); + norm!(success_unit_OptionalBuildFoldFusion, "unit/OptionalBuildFoldFusion"); + norm!(success_unit_OptionalBuildImplementation, "unit/OptionalBuildImplementation"); + norm!(success_unit_OptionalFold, "unit/OptionalFold"); + norm!(success_unit_OptionalFoldNone, "unit/OptionalFoldNone"); + norm!(success_unit_OptionalFoldSome, "unit/OptionalFoldSome"); + norm!(success_unit_Record, "unit/Record"); + norm!(success_unit_RecordEmpty, "unit/RecordEmpty"); + norm!(success_unit_RecordProjection, "unit/RecordProjection"); + norm!(success_unit_RecordProjectionEmpty, "unit/RecordProjectionEmpty"); + norm!(success_unit_RecordProjectionNormalizeArguments, "unit/RecordProjectionNormalizeArguments"); + norm!(success_unit_RecordSelection, "unit/RecordSelection"); + norm!(success_unit_RecordSelectionNormalizeArguments, "unit/RecordSelectionNormalizeArguments"); + norm!(success_unit_RecordType, "unit/RecordType"); + norm!(success_unit_RecordTypeEmpty, "unit/RecordTypeEmpty"); + // norm!(success_unit_RecursiveRecordMergeCollision, "unit/RecursiveRecordMergeCollision"); + // norm!(success_unit_RecursiveRecordMergeLhsEmpty, "unit/RecursiveRecordMergeLhsEmpty"); + // norm!(success_unit_RecursiveRecordMergeNoCollision, "unit/RecursiveRecordMergeNoCollision"); + // norm!(success_unit_RecursiveRecordMergeNormalizeArguments, "unit/RecursiveRecordMergeNormalizeArguments"); + // norm!(success_unit_RecursiveRecordMergeRhsEmpty, "unit/RecursiveRecordMergeRhsEmpty"); + // norm!(success_unit_RecursiveRecordTypeMergeCollision, "unit/RecursiveRecordTypeMergeCollision"); + // norm!(success_unit_RecursiveRecordTypeMergeLhsEmpty, "unit/RecursiveRecordTypeMergeLhsEmpty"); + // norm!(success_unit_RecursiveRecordTypeMergeNoCollision, "unit/RecursiveRecordTypeMergeNoCollision"); + // norm!(success_unit_RecursiveRecordTypeMergeNormalizeArguments, "unit/RecursiveRecordTypeMergeNormalizeArguments"); + // norm!(success_unit_RecursiveRecordTypeMergeRhsEmpty, "unit/RecursiveRecordTypeMergeRhsEmpty"); + // norm!(success_unit_RecursiveRecordTypeMergeSorts, "unit/RecursiveRecordTypeMergeSorts"); + // norm!(success_unit_RightBiasedRecordMergeCollision, "unit/RightBiasedRecordMergeCollision"); + // norm!(success_unit_RightBiasedRecordMergeLhsEmpty, "unit/RightBiasedRecordMergeLhsEmpty"); + // norm!(success_unit_RightBiasedRecordMergeNoCollision, "unit/RightBiasedRecordMergeNoCollision"); + // norm!(success_unit_RightBiasedRecordMergeNormalizeArguments, "unit/RightBiasedRecordMergeNormalizeArguments"); + // norm!(success_unit_RightBiasedRecordMergeRhsEmpty, "unit/RightBiasedRecordMergeRhsEmpty"); + norm!(success_unit_SomeNormalizeArguments, "unit/SomeNormalizeArguments"); + norm!(success_unit_Sort, "unit/Sort"); + norm!(success_unit_Text, "unit/Text"); + norm!(success_unit_TextInterpolate, "unit/TextInterpolate"); + norm!(success_unit_TextLiteral, "unit/TextLiteral"); + norm!(success_unit_TextNormalizeInterpolations, "unit/TextNormalizeInterpolations"); + norm!(success_unit_TextShow, "unit/TextShow"); + // norm!(success_unit_TextShowAllEscapes, "unit/TextShowAllEscapes"); + norm!(success_unit_True, "unit/True"); + norm!(success_unit_Type, "unit/Type"); + norm!(success_unit_TypeAnnotation, "unit/TypeAnnotation"); + norm!(success_unit_UnionNormalizeAlternatives, "unit/UnionNormalizeAlternatives"); + norm!(success_unit_UnionNormalizeArguments, "unit/UnionNormalizeArguments"); + norm!(success_unit_UnionProjectConstructor, "unit/UnionProjectConstructor"); + norm!(success_unit_UnionSortAlternatives, "unit/UnionSortAlternatives"); + norm!(success_unit_UnionType, "unit/UnionType"); + norm!(success_unit_UnionTypeEmpty, "unit/UnionTypeEmpty"); + norm!(success_unit_UnionTypeNormalizeArguments, "unit/UnionTypeNormalizeArguments"); + norm!(success_unit_Variable, "unit/Variable"); + + alpha_norm!(alpha_success_unit_FunctionBindingUnderscore, "unit/FunctionBindingUnderscore"); + alpha_norm!(alpha_success_unit_FunctionBindingX, "unit/FunctionBindingX"); + alpha_norm!(alpha_success_unit_FunctionNestedBindingX, "unit/FunctionNestedBindingX"); + alpha_norm!(alpha_success_unit_FunctionTypeBindingUnderscore, "unit/FunctionTypeBindingUnderscore"); + alpha_norm!(alpha_success_unit_FunctionTypeBindingX, "unit/FunctionTypeBindingX"); + alpha_norm!(alpha_success_unit_FunctionTypeNestedBindingX, "unit/FunctionTypeNestedBindingX"); +} diff --git a/dhall/src/phase/parse.rs b/dhall/src/phase/parse.rs new file mode 100644 index 0000000..809faf4 --- /dev/null +++ b/dhall/src/phase/parse.rs @@ -0,0 +1,38 @@ +use std::fs::File; +use std::io::Read; +use std::path::Path; + +use dhall_syntax::parse_expr; + +use crate::error::Error; +use crate::phase::resolve::ImportRoot; +use crate::phase::Parsed; + +pub(crate) fn parse_file(f: &Path) -> Result<Parsed, Error> { + let mut buffer = String::new(); + File::open(f)?.read_to_string(&mut buffer)?; + let expr = parse_expr(&*buffer)?; + let root = ImportRoot::LocalDir(f.parent().unwrap().to_owned()); + Ok(Parsed(expr.unnote().note_absurd(), root)) +} + +pub(crate) fn parse_str(s: &str) -> Result<Parsed, Error> { + let expr = parse_expr(s)?; + let root = ImportRoot::LocalDir(std::env::current_dir()?); + Ok(Parsed(expr, root)) +} + +pub(crate) fn parse_binary_file(f: &Path) -> Result<Parsed, Error> { + let mut buffer = Vec::new(); + File::open(f)?.read_to_end(&mut buffer)?; + let expr = crate::phase::binary::decode(&buffer)?; + let root = ImportRoot::LocalDir(f.parent().unwrap().to_owned()); + Ok(Parsed(expr.note_absurd(), root)) +} + +#[cfg(test)] +mod spec_tests { + #![rustfmt::skip] + // See build.rs + include!(concat!(env!("OUT_DIR"), "/parser_tests.rs")); +} diff --git a/dhall/src/phase/resolve.rs b/dhall/src/phase/resolve.rs new file mode 100644 index 0000000..afb49cb --- /dev/null +++ b/dhall/src/phase/resolve.rs @@ -0,0 +1,139 @@ +use std::collections::HashMap; +use std::path::{Path, PathBuf}; + +use dhall_syntax::Import; + +use crate::error::Error; +use crate::phase::{Normalized, Parsed, Resolved}; + +#[derive(Debug)] +pub enum ImportError { + Recursive(Import, Box<Error>), + UnexpectedImport(Import), + ImportCycle(ImportStack, Import), +} + +/// A root from which to resolve relative imports. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ImportRoot { + LocalDir(PathBuf), +} + +type ImportCache = HashMap<Import, Normalized>; + +type ImportStack = Vec<Import>; + +fn resolve_import( + import: &Import, + root: &ImportRoot, + import_cache: &mut ImportCache, + import_stack: &ImportStack, +) -> Result<Normalized, ImportError> { + use self::ImportRoot::*; + use dhall_syntax::FilePrefix::*; + use dhall_syntax::ImportLocation::*; + let cwd = match root { + LocalDir(cwd) => cwd, + }; + match &import.location_hashed.location { + Local(prefix, path) => { + let path = match prefix { + // TODO: fail gracefully + Parent => cwd.parent().unwrap().join(path), + Here => cwd.join(path), + _ => unimplemented!("{:?}", import), + }; + Ok(load_import(&path, import_cache, import_stack).map_err(|e| { + ImportError::Recursive(import.clone(), Box::new(e)) + })?) + } + _ => unimplemented!("{:?}", import), + } +} + +fn load_import( + f: &Path, + import_cache: &mut ImportCache, + import_stack: &ImportStack, +) -> Result<Normalized, Error> { + Ok( + do_resolve_expr(Parsed::parse_file(f)?, import_cache, import_stack)? + .typecheck()? + .normalize(), + ) +} + +fn do_resolve_expr( + Parsed(expr, root): Parsed, + import_cache: &mut ImportCache, + import_stack: &ImportStack, +) -> Result<Resolved, ImportError> { + let resolve = |import: &Import| -> Result<Normalized, ImportError> { + if import_stack.contains(import) { + return Err(ImportError::ImportCycle( + import_stack.clone(), + import.clone(), + )); + } + match import_cache.get(import) { + Some(expr) => Ok(expr.clone()), + None => { + // Copy the import stack and push the current import + let mut import_stack = import_stack.clone(); + import_stack.push(import.clone()); + + // Resolve the import recursively + let expr = + resolve_import(import, &root, import_cache, &import_stack)?; + + // Add the import to the cache + import_cache.insert(import.clone(), expr.clone()); + Ok(expr) + } + } + }; + let expr = expr.traverse_embed(resolve)?; + Ok(Resolved(expr)) +} + +pub(crate) fn resolve(e: Parsed) -> Result<Resolved, ImportError> { + do_resolve_expr(e, &mut HashMap::new(), &Vec::new()) +} + +pub(crate) fn skip_resolve_expr( + Parsed(expr, _root): Parsed, +) -> Result<Resolved, ImportError> { + let resolve = |import: &Import| -> Result<Normalized, ImportError> { + Err(ImportError::UnexpectedImport(import.clone())) + }; + let expr = expr.traverse_embed(resolve)?; + Ok(Resolved(expr)) +} + +#[cfg(test)] +mod spec_tests { + #![rustfmt::skip] + + macro_rules! import_success { + ($name:ident, $path:expr) => { + make_spec_test!(Import, Success, $name, $path); + }; + } + + // macro_rules! import_failure { + // ($name:ident, $path:expr) => { + // make_spec_test!(Import, Failure, $name, $path); + // }; + // } + + // import_success!(success_alternativeEnvNatural, "alternativeEnvNatural"); + // import_success!(success_alternativeEnvSimple, "alternativeEnvSimple"); + // import_success!(success_alternativeNatural, "alternativeNatural"); + // import_success!(success_asText, "asText"); + import_success!(success_fieldOrder, "fieldOrder"); + // import_failure!(failure_alternativeEnv, "alternativeEnv"); + // import_failure!(failure_alternativeEnvMissing, "alternativeEnvMissing"); + // import_failure!(failure_cycle, "cycle"); + // import_failure!(failure_missing, "missing"); + // import_failure!(failure_referentiallyInsane, "referentiallyInsane"); +} diff --git a/dhall/src/phase/typecheck.rs b/dhall/src/phase/typecheck.rs new file mode 100644 index 0000000..6b12077 --- /dev/null +++ b/dhall/src/phase/typecheck.rs @@ -0,0 +1,1276 @@ +#![allow(non_snake_case)] +use std::borrow::Borrow; +use std::borrow::Cow; +use std::collections::BTreeMap; +use std::fmt; + +use crate::phase::normalize::{ + AlphaVar, NormalizationContext, Thunk, TypeThunk, Value, +}; +use crate::phase::*; +use crate::traits::DynamicType; +use dhall_proc_macros as dhall; +use dhall_syntax; +use dhall_syntax::context::Context; +use dhall_syntax::*; + +use self::TypeMessage::*; + +impl TypeThunk { + fn to_type(&self, ctx: &TypecheckContext) -> Result<Type, TypeError> { + match self { + TypeThunk::Type(t) => Ok(t.clone()), + TypeThunk::Thunk(th) => { + // TODO: rule out statically + mktype(ctx, th.normalize_to_expr().embed_absurd().note_absurd()) + } + } + } +} + +#[derive(Debug, Clone)] +pub(crate) enum EnvItem { + Type(AlphaVar, Type), + Value(Normalized), +} + +impl EnvItem { + fn shift(&self, delta: isize, var: &AlphaVar) -> Self { + use EnvItem::*; + match self { + Type(v, e) => Type(v.shift(delta, var), e.shift(delta, var)), + Value(e) => Value(e.shift(delta, var)), + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct TypecheckContext(pub(crate) Context<Label, EnvItem>); + +impl TypecheckContext { + pub(crate) fn new() -> Self { + TypecheckContext(Context::new()) + } + pub(crate) fn insert_type(&self, x: &Label, t: Type) -> Self { + TypecheckContext( + self.0.map(|_, e| e.shift(1, &x.into())).insert( + x.clone(), + EnvItem::Type(x.into(), t.shift(1, &x.into())), + ), + ) + } + pub(crate) fn insert_value(&self, x: &Label, t: Normalized) -> Self { + TypecheckContext(self.0.insert(x.clone(), EnvItem::Value(t))) + } + pub(crate) fn lookup(&self, var: &V<Label>) -> Option<Cow<'_, Type>> { + let V(x, n) = var; + match self.0.lookup(x, *n) { + Some(EnvItem::Type(_, t)) => Some(Cow::Borrowed(&t)), + Some(EnvItem::Value(t)) => Some(t.get_type()?), + None => None, + } + } + fn to_normalization_ctx(&self) -> NormalizationContext { + NormalizationContext::from_typecheck_ctx(self) + } +} + +impl PartialEq for TypecheckContext { + fn eq(&self, _: &Self) -> bool { + // don't count contexts when comparing stuff + // this is dirty but needed for now + true + } +} +impl Eq for TypecheckContext {} + +fn function_check(a: Const, b: Const) -> Result<Const, ()> { + use dhall_syntax::Const::*; + match (a, b) { + (_, Type) => Ok(Type), + (Kind, Kind) => Ok(Kind), + (Sort, Sort) => Ok(Sort), + (Sort, Kind) => Ok(Sort), + _ => Err(()), + } +} + +// Equality up to alpha-equivalence (renaming of bound variables) +fn prop_equal<T, U>(eL0: T, eR0: U) -> bool +where + T: Borrow<Type>, + U: Borrow<Type>, +{ + eL0.borrow().to_value() == eR0.borrow().to_value() +} + +pub(crate) fn const_to_typed(c: Const) -> Typed { + match c { + Const::Sort => Typed::const_sort(), + _ => Typed::from_thunk_and_type( + Value::Const(c).into_thunk(), + type_of_const(c).unwrap(), + ), + } +} + +fn const_to_type(c: Const) -> Type { + Type(TypeInternal::Const(c)) +} + +pub(crate) fn type_of_const(c: Const) -> Result<Type, TypeError> { + match c { + Const::Type => Ok(Type::const_kind()), + Const::Kind => Ok(Type::const_sort()), + Const::Sort => { + return Err(TypeError::new( + &TypecheckContext::new(), + TypeMessage::Sort, + )) + } + } +} + +fn type_of_builtin<E>(b: Builtin) -> Expr<X, E> { + use dhall_syntax::Builtin::*; + match b { + Bool | Natural | Integer | Double | Text => dhall::expr!(Type), + List | Optional => dhall::expr!( + Type -> Type + ), + + NaturalFold => dhall::expr!( + Natural -> + forall (natural: Type) -> + forall (succ: natural -> natural) -> + forall (zero: natural) -> + natural + ), + NaturalBuild => dhall::expr!( + (forall (natural: Type) -> + forall (succ: natural -> natural) -> + forall (zero: natural) -> + natural) -> + Natural + ), + NaturalIsZero | NaturalEven | NaturalOdd => dhall::expr!( + Natural -> Bool + ), + NaturalToInteger => dhall::expr!(Natural -> Integer), + NaturalShow => dhall::expr!(Natural -> Text), + + IntegerToDouble => dhall::expr!(Integer -> Double), + IntegerShow => dhall::expr!(Integer -> Text), + DoubleShow => dhall::expr!(Double -> Text), + TextShow => dhall::expr!(Text -> Text), + + ListBuild => dhall::expr!( + forall (a: Type) -> + (forall (list: Type) -> + forall (cons: a -> list -> list) -> + forall (nil: list) -> + list) -> + List a + ), + ListFold => dhall::expr!( + forall (a: Type) -> + List a -> + forall (list: Type) -> + forall (cons: a -> list -> list) -> + forall (nil: list) -> + list + ), + ListLength => dhall::expr!(forall (a: Type) -> List a -> Natural), + ListHead | ListLast => { + dhall::expr!(forall (a: Type) -> List a -> Optional a) + } + ListIndexed => dhall::expr!( + forall (a: Type) -> + List a -> + List { index: Natural, value: a } + ), + ListReverse => dhall::expr!( + forall (a: Type) -> List a -> List a + ), + + OptionalBuild => dhall::expr!( + forall (a: Type) -> + (forall (optional: Type) -> + forall (just: a -> optional) -> + forall (nothing: optional) -> + optional) -> + Optional a + ), + OptionalFold => dhall::expr!( + forall (a: Type) -> + Optional a -> + forall (optional: Type) -> + forall (just: a -> optional) -> + forall (nothing: optional) -> + optional + ), + OptionalNone => dhall::expr!( + forall (a: Type) -> Optional a + ), + } +} + +macro_rules! ensure_equal { + ($x:expr, $y:expr, $err:expr $(,)*) => { + if !prop_equal($x, $y) { + return Err($err); + } + }; +} + +// Ensure the provided type has type `Type` +macro_rules! ensure_simple_type { + ($x:expr, $err:expr $(,)*) => {{ + match $x.get_type()?.as_const() { + Some(dhall_syntax::Const::Type) => {} + _ => return Err($err), + } + }}; +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) enum TypeIntermediate { + Pi(TypecheckContext, Label, Type, Type), + RecordType(TypecheckContext, BTreeMap<Label, Type>), + UnionType(TypecheckContext, BTreeMap<Label, Option<Type>>), + ListType(TypecheckContext, Type), + OptionalType(TypecheckContext, Type), +} + +impl TypeIntermediate { + fn typecheck(self) -> Result<Typed, TypeError> { + let mkerr = |ctx, msg| TypeError::new(ctx, msg); + Ok(match &self { + TypeIntermediate::Pi(ctx, x, ta, tb) => { + let ctx2 = ctx.insert_type(x, ta.clone()); + + let kA = match ta.get_type()?.as_const() { + Some(k) => k, + _ => { + return Err(mkerr( + ctx, + InvalidInputType(ta.clone().to_normalized()), + )) + } + }; + + let kB = match tb.get_type()?.as_const() { + Some(k) => k, + _ => { + return Err(mkerr( + &ctx2, + InvalidOutputType( + tb.clone() + .to_normalized() + .get_type()? + .to_normalized(), + ), + )) + } + }; + + let k = match function_check(kA, kB) { + Ok(k) => k, + Err(()) => { + return Err(mkerr( + ctx, + NoDependentTypes( + ta.clone().to_normalized(), + tb.clone() + .to_normalized() + .get_type()? + .to_normalized(), + ), + )) + } + }; + + Typed::from_thunk_and_type( + Value::Pi( + x.clone().into(), + TypeThunk::from_type(ta.clone()), + TypeThunk::from_type(tb.clone()), + ) + .into_thunk(), + const_to_type(k), + ) + } + TypeIntermediate::RecordType(ctx, kts) => { + // Check that all types are the same const + let mut k = None; + for (x, t) in kts { + match (k, t.get_type()?.as_const()) { + (None, Some(k2)) => k = Some(k2), + (Some(k1), Some(k2)) if k1 == k2 => {} + _ => { + return Err(mkerr( + ctx, + InvalidFieldType(x.clone(), t.clone()), + )) + } + } + } + // An empty record type has type Type + let k = k.unwrap_or(dhall_syntax::Const::Type); + + Typed::from_thunk_and_type( + Value::RecordType( + kts.iter() + .map(|(k, t)| { + (k.clone(), TypeThunk::from_type(t.clone())) + }) + .collect(), + ) + .into_thunk(), + const_to_type(k), + ) + } + TypeIntermediate::UnionType(ctx, kts) => { + // Check that all types are the same const + let mut k = None; + for (x, t) in kts { + if let Some(t) = t { + match (k, t.get_type()?.as_const()) { + (None, Some(k2)) => k = Some(k2), + (Some(k1), Some(k2)) if k1 == k2 => {} + _ => { + return Err(mkerr( + ctx, + InvalidFieldType(x.clone(), t.clone()), + )) + } + } + } + } + + // An empty union type has type Type; + // an union type with only unary variants also has type Type + let k = k.unwrap_or(dhall_syntax::Const::Type); + + Typed::from_thunk_and_type( + Value::UnionType( + kts.iter() + .map(|(k, t)| { + ( + k.clone(), + t.as_ref().map(|t| { + TypeThunk::from_type(t.clone()) + }), + ) + }) + .collect(), + ) + .into_thunk(), + const_to_type(k), + ) + } + TypeIntermediate::ListType(ctx, t) => { + ensure_simple_type!( + t, + mkerr(ctx, InvalidListType(t.clone().to_normalized())), + ); + Typed::from_thunk_and_type( + Value::from_builtin(Builtin::List) + .app(t.to_value()) + .into_thunk(), + const_to_type(Const::Type), + ) + } + TypeIntermediate::OptionalType(ctx, t) => { + ensure_simple_type!( + t, + mkerr(ctx, InvalidOptionalType(t.clone().to_normalized())), + ); + Typed::from_thunk_and_type( + Value::from_builtin(Builtin::Optional) + .app(t.to_value()) + .into_thunk(), + const_to_type(Const::Type), + ) + } + }) + } +} + +/// Takes an expression that is meant to contain a Type +/// and turn it into a type, typechecking it along the way. +fn mktype( + ctx: &TypecheckContext, + e: SubExpr<Span, Normalized>, +) -> Result<Type, TypeError> { + Ok(type_with(ctx, e)?.to_type()) +} + +fn builtin_to_type(b: Builtin) -> Result<Type, TypeError> { + mktype( + &TypecheckContext::new(), + SubExpr::from_expr_no_note(ExprF::Builtin(b)), + ) +} + +/// Intermediary return type +enum Ret { + /// Returns the contained value as is + RetTyped(Typed), + /// Use the contained Type as the type of the input expression + RetType(Type), +} + +/// Type-check an expression and return the expression alongside its type if type-checking +/// succeeded, or an error if type-checking failed +fn type_with( + ctx: &TypecheckContext, + e: SubExpr<Span, Normalized>, +) -> Result<Typed, TypeError> { + use dhall_syntax::ExprF::{ + Annot, App, Embed, Lam, Let, OldOptionalLit, Pi, SomeLit, + }; + + use Ret::*; + let ret = match e.as_ref() { + Lam(x, t, b) => { + let tx = mktype(ctx, t.clone())?; + let ctx2 = ctx.insert_type(x, tx.clone()); + let b = type_with(&ctx2, b.clone())?; + let tb = b.get_type()?.into_owned(); + Ok(RetType( + TypeIntermediate::Pi(ctx.clone(), x.clone(), tx, tb) + .typecheck()? + .to_type(), + )) + } + Pi(x, ta, tb) => { + let ta = mktype(ctx, ta.clone())?; + let ctx2 = ctx.insert_type(x, ta.clone()); + let tb = mktype(&ctx2, tb.clone())?; + Ok(RetTyped( + TypeIntermediate::Pi(ctx.clone(), x.clone(), ta, tb) + .typecheck()?, + )) + } + Let(x, t, v, e) => { + let v = if let Some(t) = t { + t.rewrap(Annot(v.clone(), t.clone())) + } else { + v.clone() + }; + + let v = type_with(ctx, v)?.normalize(); + let e = type_with(&ctx.insert_value(x, v.clone()), e.clone())?; + + Ok(RetType(e.get_type()?.into_owned())) + } + OldOptionalLit(None, t) => { + let none = SubExpr::from_expr_no_note(ExprF::Builtin( + Builtin::OptionalNone, + )); + let e = e.rewrap(App(none, t.clone())); + return type_with(ctx, e); + } + OldOptionalLit(Some(x), t) => { + let optional = + SubExpr::from_expr_no_note(ExprF::Builtin(Builtin::Optional)); + let x = x.rewrap(SomeLit(x.clone())); + let t = t.rewrap(App(optional, t.clone())); + let e = e.rewrap(Annot(x, t)); + return type_with(ctx, e); + } + Embed(p) => Ok(RetTyped(p.clone().into())), + _ => type_last_layer( + ctx, + // Typecheck recursively all subexpressions + e.as_ref() + .traverse_ref_simple(|e| Ok(type_with(ctx, e.clone())?))?, + ), + }?; + Ok(match ret { + RetType(typ) => Typed::from_thunk_and_type( + Thunk::new(ctx.to_normalization_ctx(), e), + typ, + ), + RetTyped(tt) => tt, + }) +} + +/// When all sub-expressions have been typed, check the remaining toplevel +/// layer. +fn type_last_layer( + ctx: &TypecheckContext, + e: ExprF<Typed, Label, Normalized>, +) -> Result<Ret, TypeError> { + use dhall_syntax::BinOp::*; + use dhall_syntax::Builtin::*; + use dhall_syntax::ExprF::*; + let mkerr = |msg: TypeMessage| TypeError::new(ctx, msg); + + use Ret::*; + match e { + Lam(_, _, _) => unreachable!(), + Pi(_, _, _) => unreachable!(), + Let(_, _, _, _) => unreachable!(), + Embed(_) => unreachable!(), + Var(var) => match ctx.lookup(&var) { + Some(e) => Ok(RetType(e.into_owned())), + None => Err(mkerr(UnboundVariable(var.clone()))), + }, + App(f, a) => { + let tf = f.get_type()?; + let tf_internal = tf.internal_whnf(); + let (x, tx, tb) = match &tf_internal { + Some(Value::Pi( + x, + TypeThunk::Type(tx), + TypeThunk::Type(tb), + )) => (x, tx, tb), + Some(Value::Pi(_, _, _)) => { + panic!("ICE: this should not have happened") + } + _ => return Err(mkerr(NotAFunction(f.clone()))), + }; + ensure_equal!(a.get_type()?, tx, { + mkerr(TypeMismatch(f.clone(), tx.clone().to_normalized(), a)) + }); + + Ok(RetType(tb.subst_shift(&x.into(), &a))) + } + Annot(x, t) => { + let t = t.to_type(); + ensure_equal!( + &t, + x.get_type()?, + mkerr(AnnotMismatch(x, t.to_normalized())) + ); + Ok(RetType(x.get_type()?.into_owned())) + } + BoolIf(x, y, z) => { + ensure_equal!( + x.get_type()?, + &builtin_to_type(Bool)?, + mkerr(InvalidPredicate(x)), + ); + + ensure_simple_type!( + y.get_type()?, + mkerr(IfBranchMustBeTerm(true, y)), + ); + + ensure_simple_type!( + z.get_type()?, + mkerr(IfBranchMustBeTerm(false, z)), + ); + + ensure_equal!( + y.get_type()?, + z.get_type()?, + mkerr(IfBranchMismatch(y, z)) + ); + + Ok(RetType(y.get_type()?.into_owned())) + } + EmptyListLit(t) => { + let t = t.to_type(); + Ok(RetType( + TypeIntermediate::ListType(ctx.clone(), t) + .typecheck()? + .to_type(), + )) + } + NEListLit(xs) => { + let mut iter = xs.into_iter().enumerate(); + let (_, x) = iter.next().unwrap(); + for (i, y) in iter { + ensure_equal!( + x.get_type()?, + y.get_type()?, + mkerr(InvalidListElement( + i, + x.get_type()?.to_normalized(), + y + )) + ); + } + let t = x.get_type()?.into_owned(); + Ok(RetType( + TypeIntermediate::ListType(ctx.clone(), t) + .typecheck()? + .to_type(), + )) + } + SomeLit(x) => { + let t = x.get_type()?.into_owned(); + Ok(RetType( + TypeIntermediate::OptionalType(ctx.clone(), t) + .typecheck()? + .to_type(), + )) + } + RecordType(kts) => { + let kts: BTreeMap<_, _> = kts + .into_iter() + .map(|(x, t)| Ok((x, t.to_type()))) + .collect::<Result<_, _>>()?; + Ok(RetTyped( + TypeIntermediate::RecordType(ctx.clone(), kts).typecheck()?, + )) + } + UnionType(kts) => { + let kts: BTreeMap<_, _> = kts + .into_iter() + .map(|(x, t)| { + Ok(( + x, + match t { + None => None, + Some(t) => Some(t.to_type()), + }, + )) + }) + .collect::<Result<_, _>>()?; + Ok(RetTyped( + TypeIntermediate::UnionType(ctx.clone(), kts).typecheck()?, + )) + } + RecordLit(kvs) => { + let kts = kvs + .into_iter() + .map(|(x, v)| Ok((x, v.get_type()?.into_owned()))) + .collect::<Result<_, _>>()?; + Ok(RetType( + TypeIntermediate::RecordType(ctx.clone(), kts) + .typecheck()? + .to_type(), + )) + } + UnionLit(x, v, kvs) => { + let mut kts: std::collections::BTreeMap<_, _> = kvs + .into_iter() + .map(|(x, v)| { + let t = match v { + Some(x) => Some(x.to_type()), + None => None, + }; + Ok((x, t)) + }) + .collect::<Result<_, _>>()?; + let t = v.get_type()?.into_owned(); + kts.insert(x, Some(t)); + Ok(RetType( + TypeIntermediate::UnionType(ctx.clone(), kts) + .typecheck()? + .to_type(), + )) + } + Field(r, x) => { + match &r.get_type()?.internal_whnf() { + Some(Value::RecordType(kts)) => match kts.get(&x) { + Some(tth) => { + Ok(RetType(tth.to_type(ctx)?)) + }, + None => Err(mkerr(MissingRecordField(x, r.clone()))), + }, + // TODO: branch here only when r.get_type() is a Const + _ => { + let r = r.to_type(); + match &r.internal_whnf() { + Some(Value::UnionType(kts)) => match kts.get(&x) { + // Constructor has type T -> < x: T, ... > + Some(Some(t)) => { + Ok(RetType( + TypeIntermediate::Pi( + ctx.clone(), + "_".into(), + t.to_type(ctx)?, + r.clone(), + ) + .typecheck()? + .to_type(), + )) + }, + Some(None) => { + Ok(RetType(r.clone())) + }, + None => { + Err(mkerr(MissingUnionField( + x, + r.to_normalized(), + ))) + }, + }, + _ => { + Err(mkerr(NotARecord( + x, + r.to_normalized() + ))) + }, + } + } + // _ => Err(mkerr(NotARecord( + // x, + // r.to_type()?.to_normalized(), + // ))), + } + } + Const(c) => Ok(RetTyped(const_to_typed(c))), + Builtin(b) => { + Ok(RetType(mktype(ctx, rc(type_of_builtin(b)).note_absurd())?)) + } + BoolLit(_) => Ok(RetType(builtin_to_type(Bool)?)), + NaturalLit(_) => Ok(RetType(builtin_to_type(Natural)?)), + IntegerLit(_) => Ok(RetType(builtin_to_type(Integer)?)), + DoubleLit(_) => Ok(RetType(builtin_to_type(Double)?)), + TextLit(interpolated) => { + let text_type = builtin_to_type(Text)?; + for contents in interpolated.iter() { + use InterpolatedTextContents::Expr; + if let Expr(x) = contents { + ensure_equal!( + x.get_type()?, + &text_type, + mkerr(InvalidTextInterpolation(x)), + ); + } + } + Ok(RetType(text_type)) + } + BinOp(o @ ListAppend, l, r) => { + match l.get_type()?.internal_whnf() { + Some(Value::AppliedBuiltin(List, _)) => {} + _ => return Err(mkerr(BinOpTypeMismatch(o, l.clone()))), + } + + ensure_equal!( + l.get_type()?, + r.get_type()?, + mkerr(BinOpTypeMismatch(o, r)) + ); + + Ok(RetType(l.get_type()?.into_owned())) + } + BinOp(o, l, r) => { + let t = builtin_to_type(match o { + BoolAnd => Bool, + BoolOr => Bool, + BoolEQ => Bool, + BoolNE => Bool, + NaturalPlus => Natural, + NaturalTimes => Natural, + TextAppend => Text, + ListAppend => unreachable!(), + _ => return Err(mkerr(Unimplemented)), + })?; + + ensure_equal!(l.get_type()?, &t, mkerr(BinOpTypeMismatch(o, l))); + + ensure_equal!(r.get_type()?, &t, mkerr(BinOpTypeMismatch(o, r))); + + Ok(RetType(t)) + } + _ => Err(mkerr(Unimplemented)), + } +} + +/// `typeOf` is the same as `type_with` with an empty context, meaning that the +/// expression must be closed (i.e. no free variables), otherwise type-checking +/// will fail. +fn type_of(e: SubExpr<Span, Normalized>) -> Result<Typed, TypeError> { + let ctx = TypecheckContext::new(); + let e = type_with(&ctx, e)?; + // Ensure `e` has a type (i.e. `e` is not `Sort`) + e.get_type()?; + Ok(e) +} + +pub(crate) fn typecheck(e: Resolved) -> Result<Typed, TypeError> { + type_of(e.0) +} + +pub(crate) fn typecheck_with( + e: Resolved, + ty: &Type, +) -> Result<Typed, TypeError> { + let expr: SubExpr<_, _> = e.0; + let ty: SubExpr<_, _> = ty.to_expr().embed_absurd().note_absurd(); + type_of(expr.rewrap(ExprF::Annot(expr.clone(), ty))) +} +pub(crate) fn skip_typecheck(e: Resolved) -> Typed { + Typed::from_thunk_untyped(Thunk::new(NormalizationContext::new(), e.0)) +} + +/// The specific type error +#[derive(Debug)] +pub(crate) enum TypeMessage { + UnboundVariable(V<Label>), + InvalidInputType(Normalized), + InvalidOutputType(Normalized), + NotAFunction(Typed), + TypeMismatch(Typed, Normalized, Typed), + AnnotMismatch(Typed, Normalized), + Untyped, + InvalidListElement(usize, Normalized, Typed), + InvalidListType(Normalized), + InvalidOptionalType(Normalized), + InvalidPredicate(Typed), + IfBranchMismatch(Typed, Typed), + IfBranchMustBeTerm(bool, Typed), + InvalidFieldType(Label, Type), + NotARecord(Label, Normalized), + MissingRecordField(Label, Typed), + MissingUnionField(Label, Normalized), + BinOpTypeMismatch(BinOp, Typed), + NoDependentTypes(Normalized, Normalized), + InvalidTextInterpolation(Typed), + Sort, + Unimplemented, +} + +/// A structured type error that includes context +#[derive(Debug)] +pub struct TypeError { + type_message: TypeMessage, + context: TypecheckContext, +} + +impl TypeError { + pub(crate) fn new( + context: &TypecheckContext, + type_message: TypeMessage, + ) -> Self { + TypeError { + context: context.clone(), + type_message, + } + } +} + +impl From<TypeError> for std::option::NoneError { + fn from(_: TypeError) -> std::option::NoneError { + std::option::NoneError + } +} + +impl ::std::error::Error for TypeMessage { + fn description(&self) -> &str { + match *self { + // UnboundVariable => "Unbound variable", + InvalidInputType(_) => "Invalid function input", + InvalidOutputType(_) => "Invalid function output", + NotAFunction(_) => "Not a function", + TypeMismatch(_, _, _) => "Wrong type of function argument", + _ => "Unhandled error", + } + } +} + +impl fmt::Display for TypeMessage { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match self { + // UnboundVariable(_) => { + // f.write_str(include_str!("errors/UnboundVariable.txt")) + // } + // TypeMismatch(e0, e1, e2) => { + // let template = include_str!("errors/TypeMismatch.txt"); + // let s = template + // .replace("$txt0", &format!("{}", e0.as_expr())) + // .replace("$txt1", &format!("{}", e1.as_expr())) + // .replace("$txt2", &format!("{}", e2.as_expr())) + // .replace( + // "$txt3", + // &format!( + // "{}", + // e2.get_type() + // .unwrap() + // .as_normalized() + // .unwrap() + // .as_expr() + // ), + // ); + // f.write_str(&s) + // } + _ => f.write_str("Unhandled error message"), + } + } +} + +#[cfg(test)] +mod spec_tests { + #![rustfmt::skip] + + macro_rules! tc_success { + ($name:ident, $path:expr) => { + make_spec_test!(Typecheck, Success, $name, $path); + }; + } + macro_rules! tc_failure { + ($name:ident, $path:expr) => { + make_spec_test!(Typecheck, Failure, $name, $path); + }; + } + + macro_rules! ti_success { + ($name:ident, $path:expr) => { + make_spec_test!(TypeInference, Success, $name, $path); + }; + } + // macro_rules! ti_failure { + // ($name:ident, $path:expr) => { + // make_spec_test!(TypeInference, Failure, $name, $path); + // }; + // } + + // tc_success!(tc_success_accessEncodedType, "accessEncodedType"); + // tc_success!(tc_success_accessType, "accessType"); + tc_success!(tc_success_prelude_Bool_and_0, "prelude/Bool/and/0"); + tc_success!(tc_success_prelude_Bool_and_1, "prelude/Bool/and/1"); + tc_success!(tc_success_prelude_Bool_build_0, "prelude/Bool/build/0"); + tc_success!(tc_success_prelude_Bool_build_1, "prelude/Bool/build/1"); + tc_success!(tc_success_prelude_Bool_even_0, "prelude/Bool/even/0"); + tc_success!(tc_success_prelude_Bool_even_1, "prelude/Bool/even/1"); + tc_success!(tc_success_prelude_Bool_even_2, "prelude/Bool/even/2"); + tc_success!(tc_success_prelude_Bool_even_3, "prelude/Bool/even/3"); + tc_success!(tc_success_prelude_Bool_fold_0, "prelude/Bool/fold/0"); + tc_success!(tc_success_prelude_Bool_fold_1, "prelude/Bool/fold/1"); + tc_success!(tc_success_prelude_Bool_not_0, "prelude/Bool/not/0"); + tc_success!(tc_success_prelude_Bool_not_1, "prelude/Bool/not/1"); + tc_success!(tc_success_prelude_Bool_odd_0, "prelude/Bool/odd/0"); + tc_success!(tc_success_prelude_Bool_odd_1, "prelude/Bool/odd/1"); + tc_success!(tc_success_prelude_Bool_odd_2, "prelude/Bool/odd/2"); + tc_success!(tc_success_prelude_Bool_odd_3, "prelude/Bool/odd/3"); + tc_success!(tc_success_prelude_Bool_or_0, "prelude/Bool/or/0"); + tc_success!(tc_success_prelude_Bool_or_1, "prelude/Bool/or/1"); + tc_success!(tc_success_prelude_Bool_show_0, "prelude/Bool/show/0"); + tc_success!(tc_success_prelude_Bool_show_1, "prelude/Bool/show/1"); + tc_success!(tc_success_prelude_Double_show_0, "prelude/Double/show/0"); + tc_success!(tc_success_prelude_Double_show_1, "prelude/Double/show/1"); + tc_success!(tc_success_prelude_Integer_show_0, "prelude/Integer/show/0"); + tc_success!(tc_success_prelude_Integer_show_1, "prelude/Integer/show/1"); + tc_success!(tc_success_prelude_Integer_toDouble_0, "prelude/Integer/toDouble/0"); + tc_success!(tc_success_prelude_Integer_toDouble_1, "prelude/Integer/toDouble/1"); + tc_success!(tc_success_prelude_List_all_0, "prelude/List/all/0"); + tc_success!(tc_success_prelude_List_all_1, "prelude/List/all/1"); + tc_success!(tc_success_prelude_List_any_0, "prelude/List/any/0"); + tc_success!(tc_success_prelude_List_any_1, "prelude/List/any/1"); + tc_success!(tc_success_prelude_List_build_0, "prelude/List/build/0"); + tc_success!(tc_success_prelude_List_build_1, "prelude/List/build/1"); + tc_success!(tc_success_prelude_List_concat_0, "prelude/List/concat/0"); + tc_success!(tc_success_prelude_List_concat_1, "prelude/List/concat/1"); + tc_success!(tc_success_prelude_List_concatMap_0, "prelude/List/concatMap/0"); + tc_success!(tc_success_prelude_List_concatMap_1, "prelude/List/concatMap/1"); + tc_success!(tc_success_prelude_List_filter_0, "prelude/List/filter/0"); + tc_success!(tc_success_prelude_List_filter_1, "prelude/List/filter/1"); + tc_success!(tc_success_prelude_List_fold_0, "prelude/List/fold/0"); + tc_success!(tc_success_prelude_List_fold_1, "prelude/List/fold/1"); + tc_success!(tc_success_prelude_List_fold_2, "prelude/List/fold/2"); + tc_success!(tc_success_prelude_List_generate_0, "prelude/List/generate/0"); + tc_success!(tc_success_prelude_List_generate_1, "prelude/List/generate/1"); + tc_success!(tc_success_prelude_List_head_0, "prelude/List/head/0"); + tc_success!(tc_success_prelude_List_head_1, "prelude/List/head/1"); + tc_success!(tc_success_prelude_List_indexed_0, "prelude/List/indexed/0"); + tc_success!(tc_success_prelude_List_indexed_1, "prelude/List/indexed/1"); + tc_success!(tc_success_prelude_List_iterate_0, "prelude/List/iterate/0"); + tc_success!(tc_success_prelude_List_iterate_1, "prelude/List/iterate/1"); + tc_success!(tc_success_prelude_List_last_0, "prelude/List/last/0"); + tc_success!(tc_success_prelude_List_last_1, "prelude/List/last/1"); + tc_success!(tc_success_prelude_List_length_0, "prelude/List/length/0"); + tc_success!(tc_success_prelude_List_length_1, "prelude/List/length/1"); + tc_success!(tc_success_prelude_List_map_0, "prelude/List/map/0"); + tc_success!(tc_success_prelude_List_map_1, "prelude/List/map/1"); + tc_success!(tc_success_prelude_List_null_0, "prelude/List/null/0"); + tc_success!(tc_success_prelude_List_null_1, "prelude/List/null/1"); + tc_success!(tc_success_prelude_List_replicate_0, "prelude/List/replicate/0"); + tc_success!(tc_success_prelude_List_replicate_1, "prelude/List/replicate/1"); + tc_success!(tc_success_prelude_List_reverse_0, "prelude/List/reverse/0"); + tc_success!(tc_success_prelude_List_reverse_1, "prelude/List/reverse/1"); + tc_success!(tc_success_prelude_List_shifted_0, "prelude/List/shifted/0"); + tc_success!(tc_success_prelude_List_shifted_1, "prelude/List/shifted/1"); + tc_success!(tc_success_prelude_List_unzip_0, "prelude/List/unzip/0"); + tc_success!(tc_success_prelude_List_unzip_1, "prelude/List/unzip/1"); + tc_success!(tc_success_prelude_Monoid_00, "prelude/Monoid/00"); + tc_success!(tc_success_prelude_Monoid_01, "prelude/Monoid/01"); + tc_success!(tc_success_prelude_Monoid_02, "prelude/Monoid/02"); + tc_success!(tc_success_prelude_Monoid_03, "prelude/Monoid/03"); + tc_success!(tc_success_prelude_Monoid_04, "prelude/Monoid/04"); + tc_success!(tc_success_prelude_Monoid_05, "prelude/Monoid/05"); + tc_success!(tc_success_prelude_Monoid_06, "prelude/Monoid/06"); + tc_success!(tc_success_prelude_Monoid_07, "prelude/Monoid/07"); + tc_success!(tc_success_prelude_Monoid_08, "prelude/Monoid/08"); + tc_success!(tc_success_prelude_Monoid_09, "prelude/Monoid/09"); + tc_success!(tc_success_prelude_Monoid_10, "prelude/Monoid/10"); + tc_success!(tc_success_prelude_Natural_build_0, "prelude/Natural/build/0"); + tc_success!(tc_success_prelude_Natural_build_1, "prelude/Natural/build/1"); + tc_success!(tc_success_prelude_Natural_enumerate_0, "prelude/Natural/enumerate/0"); + tc_success!(tc_success_prelude_Natural_enumerate_1, "prelude/Natural/enumerate/1"); + tc_success!(tc_success_prelude_Natural_even_0, "prelude/Natural/even/0"); + tc_success!(tc_success_prelude_Natural_even_1, "prelude/Natural/even/1"); + tc_success!(tc_success_prelude_Natural_fold_0, "prelude/Natural/fold/0"); + tc_success!(tc_success_prelude_Natural_fold_1, "prelude/Natural/fold/1"); + tc_success!(tc_success_prelude_Natural_fold_2, "prelude/Natural/fold/2"); + tc_success!(tc_success_prelude_Natural_isZero_0, "prelude/Natural/isZero/0"); + tc_success!(tc_success_prelude_Natural_isZero_1, "prelude/Natural/isZero/1"); + tc_success!(tc_success_prelude_Natural_odd_0, "prelude/Natural/odd/0"); + tc_success!(tc_success_prelude_Natural_odd_1, "prelude/Natural/odd/1"); + tc_success!(tc_success_prelude_Natural_product_0, "prelude/Natural/product/0"); + tc_success!(tc_success_prelude_Natural_product_1, "prelude/Natural/product/1"); + tc_success!(tc_success_prelude_Natural_show_0, "prelude/Natural/show/0"); + tc_success!(tc_success_prelude_Natural_show_1, "prelude/Natural/show/1"); + tc_success!(tc_success_prelude_Natural_sum_0, "prelude/Natural/sum/0"); + tc_success!(tc_success_prelude_Natural_sum_1, "prelude/Natural/sum/1"); + tc_success!(tc_success_prelude_Natural_toDouble_0, "prelude/Natural/toDouble/0"); + tc_success!(tc_success_prelude_Natural_toDouble_1, "prelude/Natural/toDouble/1"); + tc_success!(tc_success_prelude_Natural_toInteger_0, "prelude/Natural/toInteger/0"); + tc_success!(tc_success_prelude_Natural_toInteger_1, "prelude/Natural/toInteger/1"); + tc_success!(tc_success_prelude_Optional_all_0, "prelude/Optional/all/0"); + tc_success!(tc_success_prelude_Optional_all_1, "prelude/Optional/all/1"); + tc_success!(tc_success_prelude_Optional_any_0, "prelude/Optional/any/0"); + tc_success!(tc_success_prelude_Optional_any_1, "prelude/Optional/any/1"); + tc_success!(tc_success_prelude_Optional_build_0, "prelude/Optional/build/0"); + tc_success!(tc_success_prelude_Optional_build_1, "prelude/Optional/build/1"); + tc_success!(tc_success_prelude_Optional_concat_0, "prelude/Optional/concat/0"); + tc_success!(tc_success_prelude_Optional_concat_1, "prelude/Optional/concat/1"); + tc_success!(tc_success_prelude_Optional_concat_2, "prelude/Optional/concat/2"); + tc_success!(tc_success_prelude_Optional_filter_0, "prelude/Optional/filter/0"); + tc_success!(tc_success_prelude_Optional_filter_1, "prelude/Optional/filter/1"); + tc_success!(tc_success_prelude_Optional_fold_0, "prelude/Optional/fold/0"); + tc_success!(tc_success_prelude_Optional_fold_1, "prelude/Optional/fold/1"); + tc_success!(tc_success_prelude_Optional_head_0, "prelude/Optional/head/0"); + tc_success!(tc_success_prelude_Optional_head_1, "prelude/Optional/head/1"); + tc_success!(tc_success_prelude_Optional_head_2, "prelude/Optional/head/2"); + tc_success!(tc_success_prelude_Optional_last_0, "prelude/Optional/last/0"); + tc_success!(tc_success_prelude_Optional_last_1, "prelude/Optional/last/1"); + tc_success!(tc_success_prelude_Optional_last_2, "prelude/Optional/last/2"); + tc_success!(tc_success_prelude_Optional_length_0, "prelude/Optional/length/0"); + tc_success!(tc_success_prelude_Optional_length_1, "prelude/Optional/length/1"); + tc_success!(tc_success_prelude_Optional_map_0, "prelude/Optional/map/0"); + tc_success!(tc_success_prelude_Optional_map_1, "prelude/Optional/map/1"); + tc_success!(tc_success_prelude_Optional_null_0, "prelude/Optional/null/0"); + tc_success!(tc_success_prelude_Optional_null_1, "prelude/Optional/null/1"); + tc_success!(tc_success_prelude_Optional_toList_0, "prelude/Optional/toList/0"); + tc_success!(tc_success_prelude_Optional_toList_1, "prelude/Optional/toList/1"); + tc_success!(tc_success_prelude_Optional_unzip_0, "prelude/Optional/unzip/0"); + tc_success!(tc_success_prelude_Optional_unzip_1, "prelude/Optional/unzip/1"); + tc_success!(tc_success_prelude_Text_concat_0, "prelude/Text/concat/0"); + tc_success!(tc_success_prelude_Text_concat_1, "prelude/Text/concat/1"); + tc_success!(tc_success_prelude_Text_concatMap_0, "prelude/Text/concatMap/0"); + tc_success!(tc_success_prelude_Text_concatMap_1, "prelude/Text/concatMap/1"); + // tc_success!(tc_success_prelude_Text_concatMapSep_0, "prelude/Text/concatMapSep/0"); + // tc_success!(tc_success_prelude_Text_concatMapSep_1, "prelude/Text/concatMapSep/1"); + // tc_success!(tc_success_prelude_Text_concatSep_0, "prelude/Text/concatSep/0"); + // tc_success!(tc_success_prelude_Text_concatSep_1, "prelude/Text/concatSep/1"); + tc_success!(tc_success_recordOfRecordOfTypes, "recordOfRecordOfTypes"); + tc_success!(tc_success_recordOfTypes, "recordOfTypes"); + // tc_success!(tc_success_simple_access_0, "simple/access/0"); + // tc_success!(tc_success_simple_access_1, "simple/access/1"); + // tc_success!(tc_success_simple_alternativesAreTypes, "simple/alternativesAreTypes"); + // tc_success!(tc_success_simple_anonymousFunctionsInTypes, "simple/anonymousFunctionsInTypes"); + // tc_success!(tc_success_simple_fieldsAreTypes, "simple/fieldsAreTypes"); + // tc_success!(tc_success_simple_kindParameter, "simple/kindParameter"); + // tc_success!(tc_success_simple_mergeEquivalence, "simple/mergeEquivalence"); + // tc_success!(tc_success_simple_mixedFieldAccess, "simple/mixedFieldAccess"); + tc_success!(tc_success_simple_unionsOfTypes, "simple/unionsOfTypes"); + + tc_failure!(tc_failure_combineMixedRecords, "combineMixedRecords"); + // tc_failure!(tc_failure_duplicateFields, "duplicateFields"); + tc_failure!(tc_failure_hurkensParadox, "hurkensParadox"); + tc_failure!(tc_failure_mixedUnions, "mixedUnions"); + tc_failure!(tc_failure_preferMixedRecords, "preferMixedRecords"); + tc_failure!(tc_failure_unit_FunctionApplicationArgumentNotMatch, "unit/FunctionApplicationArgumentNotMatch"); + tc_failure!(tc_failure_unit_FunctionApplicationIsNotFunction, "unit/FunctionApplicationIsNotFunction"); + tc_failure!(tc_failure_unit_FunctionArgumentTypeNotAType, "unit/FunctionArgumentTypeNotAType"); + tc_failure!(tc_failure_unit_FunctionDependentType, "unit/FunctionDependentType"); + tc_failure!(tc_failure_unit_FunctionDependentType2, "unit/FunctionDependentType2"); + tc_failure!(tc_failure_unit_FunctionTypeArgumentTypeNotAType, "unit/FunctionTypeArgumentTypeNotAType"); + tc_failure!(tc_failure_unit_FunctionTypeKindSort, "unit/FunctionTypeKindSort"); + tc_failure!(tc_failure_unit_FunctionTypeTypeKind, "unit/FunctionTypeTypeKind"); + tc_failure!(tc_failure_unit_FunctionTypeTypeSort, "unit/FunctionTypeTypeSort"); + tc_failure!(tc_failure_unit_IfBranchesNotMatch, "unit/IfBranchesNotMatch"); + tc_failure!(tc_failure_unit_IfBranchesNotType, "unit/IfBranchesNotType"); + tc_failure!(tc_failure_unit_IfNotBool, "unit/IfNotBool"); + tc_failure!(tc_failure_unit_LetWithWrongAnnotation, "unit/LetWithWrongAnnotation"); + tc_failure!(tc_failure_unit_ListLiteralEmptyNotType, "unit/ListLiteralEmptyNotType"); + tc_failure!(tc_failure_unit_ListLiteralNotType, "unit/ListLiteralNotType"); + tc_failure!(tc_failure_unit_ListLiteralTypesNotMatch, "unit/ListLiteralTypesNotMatch"); + tc_failure!(tc_failure_unit_MergeAlternativeHasNoHandler, "unit/MergeAlternativeHasNoHandler"); + tc_failure!(tc_failure_unit_MergeAnnotationNotType, "unit/MergeAnnotationNotType"); + tc_failure!(tc_failure_unit_MergeEmptyWithoutAnnotation, "unit/MergeEmptyWithoutAnnotation"); + tc_failure!(tc_failure_unit_MergeHandlerNotFunction, "unit/MergeHandlerNotFunction"); + tc_failure!(tc_failure_unit_MergeHandlerNotInUnion, "unit/MergeHandlerNotInUnion"); + tc_failure!(tc_failure_unit_MergeHandlerNotMatchAlternativeType, "unit/MergeHandlerNotMatchAlternativeType"); + tc_failure!(tc_failure_unit_MergeHandlersWithDifferentType, "unit/MergeHandlersWithDifferentType"); + tc_failure!(tc_failure_unit_MergeLhsNotRecord, "unit/MergeLhsNotRecord"); + tc_failure!(tc_failure_unit_MergeRhsNotUnion, "unit/MergeRhsNotUnion"); + tc_failure!(tc_failure_unit_MergeWithWrongAnnotation, "unit/MergeWithWrongAnnotation"); + tc_failure!(tc_failure_unit_OperatorAndNotBool, "unit/OperatorAndNotBool"); + tc_failure!(tc_failure_unit_OperatorEqualNotBool, "unit/OperatorEqualNotBool"); + tc_failure!(tc_failure_unit_OperatorListConcatenateLhsNotList, "unit/OperatorListConcatenateLhsNotList"); + tc_failure!(tc_failure_unit_OperatorListConcatenateListsNotMatch, "unit/OperatorListConcatenateListsNotMatch"); + tc_failure!(tc_failure_unit_OperatorListConcatenateRhsNotList, "unit/OperatorListConcatenateRhsNotList"); + tc_failure!(tc_failure_unit_OperatorNotEqualNotBool, "unit/OperatorNotEqualNotBool"); + tc_failure!(tc_failure_unit_OperatorOrNotBool, "unit/OperatorOrNotBool"); + tc_failure!(tc_failure_unit_OperatorPlusNotNatural, "unit/OperatorPlusNotNatural"); + tc_failure!(tc_failure_unit_OperatorTextConcatenateLhsNotText, "unit/OperatorTextConcatenateLhsNotText"); + tc_failure!(tc_failure_unit_OperatorTextConcatenateRhsNotText, "unit/OperatorTextConcatenateRhsNotText"); + tc_failure!(tc_failure_unit_OperatorTimesNotNatural, "unit/OperatorTimesNotNatural"); + tc_failure!(tc_failure_unit_RecordMixedKinds, "unit/RecordMixedKinds"); + tc_failure!(tc_failure_unit_RecordMixedKinds2, "unit/RecordMixedKinds2"); + tc_failure!(tc_failure_unit_RecordMixedKinds3, "unit/RecordMixedKinds3"); + tc_failure!(tc_failure_unit_RecordProjectionEmpty, "unit/RecordProjectionEmpty"); + tc_failure!(tc_failure_unit_RecordProjectionNotPresent, "unit/RecordProjectionNotPresent"); + tc_failure!(tc_failure_unit_RecordProjectionNotRecord, "unit/RecordProjectionNotRecord"); + tc_failure!(tc_failure_unit_RecordSelectionEmpty, "unit/RecordSelectionEmpty"); + tc_failure!(tc_failure_unit_RecordSelectionNotPresent, "unit/RecordSelectionNotPresent"); + tc_failure!(tc_failure_unit_RecordSelectionNotRecord, "unit/RecordSelectionNotRecord"); + tc_failure!(tc_failure_unit_RecordSelectionTypeNotUnionType, "unit/RecordSelectionTypeNotUnionType"); + tc_failure!(tc_failure_unit_RecordTypeMixedKinds, "unit/RecordTypeMixedKinds"); + tc_failure!(tc_failure_unit_RecordTypeMixedKinds2, "unit/RecordTypeMixedKinds2"); + tc_failure!(tc_failure_unit_RecordTypeMixedKinds3, "unit/RecordTypeMixedKinds3"); + tc_failure!(tc_failure_unit_RecordTypeValueMember, "unit/RecordTypeValueMember"); + tc_failure!(tc_failure_unit_RecursiveRecordMergeLhsNotRecord, "unit/RecursiveRecordMergeLhsNotRecord"); + tc_failure!(tc_failure_unit_RecursiveRecordMergeMixedKinds, "unit/RecursiveRecordMergeMixedKinds"); + tc_failure!(tc_failure_unit_RecursiveRecordMergeOverlapping, "unit/RecursiveRecordMergeOverlapping"); + tc_failure!(tc_failure_unit_RecursiveRecordMergeRhsNotRecord, "unit/RecursiveRecordMergeRhsNotRecord"); + tc_failure!(tc_failure_unit_RecursiveRecordTypeMergeLhsNotRecordType, "unit/RecursiveRecordTypeMergeLhsNotRecordType"); + tc_failure!(tc_failure_unit_RecursiveRecordTypeMergeOverlapping, "unit/RecursiveRecordTypeMergeOverlapping"); + tc_failure!(tc_failure_unit_RecursiveRecordTypeMergeRhsNotRecordType, "unit/RecursiveRecordTypeMergeRhsNotRecordType"); + tc_failure!(tc_failure_unit_RightBiasedRecordMergeLhsNotRecord, "unit/RightBiasedRecordMergeLhsNotRecord"); + tc_failure!(tc_failure_unit_RightBiasedRecordMergeMixedKinds, "unit/RightBiasedRecordMergeMixedKinds"); + tc_failure!(tc_failure_unit_RightBiasedRecordMergeMixedKinds2, "unit/RightBiasedRecordMergeMixedKinds2"); + tc_failure!(tc_failure_unit_RightBiasedRecordMergeMixedKinds3, "unit/RightBiasedRecordMergeMixedKinds3"); + tc_failure!(tc_failure_unit_RightBiasedRecordMergeRhsNotRecord, "unit/RightBiasedRecordMergeRhsNotRecord"); + tc_failure!(tc_failure_unit_SomeNotType, "unit/SomeNotType"); + tc_failure!(tc_failure_unit_Sort, "unit/Sort"); + tc_failure!(tc_failure_unit_TextLiteralInterpolateNotText, "unit/TextLiteralInterpolateNotText"); + tc_failure!(tc_failure_unit_TypeAnnotationWrong, "unit/TypeAnnotationWrong"); + tc_failure!(tc_failure_unit_UnionConstructorFieldNotPresent, "unit/UnionConstructorFieldNotPresent"); + tc_failure!(tc_failure_unit_UnionTypeMixedKinds, "unit/UnionTypeMixedKinds"); + tc_failure!(tc_failure_unit_UnionTypeMixedKinds2, "unit/UnionTypeMixedKinds2"); + tc_failure!(tc_failure_unit_UnionTypeMixedKinds3, "unit/UnionTypeMixedKinds3"); + tc_failure!(tc_failure_unit_UnionTypeNotType, "unit/UnionTypeNotType"); + tc_failure!(tc_failure_unit_VariableFree, "unit/VariableFree"); + + // ti_success!(ti_success_simple_alternativesAreTypes, "simple/alternativesAreTypes"); + // ti_success!(ti_success_simple_kindParameter, "simple/kindParameter"); + ti_success!(ti_success_unit_Bool, "unit/Bool"); + ti_success!(ti_success_unit_Double, "unit/Double"); + ti_success!(ti_success_unit_DoubleLiteral, "unit/DoubleLiteral"); + ti_success!(ti_success_unit_DoubleShow, "unit/DoubleShow"); + ti_success!(ti_success_unit_False, "unit/False"); + ti_success!(ti_success_unit_Function, "unit/Function"); + ti_success!(ti_success_unit_FunctionApplication, "unit/FunctionApplication"); + ti_success!(ti_success_unit_FunctionNamedArg, "unit/FunctionNamedArg"); + ti_success!(ti_success_unit_FunctionTypeKindKind, "unit/FunctionTypeKindKind"); + ti_success!(ti_success_unit_FunctionTypeKindTerm, "unit/FunctionTypeKindTerm"); + ti_success!(ti_success_unit_FunctionTypeKindType, "unit/FunctionTypeKindType"); + ti_success!(ti_success_unit_FunctionTypeTermTerm, "unit/FunctionTypeTermTerm"); + ti_success!(ti_success_unit_FunctionTypeTypeTerm, "unit/FunctionTypeTypeTerm"); + ti_success!(ti_success_unit_FunctionTypeTypeType, "unit/FunctionTypeTypeType"); + ti_success!(ti_success_unit_FunctionTypeUsingArgument, "unit/FunctionTypeUsingArgument"); + ti_success!(ti_success_unit_If, "unit/If"); + ti_success!(ti_success_unit_IfNormalizeArguments, "unit/IfNormalizeArguments"); + ti_success!(ti_success_unit_Integer, "unit/Integer"); + ti_success!(ti_success_unit_IntegerLiteral, "unit/IntegerLiteral"); + ti_success!(ti_success_unit_IntegerShow, "unit/IntegerShow"); + ti_success!(ti_success_unit_IntegerToDouble, "unit/IntegerToDouble"); + ti_success!(ti_success_unit_Kind, "unit/Kind"); + ti_success!(ti_success_unit_Let, "unit/Let"); + ti_success!(ti_success_unit_LetNestedTypeSynonym, "unit/LetNestedTypeSynonym"); + ti_success!(ti_success_unit_LetTypeSynonym, "unit/LetTypeSynonym"); + ti_success!(ti_success_unit_LetWithAnnotation, "unit/LetWithAnnotation"); + ti_success!(ti_success_unit_List, "unit/List"); + ti_success!(ti_success_unit_ListBuild, "unit/ListBuild"); + ti_success!(ti_success_unit_ListFold, "unit/ListFold"); + ti_success!(ti_success_unit_ListHead, "unit/ListHead"); + ti_success!(ti_success_unit_ListIndexed, "unit/ListIndexed"); + ti_success!(ti_success_unit_ListLast, "unit/ListLast"); + ti_success!(ti_success_unit_ListLength, "unit/ListLength"); + ti_success!(ti_success_unit_ListLiteralEmpty, "unit/ListLiteralEmpty"); + ti_success!(ti_success_unit_ListLiteralNormalizeArguments, "unit/ListLiteralNormalizeArguments"); + ti_success!(ti_success_unit_ListLiteralOne, "unit/ListLiteralOne"); + ti_success!(ti_success_unit_ListReverse, "unit/ListReverse"); + // ti_success!(ti_success_unit_MergeEmptyUnion, "unit/MergeEmptyUnion"); + // ti_success!(ti_success_unit_MergeOne, "unit/MergeOne"); + // ti_success!(ti_success_unit_MergeOneWithAnnotation, "unit/MergeOneWithAnnotation"); + ti_success!(ti_success_unit_Natural, "unit/Natural"); + ti_success!(ti_success_unit_NaturalBuild, "unit/NaturalBuild"); + ti_success!(ti_success_unit_NaturalEven, "unit/NaturalEven"); + ti_success!(ti_success_unit_NaturalFold, "unit/NaturalFold"); + ti_success!(ti_success_unit_NaturalIsZero, "unit/NaturalIsZero"); + ti_success!(ti_success_unit_NaturalLiteral, "unit/NaturalLiteral"); + ti_success!(ti_success_unit_NaturalOdd, "unit/NaturalOdd"); + ti_success!(ti_success_unit_NaturalShow, "unit/NaturalShow"); + ti_success!(ti_success_unit_NaturalToInteger, "unit/NaturalToInteger"); + ti_success!(ti_success_unit_None, "unit/None"); + ti_success!(ti_success_unit_OldOptionalNone, "unit/OldOptionalNone"); + ti_success!(ti_success_unit_OldOptionalTrue, "unit/OldOptionalTrue"); + ti_success!(ti_success_unit_OperatorAnd, "unit/OperatorAnd"); + ti_success!(ti_success_unit_OperatorAndNormalizeArguments, "unit/OperatorAndNormalizeArguments"); + ti_success!(ti_success_unit_OperatorEqual, "unit/OperatorEqual"); + ti_success!(ti_success_unit_OperatorEqualNormalizeArguments, "unit/OperatorEqualNormalizeArguments"); + ti_success!(ti_success_unit_OperatorListConcatenate, "unit/OperatorListConcatenate"); + ti_success!(ti_success_unit_OperatorListConcatenateNormalizeArguments, "unit/OperatorListConcatenateNormalizeArguments"); + ti_success!(ti_success_unit_OperatorNotEqual, "unit/OperatorNotEqual"); + ti_success!(ti_success_unit_OperatorNotEqualNormalizeArguments, "unit/OperatorNotEqualNormalizeArguments"); + ti_success!(ti_success_unit_OperatorOr, "unit/OperatorOr"); + ti_success!(ti_success_unit_OperatorOrNormalizeArguments, "unit/OperatorOrNormalizeArguments"); + ti_success!(ti_success_unit_OperatorPlus, "unit/OperatorPlus"); + ti_success!(ti_success_unit_OperatorPlusNormalizeArguments, "unit/OperatorPlusNormalizeArguments"); + ti_success!(ti_success_unit_OperatorTextConcatenate, "unit/OperatorTextConcatenate"); + ti_success!(ti_success_unit_OperatorTextConcatenateNormalizeArguments, "unit/OperatorTextConcatenateNormalizeArguments"); + ti_success!(ti_success_unit_OperatorTimes, "unit/OperatorTimes"); + ti_success!(ti_success_unit_OperatorTimesNormalizeArguments, "unit/OperatorTimesNormalizeArguments"); + ti_success!(ti_success_unit_Optional, "unit/Optional"); + ti_success!(ti_success_unit_OptionalBuild, "unit/OptionalBuild"); + ti_success!(ti_success_unit_OptionalFold, "unit/OptionalFold"); + ti_success!(ti_success_unit_RecordEmpty, "unit/RecordEmpty"); + ti_success!(ti_success_unit_RecordOneKind, "unit/RecordOneKind"); + ti_success!(ti_success_unit_RecordOneType, "unit/RecordOneType"); + ti_success!(ti_success_unit_RecordOneValue, "unit/RecordOneValue"); + // ti_success!(ti_success_unit_RecordProjectionEmpty, "unit/RecordProjectionEmpty"); + // ti_success!(ti_success_unit_RecordProjectionKind, "unit/RecordProjectionKind"); + // ti_success!(ti_success_unit_RecordProjectionType, "unit/RecordProjectionType"); + // ti_success!(ti_success_unit_RecordProjectionValue, "unit/RecordProjectionValue"); + // ti_success!(ti_success_unit_RecordSelectionKind, "unit/RecordSelectionKind"); + // ti_success!(ti_success_unit_RecordSelectionType, "unit/RecordSelectionType"); + ti_success!(ti_success_unit_RecordSelectionValue, "unit/RecordSelectionValue"); + ti_success!(ti_success_unit_RecordType, "unit/RecordType"); + ti_success!(ti_success_unit_RecordTypeEmpty, "unit/RecordTypeEmpty"); + ti_success!(ti_success_unit_RecordTypeKind, "unit/RecordTypeKind"); + ti_success!(ti_success_unit_RecordTypeType, "unit/RecordTypeType"); + // ti_success!(ti_success_unit_RecursiveRecordMergeLhsEmpty, "unit/RecursiveRecordMergeLhsEmpty"); + // ti_success!(ti_success_unit_RecursiveRecordMergeRecursively, "unit/RecursiveRecordMergeRecursively"); + // ti_success!(ti_success_unit_RecursiveRecordMergeRecursivelyTypes, "unit/RecursiveRecordMergeRecursivelyTypes"); + // ti_success!(ti_success_unit_RecursiveRecordMergeRhsEmpty, "unit/RecursiveRecordMergeRhsEmpty"); + // ti_success!(ti_success_unit_RecursiveRecordMergeTwo, "unit/RecursiveRecordMergeTwo"); + // ti_success!(ti_success_unit_RecursiveRecordMergeTwoKinds, "unit/RecursiveRecordMergeTwoKinds"); + // ti_success!(ti_success_unit_RecursiveRecordMergeTwoTypes, "unit/RecursiveRecordMergeTwoTypes"); + // ti_success!(ti_success_unit_RecursiveRecordTypeMergeRecursively, "unit/RecursiveRecordTypeMergeRecursively"); + // ti_success!(ti_success_unit_RecursiveRecordTypeMergeRecursivelyTypes, "unit/RecursiveRecordTypeMergeRecursivelyTypes"); + // ti_success!(ti_success_unit_RecursiveRecordTypeMergeRhsEmpty, "unit/RecursiveRecordTypeMergeRhsEmpty"); + // ti_success!(ti_success_unit_RecursiveRecordTypeMergeTwo, "unit/RecursiveRecordTypeMergeTwo"); + // ti_success!(ti_success_unit_RecursiveRecordTypeMergeTwoKinds, "unit/RecursiveRecordTypeMergeTwoKinds"); + // ti_success!(ti_success_unit_RecursiveRecordTypeMergeTwoTypes, "unit/RecursiveRecordTypeMergeTwoTypes"); + // ti_success!(ti_success_unit_RightBiasedRecordMergeRhsEmpty, "unit/RightBiasedRecordMergeRhsEmpty"); + // ti_success!(ti_success_unit_RightBiasedRecordMergeTwo, "unit/RightBiasedRecordMergeTwo"); + // ti_success!(ti_success_unit_RightBiasedRecordMergeTwoDifferent, "unit/RightBiasedRecordMergeTwoDifferent"); + // ti_success!(ti_success_unit_RightBiasedRecordMergeTwoKinds, "unit/RightBiasedRecordMergeTwoKinds"); + // ti_success!(ti_success_unit_RightBiasedRecordMergeTwoTypes, "unit/RightBiasedRecordMergeTwoTypes"); + ti_success!(ti_success_unit_SomeTrue, "unit/SomeTrue"); + ti_success!(ti_success_unit_Text, "unit/Text"); + ti_success!(ti_success_unit_TextLiteral, "unit/TextLiteral"); + ti_success!(ti_success_unit_TextLiteralNormalizeArguments, "unit/TextLiteralNormalizeArguments"); + ti_success!(ti_success_unit_TextLiteralWithInterpolation, "unit/TextLiteralWithInterpolation"); + ti_success!(ti_success_unit_TextShow, "unit/TextShow"); + ti_success!(ti_success_unit_True, "unit/True"); + ti_success!(ti_success_unit_Type, "unit/Type"); + ti_success!(ti_success_unit_TypeAnnotation, "unit/TypeAnnotation"); + ti_success!(ti_success_unit_TypeAnnotationSort, "unit/TypeAnnotationSort"); + ti_success!(ti_success_unit_UnionConstructorField, "unit/UnionConstructorField"); + ti_success!(ti_success_unit_UnionOne, "unit/UnionOne"); + ti_success!(ti_success_unit_UnionTypeEmpty, "unit/UnionTypeEmpty"); + ti_success!(ti_success_unit_UnionTypeKind, "unit/UnionTypeKind"); + ti_success!(ti_success_unit_UnionTypeOne, "unit/UnionTypeOne"); + ti_success!(ti_success_unit_UnionTypeType, "unit/UnionTypeType"); +} |