summaryrefslogtreecommitdiff
path: root/dhall/src/phase
diff options
context:
space:
mode:
authorNadrieril2019-05-06 22:09:55 +0200
committerNadrieril2019-05-06 22:09:55 +0200
commit423fdeebe9247b16744fae4b50df415bbd08be04 (patch)
treef2f16407d7e365e6fecee400a1959ca08b2a5156 /dhall/src/phase
parent2075cba6d883278a534afd2d8fe8f0a5e9b2f0d0 (diff)
Reorganize dhall into a phase structure
Diffstat (limited to 'dhall/src/phase')
-rw-r--r--dhall/src/phase/binary.rs363
-rw-r--r--dhall/src/phase/mod.rs389
-rw-r--r--dhall/src/phase/normalize.rs1888
-rw-r--r--dhall/src/phase/parse.rs38
-rw-r--r--dhall/src/phase/resolve.rs139
-rw-r--r--dhall/src/phase/typecheck.rs1276
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");
+}