summaryrefslogtreecommitdiff
path: root/dhall
diff options
context:
space:
mode:
Diffstat (limited to '')
m---------dhall-lang0
-rw-r--r--dhall/Cargo.toml2
-rw-r--r--dhall/build.rs185
-rw-r--r--dhall/src/dhall.pest.visibility7
-rw-r--r--dhall/src/error/mod.rs5
-rw-r--r--dhall/src/semantics/phase/normalize.rs155
-rw-r--r--dhall/src/semantics/phase/typecheck.rs30
-rw-r--r--dhall/src/syntax/ast/expr.rs4
-rw-r--r--dhall/src/syntax/ast/visitor.rs7
-rw-r--r--dhall/src/syntax/binary/decode.rs5
-rw-r--r--dhall/src/syntax/binary/encode.rs1
-rw-r--r--dhall/src/syntax/text/parser.rs81
-rw-r--r--dhall/src/syntax/text/printer.rs31
-rw-r--r--dhall/tests/type-errors/mixedUnions.txt2
-rw-r--r--dhall/tests/type-errors/recordOfKind.txt2
-rw-r--r--dhall/tests/type-errors/unit/AnnotationRecordWrongFieldName.txt2
-rw-r--r--dhall/tests/type-errors/unit/AnnotationRecordWrongFieldType.txt2
-rw-r--r--dhall/tests/type-errors/unit/AssertDoubleZeros.txt1
-rw-r--r--dhall/tests/type-errors/unit/AssertNotEquivalence.txt2
-rw-r--r--dhall/tests/type-errors/unit/AssertTriviallyFalse.txt2
-rw-r--r--dhall/tests/type-errors/unit/EquivalenceNotSameType.txt2
-rw-r--r--dhall/tests/type-errors/unit/EquivalenceNotTerms.txt2
-rw-r--r--dhall/tests/type-errors/unit/FunctionTypeKindSort.txt2
-rw-r--r--dhall/tests/type-errors/unit/FunctionTypeTypeSort.txt2
-rw-r--r--dhall/tests/type-errors/unit/IfBranchesNotMatch.txt2
-rw-r--r--dhall/tests/type-errors/unit/IfBranchesNotType.txt2
-rw-r--r--dhall/tests/type-errors/unit/IfNotBool.txt2
-rw-r--r--dhall/tests/type-errors/unit/LetWithWrongAnnotation.txt2
-rw-r--r--dhall/tests/type-errors/unit/ListLiteralNotType.txt2
-rw-r--r--dhall/tests/type-errors/unit/ListLiteralTypesNotMatch.txt2
-rw-r--r--dhall/tests/type-errors/unit/MergeAlternativeHasNoHandler.txt2
-rw-r--r--dhall/tests/type-errors/unit/MergeAnnotationMismatch.txt2
-rw-r--r--dhall/tests/type-errors/unit/MergeAnnotationNotType.txt2
-rw-r--r--dhall/tests/type-errors/unit/MergeEmptyNeedsDirectAnnotation1.txt2
-rw-r--r--dhall/tests/type-errors/unit/MergeEmptyNeedsDirectAnnotation2.txt2
-rw-r--r--dhall/tests/type-errors/unit/MergeEmptyWithoutAnnotation.txt2
-rw-r--r--dhall/tests/type-errors/unit/MergeHandlerFreeVar.txt2
-rw-r--r--dhall/tests/type-errors/unit/MergeHandlerNotInUnion.txt2
-rw-r--r--dhall/tests/type-errors/unit/MergeHandlersWithDifferentType.txt2
-rw-r--r--dhall/tests/type-errors/unit/MergeLhsNotRecord.txt2
-rw-r--r--dhall/tests/type-errors/unit/MergeMissingHandler1.txt2
-rw-r--r--dhall/tests/type-errors/unit/MergeMissingHandler2.txt2
-rw-r--r--dhall/tests/type-errors/unit/MergeRhsNotUnion.txt2
-rw-r--r--dhall/tests/type-errors/unit/MergeUnusedHandler.txt1
-rw-r--r--dhall/tests/type-errors/unit/OperatorAndNotBool.txt2
-rw-r--r--dhall/tests/type-errors/unit/OperatorEqualNotBool.txt2
-rw-r--r--dhall/tests/type-errors/unit/OperatorListConcatenateLhsNotList.txt2
-rw-r--r--dhall/tests/type-errors/unit/OperatorListConcatenateListsNotMatch.txt2
-rw-r--r--dhall/tests/type-errors/unit/OperatorListConcatenateNotListsButMatch.txt2
-rw-r--r--dhall/tests/type-errors/unit/OperatorListConcatenateRhsNotList.txt2
-rw-r--r--dhall/tests/type-errors/unit/OperatorNotEqualNotBool.txt2
-rw-r--r--dhall/tests/type-errors/unit/OperatorOrNotBool.txt2
-rw-r--r--dhall/tests/type-errors/unit/OperatorPlusNotNatural.txt2
-rw-r--r--dhall/tests/type-errors/unit/OperatorTextConcatenateLhsNotText.txt2
-rw-r--r--dhall/tests/type-errors/unit/OperatorTextConcatenateRhsNotText.txt2
-rw-r--r--dhall/tests/type-errors/unit/OperatorTimesNotNatural.txt2
-rw-r--r--dhall/tests/type-errors/unit/OptionalDeprecatedSyntaxAbsent.txt2
-rw-r--r--dhall/tests/type-errors/unit/OptionalDeprecatedSyntaxPresent.txt2
-rw-r--r--dhall/tests/type-errors/unit/RecordLitDuplicateFields.txt2
-rw-r--r--dhall/tests/type-errors/unit/RecordMixedKinds3.txt2
-rw-r--r--dhall/tests/type-errors/unit/RecordProjectionDuplicateFields.txt1
-rw-r--r--dhall/tests/type-errors/unit/RecordProjectionEmpty.txt2
-rw-r--r--dhall/tests/type-errors/unit/RecordProjectionNotPresent.txt2
-rw-r--r--dhall/tests/type-errors/unit/RecordProjectionNotRecord.txt2
-rw-r--r--dhall/tests/type-errors/unit/RecordSelectionEmpty.txt2
-rw-r--r--dhall/tests/type-errors/unit/RecordSelectionNotPresent.txt2
-rw-r--r--dhall/tests/type-errors/unit/RecordSelectionNotRecord.txt2
-rw-r--r--dhall/tests/type-errors/unit/RecordSelectionTypeNotUnionType.txt2
-rw-r--r--dhall/tests/type-errors/unit/RecordTypeDuplicateFields.txt2
-rw-r--r--dhall/tests/type-errors/unit/RecordTypeValueMember.txt2
-rw-r--r--dhall/tests/type-errors/unit/RecursiveRecordMergeLhsNotRecord.txt2
-rw-r--r--dhall/tests/type-errors/unit/RecursiveRecordMergeOverlapping.txt2
-rw-r--r--dhall/tests/type-errors/unit/RecursiveRecordMergeRhsNotRecord.txt2
-rw-r--r--dhall/tests/type-errors/unit/RecursiveRecordTypeMergeLhsNotRecordType.txt2
-rw-r--r--dhall/tests/type-errors/unit/RecursiveRecordTypeMergeOverlapping.txt2
-rw-r--r--dhall/tests/type-errors/unit/RecursiveRecordTypeMergeRhsNotRecordType.txt2
-rw-r--r--dhall/tests/type-errors/unit/RightBiasedRecordMergeLhsNotRecord.txt2
-rw-r--r--dhall/tests/type-errors/unit/RightBiasedRecordMergeMixedKinds2.txt2
-rw-r--r--dhall/tests/type-errors/unit/RightBiasedRecordMergeMixedKinds3.txt2
-rw-r--r--dhall/tests/type-errors/unit/RightBiasedRecordMergeRhsNotRecord.txt2
-rw-r--r--dhall/tests/type-errors/unit/SomeNotType.txt2
-rw-r--r--dhall/tests/type-errors/unit/Sort.txt2
-rw-r--r--dhall/tests/type-errors/unit/TextLiteralInterpolateNotText.txt2
-rw-r--r--dhall/tests/type-errors/unit/TypeAnnotationWrong.txt2
-rw-r--r--dhall/tests/type-errors/unit/UnionConstructorFieldNotPresent.txt2
-rw-r--r--dhall/tests/type-errors/unit/UnionTypeDuplicateVariants1.txt2
-rw-r--r--dhall/tests/type-errors/unit/UnionTypeDuplicateVariants2.txt2
-rw-r--r--dhall/tests/type-errors/unit/UnionTypeMixedKinds.txt2
-rw-r--r--dhall/tests/type-errors/unit/UnionTypeMixedKinds2.txt2
-rw-r--r--dhall/tests/type-errors/unit/UnionTypeMixedKinds3.txt2
-rw-r--r--dhall/tests/type-errors/unit/UnionTypeNotType.txt2
91 files changed, 382 insertions, 284 deletions
diff --git a/dhall-lang b/dhall-lang
-Subproject 781961f4694667667f093d28e7b35569c67678c
+Subproject 0c99dc6f53919fc2df6b965877c355b62cf6ba0
diff --git a/dhall/Cargo.toml b/dhall/Cargo.toml
index 34cf444..8674f44 100644
--- a/dhall/Cargo.toml
+++ b/dhall/Cargo.toml
@@ -26,7 +26,7 @@ pretty_assertions = "0.6.1"
[build-dependencies]
walkdir = "2"
-abnf_to_pest = { version = "0.1.2", path = "../abnf_to_pest" }
+abnf_to_pest = { version = "0.2.0", path = "../abnf_to_pest" }
pest_generator = "2.1"
quote = "1.0"
diff --git a/dhall/build.rs b/dhall/build.rs
index 3021f03..9b00684 100644
--- a/dhall/build.rs
+++ b/dhall/build.rs
@@ -1,7 +1,7 @@
use std::env;
use std::ffi::OsString;
-use std::fs::File;
-use std::io::{BufRead, BufReader, Read, Write};
+use std::fs::{read_to_string, File};
+use std::io::{BufRead, BufReader, Write};
use std::path::{Path, PathBuf};
use walkdir::WalkDir;
@@ -51,8 +51,7 @@ fn dhall_files_in_dir<'a>(
})
}
-#[derive(Debug, Clone)]
-struct TestFeature<F> {
+struct TestFeature {
/// Name of the module, used in the output of `cargo test`
module_name: &'static str,
/// Directory containing the tests files
@@ -60,7 +59,7 @@ struct TestFeature<F> {
/// Relevant variant of `dhall::tests::Test`
variant: &'static str,
/// Given a file name, whether to exclude it
- path_filter: F,
+ path_filter: Box<dyn FnMut(&str) -> bool>,
/// Type of the input file
input_type: FileType,
/// Type of the output file, if any
@@ -69,7 +68,7 @@ struct TestFeature<F> {
fn make_test_module(
w: &mut impl Write, // Where to output the generated code
- mut feature: TestFeature<impl FnMut(&str) -> bool>,
+ mut feature: TestFeature,
) -> std::io::Result<()> {
let tests_dir = feature.directory;
writeln!(w, "mod {} {{", feature.module_name)?;
@@ -113,15 +112,13 @@ fn generate_tests() -> std::io::Result<()> {
let parser_tests_path = Path::new(&out_dir).join("spec_tests.rs");
let spec_tests_dir = Path::new("../dhall-lang/tests/");
- let mut file = File::create(parser_tests_path)?;
- make_test_module(
- &mut file,
+ let tests = vec![
TestFeature {
module_name: "parser_success",
directory: spec_tests_dir.join("parser/success/"),
variant: "ParserSuccess",
- path_filter: |path: &str| {
+ path_filter: Box::new(|path: &str| {
false
// Too slow in debug mode
|| path == "largeExpression"
@@ -131,31 +128,25 @@ fn generate_tests() -> std::io::Result<()> {
|| path == "unit/import/urls/emptyPath0"
|| path == "unit/import/urls/emptyPath1"
|| path == "unit/import/urls/emptyPathSegment"
- },
+ // TODO: waiting for https://github.com/dhall-lang/dhall-lang/pull/871
+ || path == "preferMissingNoSpaces"
+ }),
input_type: FileType::Text,
output_type: Some(FileType::Binary),
},
- )?;
-
- make_test_module(
- &mut file,
TestFeature {
module_name: "parser_failure",
directory: spec_tests_dir.join("parser/failure/"),
variant: "ParserFailure",
- path_filter: |_path: &str| false,
+ path_filter: Box::new(|_path: &str| false),
input_type: FileType::Text,
output_type: None,
},
- )?;
-
- make_test_module(
- &mut file,
TestFeature {
module_name: "printer",
directory: spec_tests_dir.join("parser/success/"),
variant: "Printer",
- path_filter: |path: &str| {
+ path_filter: Box::new(|path: &str| {
false
// Too slow in debug mode
|| path == "largeExpression"
@@ -163,19 +154,17 @@ fn generate_tests() -> std::io::Result<()> {
|| path == "unit/import/urls/emptyPath0"
|| path == "unit/import/urls/emptyPath1"
|| path == "unit/import/urls/emptyPathSegment"
- },
+ // TODO: waiting for https://github.com/dhall-lang/dhall-lang/pull/871
+ || path == "preferMissingNoSpaces"
+ }),
input_type: FileType::Text,
output_type: Some(FileType::Binary),
},
- )?;
-
- make_test_module(
- &mut file,
TestFeature {
module_name: "binary_encoding",
directory: spec_tests_dir.join("parser/success/"),
variant: "BinaryEncoding",
- path_filter: |path: &str| {
+ path_filter: Box::new(|path: &str| {
false
// Too slow in debug mode
|| path == "largeExpression"
@@ -189,43 +178,39 @@ fn generate_tests() -> std::io::Result<()> {
|| path == "unit/import/urls/emptyPath0"
|| path == "unit/import/urls/emptyPath1"
|| path == "unit/import/urls/emptyPathSegment"
- },
+ // TODO: waiting for https://github.com/dhall-lang/dhall-lang/pull/871
+ || path == "preferMissingNoSpaces"
+ }),
input_type: FileType::Text,
output_type: Some(FileType::Binary),
},
- )?;
-
- make_test_module(
- &mut file,
TestFeature {
module_name: "binary_decoding_success",
directory: spec_tests_dir.join("binary-decode/success/"),
variant: "BinaryDecodingSuccess",
- path_filter: |_path: &str| false,
+ path_filter: Box::new(|path: &str| {
+ false
+ // We don't support bignums
+ || path == "unit/IntegerBigNegative"
+ || path == "unit/IntegerBigPositive"
+ || path == "unit/NaturalBig"
+ }),
input_type: FileType::Binary,
output_type: Some(FileType::Text),
},
- )?;
-
- make_test_module(
- &mut file,
TestFeature {
module_name: "binary_decoding_failure",
directory: spec_tests_dir.join("binary-decode/failure/"),
variant: "BinaryDecodingFailure",
- path_filter: |_path: &str| false,
+ path_filter: Box::new(|_path: &str| false),
input_type: FileType::Binary,
output_type: None,
},
- )?;
-
- make_test_module(
- &mut file,
TestFeature {
module_name: "import_success",
directory: spec_tests_dir.join("import/success/"),
variant: "ImportSuccess",
- path_filter: |path: &str| {
+ path_filter: Box::new(|path: &str| {
false
|| path == "alternativeEnvNatural"
|| path == "alternativeEnvSimple"
@@ -236,38 +221,31 @@ fn generate_tests() -> std::io::Result<()> {
|| path == "hashFromCache"
|| path == "headerForwarding"
|| path == "noHeaderForwarding"
- },
+ }),
input_type: FileType::Text,
output_type: Some(FileType::Text),
},
- )?;
-
- make_test_module(
- &mut file,
TestFeature {
module_name: "import_failure",
directory: spec_tests_dir.join("import/failure/"),
variant: "ImportFailure",
- path_filter: |path: &str| {
+ path_filter: Box::new(|path: &str| {
false
|| path == "alternativeEnv"
|| path == "alternativeEnvMissing"
|| path == "hashMismatch"
|| path == "missing"
|| path == "referentiallyInsane"
- },
+ || path == "customHeadersUsingBoundVariable"
+ }),
input_type: FileType::Text,
output_type: None,
},
- )?;
-
- make_test_module(
- &mut file,
TestFeature {
module_name: "beta_normalize",
directory: spec_tests_dir.join("normalization/success/"),
variant: "Normalization",
- path_filter: |path: &str| {
+ path_filter: Box::new(|path: &str| {
// We don't support bignums
path == "simple/integerToDouble"
// Too slow
@@ -301,34 +279,32 @@ fn generate_tests() -> std::io::Result<()> {
|| path == "unit/RightBiasedMergeEquivalentArguments"
|| path == "unit/NestedRecordProjection"
|| path == "unit/NestedRecordProjectionByType"
- },
+ // TODO: record completion
+ || path == "simple/completion"
+ || path == "unit/Completion"
+ // TODO: waiting for https://github.com/dhall-lang/dhall-lang/pull/871
+ || path == "unit/MergeNone"
+ || path == "unit/MergeSome"
+ }),
input_type: FileType::Text,
output_type: Some(FileType::Text),
},
- )?;
-
- make_test_module(
- &mut file,
TestFeature {
module_name: "alpha_normalize",
directory: spec_tests_dir.join("alpha-normalization/success/"),
variant: "AlphaNormalization",
- path_filter: |path: &str| {
+ path_filter: Box::new(|path: &str| {
// This test doesn't typecheck
path == "unit/FunctionNestedBindingXXFree"
- },
+ }),
input_type: FileType::Text,
output_type: Some(FileType::Text),
},
- )?;
-
- make_test_module(
- &mut file,
TestFeature {
module_name: "type_inference_success",
directory: spec_tests_dir.join("type-inference/success/"),
variant: "TypeInferenceSuccess",
- path_filter: |path: &str| {
+ path_filter: Box::new(|path: &str| {
false
// Too slow
|| path == "prelude"
@@ -339,24 +315,24 @@ fn generate_tests() -> std::io::Result<()> {
// TODO: toMap
|| path == "unit/ToMap"
|| path == "unit/ToMapAnnotated"
+ || path == "unit/ToMapInferTypeFromRecord"
|| path == "simple/toMapEmptyNormalizeAnnotation"
- },
+ // TODO: record completion
+ || path == "simple/completion"
+ || path == "unit/Completion"
+ // TODO: waiting for https://github.com/dhall-lang/dhall-lang/pull/871
+ || path == "unit/MergeNone"
+ || path == "unit/MergeSome"
+ }),
input_type: FileType::Text,
output_type: Some(FileType::Text),
},
- )?;
-
- make_test_module(
- &mut file,
TestFeature {
module_name: "type_inference_failure",
directory: spec_tests_dir.join("type-inference/failure/"),
variant: "TypeInferenceFailure",
- path_filter: |path: &str| {
+ path_filter: Box::new(|path: &str| {
false
- // TODO: Enable imports in typecheck tests
- || path == "importBoundary"
- || path == "customHeadersUsingBoundVariable"
// TODO: projection by expression
|| path == "unit/RecordProjectionByTypeFieldTypeMismatch"
|| path == "unit/RecordProjectionByTypeNotPresent"
@@ -368,24 +344,23 @@ fn generate_tests() -> std::io::Result<()> {
|| path == "unit/MistypedToMap3"
|| path == "unit/MistypedToMap4"
|| path == "unit/NonRecordToMap"
+ || path == "unit/ToMapEmptyInvalidAnnotation"
|| path == "unit/ToMapWrongKind"
- },
+ // TODO: record completion
+ || path == "unit/CompletionMissingRequiredField"
+ || path == "unit/CompletionWithWrongDefaultType"
+ || path == "unit/CompletionWithWrongFieldName"
+ || path == "unit/CompletionWithWrongOverridenType"
+ }),
input_type: FileType::Text,
output_type: None,
},
- )?;
-
- make_test_module(
- &mut file,
TestFeature {
module_name: "type_error",
directory: spec_tests_dir.join("type-inference/failure/"),
variant: "TypeError",
- path_filter: |path: &str| {
+ path_filter: Box::new(|path: &str| {
false
- // TODO: Enable imports in typecheck tests
- || path == "importBoundary"
- || path == "customHeadersUsingBoundVariable"
// TODO: projection by expression
|| path == "unit/RecordProjectionByTypeFieldTypeMismatch"
|| path == "unit/RecordProjectionByTypeNotPresent"
@@ -397,12 +372,23 @@ fn generate_tests() -> std::io::Result<()> {
|| path == "unit/MistypedToMap3"
|| path == "unit/MistypedToMap4"
|| path == "unit/NonRecordToMap"
+ || path == "unit/ToMapEmptyInvalidAnnotation"
|| path == "unit/ToMapWrongKind"
- },
+ // TODO: record completion
+ || path == "unit/CompletionMissingRequiredField"
+ || path == "unit/CompletionWithWrongDefaultType"
+ || path == "unit/CompletionWithWrongFieldName"
+ || path == "unit/CompletionWithWrongOverridenType"
+ }),
input_type: FileType::Text,
output_type: None,
},
- )?;
+ ];
+
+ let mut file = File::create(parser_tests_path)?;
+ for test in tests {
+ make_test_module(&mut file, test)?;
+ }
Ok(())
}
@@ -415,10 +401,9 @@ fn convert_abnf_to_pest() -> std::io::Result<()> {
println!("cargo:rerun-if-changed={}", abnf_path);
println!("cargo:rerun-if-changed={}", visibility_path);
- let mut file = File::open(abnf_path)?;
- let mut data = Vec::new();
- file.read_to_end(&mut data)?;
- data.push('\n' as u8);
+ let mut data = read_to_string(abnf_path)?;
+ data.push('\n');
+ let data = data.replace('∀', ""); // TODO: waiting for abnf 0.6.1
let mut rules = abnf_to_pest::parse_abnf(&data)?;
for line in BufReader::new(File::open(visibility_path)?).lines() {
@@ -435,6 +420,24 @@ fn convert_abnf_to_pest() -> std::io::Result<()> {
rules.remove("url_path");
writeln!(&mut file, "url_path = _{{ path }}")?;
+ // Work around some greediness issue in the grammar.
+ rules.remove("missing");
+ writeln!(
+ &mut file,
+ r#"missing = {{ "missing" ~ !simple_label_next_char }}"#
+ )?;
+
+ // Prefer my nice error message to illegible parse errors.
+ rules.remove("unicode_escape");
+ rules.remove("unbraced_escape");
+ rules.remove("braced_escape");
+ rules.remove("braced_codepoint");
+ rules.remove("unicode_suffix");
+ writeln!(
+ &mut file,
+ r#"unicode_escape = _{{ HEXDIG{{4}} | "{{" ~ HEXDIG+ ~ "}}" }}"#
+ )?;
+
rules.remove("simple_label");
writeln!(
&mut file,
diff --git a/dhall/src/dhall.pest.visibility b/dhall/src/dhall.pest.visibility
index 17c1edc..03a000b 100644
--- a/dhall/src/dhall.pest.visibility
+++ b/dhall/src/dhall.pest.visibility
@@ -21,9 +21,14 @@ quoted_label
# label
# nonreserved_label
# any_label
+any_label_or_some
double_quote_chunk
double_quote_escaped
# unicode_escape
+# unicode_suffix
+# unbraced_escape
+# braced_codepoint
+# braced_escape
double_quote_char
double_quote_literal
single_quote_continue
@@ -91,6 +96,7 @@ prefer
lambda
forall
arrow
+# complete
# exponent
numeric_double_literal
minus_infinity_literal
@@ -161,6 +167,7 @@ equivalent_expression
application_expression
first_application_expression
# import_expression
+completion_expression
selector_expression
selector
labels
diff --git a/dhall/src/error/mod.rs b/dhall/src/error/mod.rs
index 1288c12..6e7be64 100644
--- a/dhall/src/error/mod.rs
+++ b/dhall/src/error/mod.rs
@@ -67,7 +67,7 @@ pub(crate) enum TypeMessage {
BinOpTypeMismatch(BinOp, Value),
InvalidTextInterpolation(Value),
Merge1ArgMustBeRecord(Value),
- Merge2ArgMustBeUnion(Value),
+ Merge2ArgMustBeUnionOrOptional(Value),
MergeEmptyNeedsAnnotation,
MergeHandlerMissingVariant(Label),
MergeVariantMissingHandler(Label),
@@ -76,6 +76,7 @@ pub(crate) enum TypeMessage {
MergeHandlerReturnTypeMustNotBeDependent,
ProjectionMustBeRecord,
ProjectionMissingEntry,
+ ProjectionDuplicateField,
Sort,
RecordTypeDuplicateField,
RecordTypeMergeRequiresRecordType(Value),
@@ -124,7 +125,7 @@ impl std::fmt::Display for TypeError {
y
))
}
- _ => "Type error: Unhandled error".to_string(),
+ _ => format!("Type error: Unhandled error: {:?}", self.message),
};
write!(f, "{}", msg)
}
diff --git a/dhall/src/semantics/phase/normalize.rs b/dhall/src/semantics/phase/normalize.rs
index d81a910..459eaf1 100644
--- a/dhall/src/semantics/phase/normalize.rs
+++ b/dhall/src/semantics/phase/normalize.rs
@@ -1,4 +1,5 @@
use std::collections::HashMap;
+use std::convert::TryInto;
use crate::semantics::core::value::Value;
use crate::semantics::core::value::ValueKind;
@@ -141,6 +142,16 @@ pub(crate) fn apply_builtin(
}
_ => Ret::DoneAsIs,
},
+ (IntegerNegate, [n]) => match &*n.as_whnf() {
+ IntegerLit(n) => Ret::ValueKind(IntegerLit(-n)),
+ _ => Ret::DoneAsIs,
+ },
+ (IntegerClamp, [n]) => match &*n.as_whnf() {
+ IntegerLit(n) => {
+ Ret::ValueKind(NaturalLit((*n).try_into().unwrap_or(0)))
+ }
+ _ => Ret::DoneAsIs,
+ },
(DoubleShow, [n]) => {
match &*n.as_whnf() {
DoubleLit(n) => Ret::ValueKind(TextLit(vec![
@@ -259,37 +270,23 @@ pub(crate) fn apply_builtin(
_ => Ret::DoneAsIs,
}
}
- (ListBuild, [t, f]) => match &*f.as_whnf() {
- // fold/build fusion
- ValueKind::AppliedBuiltin(ListFold, args) => {
- if args.len() >= 2 {
- Ret::Value(args[1].clone())
- } else {
- // Do we really need to handle this case ?
- unimplemented!()
- }
- }
- _ => {
- let list_t = Value::from_builtin(List).app(t.clone());
- Ret::Value(
- f.app(list_t.clone())
- .app({
- // Move `t` under new variables
- let t1 = t.under_binder(Label::from("x"));
- let t2 = t1.under_binder(Label::from("xs"));
- make_closure!(
- λ(x : #t) ->
- λ(xs : List #t1) ->
- [ var(x, 1, #t2) ] # var(xs, 0, List #t2)
- )
- })
- .app(
- EmptyListLit(t.clone())
- .into_value_with_type(list_t),
- ),
- )
- }
- },
+ (ListBuild, [t, f]) => {
+ let list_t = Value::from_builtin(List).app(t.clone());
+ Ret::Value(
+ f.app(list_t.clone())
+ .app({
+ // Move `t` under new variables
+ let t1 = t.under_binder(Label::from("x"));
+ let t2 = t1.under_binder(Label::from("xs"));
+ make_closure!(
+ λ(x : #t) ->
+ λ(xs : List #t1) ->
+ [ var(x, 1, #t2) ] # var(xs, 0, List #t2)
+ )
+ })
+ .app(EmptyListLit(t.clone()).into_value_with_type(list_t)),
+ )
+ }
(ListFold, [_, l, _, cons, nil, r @ ..]) => match &*l.as_whnf() {
EmptyListLit(_) => Ret::ValueWithRemainingArgs(r, nil.clone()),
NEListLit(xs) => {
@@ -301,31 +298,20 @@ pub(crate) fn apply_builtin(
}
_ => Ret::DoneAsIs,
},
- (OptionalBuild, [t, f]) => match &*f.as_whnf() {
- // fold/build fusion
- ValueKind::AppliedBuiltin(OptionalFold, args) => {
- if args.len() >= 2 {
- Ret::Value(args[1].clone())
- } else {
- // Do we really need to handle this case ?
- unimplemented!()
- }
- }
- _ => {
- let optional_t = Value::from_builtin(Optional).app(t.clone());
- Ret::Value(
- f.app(optional_t.clone())
- .app({
- let t1 = t.under_binder(Label::from("x"));
- make_closure!(λ(x: #t) -> Some(var(x, 0, #t1)))
- })
- .app(
- EmptyOptionalLit(t.clone())
- .into_value_with_type(optional_t),
- ),
- )
- }
- },
+ (OptionalBuild, [t, f]) => {
+ let optional_t = Value::from_builtin(Optional).app(t.clone());
+ Ret::Value(
+ f.app(optional_t.clone())
+ .app({
+ let t1 = t.under_binder(Label::from("x"));
+ make_closure!(λ(x: #t) -> Some(var(x, 0, #t1)))
+ })
+ .app(
+ EmptyOptionalLit(t.clone())
+ .into_value_with_type(optional_t),
+ ),
+ )
+ }
(OptionalFold, [_, v, _, just, nothing, r @ ..]) => match &*v.as_whnf()
{
EmptyOptionalLit(_) => {
@@ -336,27 +322,17 @@ pub(crate) fn apply_builtin(
}
_ => Ret::DoneAsIs,
},
- (NaturalBuild, [f]) => match &*f.as_whnf() {
- // fold/build fusion
- ValueKind::AppliedBuiltin(NaturalFold, args) => {
- if !args.is_empty() {
- Ret::Value(args[0].clone())
- } else {
- // Do we really need to handle this case ?
- unimplemented!()
- }
- }
- _ => Ret::Value(
- f.app(Value::from_builtin(Natural))
- .app(make_closure!(
- λ(x : Natural) -> 1 + var(x, 0, Natural)
- ))
- .app(
- NaturalLit(0)
- .into_value_with_type(Value::from_builtin(Natural)),
- ),
- ),
- },
+ (NaturalBuild, [f]) => Ret::Value(
+ f.app(Value::from_builtin(Natural))
+ .app(make_closure!(
+ λ(x : Natural) -> 1 + var(x, 0, Natural)
+ ))
+ .app(
+ NaturalLit(0)
+ .into_value_with_type(Value::from_builtin(Natural)),
+ ),
+ ),
+
(NaturalFold, [n, t, succ, zero, r @ ..]) => match &*n.as_whnf() {
NaturalLit(0) => Ret::ValueWithRemainingArgs(r, zero.clone()),
NaturalLit(n) => {
@@ -614,8 +590,8 @@ pub(crate) fn normalize_one_layer(
ty: &Value,
) -> ValueKind {
use ValueKind::{
- AppliedBuiltin, BoolLit, DoubleLit, EmptyListLit, IntegerLit,
- NEListLit, NEOptionalLit, NaturalLit, RecordLit, TextLit,
+ AppliedBuiltin, BoolLit, DoubleLit, EmptyListLit, EmptyOptionalLit,
+ IntegerLit, NEListLit, NEOptionalLit, NaturalLit, RecordLit, TextLit,
UnionConstructor, UnionLit, UnionType,
};
@@ -741,6 +717,7 @@ pub(crate) fn normalize_one_layer(
ExprKind::ProjectionByExpr(_, _) => {
unimplemented!("selection by expression")
}
+ ExprKind::Completion(_, _) => unimplemented!("record completion"),
ExprKind::Merge(ref handlers, ref variant, _) => {
let handlers_borrow = handlers.as_whnf();
@@ -762,6 +739,26 @@ pub(crate) fn normalize_one_layer(
Ret::Expr(expr)
}
},
+ (RecordLit(kvs), EmptyOptionalLit(_)) => {
+ match kvs.get(&"None".into()) {
+ Some(h) => Ret::Value(h.clone()),
+ None => {
+ drop(handlers_borrow);
+ drop(variant_borrow);
+ Ret::Expr(expr)
+ }
+ }
+ }
+ (RecordLit(kvs), NEOptionalLit(v)) => {
+ match kvs.get(&"Some".into()) {
+ Some(h) => Ret::Value(h.app(v.clone())),
+ None => {
+ drop(handlers_borrow);
+ drop(variant_borrow);
+ Ret::Expr(expr)
+ }
+ }
+ }
_ => {
drop(handlers_borrow);
drop(variant_borrow);
diff --git a/dhall/src/semantics/phase/typecheck.rs b/dhall/src/semantics/phase/typecheck.rs
index c439f74..97502d4 100644
--- a/dhall/src/semantics/phase/typecheck.rs
+++ b/dhall/src/semantics/phase/typecheck.rs
@@ -1,3 +1,4 @@
+use std::borrow::Cow;
use std::cmp::max;
use std::collections::HashMap;
@@ -235,6 +236,9 @@ fn type_of_builtin<E>(b: Builtin) -> Expr<E> {
IntegerToDouble => make_type!(Integer -> Double),
IntegerShow => make_type!(Integer -> Text),
+ IntegerNegate => make_type!(Integer -> Integer),
+ IntegerClamp => make_type!(Integer -> Natural),
+
DoubleShow => make_type!(Double -> Text),
TextShow => make_type!(Text -> Text),
@@ -692,8 +696,19 @@ fn type_last_layer(
let union_type = union.get_type()?;
let union_borrow = union_type.as_whnf();
let variants = match &*union_borrow {
- ValueKind::UnionType(kts) => kts,
- _ => return mkerr(Merge2ArgMustBeUnion(union.clone())),
+ ValueKind::UnionType(kts) => Cow::Borrowed(kts),
+ ValueKind::AppliedBuiltin(syntax::Builtin::Optional, args)
+ if args.len() == 1 =>
+ {
+ let ty = &args[0];
+ let mut kts = HashMap::new();
+ kts.insert("None".into(), None);
+ kts.insert("Some".into(), Some(ty.clone()));
+ Cow::Owned(kts)
+ }
+ _ => {
+ return mkerr(Merge2ArgMustBeUnionOrOptional(union.clone()))
+ }
};
let mut inferred_type = None;
@@ -774,7 +789,15 @@ fn type_last_layer(
for l in labels {
match kts.get(l) {
None => return mkerr(ProjectionMissingEntry),
- Some(t) => new_kts.insert(l.clone(), t.clone()),
+ Some(t) => {
+ use std::collections::hash_map::Entry;
+ match new_kts.entry(l.clone()) {
+ Entry::Occupied(_) => {
+ return mkerr(ProjectionDuplicateField)
+ }
+ Entry::Vacant(e) => e.insert(t.clone()),
+ }
+ }
};
}
@@ -784,6 +807,7 @@ fn type_last_layer(
))
}
ProjectionByExpr(_, _) => unimplemented!("selection by expression"),
+ Completion(_, _) => unimplemented!("record completion"),
};
Ok(match ret {
diff --git a/dhall/src/syntax/ast/expr.rs b/dhall/src/syntax/ast/expr.rs
index 48c48d8..68cb524 100644
--- a/dhall/src/syntax/ast/expr.rs
+++ b/dhall/src/syntax/ast/expr.rs
@@ -79,6 +79,8 @@ pub enum Builtin {
NaturalSubtract,
IntegerToDouble,
IntegerShow,
+ IntegerNegate,
+ IntegerClamp,
DoubleShow,
ListBuild,
ListFold,
@@ -163,6 +165,8 @@ pub enum ExprKind<SubExpr, Embed> {
Projection(SubExpr, DupTreeSet<Label>),
/// `e.(t)`
ProjectionByExpr(SubExpr, SubExpr),
+ /// `x::y`
+ Completion(SubExpr, SubExpr),
/// `./some/path`
Import(Import<SubExpr>),
/// Embeds the result of resolving an import
diff --git a/dhall/src/syntax/ast/visitor.rs b/dhall/src/syntax/ast/visitor.rs
index b557995..424048b 100644
--- a/dhall/src/syntax/ast/visitor.rs
+++ b/dhall/src/syntax/ast/visitor.rs
@@ -164,6 +164,9 @@ where
ProjectionByExpr(e, x) => {
ProjectionByExpr(v.visit_subexpr(e)?, v.visit_subexpr(x)?)
}
+ Completion(e, x) => {
+ Completion(v.visit_subexpr(e)?, v.visit_subexpr(x)?)
+ }
Assert(e) => Assert(v.visit_subexpr(e)?),
Import(i) => Import(i.traverse_ref(|e| v.visit_subexpr(e))?),
Embed(a) => Embed(v.visit_embed(a)?),
@@ -281,6 +284,10 @@ where
v.visit_subexpr(e)?;
v.visit_subexpr(x)?;
}
+ Completion(x, y) => {
+ v.visit_subexpr(x)?;
+ v.visit_subexpr(y)?;
+ }
Assert(e) => v.visit_subexpr(e)?,
Import(i) => i.traverse_mut(|e| v.visit_subexpr(e))?,
Embed(a) => v.visit_embed(a)?,
diff --git a/dhall/src/syntax/binary/decode.rs b/dhall/src/syntax/binary/decode.rs
index 254ab07..c18deb5 100644
--- a/dhall/src/syntax/binary/decode.rs
+++ b/dhall/src/syntax/binary/decode.rs
@@ -98,6 +98,11 @@ fn cbor_value_to_dhall(data: &cbor::Value) -> Result<DecodedExpr, DecodeError> {
let l = Label::from(l.as_str());
Pi(l, x, y)
}
+ [U64(3), U64(13), x, y] => {
+ let x = cbor_value_to_dhall(&x)?;
+ let y = cbor_value_to_dhall(&y)?;
+ Completion(x, y)
+ }
[U64(3), U64(n), x, y] => {
let x = cbor_value_to_dhall(&x)?;
let y = cbor_value_to_dhall(&y)?;
diff --git a/dhall/src/syntax/binary/encode.rs b/dhall/src/syntax/binary/encode.rs
index 25a545c..5e79f2d 100644
--- a/dhall/src/syntax/binary/encode.rs
+++ b/dhall/src/syntax/binary/encode.rs
@@ -164,6 +164,7 @@ where
ProjectionByExpr(x, y) => {
ser_seq!(ser; tag(10), expr(x), vec![expr(y)])
}
+ Completion(x, y) => ser_seq!(ser; tag(3), tag(13), expr(x), expr(y)),
Import(import) => serialize_import(ser, import),
Embed(_) => unimplemented!(
"An expression with resolved imports cannot be binary-encoded"
diff --git a/dhall/src/syntax/text/parser.rs b/dhall/src/syntax/text/parser.rs
index 832472b..feaa2a5 100644
--- a/dhall/src/syntax/text/parser.rs
+++ b/dhall/src/syntax/text/parser.rs
@@ -57,6 +57,8 @@ impl crate::syntax::Builtin {
"Natural/subtract" => Some(NaturalSubtract),
"Integer/toDouble" => Some(IntegerToDouble),
"Integer/show" => Some(IntegerShow),
+ "Integer/negate" => Some(IntegerNegate),
+ "Integer/clamp" => Some(IntegerClamp),
"Double/show" => Some(DoubleShow),
"List/build" => Some(ListBuild),
"List/fold" => Some(ListFold),
@@ -181,6 +183,15 @@ impl DhallParser {
Ok(Label::from(input.as_str()))
}
+ // TODO: waiting for https://github.com/dhall-lang/dhall-lang/pull/871
+ // #[alias(label)]
+ // fn any_label_or_some(input: ParseInput) -> ParseResult<Label> {
+ // Ok(match_nodes!(input.into_children();
+ // [label(l)] => l,
+ // [Some_(_)] => Label::from("Some"),
+ // ))
+ // }
+
fn double_quote_literal(input: ParseInput) -> ParseResult<ParsedText> {
Ok(match_nodes!(input.into_children();
[double_quote_chunk(chunks)..] => {
@@ -215,19 +226,18 @@ impl DhallParser {
"t" => "\t".to_owned(),
// "uXXXX" or "u{XXXXX}"
s => {
- use std::convert::{TryFrom, TryInto};
+ use std::convert::TryInto;
let s = &s[1..];
let s = if &s[0..1] == "{" {
&s[1..s.len() - 1]
} else {
- &s[0..s.len()]
+ s
};
if s.len() > 8 {
Err(input.error(format!(
- "Escape sequences can't have more than 8 chars: \"{}\"",
- s
+ "Escape sequences can't have more than 8 chars"
)))?
}
@@ -240,12 +250,10 @@ impl DhallParser {
// `s` has length 8, so `bytes` has length 4
let bytes: &[u8] = &hex::decode(s).unwrap();
let i = u32::from_be_bytes(bytes.try_into().unwrap());
- let c = char::try_from(i).unwrap();
match i {
- 0xD800..=0xDFFF => {
- let c_ecapsed = c.escape_unicode();
- Err(input.error(format!("Escape sequences can't contain surrogate pairs: \"{}\"", c_ecapsed)))?
- }
+ 0xD800..=0xDFFF => Err(input.error(format!(
+ "Escape sequences can't contain surrogate pairs"
+ )))?,
0x0FFFE..=0x0FFFF
| 0x1FFFE..=0x1FFFF
| 0x2FFFE..=0x2FFFF
@@ -262,12 +270,12 @@ impl DhallParser {
| 0xDFFFE..=0xDFFFF
| 0xEFFFE..=0xEFFFF
| 0xFFFFE..=0xFFFFF
- | 0x10_FFFE..=0x10_FFFF => {
- let c_ecapsed = c.escape_unicode();
- Err(input.error(format!("Escape sequences can't contain non-characters: \"{}\"", c_ecapsed)))?
- }
+ | 0x10_FFFE..=0x10_FFFF => Err(input.error(format!(
+ "Escape sequences can't contain non-characters"
+ )))?,
_ => {}
}
+ let c: char = i.try_into().unwrap();
std::iter::once(c).collect()
}
})
@@ -384,19 +392,27 @@ impl DhallParser {
}
fn natural_literal(input: ParseInput) -> ParseResult<Natural> {
- input
- .as_str()
- .trim()
- .parse()
- .map_err(|e| input.error(format!("{}", e)))
+ let s = input.as_str().trim();
+ if s.starts_with("0x") {
+ let without_prefix = s.trim_start_matches("0x");
+ usize::from_str_radix(without_prefix, 16)
+ .map_err(|e| input.error(format!("{}", e)))
+ } else {
+ s.parse().map_err(|e| input.error(format!("{}", e)))
+ }
}
fn integer_literal(input: ParseInput) -> ParseResult<Integer> {
- input
- .as_str()
- .trim()
- .parse()
- .map_err(|e| input.error(format!("{}", e)))
+ let s = input.as_str().trim();
+ let (sign, rest) = (&s[0..1], &s[1..]);
+ if rest.starts_with("0x") {
+ let without_prefix =
+ sign.to_owned() + rest.trim_start_matches("0x");
+ isize::from_str_radix(&without_prefix, 16)
+ .map_err(|e| input.error(format!("{}", e)))
+ } else {
+ s.parse().map_err(|e| input.error(format!("{}", e)))
+ }
}
#[alias(expression, shortcut = true)]
@@ -766,6 +782,25 @@ impl DhallParser {
}
#[alias(expression, shortcut = true)]
+ fn completion_expression(input: ParseInput) -> ParseResult<Expr> {
+ Ok(match_nodes!(input.children();
+ [expression(e)] => e,
+ [expression(first), expression(rest)..] => {
+ rest.fold(
+ first,
+ |acc, e| {
+ spanned_union(
+ acc.span(),
+ e.span(),
+ Completion(acc, e),
+ )
+ }
+ )
+ },
+ ))
+ }
+
+ #[alias(expression, shortcut = true)]
fn selector_expression(input: ParseInput) -> ParseResult<Expr> {
Ok(match_nodes!(input.children();
[expression(e)] => e,
diff --git a/dhall/src/syntax/text/printer.rs b/dhall/src/syntax/text/printer.rs
index 78942ed..96f4c2a 100644
--- a/dhall/src/syntax/text/printer.rs
+++ b/dhall/src/syntax/text/printer.rs
@@ -64,6 +64,9 @@ impl<E: Display + Clone> UnspannedExpr<E> {
Field(a, b) => Field(a.phase(Primitive), b),
Projection(e, ls) => Projection(e.phase(Primitive), ls),
ProjectionByExpr(a, b) => ProjectionByExpr(a.phase(Primitive), b),
+ Completion(a, b) => {
+ Completion(a.phase(Primitive), b.phase(Primitive))
+ }
e => e,
}
}
@@ -89,9 +92,10 @@ impl<E: Display + Clone> UnspannedExpr<E> {
// Precedence is magically handled by the ordering of BinOps.
ExprKind::BinOp(op, _, _) => phase > PrintPhase::BinOp(*op),
ExprKind::App(_, _) => phase > PrintPhase::App,
- Field(_, _) | Projection(_, _) | ProjectionByExpr(_, _) => {
- phase > PrintPhase::Import
- }
+ Field(_, _)
+ | Projection(_, _)
+ | ProjectionByExpr(_, _)
+ | Completion(_, _) => phase > PrintPhase::Import,
_ => false,
};
@@ -189,13 +193,6 @@ impl<SE: Display + Clone, E: Display> Display for ExprKind<SE, E> {
Field(a, b) => {
write!(f, "{}.{}", a, b)?;
}
- Projection(e, ls) => {
- write!(f, "{}.", e)?;
- fmt_list("{ ", ", ", " }", ls, f, Display::fmt)?;
- }
- ProjectionByExpr(a, b) => {
- write!(f, "{}.({})", a, b)?;
- }
Var(a) => a.fmt(f)?,
Const(k) => k.fmt(f)?,
Builtin(v) => v.fmt(f)?,
@@ -224,6 +221,16 @@ impl<SE: Display + Clone, E: Display> Display for ExprKind<SE, E> {
}
Ok(())
})?,
+ Projection(e, ls) => {
+ write!(f, "{}.", e)?;
+ fmt_list("{ ", ", ", " }", ls, f, Display::fmt)?;
+ }
+ ProjectionByExpr(a, b) => {
+ write!(f, "{}.({})", a, b)?;
+ }
+ Completion(a, b) => {
+ write!(f, "{}::{}", a, b)?;
+ }
Import(a) => a.fmt(f)?,
Embed(a) => a.fmt(f)?,
}
@@ -322,6 +329,8 @@ impl Display for NaiveDouble {
f.write_str("-Infinity")
} else if v.is_nan() {
f.write_str("NaN")
+ } else if v == 0.0 && v.is_sign_negative() {
+ f.write_str("-0.0")
} else {
let s = format!("{}", v);
if s.contains('e') || s.contains('.') {
@@ -459,6 +468,8 @@ impl Display for Builtin {
NaturalShow => "Natural/show",
NaturalSubtract => "Natural/subtract",
IntegerToDouble => "Integer/toDouble",
+ IntegerNegate => "Integer/negate",
+ IntegerClamp => "Integer/clamp",
IntegerShow => "Integer/show",
DoubleShow => "Double/show",
ListBuild => "List/build",
diff --git a/dhall/tests/type-errors/mixedUnions.txt b/dhall/tests/type-errors/mixedUnions.txt
index 4b68fea..30fcdf8 100644
--- a/dhall/tests/type-errors/mixedUnions.txt
+++ b/dhall/tests/type-errors/mixedUnions.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: InvalidFieldType(Label("Right"), Type)
diff --git a/dhall/tests/type-errors/recordOfKind.txt b/dhall/tests/type-errors/recordOfKind.txt
index 4b68fea..5b88ff7 100644
--- a/dhall/tests/type-errors/recordOfKind.txt
+++ b/dhall/tests/type-errors/recordOfKind.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: Sort
diff --git a/dhall/tests/type-errors/unit/AnnotationRecordWrongFieldName.txt b/dhall/tests/type-errors/unit/AnnotationRecordWrongFieldName.txt
index 4b68fea..e15da48 100644
--- a/dhall/tests/type-errors/unit/AnnotationRecordWrongFieldName.txt
+++ b/dhall/tests/type-errors/unit/AnnotationRecordWrongFieldName.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: AnnotMismatch(Value@Unevaled { value: PartialExpr(RecordLit(DupTreeMap { map: {Label("x"): [Value@Unevaled { value: PartialExpr(NaturalLit(1)), type: Value@Unevaled { value: AppliedBuiltin(Natural, []), type: Type } }]}, size: 1 })), type: Value@WHNF { value: RecordType({Label("x"): Value@Unevaled { value: AppliedBuiltin(Natural, []), type: Type }}), type: Type } }, Value@WHNF { value: RecordType({Label("y"): Value@Unevaled { value: AppliedBuiltin(Natural, []), type: Type }}), type: Type })
diff --git a/dhall/tests/type-errors/unit/AnnotationRecordWrongFieldType.txt b/dhall/tests/type-errors/unit/AnnotationRecordWrongFieldType.txt
index 4b68fea..16b2d83 100644
--- a/dhall/tests/type-errors/unit/AnnotationRecordWrongFieldType.txt
+++ b/dhall/tests/type-errors/unit/AnnotationRecordWrongFieldType.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: AnnotMismatch(Value@Unevaled { value: PartialExpr(RecordLit(DupTreeMap { map: {Label("x"): [Value@Unevaled { value: PartialExpr(NaturalLit(1)), type: Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type } }]}, size: 1 })), type: Value@WHNF { value: RecordType({Label("x"): Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type }}), type: Type } }, Value@WHNF { value: RecordType({Label("x"): Value@WHNF { value: AppliedBuiltin(Text, []), type: Type }}), type: Type })
diff --git a/dhall/tests/type-errors/unit/AssertDoubleZeros.txt b/dhall/tests/type-errors/unit/AssertDoubleZeros.txt
new file mode 100644
index 0000000..d9df5c7
--- /dev/null
+++ b/dhall/tests/type-errors/unit/AssertDoubleZeros.txt
@@ -0,0 +1 @@
+Type error: Unhandled error: AssertMismatch(Value@WHNF { value: DoubleLit(NaiveDouble(-0.0)), type: Value@WHNF { value: AppliedBuiltin(Double, []), type: Type } }, Value@WHNF { value: DoubleLit(NaiveDouble(0.0)), type: Value@WHNF { value: AppliedBuiltin(Double, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/AssertNotEquivalence.txt b/dhall/tests/type-errors/unit/AssertNotEquivalence.txt
index 4b68fea..537d31a 100644
--- a/dhall/tests/type-errors/unit/AssertNotEquivalence.txt
+++ b/dhall/tests/type-errors/unit/AssertNotEquivalence.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: AssertMustTakeEquivalence
diff --git a/dhall/tests/type-errors/unit/AssertTriviallyFalse.txt b/dhall/tests/type-errors/unit/AssertTriviallyFalse.txt
index 4b68fea..c7bbf8d 100644
--- a/dhall/tests/type-errors/unit/AssertTriviallyFalse.txt
+++ b/dhall/tests/type-errors/unit/AssertTriviallyFalse.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: AssertMismatch(Value@WHNF { value: NaturalLit(1), type: Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type } }, Value@WHNF { value: NaturalLit(2), type: Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/EquivalenceNotSameType.txt b/dhall/tests/type-errors/unit/EquivalenceNotSameType.txt
index 4b68fea..4dbe2ad 100644
--- a/dhall/tests/type-errors/unit/EquivalenceNotSameType.txt
+++ b/dhall/tests/type-errors/unit/EquivalenceNotSameType.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: EquivalenceTypeMismatch(Value@Unevaled { value: PartialExpr(BoolLit(false)), type: Value@WHNF { value: AppliedBuiltin(Bool, []), type: Type } }, Value@Unevaled { value: PartialExpr(NaturalLit(1)), type: Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/EquivalenceNotTerms.txt b/dhall/tests/type-errors/unit/EquivalenceNotTerms.txt
index 4b68fea..dbce067 100644
--- a/dhall/tests/type-errors/unit/EquivalenceNotTerms.txt
+++ b/dhall/tests/type-errors/unit/EquivalenceNotTerms.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: EquivalenceArgumentMustBeTerm(true, Value@Unevaled { value: AppliedBuiltin(Bool, []), type: Type })
diff --git a/dhall/tests/type-errors/unit/FunctionTypeKindSort.txt b/dhall/tests/type-errors/unit/FunctionTypeKindSort.txt
index 4b68fea..5b88ff7 100644
--- a/dhall/tests/type-errors/unit/FunctionTypeKindSort.txt
+++ b/dhall/tests/type-errors/unit/FunctionTypeKindSort.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: Sort
diff --git a/dhall/tests/type-errors/unit/FunctionTypeTypeSort.txt b/dhall/tests/type-errors/unit/FunctionTypeTypeSort.txt
index 4b68fea..5b88ff7 100644
--- a/dhall/tests/type-errors/unit/FunctionTypeTypeSort.txt
+++ b/dhall/tests/type-errors/unit/FunctionTypeTypeSort.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: Sort
diff --git a/dhall/tests/type-errors/unit/IfBranchesNotMatch.txt b/dhall/tests/type-errors/unit/IfBranchesNotMatch.txt
index 4b68fea..262e54f 100644
--- a/dhall/tests/type-errors/unit/IfBranchesNotMatch.txt
+++ b/dhall/tests/type-errors/unit/IfBranchesNotMatch.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: IfBranchMismatch(Value@Unevaled { value: PartialExpr(NaturalLit(1)), type: Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type } }, Value@Unevaled { value: PartialExpr(TextLit(InterpolatedText { head: "", tail: [] })), type: Value@WHNF { value: AppliedBuiltin(Text, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/IfBranchesNotType.txt b/dhall/tests/type-errors/unit/IfBranchesNotType.txt
index 4b68fea..8ef3a40 100644
--- a/dhall/tests/type-errors/unit/IfBranchesNotType.txt
+++ b/dhall/tests/type-errors/unit/IfBranchesNotType.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: IfBranchMustBeTerm(true, Type)
diff --git a/dhall/tests/type-errors/unit/IfNotBool.txt b/dhall/tests/type-errors/unit/IfNotBool.txt
index 4b68fea..64c0465 100644
--- a/dhall/tests/type-errors/unit/IfNotBool.txt
+++ b/dhall/tests/type-errors/unit/IfNotBool.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: InvalidPredicate(Value@Unevaled { value: PartialExpr(NaturalLit(1)), type: Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/LetWithWrongAnnotation.txt b/dhall/tests/type-errors/unit/LetWithWrongAnnotation.txt
index 4b68fea..0862162 100644
--- a/dhall/tests/type-errors/unit/LetWithWrongAnnotation.txt
+++ b/dhall/tests/type-errors/unit/LetWithWrongAnnotation.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: AnnotMismatch(Value@Unevaled { value: PartialExpr(BoolLit(true)), type: Value@WHNF { value: AppliedBuiltin(Bool, []), type: Type } }, Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type })
diff --git a/dhall/tests/type-errors/unit/ListLiteralNotType.txt b/dhall/tests/type-errors/unit/ListLiteralNotType.txt
index 4b68fea..99b95a3 100644
--- a/dhall/tests/type-errors/unit/ListLiteralNotType.txt
+++ b/dhall/tests/type-errors/unit/ListLiteralNotType.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: InvalidListType(Type)
diff --git a/dhall/tests/type-errors/unit/ListLiteralTypesNotMatch.txt b/dhall/tests/type-errors/unit/ListLiteralTypesNotMatch.txt
index 4b68fea..7916dbb 100644
--- a/dhall/tests/type-errors/unit/ListLiteralTypesNotMatch.txt
+++ b/dhall/tests/type-errors/unit/ListLiteralTypesNotMatch.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: InvalidListElement(1, Value@WHNF { value: AppliedBuiltin(Bool, []), type: Type }, Value@Unevaled { value: PartialExpr(NaturalLit(1)), type: Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/MergeAlternativeHasNoHandler.txt b/dhall/tests/type-errors/unit/MergeAlternativeHasNoHandler.txt
index 4b68fea..d4e9024 100644
--- a/dhall/tests/type-errors/unit/MergeAlternativeHasNoHandler.txt
+++ b/dhall/tests/type-errors/unit/MergeAlternativeHasNoHandler.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: MergeVariantMissingHandler(Label("x"))
diff --git a/dhall/tests/type-errors/unit/MergeAnnotationMismatch.txt b/dhall/tests/type-errors/unit/MergeAnnotationMismatch.txt
index 4b68fea..823f2f7 100644
--- a/dhall/tests/type-errors/unit/MergeAnnotationMismatch.txt
+++ b/dhall/tests/type-errors/unit/MergeAnnotationMismatch.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: MergeAnnotMismatch
diff --git a/dhall/tests/type-errors/unit/MergeAnnotationNotType.txt b/dhall/tests/type-errors/unit/MergeAnnotationNotType.txt
index 4b68fea..7da5cb9 100644
--- a/dhall/tests/type-errors/unit/MergeAnnotationNotType.txt
+++ b/dhall/tests/type-errors/unit/MergeAnnotationNotType.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: Merge2ArgMustBeUnionOrOptional(Value@Unevaled { value: UnionType({}), type: Type })
diff --git a/dhall/tests/type-errors/unit/MergeEmptyNeedsDirectAnnotation1.txt b/dhall/tests/type-errors/unit/MergeEmptyNeedsDirectAnnotation1.txt
index 4b68fea..563b3bf 100644
--- a/dhall/tests/type-errors/unit/MergeEmptyNeedsDirectAnnotation1.txt
+++ b/dhall/tests/type-errors/unit/MergeEmptyNeedsDirectAnnotation1.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: MergeEmptyNeedsAnnotation
diff --git a/dhall/tests/type-errors/unit/MergeEmptyNeedsDirectAnnotation2.txt b/dhall/tests/type-errors/unit/MergeEmptyNeedsDirectAnnotation2.txt
index 4b68fea..563b3bf 100644
--- a/dhall/tests/type-errors/unit/MergeEmptyNeedsDirectAnnotation2.txt
+++ b/dhall/tests/type-errors/unit/MergeEmptyNeedsDirectAnnotation2.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: MergeEmptyNeedsAnnotation
diff --git a/dhall/tests/type-errors/unit/MergeEmptyWithoutAnnotation.txt b/dhall/tests/type-errors/unit/MergeEmptyWithoutAnnotation.txt
index 4b68fea..7da5cb9 100644
--- a/dhall/tests/type-errors/unit/MergeEmptyWithoutAnnotation.txt
+++ b/dhall/tests/type-errors/unit/MergeEmptyWithoutAnnotation.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: Merge2ArgMustBeUnionOrOptional(Value@Unevaled { value: UnionType({}), type: Type })
diff --git a/dhall/tests/type-errors/unit/MergeHandlerFreeVar.txt b/dhall/tests/type-errors/unit/MergeHandlerFreeVar.txt
index 4b68fea..de9e256 100644
--- a/dhall/tests/type-errors/unit/MergeHandlerFreeVar.txt
+++ b/dhall/tests/type-errors/unit/MergeHandlerFreeVar.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: MergeHandlerReturnTypeMustNotBeDependent
diff --git a/dhall/tests/type-errors/unit/MergeHandlerNotInUnion.txt b/dhall/tests/type-errors/unit/MergeHandlerNotInUnion.txt
index 4b68fea..7da5cb9 100644
--- a/dhall/tests/type-errors/unit/MergeHandlerNotInUnion.txt
+++ b/dhall/tests/type-errors/unit/MergeHandlerNotInUnion.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: Merge2ArgMustBeUnionOrOptional(Value@Unevaled { value: UnionType({}), type: Type })
diff --git a/dhall/tests/type-errors/unit/MergeHandlersWithDifferentType.txt b/dhall/tests/type-errors/unit/MergeHandlersWithDifferentType.txt
index 4b68fea..8b729a4 100644
--- a/dhall/tests/type-errors/unit/MergeHandlersWithDifferentType.txt
+++ b/dhall/tests/type-errors/unit/MergeHandlersWithDifferentType.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: MergeHandlerTypeMismatch
diff --git a/dhall/tests/type-errors/unit/MergeLhsNotRecord.txt b/dhall/tests/type-errors/unit/MergeLhsNotRecord.txt
index 4b68fea..da600de 100644
--- a/dhall/tests/type-errors/unit/MergeLhsNotRecord.txt
+++ b/dhall/tests/type-errors/unit/MergeLhsNotRecord.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: Merge1ArgMustBeRecord(Value@Unevaled { value: PartialExpr(BoolLit(true)), type: Value@WHNF { value: AppliedBuiltin(Bool, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/MergeMissingHandler1.txt b/dhall/tests/type-errors/unit/MergeMissingHandler1.txt
index 4b68fea..d4e9024 100644
--- a/dhall/tests/type-errors/unit/MergeMissingHandler1.txt
+++ b/dhall/tests/type-errors/unit/MergeMissingHandler1.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: MergeVariantMissingHandler(Label("x"))
diff --git a/dhall/tests/type-errors/unit/MergeMissingHandler2.txt b/dhall/tests/type-errors/unit/MergeMissingHandler2.txt
index 4b68fea..d583de7 100644
--- a/dhall/tests/type-errors/unit/MergeMissingHandler2.txt
+++ b/dhall/tests/type-errors/unit/MergeMissingHandler2.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: MergeVariantMissingHandler(Label("y"))
diff --git a/dhall/tests/type-errors/unit/MergeRhsNotUnion.txt b/dhall/tests/type-errors/unit/MergeRhsNotUnion.txt
index 4b68fea..db93650 100644
--- a/dhall/tests/type-errors/unit/MergeRhsNotUnion.txt
+++ b/dhall/tests/type-errors/unit/MergeRhsNotUnion.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: Merge2ArgMustBeUnionOrOptional(Value@Unevaled { value: PartialExpr(BoolLit(true)), type: Value@WHNF { value: AppliedBuiltin(Bool, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/MergeUnusedHandler.txt b/dhall/tests/type-errors/unit/MergeUnusedHandler.txt
new file mode 100644
index 0000000..b7eefdd
--- /dev/null
+++ b/dhall/tests/type-errors/unit/MergeUnusedHandler.txt
@@ -0,0 +1 @@
+Type error: Unhandled error: MergeHandlerMissingVariant(Label("y"))
diff --git a/dhall/tests/type-errors/unit/OperatorAndNotBool.txt b/dhall/tests/type-errors/unit/OperatorAndNotBool.txt
index 4b68fea..a4346f2 100644
--- a/dhall/tests/type-errors/unit/OperatorAndNotBool.txt
+++ b/dhall/tests/type-errors/unit/OperatorAndNotBool.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: BinOpTypeMismatch(BoolAnd, Value@Unevaled { value: PartialExpr(NaturalLit(1)), type: Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/OperatorEqualNotBool.txt b/dhall/tests/type-errors/unit/OperatorEqualNotBool.txt
index 4b68fea..57f9095 100644
--- a/dhall/tests/type-errors/unit/OperatorEqualNotBool.txt
+++ b/dhall/tests/type-errors/unit/OperatorEqualNotBool.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: BinOpTypeMismatch(BoolEQ, Value@Unevaled { value: PartialExpr(NaturalLit(1)), type: Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/OperatorListConcatenateLhsNotList.txt b/dhall/tests/type-errors/unit/OperatorListConcatenateLhsNotList.txt
index 4b68fea..2bcb4fc 100644
--- a/dhall/tests/type-errors/unit/OperatorListConcatenateLhsNotList.txt
+++ b/dhall/tests/type-errors/unit/OperatorListConcatenateLhsNotList.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: BinOpTypeMismatch(ListAppend, Value@Unevaled { value: PartialExpr(NaturalLit(1)), type: Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/OperatorListConcatenateListsNotMatch.txt b/dhall/tests/type-errors/unit/OperatorListConcatenateListsNotMatch.txt
index 4b68fea..e314702 100644
--- a/dhall/tests/type-errors/unit/OperatorListConcatenateListsNotMatch.txt
+++ b/dhall/tests/type-errors/unit/OperatorListConcatenateListsNotMatch.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: BinOpTypeMismatch(ListAppend, Value@Unevaled { value: PartialExpr(NEListLit([Value@Unevaled { value: PartialExpr(NaturalLit(1)), type: Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type } }])), type: Value@WHNF { value: AppliedBuiltin(List, [Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type }]), type: Type } })
diff --git a/dhall/tests/type-errors/unit/OperatorListConcatenateNotListsButMatch.txt b/dhall/tests/type-errors/unit/OperatorListConcatenateNotListsButMatch.txt
index 4b68fea..2bcb4fc 100644
--- a/dhall/tests/type-errors/unit/OperatorListConcatenateNotListsButMatch.txt
+++ b/dhall/tests/type-errors/unit/OperatorListConcatenateNotListsButMatch.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: BinOpTypeMismatch(ListAppend, Value@Unevaled { value: PartialExpr(NaturalLit(1)), type: Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/OperatorListConcatenateRhsNotList.txt b/dhall/tests/type-errors/unit/OperatorListConcatenateRhsNotList.txt
index 4b68fea..2bcb4fc 100644
--- a/dhall/tests/type-errors/unit/OperatorListConcatenateRhsNotList.txt
+++ b/dhall/tests/type-errors/unit/OperatorListConcatenateRhsNotList.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: BinOpTypeMismatch(ListAppend, Value@Unevaled { value: PartialExpr(NaturalLit(1)), type: Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/OperatorNotEqualNotBool.txt b/dhall/tests/type-errors/unit/OperatorNotEqualNotBool.txt
index 4b68fea..32b3549 100644
--- a/dhall/tests/type-errors/unit/OperatorNotEqualNotBool.txt
+++ b/dhall/tests/type-errors/unit/OperatorNotEqualNotBool.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: BinOpTypeMismatch(BoolNE, Value@Unevaled { value: PartialExpr(NaturalLit(1)), type: Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/OperatorOrNotBool.txt b/dhall/tests/type-errors/unit/OperatorOrNotBool.txt
index 4b68fea..f91bd83 100644
--- a/dhall/tests/type-errors/unit/OperatorOrNotBool.txt
+++ b/dhall/tests/type-errors/unit/OperatorOrNotBool.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: BinOpTypeMismatch(BoolOr, Value@Unevaled { value: PartialExpr(NaturalLit(1)), type: Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/OperatorPlusNotNatural.txt b/dhall/tests/type-errors/unit/OperatorPlusNotNatural.txt
index 4b68fea..9194f3a 100644
--- a/dhall/tests/type-errors/unit/OperatorPlusNotNatural.txt
+++ b/dhall/tests/type-errors/unit/OperatorPlusNotNatural.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: BinOpTypeMismatch(NaturalPlus, Value@Unevaled { value: PartialExpr(BoolLit(true)), type: Value@WHNF { value: AppliedBuiltin(Bool, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/OperatorTextConcatenateLhsNotText.txt b/dhall/tests/type-errors/unit/OperatorTextConcatenateLhsNotText.txt
index 4b68fea..e67e9e5 100644
--- a/dhall/tests/type-errors/unit/OperatorTextConcatenateLhsNotText.txt
+++ b/dhall/tests/type-errors/unit/OperatorTextConcatenateLhsNotText.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: BinOpTypeMismatch(TextAppend, Value@Unevaled { value: PartialExpr(NaturalLit(1)), type: Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/OperatorTextConcatenateRhsNotText.txt b/dhall/tests/type-errors/unit/OperatorTextConcatenateRhsNotText.txt
index 4b68fea..e67e9e5 100644
--- a/dhall/tests/type-errors/unit/OperatorTextConcatenateRhsNotText.txt
+++ b/dhall/tests/type-errors/unit/OperatorTextConcatenateRhsNotText.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: BinOpTypeMismatch(TextAppend, Value@Unevaled { value: PartialExpr(NaturalLit(1)), type: Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/OperatorTimesNotNatural.txt b/dhall/tests/type-errors/unit/OperatorTimesNotNatural.txt
index 4b68fea..8146ba9 100644
--- a/dhall/tests/type-errors/unit/OperatorTimesNotNatural.txt
+++ b/dhall/tests/type-errors/unit/OperatorTimesNotNatural.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: BinOpTypeMismatch(NaturalTimes, Value@Unevaled { value: PartialExpr(BoolLit(true)), type: Value@WHNF { value: AppliedBuiltin(Bool, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/OptionalDeprecatedSyntaxAbsent.txt b/dhall/tests/type-errors/unit/OptionalDeprecatedSyntaxAbsent.txt
index 4b68fea..c319c81 100644
--- a/dhall/tests/type-errors/unit/OptionalDeprecatedSyntaxAbsent.txt
+++ b/dhall/tests/type-errors/unit/OptionalDeprecatedSyntaxAbsent.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: InvalidListType(Value@WHNF { value: AppliedBuiltin(Optional, [Value@Unevaled { value: AppliedBuiltin(Bool, []), type: Type }]), type: Type })
diff --git a/dhall/tests/type-errors/unit/OptionalDeprecatedSyntaxPresent.txt b/dhall/tests/type-errors/unit/OptionalDeprecatedSyntaxPresent.txt
index 4b68fea..99df27c 100644
--- a/dhall/tests/type-errors/unit/OptionalDeprecatedSyntaxPresent.txt
+++ b/dhall/tests/type-errors/unit/OptionalDeprecatedSyntaxPresent.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: AnnotMismatch(Value@Unevaled { value: PartialExpr(NEListLit([Value@Unevaled { value: PartialExpr(NaturalLit(1)), type: Value@Unevaled { value: AppliedBuiltin(Natural, []), type: Type } }])), type: Value@WHNF { value: AppliedBuiltin(List, [Value@Unevaled { value: AppliedBuiltin(Natural, []), type: Type }]), type: Type } }, Value@WHNF { value: AppliedBuiltin(Optional, [Value@Unevaled { value: AppliedBuiltin(Natural, []), type: Type }]), type: Type })
diff --git a/dhall/tests/type-errors/unit/RecordLitDuplicateFields.txt b/dhall/tests/type-errors/unit/RecordLitDuplicateFields.txt
index 4b68fea..4fc4a50 100644
--- a/dhall/tests/type-errors/unit/RecordLitDuplicateFields.txt
+++ b/dhall/tests/type-errors/unit/RecordLitDuplicateFields.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: RecordTypeDuplicateField
diff --git a/dhall/tests/type-errors/unit/RecordMixedKinds3.txt b/dhall/tests/type-errors/unit/RecordMixedKinds3.txt
index 4b68fea..5b88ff7 100644
--- a/dhall/tests/type-errors/unit/RecordMixedKinds3.txt
+++ b/dhall/tests/type-errors/unit/RecordMixedKinds3.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: Sort
diff --git a/dhall/tests/type-errors/unit/RecordProjectionDuplicateFields.txt b/dhall/tests/type-errors/unit/RecordProjectionDuplicateFields.txt
new file mode 100644
index 0000000..0d2e83e
--- /dev/null
+++ b/dhall/tests/type-errors/unit/RecordProjectionDuplicateFields.txt
@@ -0,0 +1 @@
+Type error: Unhandled error: ProjectionDuplicateField
diff --git a/dhall/tests/type-errors/unit/RecordProjectionEmpty.txt b/dhall/tests/type-errors/unit/RecordProjectionEmpty.txt
index 4b68fea..ddbe5e4 100644
--- a/dhall/tests/type-errors/unit/RecordProjectionEmpty.txt
+++ b/dhall/tests/type-errors/unit/RecordProjectionEmpty.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: ProjectionMissingEntry
diff --git a/dhall/tests/type-errors/unit/RecordProjectionNotPresent.txt b/dhall/tests/type-errors/unit/RecordProjectionNotPresent.txt
index 4b68fea..ddbe5e4 100644
--- a/dhall/tests/type-errors/unit/RecordProjectionNotPresent.txt
+++ b/dhall/tests/type-errors/unit/RecordProjectionNotPresent.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: ProjectionMissingEntry
diff --git a/dhall/tests/type-errors/unit/RecordProjectionNotRecord.txt b/dhall/tests/type-errors/unit/RecordProjectionNotRecord.txt
index 4b68fea..cb4b52f 100644
--- a/dhall/tests/type-errors/unit/RecordProjectionNotRecord.txt
+++ b/dhall/tests/type-errors/unit/RecordProjectionNotRecord.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: ProjectionMustBeRecord
diff --git a/dhall/tests/type-errors/unit/RecordSelectionEmpty.txt b/dhall/tests/type-errors/unit/RecordSelectionEmpty.txt
index 4b68fea..1770faa 100644
--- a/dhall/tests/type-errors/unit/RecordSelectionEmpty.txt
+++ b/dhall/tests/type-errors/unit/RecordSelectionEmpty.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: MissingRecordField(Label("x"), Value@Unevaled { value: PartialExpr(RecordLit(DupTreeMap { map: {}, size: 0 })), type: Value@WHNF { value: RecordType({}), type: Type } })
diff --git a/dhall/tests/type-errors/unit/RecordSelectionNotPresent.txt b/dhall/tests/type-errors/unit/RecordSelectionNotPresent.txt
index 4b68fea..9211e05 100644
--- a/dhall/tests/type-errors/unit/RecordSelectionNotPresent.txt
+++ b/dhall/tests/type-errors/unit/RecordSelectionNotPresent.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: MissingRecordField(Label("x"), Value@Unevaled { value: PartialExpr(RecordLit(DupTreeMap { map: {Label("y"): [Value@Unevaled { value: PartialExpr(RecordLit(DupTreeMap { map: {}, size: 0 })), type: Value@Unevaled { value: RecordType({}), type: Type } }]}, size: 1 })), type: Value@WHNF { value: RecordType({Label("y"): Value@Unevaled { value: RecordType({}), type: Type }}), type: Type } })
diff --git a/dhall/tests/type-errors/unit/RecordSelectionNotRecord.txt b/dhall/tests/type-errors/unit/RecordSelectionNotRecord.txt
index 4b68fea..27db6a2 100644
--- a/dhall/tests/type-errors/unit/RecordSelectionNotRecord.txt
+++ b/dhall/tests/type-errors/unit/RecordSelectionNotRecord.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: NotARecord(Label("x"), Value@WHNF { value: BoolLit(true), type: Value@WHNF { value: AppliedBuiltin(Bool, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/RecordSelectionTypeNotUnionType.txt b/dhall/tests/type-errors/unit/RecordSelectionTypeNotUnionType.txt
index 4b68fea..36c8e28 100644
--- a/dhall/tests/type-errors/unit/RecordSelectionTypeNotUnionType.txt
+++ b/dhall/tests/type-errors/unit/RecordSelectionTypeNotUnionType.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: NotARecord(Label("x"), Value@WHNF { value: AppliedBuiltin(Bool, []), type: Type })
diff --git a/dhall/tests/type-errors/unit/RecordTypeDuplicateFields.txt b/dhall/tests/type-errors/unit/RecordTypeDuplicateFields.txt
index 4b68fea..4fc4a50 100644
--- a/dhall/tests/type-errors/unit/RecordTypeDuplicateFields.txt
+++ b/dhall/tests/type-errors/unit/RecordTypeDuplicateFields.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: RecordTypeDuplicateField
diff --git a/dhall/tests/type-errors/unit/RecordTypeValueMember.txt b/dhall/tests/type-errors/unit/RecordTypeValueMember.txt
index 4b68fea..72d4316 100644
--- a/dhall/tests/type-errors/unit/RecordTypeValueMember.txt
+++ b/dhall/tests/type-errors/unit/RecordTypeValueMember.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: InvalidFieldType(Label("x"), Value@Unevaled { value: PartialExpr(BoolLit(true)), type: Value@WHNF { value: AppliedBuiltin(Bool, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/RecursiveRecordMergeLhsNotRecord.txt b/dhall/tests/type-errors/unit/RecursiveRecordMergeLhsNotRecord.txt
index 4b68fea..42cdf65 100644
--- a/dhall/tests/type-errors/unit/RecursiveRecordMergeLhsNotRecord.txt
+++ b/dhall/tests/type-errors/unit/RecursiveRecordMergeLhsNotRecord.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: RecordTypeMergeRequiresRecordType(Value@WHNF { value: AppliedBuiltin(Bool, []), type: Type })
diff --git a/dhall/tests/type-errors/unit/RecursiveRecordMergeOverlapping.txt b/dhall/tests/type-errors/unit/RecursiveRecordMergeOverlapping.txt
index 4b68fea..42cdf65 100644
--- a/dhall/tests/type-errors/unit/RecursiveRecordMergeOverlapping.txt
+++ b/dhall/tests/type-errors/unit/RecursiveRecordMergeOverlapping.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: RecordTypeMergeRequiresRecordType(Value@WHNF { value: AppliedBuiltin(Bool, []), type: Type })
diff --git a/dhall/tests/type-errors/unit/RecursiveRecordMergeRhsNotRecord.txt b/dhall/tests/type-errors/unit/RecursiveRecordMergeRhsNotRecord.txt
index 4b68fea..42cdf65 100644
--- a/dhall/tests/type-errors/unit/RecursiveRecordMergeRhsNotRecord.txt
+++ b/dhall/tests/type-errors/unit/RecursiveRecordMergeRhsNotRecord.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: RecordTypeMergeRequiresRecordType(Value@WHNF { value: AppliedBuiltin(Bool, []), type: Type })
diff --git a/dhall/tests/type-errors/unit/RecursiveRecordTypeMergeLhsNotRecordType.txt b/dhall/tests/type-errors/unit/RecursiveRecordTypeMergeLhsNotRecordType.txt
index 4b68fea..42cdf65 100644
--- a/dhall/tests/type-errors/unit/RecursiveRecordTypeMergeLhsNotRecordType.txt
+++ b/dhall/tests/type-errors/unit/RecursiveRecordTypeMergeLhsNotRecordType.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: RecordTypeMergeRequiresRecordType(Value@WHNF { value: AppliedBuiltin(Bool, []), type: Type })
diff --git a/dhall/tests/type-errors/unit/RecursiveRecordTypeMergeOverlapping.txt b/dhall/tests/type-errors/unit/RecursiveRecordTypeMergeOverlapping.txt
index 4b68fea..42cdf65 100644
--- a/dhall/tests/type-errors/unit/RecursiveRecordTypeMergeOverlapping.txt
+++ b/dhall/tests/type-errors/unit/RecursiveRecordTypeMergeOverlapping.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: RecordTypeMergeRequiresRecordType(Value@WHNF { value: AppliedBuiltin(Bool, []), type: Type })
diff --git a/dhall/tests/type-errors/unit/RecursiveRecordTypeMergeRhsNotRecordType.txt b/dhall/tests/type-errors/unit/RecursiveRecordTypeMergeRhsNotRecordType.txt
index 4b68fea..42cdf65 100644
--- a/dhall/tests/type-errors/unit/RecursiveRecordTypeMergeRhsNotRecordType.txt
+++ b/dhall/tests/type-errors/unit/RecursiveRecordTypeMergeRhsNotRecordType.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: RecordTypeMergeRequiresRecordType(Value@WHNF { value: AppliedBuiltin(Bool, []), type: Type })
diff --git a/dhall/tests/type-errors/unit/RightBiasedRecordMergeLhsNotRecord.txt b/dhall/tests/type-errors/unit/RightBiasedRecordMergeLhsNotRecord.txt
index 4b68fea..9a2913b 100644
--- a/dhall/tests/type-errors/unit/RightBiasedRecordMergeLhsNotRecord.txt
+++ b/dhall/tests/type-errors/unit/RightBiasedRecordMergeLhsNotRecord.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: MustCombineRecord(Value@Unevaled { value: PartialExpr(BoolLit(true)), type: Value@WHNF { value: AppliedBuiltin(Bool, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/RightBiasedRecordMergeMixedKinds2.txt b/dhall/tests/type-errors/unit/RightBiasedRecordMergeMixedKinds2.txt
index 4b68fea..5b88ff7 100644
--- a/dhall/tests/type-errors/unit/RightBiasedRecordMergeMixedKinds2.txt
+++ b/dhall/tests/type-errors/unit/RightBiasedRecordMergeMixedKinds2.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: Sort
diff --git a/dhall/tests/type-errors/unit/RightBiasedRecordMergeMixedKinds3.txt b/dhall/tests/type-errors/unit/RightBiasedRecordMergeMixedKinds3.txt
index 4b68fea..5b88ff7 100644
--- a/dhall/tests/type-errors/unit/RightBiasedRecordMergeMixedKinds3.txt
+++ b/dhall/tests/type-errors/unit/RightBiasedRecordMergeMixedKinds3.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: Sort
diff --git a/dhall/tests/type-errors/unit/RightBiasedRecordMergeRhsNotRecord.txt b/dhall/tests/type-errors/unit/RightBiasedRecordMergeRhsNotRecord.txt
index 4b68fea..9a2913b 100644
--- a/dhall/tests/type-errors/unit/RightBiasedRecordMergeRhsNotRecord.txt
+++ b/dhall/tests/type-errors/unit/RightBiasedRecordMergeRhsNotRecord.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: MustCombineRecord(Value@Unevaled { value: PartialExpr(BoolLit(true)), type: Value@WHNF { value: AppliedBuiltin(Bool, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/SomeNotType.txt b/dhall/tests/type-errors/unit/SomeNotType.txt
index 4b68fea..43581a0 100644
--- a/dhall/tests/type-errors/unit/SomeNotType.txt
+++ b/dhall/tests/type-errors/unit/SomeNotType.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: InvalidOptionalType(Type)
diff --git a/dhall/tests/type-errors/unit/Sort.txt b/dhall/tests/type-errors/unit/Sort.txt
index 4b68fea..5b88ff7 100644
--- a/dhall/tests/type-errors/unit/Sort.txt
+++ b/dhall/tests/type-errors/unit/Sort.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: Sort
diff --git a/dhall/tests/type-errors/unit/TextLiteralInterpolateNotText.txt b/dhall/tests/type-errors/unit/TextLiteralInterpolateNotText.txt
index 4b68fea..ba2aecd 100644
--- a/dhall/tests/type-errors/unit/TextLiteralInterpolateNotText.txt
+++ b/dhall/tests/type-errors/unit/TextLiteralInterpolateNotText.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: InvalidTextInterpolation(Value@Unevaled { value: PartialExpr(NaturalLit(1)), type: Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type } })
diff --git a/dhall/tests/type-errors/unit/TypeAnnotationWrong.txt b/dhall/tests/type-errors/unit/TypeAnnotationWrong.txt
index 4b68fea..77ff57f 100644
--- a/dhall/tests/type-errors/unit/TypeAnnotationWrong.txt
+++ b/dhall/tests/type-errors/unit/TypeAnnotationWrong.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: AnnotMismatch(Value@Unevaled { value: PartialExpr(NaturalLit(1)), type: Value@WHNF { value: AppliedBuiltin(Natural, []), type: Type } }, Value@WHNF { value: AppliedBuiltin(Bool, []), type: Type })
diff --git a/dhall/tests/type-errors/unit/UnionConstructorFieldNotPresent.txt b/dhall/tests/type-errors/unit/UnionConstructorFieldNotPresent.txt
index 4b68fea..7b2e610 100644
--- a/dhall/tests/type-errors/unit/UnionConstructorFieldNotPresent.txt
+++ b/dhall/tests/type-errors/unit/UnionConstructorFieldNotPresent.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: MissingUnionField(Label("y"), Value@WHNF { value: UnionType({Label("x"): Some(Value@Unevaled { value: AppliedBuiltin(Bool, []), type: Type })}), type: Type })
diff --git a/dhall/tests/type-errors/unit/UnionTypeDuplicateVariants1.txt b/dhall/tests/type-errors/unit/UnionTypeDuplicateVariants1.txt
index 4b68fea..27c6cbe 100644
--- a/dhall/tests/type-errors/unit/UnionTypeDuplicateVariants1.txt
+++ b/dhall/tests/type-errors/unit/UnionTypeDuplicateVariants1.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: UnionTypeDuplicateField
diff --git a/dhall/tests/type-errors/unit/UnionTypeDuplicateVariants2.txt b/dhall/tests/type-errors/unit/UnionTypeDuplicateVariants2.txt
index 4b68fea..27c6cbe 100644
--- a/dhall/tests/type-errors/unit/UnionTypeDuplicateVariants2.txt
+++ b/dhall/tests/type-errors/unit/UnionTypeDuplicateVariants2.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: UnionTypeDuplicateField
diff --git a/dhall/tests/type-errors/unit/UnionTypeMixedKinds.txt b/dhall/tests/type-errors/unit/UnionTypeMixedKinds.txt
index 4b68fea..07604ba 100644
--- a/dhall/tests/type-errors/unit/UnionTypeMixedKinds.txt
+++ b/dhall/tests/type-errors/unit/UnionTypeMixedKinds.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: InvalidFieldType(Label("y"), Type)
diff --git a/dhall/tests/type-errors/unit/UnionTypeMixedKinds2.txt b/dhall/tests/type-errors/unit/UnionTypeMixedKinds2.txt
index 4b68fea..07604ba 100644
--- a/dhall/tests/type-errors/unit/UnionTypeMixedKinds2.txt
+++ b/dhall/tests/type-errors/unit/UnionTypeMixedKinds2.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: InvalidFieldType(Label("y"), Type)
diff --git a/dhall/tests/type-errors/unit/UnionTypeMixedKinds3.txt b/dhall/tests/type-errors/unit/UnionTypeMixedKinds3.txt
index 4b68fea..83fc426 100644
--- a/dhall/tests/type-errors/unit/UnionTypeMixedKinds3.txt
+++ b/dhall/tests/type-errors/unit/UnionTypeMixedKinds3.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: InvalidFieldType(Label("y"), Value@Unevaled { value: AppliedBuiltin(Bool, []), type: Type })
diff --git a/dhall/tests/type-errors/unit/UnionTypeNotType.txt b/dhall/tests/type-errors/unit/UnionTypeNotType.txt
index 4b68fea..72d4316 100644
--- a/dhall/tests/type-errors/unit/UnionTypeNotType.txt
+++ b/dhall/tests/type-errors/unit/UnionTypeNotType.txt
@@ -1 +1 @@
-Type error: Unhandled error
+Type error: Unhandled error: InvalidFieldType(Label("x"), Value@Unevaled { value: PartialExpr(BoolLit(true)), type: Value@WHNF { value: AppliedBuiltin(Bool, []), type: Type } })