From 66ea301fc25a07485286560c434a9fdaf460c431 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 1 Nov 2020 16:10:00 +0000 Subject: Untangle caching code --- dhall/tests/spec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'dhall/tests') diff --git a/dhall/tests/spec.rs b/dhall/tests/spec.rs index 8d67892..ae53f4a 100644 --- a/dhall/tests/spec.rs +++ b/dhall/tests/spec.rs @@ -670,7 +670,7 @@ fn run_test(test: &SpecTest) -> Result<()> { } SemanticHash => { let expr = expr.normalize()?.to_expr_alpha(); - let hash = hex::encode(expr.hash()?); + let hash = hex::encode(expr.sha256_hash()?); expected.compare_ui(format!("sha256:{}", hash))?; } TypeInferenceSuccess => { -- cgit v1.3.1 From 9b67852327aff5263a3c119c068170b2edef69f8 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 1 Nov 2020 18:00:07 +0000 Subject: Don't pollute the dhall cache directory --- Cargo.lock | 7 +++++ dhall/Cargo.toml | 4 +-- dhall/tests/spec.rs | 80 +++++++++++++++++++++++++++++++---------------------- 3 files changed, 55 insertions(+), 36 deletions(-) (limited to 'dhall/tests') diff --git a/Cargo.lock b/Cargo.lock index 63522c2..503f711 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -300,6 +300,7 @@ dependencies = [ "annotate-snippets", "anyhow", "colored-diff", + "fs_extra", "hex", "itertools 0.9.0", "lazy_static", @@ -408,6 +409,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "fs_extra" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" + [[package]] name = "fuchsia-zircon" version = "0.3.3" diff --git a/dhall/Cargo.toml b/dhall/Cargo.toml index 7923009..207539a 100644 --- a/dhall/Cargo.toml +++ b/dhall/Cargo.toml @@ -41,15 +41,13 @@ reqwest = { version = "0.10", features = ["blocking"], optional = true } [dev-dependencies] anyhow = "1.0.28" colored-diff = "0.2.2" +fs_extra = "1.2.0" libtest-mimic = "0.3.0" rand = "0.7" version-sync = "0.9" walkdir = "2" [build-dependencies] -walkdir = "2" abnf_to_pest = { version = "^0.5.0", path = "../abnf_to_pest" } pest_generator = "2.1.3" quote = "1.0" - - diff --git a/dhall/tests/spec.rs b/dhall/tests/spec.rs index ae53f4a..36cbd81 100644 --- a/dhall/tests/spec.rs +++ b/dhall/tests/spec.rs @@ -1,4 +1,6 @@ use anyhow::Result; +use rand::distributions::Alphanumeric; +use rand::Rng; use std::env; use std::ffi::OsString; use std::fmt::{Debug, Display}; @@ -593,26 +595,6 @@ fn unwrap_err(x: Result) -> Result { fn run_test(test: &SpecTest) -> Result<()> { use self::SpecTestKind::*; - // Setup current directory to the root of the repository. Important for `as Location` tests. - let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .parent() - .unwrap() - .to_path_buf(); - env::set_current_dir(root_dir.as_path())?; - // Set environment variable for import tests. - env::set_var("DHALL_TEST_VAR", "6 * 7"); - - // Configure cache for import tests - env::set_var( - "XDG_CACHE_HOME", - root_dir - .join("dhall-lang") - .join("tests") - .join("import") - .join("cache") - .as_path(), - ); - let SpecTest { input: expr, output: expected, @@ -690,7 +672,6 @@ fn run_test(test: &SpecTest) -> Result<()> { expected.compare(expr)?; } } - Ok(()) } @@ -700,17 +681,50 @@ fn main() { .flat_map(discover_tests_for_feature) .collect(); - libtest_mimic::run_tests(&Arguments::from_args(), tests, |test| { - let result = std::panic::catch_unwind(move || { - run_test_stringy_error(&test.data) + // Setup current directory to the root of the repository. Important for `as Location` tests. + let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .parent() + .unwrap() + .to_path_buf(); + env::set_current_dir(root_dir.as_path()).unwrap(); + + // Set environment variable for import tests. + env::set_var("DHALL_TEST_VAR", "6 * 7"); + + // Configure cache for import tests + let dhall_cache_dir = root_dir + .join("dhall-lang") + .join("tests") + .join("import") + .join("cache") + .join("dhall"); + let random_id = rand::thread_rng() + .sample_iter(Alphanumeric) + .take(36) + .collect::(); + let cache_dir = format!("dhall-tests-{}", random_id); + let cache_dir = env::temp_dir().join(cache_dir); + + std::fs::create_dir_all(&cache_dir).unwrap(); + fs_extra::dir::copy(&dhall_cache_dir, &cache_dir, &Default::default()) + .unwrap(); + env::set_var("XDG_CACHE_HOME", &cache_dir); + + let res = + libtest_mimic::run_tests(&Arguments::from_args(), tests, |test| { + let result = std::panic::catch_unwind(move || { + run_test_stringy_error(&test.data) + }); + match result { + Ok(Ok(_)) => Outcome::Passed, + Ok(Err(e)) => Outcome::Failed { msg: Some(e) }, + Err(_) => Outcome::Failed { + msg: Some("thread panicked".to_string()), + }, + } }); - match result { - Ok(Ok(_)) => Outcome::Passed, - Ok(Err(e)) => Outcome::Failed { msg: Some(e) }, - Err(_) => Outcome::Failed { - msg: Some("thread panicked".to_string()), - }, - } - }) - .exit(); + + std::fs::remove_dir_all(&cache_dir).unwrap(); + + res.exit(); } -- cgit v1.3.1 From e5381c9b76f1d88dedb4a453cd026c8e98be5533 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 1 Nov 2020 18:32:33 +0000 Subject: Ensure that the hash always gets checked --- dhall/src/semantics/resolve/env.rs | 31 +++++++++++++--------- dhall/src/semantics/resolve/resolve.rs | 28 ++++++++++++++----- dhall/tests/import/data/simple.dhall | 1 + .../tests/import/failure/unit/HashMismatch2.dhall | 2 ++ dhall/tests/import/failure/unit/HashMismatch2.txt | 9 +++++++ 5 files changed, 52 insertions(+), 19 deletions(-) create mode 100644 dhall/tests/import/data/simple.dhall create mode 100644 dhall/tests/import/failure/unit/HashMismatch2.dhall create mode 100644 dhall/tests/import/failure/unit/HashMismatch2.txt (limited to 'dhall/tests') diff --git a/dhall/src/semantics/resolve/env.rs b/dhall/src/semantics/resolve/env.rs index a24c274..29dd16b 100644 --- a/dhall/src/semantics/resolve/env.rs +++ b/dhall/src/semantics/resolve/env.rs @@ -17,7 +17,7 @@ pub type CyclesStack = Vec; /// Environment for resolving imports #[derive(Debug)] pub struct ImportEnv { - file_cache: Option, + disk_cache: Option, // Missing if it failed to initialize mem_cache: MemCache, stack: CyclesStack, } @@ -69,37 +69,42 @@ impl NameEnv { impl ImportEnv { pub fn new() -> Self { ImportEnv { - file_cache: Cache::new().ok(), + disk_cache: Cache::new().ok(), mem_cache: Default::default(), stack: Default::default(), } } - pub fn get_from_cache( + pub fn get_from_mem_cache( &mut self, location: &ImportLocation, - hash: Option<&Hash>, ) -> Option { - if let Some(expr) = self.mem_cache.get(location) { - return Some(expr.clone()); - } + Some(self.mem_cache.get(location)?.clone()) + } + + pub fn get_from_disk_cache( + &mut self, + hash: &Option, + ) -> Option { let hash = hash.as_ref()?; - let expr = self.file_cache.as_ref()?.get(hash).ok()?; + let expr = self.disk_cache.as_ref()?.get(hash).ok()?; Some(expr) } - pub fn set_cache( + pub fn write_to_mem_cache( &mut self, location: ImportLocation, - hash: Option<&Hash>, expr: Typed, ) { - if let Some(file_cache) = self.file_cache.as_ref() { + self.mem_cache.insert(location, expr); + } + + pub fn write_to_disk_cache(&mut self, hash: &Option, expr: &Typed) { + if let Some(disk_cache) = self.disk_cache.as_ref() { if let Some(hash) = hash { - let _ = file_cache.insert(hash, &expr); + let _ = disk_cache.insert(hash, &expr); } } - self.mem_cache.insert(location, expr); } pub fn with_cycle_detection( diff --git a/dhall/src/semantics/resolve/resolve.rs b/dhall/src/semantics/resolve/resolve.rs index 002490b..614ea22 100644 --- a/dhall/src/semantics/resolve/resolve.rs +++ b/dhall/src/semantics/resolve/resolve.rs @@ -390,19 +390,35 @@ fn resolve_with_env( let do_sanity_check = import.mode != ImportMode::Location; let location = base_location.chain(&import.location, do_sanity_check)?; + // If the import is in the in-memory cache, or the hash is in the on-disk cache, return // the cached contents. - if let Some(resolved) = - env.get_from_cache(&location, import.hash.as_ref()) - { - return Ok(resolved); + if let Some(typed) = env.get_from_mem_cache(&location) { + // The same location may be used with different or no hashes. Thus we need to check + // the hashes every time. + check_hash(&import, &typed, span)?; + env.write_to_disk_cache(&import.hash, &typed); + return Ok(typed); + } + if let Some(typed) = env.get_from_disk_cache(&import.hash) { + // No need to check the hash, it was checked before reading the file. We also don't + // write to the in-memory cache, because the location might be completely unrelated + // to the cached file (e.g. `missing sha256:...` is valid). + // This actually means that importing many times a same hashed import will take + // longer than importing many times a same non-hashed import. + return Ok(typed); } + + // Resolve this import, making sure that recursive imports don't cycle back to the + // current one. let typed = env.with_cycle_detection(location.clone(), |env| { resolve_one_import(env, &import, location.clone(), span.clone()) })?; - check_hash(&import, &typed, span)?; + // Add the resolved import to the caches - env.set_cache(location, import.hash.as_ref(), typed.clone()); + check_hash(&import, &typed, span)?; + env.write_to_disk_cache(&import.hash, &typed); + env.write_to_mem_cache(location, typed.clone()); Ok(typed) }, )?; diff --git a/dhall/tests/import/data/simple.dhall b/dhall/tests/import/data/simple.dhall new file mode 100644 index 0000000..00750ed --- /dev/null +++ b/dhall/tests/import/data/simple.dhall @@ -0,0 +1 @@ +3 diff --git a/dhall/tests/import/failure/unit/HashMismatch2.dhall b/dhall/tests/import/failure/unit/HashMismatch2.dhall new file mode 100644 index 0000000..5fae772 --- /dev/null +++ b/dhall/tests/import/failure/unit/HashMismatch2.dhall @@ -0,0 +1,2 @@ +-- This ensures that even if the file gets imported without hash first, the hash check is not skipped later +../../data/simple.dhall + ../../data/simple.dhall sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ../../data/simple.dhall diff --git a/dhall/tests/import/failure/unit/HashMismatch2.txt b/dhall/tests/import/failure/unit/HashMismatch2.txt new file mode 100644 index 0000000..f03ab24 --- /dev/null +++ b/dhall/tests/import/failure/unit/HashMismatch2.txt @@ -0,0 +1,9 @@ +Type error: error: hash mismatch + --> :2:27 + | +1 | -- This ensures that even if the file gets imported without hash first, the hash check is not skipped later +2 | ../../data/simple.dhall + ../../data/simple.dhall sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ../../data/simple.dhall + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ hash mismatch + | + = note: Expected sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + = note: Found sha256:15f52ecf91c94c1baac02d5a4964b2ed8fa401641a2c8a95e8306ec7c1e3b8d2 -- cgit v1.3.1 From 8cfb6d563d26abb2fa80124eda2231eb05c7f3e6 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 1 Nov 2020 18:59:01 +0000 Subject: feat: add location to import error messages This is crude but helpful. See #40 --- dhall/src/semantics/resolve/resolve.rs | 12 ++++++++++-- dhall/tests/import/failure/alternativeEnv.txt | 7 ++++++- dhall/tests/import/failure/alternativeEnvMissing.txt | 7 ++++++- dhall/tests/import/failure/cycle.txt | 17 ++++++++++++++++- dhall/tests/import/failure/importBoundary.txt | 7 ++++++- dhall/tests/import/failure/missing.txt | 7 ++++++- dhall/tests/import/failure/referentiallyInsane.txt | 13 ++++++++++++- dhall/tests/import/failure/unit/EnvUnset.txt | 7 ++++++- dhall/tests/import/failure/unit/EnvUnsetAsText.txt | 7 ++++++- dhall/tests/import/failure/unit/FileMissing.dhall | 1 + dhall/tests/import/failure/unit/FileMissing.txt | 6 ++++++ 11 files changed, 81 insertions(+), 10 deletions(-) create mode 100644 dhall/tests/import/failure/unit/FileMissing.dhall create mode 100644 dhall/tests/import/failure/unit/FileMissing.txt (limited to 'dhall/tests') diff --git a/dhall/src/semantics/resolve/resolve.rs b/dhall/src/semantics/resolve/resolve.rs index 614ea22..68b899c 100644 --- a/dhall/src/semantics/resolve/resolve.rs +++ b/dhall/src/semantics/resolve/resolve.rs @@ -411,9 +411,17 @@ fn resolve_with_env( // Resolve this import, making sure that recursive imports don't cycle back to the // current one. - let typed = env.with_cycle_detection(location.clone(), |env| { + let res = env.with_cycle_detection(location.clone(), |env| { resolve_one_import(env, &import, location.clone(), span.clone()) - })?; + }); + let typed = match res { + Ok(typed) => typed, + Err(e) => mkerr( + ErrorBuilder::new("error") + .span_err(span.clone(), e.to_string()) + .format(), + )?, + }; // Add the resolved import to the caches check_hash(&import, &typed, span)?; diff --git a/dhall/tests/import/failure/alternativeEnv.txt b/dhall/tests/import/failure/alternativeEnv.txt index 482b68c..bb862c3 100644 --- a/dhall/tests/import/failure/alternativeEnv.txt +++ b/dhall/tests/import/failure/alternativeEnv.txt @@ -1 +1,6 @@ -MissingEnvVar +Type error: error: error + --> :1:45 + | +1 | env:UNSET1 as Text ? env:UNSET2 ? missing ? env:UNSET3 + | ^^^^^^^^^^ MissingEnvVar + | diff --git a/dhall/tests/import/failure/alternativeEnvMissing.txt b/dhall/tests/import/failure/alternativeEnvMissing.txt index 4666330..1f26462 100644 --- a/dhall/tests/import/failure/alternativeEnvMissing.txt +++ b/dhall/tests/import/failure/alternativeEnvMissing.txt @@ -1 +1,6 @@ -Missing +Type error: error: error + --> :1:13 + | +1 | env:UNSET ? missing + | ^^^^^^^ Missing + | diff --git a/dhall/tests/import/failure/cycle.txt b/dhall/tests/import/failure/cycle.txt index f5a1abf..899484b 100644 --- a/dhall/tests/import/failure/cycle.txt +++ b/dhall/tests/import/failure/cycle.txt @@ -1 +1,16 @@ -ImportCycle([Local("./dhall-lang/tests/import/data/cycle.dhall"), Local("./dhall-lang/tests/import/failure/cycle.dhall")], Local("./dhall-lang/tests/import/data/cycle.dhall")) +Type error: error: error + --> :1:1 + | +1 | ../data/cycle.dhall + | ^^^^^^^^^^^^^^^^^^^ Type error: error: error + --> :1:1 + | +1 | ../failure/cycle.dhall + | ^^^^^^^^^^^^^^^^^^^^^^ Type error: error: error + --> :1:1 + | +1 | ../data/cycle.dhall + | ^^^^^^^^^^^^^^^^^^^ ImportCycle([Local("./dhall-lang/tests/import/data/cycle.dhall"), Local("./dhall-lang/tests/import/failure/cycle.dhall")], Local("./dhall-lang/tests/import/data/cycle.dhall")) + | + | + | diff --git a/dhall/tests/import/failure/importBoundary.txt b/dhall/tests/import/failure/importBoundary.txt index 56810e2..6ddb326 100644 --- a/dhall/tests/import/failure/importBoundary.txt +++ b/dhall/tests/import/failure/importBoundary.txt @@ -1,7 +1,12 @@ -Type error: error: unbound variable `x` +Type error: error: error + --> :1:15 + | +1 | \(x: Bool) -> ../data/importBoundary.dhall + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Type error: error: unbound variable `x` --> :1:1 | ... 3 | x | ^ not found in this scope | + | diff --git a/dhall/tests/import/failure/missing.txt b/dhall/tests/import/failure/missing.txt index 4666330..a15e9e0 100644 --- a/dhall/tests/import/failure/missing.txt +++ b/dhall/tests/import/failure/missing.txt @@ -1 +1,6 @@ -Missing +Type error: error: error + --> :1:1 + | +1 | missing + | ^^^^^^^ Missing + | diff --git a/dhall/tests/import/failure/referentiallyInsane.txt b/dhall/tests/import/failure/referentiallyInsane.txt index 9b5fe91..1c6852a 100644 --- a/dhall/tests/import/failure/referentiallyInsane.txt +++ b/dhall/tests/import/failure/referentiallyInsane.txt @@ -1 +1,12 @@ -SanityCheck +Type error: error: error + --> :6:1 + | + 1 | {- The following remote import attempts to import an environment variable, which + 2 | must be disallowed by the referential sanity check + 3 | + 4 | One reason for doing this is to protect against remote imports exfiltrating +... +12 | -} +13 | https://raw.githubusercontent.com/dhall-lang/dhall-lang/master/tests/import/data/referentiallyOpaque.dhall + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SanityCheck + | diff --git a/dhall/tests/import/failure/unit/EnvUnset.txt b/dhall/tests/import/failure/unit/EnvUnset.txt index 482b68c..12a8953 100644 --- a/dhall/tests/import/failure/unit/EnvUnset.txt +++ b/dhall/tests/import/failure/unit/EnvUnset.txt @@ -1 +1,6 @@ -MissingEnvVar +Type error: error: error + --> :1:1 + | +1 | env:DHALL_TEST_UNSET + | ^^^^^^^^^^^^^^^^^^^^ MissingEnvVar + | diff --git a/dhall/tests/import/failure/unit/EnvUnsetAsText.txt b/dhall/tests/import/failure/unit/EnvUnsetAsText.txt index 482b68c..6571560 100644 --- a/dhall/tests/import/failure/unit/EnvUnsetAsText.txt +++ b/dhall/tests/import/failure/unit/EnvUnsetAsText.txt @@ -1 +1,6 @@ -MissingEnvVar +Type error: error: error + --> :1:1 + | +1 | env:DHALL_TEST_UNSET as Text + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ MissingEnvVar + | diff --git a/dhall/tests/import/failure/unit/FileMissing.dhall b/dhall/tests/import/failure/unit/FileMissing.dhall new file mode 100644 index 0000000..cba1bcc --- /dev/null +++ b/dhall/tests/import/failure/unit/FileMissing.dhall @@ -0,0 +1 @@ +./not-a-file.dhall diff --git a/dhall/tests/import/failure/unit/FileMissing.txt b/dhall/tests/import/failure/unit/FileMissing.txt new file mode 100644 index 0000000..351909d --- /dev/null +++ b/dhall/tests/import/failure/unit/FileMissing.txt @@ -0,0 +1,6 @@ +Type error: error: error + --> :1:1 + | +1 | ./not-a-file.dhall + | ^^^^^^^^^^^^^^^^^^ No such file or directory (os error 2) + | -- cgit v1.3.1 From 6987b275e4bf5f545d823d186ce08a2fe9a3eb44 Mon Sep 17 00:00:00 2001 From: Basile Henry Date: Sun, 1 Nov 2020 22:46:35 +0100 Subject: Implement type checking for With op --- dhall/src/operations/typecheck.rs | 51 +++++++++++++++++++++- dhall/src/semantics/resolve/resolve.rs | 32 +------------- dhall/tests/spec.rs | 7 +-- .../failure/unit/WithInvalidOverrideA.txt | 4 +- .../type-inference/failure/unit/WithNotRecord.txt | 4 +- 5 files changed, 57 insertions(+), 41 deletions(-) (limited to 'dhall/tests') diff --git a/dhall/src/operations/typecheck.rs b/dhall/src/operations/typecheck.rs index 2ccc17d..af6f706 100644 --- a/dhall/src/operations/typecheck.rs +++ b/dhall/src/operations/typecheck.rs @@ -503,7 +503,56 @@ pub fn typecheck_operation( selection_val } - Completion(..) | With(..) => { + With(record, labels, expr) => { + use crate::syntax::Label; + use std::iter::{once, FromIterator}; + + let record_entries = |nk: &NirKind| { + match nk { + NirKind::RecordType(kts) => Ok(kts.clone()), + _ => mk_span_err(span.clone(), "WithMustBeRecord"), // TODO better error + } + }; + + let mut current_nk: Option = + Some(record.ty().kind().clone()); + let mut visited: Vec<(&Label, HashMap)> = Vec::new(); + let mut to_create = Vec::new(); + + for label in labels { + match current_nk { + None => to_create.push(label), + Some(nk) => { + let kts = record_entries(&nk)?; + + current_nk = + kts.get(label).map(|nir| nir.kind().clone()); + visited.push((label, kts)); + } + } + } + + // Create Nir for record type bottom up + let mut nir = expr.ty().as_nir().clone(); + + while let Some(label) = to_create.pop() { + let rec = RecordType(FromIterator::from_iter(once(( + label.clone(), + nir, + )))); + nir = Nir::from_kind(rec); + } + + // Update visited records + while let Some((label, mut kts)) = visited.pop() { + kts.insert(label.clone(), nir); + let rec = RecordType(kts); + nir = Nir::from_kind(rec); + } + + nir.to_type(Const::Type) + } + Completion(..) => { unreachable!("This case should have been handled in resolution") } }) diff --git a/dhall/src/semantics/resolve/resolve.rs b/dhall/src/semantics/resolve/resolve.rs index 572df25..dc1951e 100644 --- a/dhall/src/semantics/resolve/resolve.rs +++ b/dhall/src/semantics/resolve/resolve.rs @@ -2,7 +2,6 @@ use itertools::Itertools; use std::borrow::Cow; use std::collections::BTreeMap; use std::env; -use std::iter::once; use std::path::PathBuf; use url::Url; @@ -13,8 +12,8 @@ use crate::operations::{BinOp, OpKind}; use crate::semantics::{mkerr, Hir, HirKind, ImportEnv, NameEnv, Type}; use crate::syntax; use crate::syntax::{ - Expr, ExprKind, FilePath, FilePrefix, Hash, ImportMode, ImportTarget, - Label, Span, UnspannedExpr, URL, + Expr, ExprKind, FilePath, FilePrefix, Hash, ImportMode, ImportTarget, Span, + UnspannedExpr, URL, }; use crate::{Parsed, Resolved, Typed}; @@ -267,30 +266,6 @@ fn resolve_one_import( Ok(Typed { hir, ty }) } -/// Desugar a `with` expression. -fn desugar_with(x: Expr, path: &[Label], y: Expr, span: Span) -> Expr { - use crate::operations::BinOp::RightBiasedRecordMerge; - use ExprKind::{Op, RecordLit}; - use OpKind::{BinOp, Field}; - let expr = |k| Expr::new(k, span.clone()); - match path { - [] => y, - [l, rest @ ..] => { - let res = desugar_with( - expr(Op(Field(x.clone(), l.clone()))), - rest, - y, - span.clone(), - ); - expr(Op(BinOp( - RightBiasedRecordMerge, - x, - expr(RecordLit(once((l.clone(), res)).collect())), - ))) - } - } -} - /// Desugar the first level of the expression. fn desugar(expr: &Expr) -> Cow<'_, Expr> { match expr.kind() { @@ -316,9 +291,6 @@ fn desugar(expr: &Expr) -> Cow<'_, Expr> { expr.span(), )) } - ExprKind::Op(OpKind::With(x, path, y)) => { - Cow::Owned(desugar_with(x.clone(), path, y.clone(), expr.span())) - } _ => Cow::Borrowed(expr), } } diff --git a/dhall/tests/spec.rs b/dhall/tests/spec.rs index 36cbd81..c93be7e 100644 --- a/dhall/tests/spec.rs +++ b/dhall/tests/spec.rs @@ -544,12 +544,7 @@ fn define_features() -> Vec { variant: SpecTestKind::TypeInferenceSuccess, // TODO: this fails because of caching shenanigans // too_slow_path: Rc::new(|path: &str| path == "prelude"), - exclude_path: Rc::new(|path: &str| { - false - || path == "prelude" - // With builtin not implemented yet - || path == "unit/WithCreateIntermediateRecords" - }), + exclude_path: Rc::new(|path: &str| path == "prelude"), ..default_feature.clone() }, TestFeature { diff --git a/dhall/tests/type-inference/failure/unit/WithInvalidOverrideA.txt b/dhall/tests/type-inference/failure/unit/WithInvalidOverrideA.txt index c34175f..c2af394 100644 --- a/dhall/tests/type-inference/failure/unit/WithInvalidOverrideA.txt +++ b/dhall/tests/type-inference/failure/unit/WithInvalidOverrideA.txt @@ -1,7 +1,7 @@ -Type error: error: MustCombineRecord +Type error: error: WithMustBeRecord --> :1:1 | ... 6 | { a = 1 } with a.b = 2 - | ^^^^^^^^^^^^^^^^^^^^^^ MustCombineRecord + | ^^^^^^^^^^^^^^^^^^^^^^ WithMustBeRecord | diff --git a/dhall/tests/type-inference/failure/unit/WithNotRecord.txt b/dhall/tests/type-inference/failure/unit/WithNotRecord.txt index 8574e48..cd76485 100644 --- a/dhall/tests/type-inference/failure/unit/WithNotRecord.txt +++ b/dhall/tests/type-inference/failure/unit/WithNotRecord.txt @@ -1,6 +1,6 @@ -Type error: error: MustCombineRecord +Type error: error: WithMustBeRecord --> :1:1 | 1 | 5 with a = 10 - | ^^^^^^^^^^^^^ MustCombineRecord + | ^^^^^^^^^^^^^ WithMustBeRecord | -- cgit v1.3.1 From 7066e625fc945e66d8af91c633e762fe5f7b17f5 Mon Sep 17 00:00:00 2001 From: Basile Henry Date: Sun, 1 Nov 2020 23:33:38 +0100 Subject: Handle partially abstract with --- dhall/src/operations/normalization.rs | 37 +++++++++++++++++++++++++++-------- dhall/tests/spec.rs | 4 ---- 2 files changed, 29 insertions(+), 12 deletions(-) (limited to 'dhall/tests') diff --git a/dhall/src/operations/normalization.rs b/dhall/src/operations/normalization.rs index 908ed93..f147f85 100644 --- a/dhall/src/operations/normalization.rs +++ b/dhall/src/operations/normalization.rs @@ -306,16 +306,24 @@ pub fn normalize_operation(opkind: &OpKind) -> Ret { let mut visited: Vec<(&Label, HashMap)> = Vec::new(); let mut to_create = Vec::new(); + // To be used when an abstract entry is reached + let mut abstract_nir = None; + for label in labels { match current_nk { - None => to_create.push(label), + None => to_create.push(label.clone()), Some(nk) => match nk { RecordLit(kvs) => { current_nk = kvs.get(label).map(|nir| nir.kind().clone()); visited.push((label, kvs)); } - _ => return nothing_to_do(), + // Handle partially abstract case + nir => { + abstract_nir = Some(Nir::from_kind(nir)); + to_create.push(label.clone()); + current_nk = None; + } }, } } @@ -323,12 +331,25 @@ pub fn normalize_operation(opkind: &OpKind) -> Ret { // Create Nir for record bottom up let mut nir = expr.clone(); - while let Some(label) = to_create.pop() { - let rec = RecordLit(FromIterator::from_iter(once(( - label.clone(), - nir, - )))); - nir = Nir::from_kind(rec); + match abstract_nir { + // No abstract nir, creating singleton records + None => { + while let Some(label) = to_create.pop() { + let rec = RecordLit(FromIterator::from_iter(once(( + label.clone(), + nir, + )))); + nir = Nir::from_kind(rec); + } + } + // Abstract nir, creating with op + Some(abstract_nir) => { + nir = Nir::from_kind(Op(OpKind::With( + abstract_nir, + to_create, + nir, + ))); + } } // Update visited records diff --git a/dhall/tests/spec.rs b/dhall/tests/spec.rs index c93be7e..357181a 100644 --- a/dhall/tests/spec.rs +++ b/dhall/tests/spec.rs @@ -521,10 +521,6 @@ fn define_features() -> Vec { || path == "simple/integerToDouble" // TODO: fix Double/show || path == "prelude/JSON/number/1" - // With builtin not implemented yet - || path == "unit/WithCreateIntermediateRecords" - || path == "unit/WithDesugar" - || path == "unit/WithPartiallyAbstract" }), ..default_feature.clone() }, -- cgit v1.3.1 From afd7bcf5f58f4fe07f41d15728cecb8a42b67a46 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 2 Nov 2020 23:02:02 +0000 Subject: tests: add regression tests for `with` kind inference --- dhall/tests/type-inference/failure/unit/WithInfersKind.dhall | 1 + dhall/tests/type-inference/failure/unit/WithInfersKind.txt | 6 ++++++ dhall/tests/type-inference/success/unit/WithInfersKindA.dhall | 1 + dhall/tests/type-inference/success/unit/WithInfersKindB.dhall | 1 + 4 files changed, 9 insertions(+) create mode 100644 dhall/tests/type-inference/failure/unit/WithInfersKind.dhall create mode 100644 dhall/tests/type-inference/failure/unit/WithInfersKind.txt create mode 100644 dhall/tests/type-inference/success/unit/WithInfersKindA.dhall create mode 100644 dhall/tests/type-inference/success/unit/WithInfersKindB.dhall (limited to 'dhall/tests') diff --git a/dhall/tests/type-inference/failure/unit/WithInfersKind.dhall b/dhall/tests/type-inference/failure/unit/WithInfersKind.dhall new file mode 100644 index 0000000..aeed835 --- /dev/null +++ b/dhall/tests/type-inference/failure/unit/WithInfersKind.dhall @@ -0,0 +1 @@ +Some ({=} with x = Bool) diff --git a/dhall/tests/type-inference/failure/unit/WithInfersKind.txt b/dhall/tests/type-inference/failure/unit/WithInfersKind.txt new file mode 100644 index 0000000..1c60eb6 --- /dev/null +++ b/dhall/tests/type-inference/failure/unit/WithInfersKind.txt @@ -0,0 +1,6 @@ +Type error: error: InvalidOptionalType + --> :1:1 + | +1 | Some ({=} with x = Bool) + | ^^^^^^^^^^^^^^^^^^^^^^^^ InvalidOptionalType + | diff --git a/dhall/tests/type-inference/success/unit/WithInfersKindA.dhall b/dhall/tests/type-inference/success/unit/WithInfersKindA.dhall new file mode 100644 index 0000000..4662447 --- /dev/null +++ b/dhall/tests/type-inference/success/unit/WithInfersKindA.dhall @@ -0,0 +1 @@ +Some ({ x = Bool } with x = 0) diff --git a/dhall/tests/type-inference/success/unit/WithInfersKindB.dhall b/dhall/tests/type-inference/success/unit/WithInfersKindB.dhall new file mode 100644 index 0000000..009f331 --- /dev/null +++ b/dhall/tests/type-inference/success/unit/WithInfersKindB.dhall @@ -0,0 +1 @@ +Optional { x : Natural } -- cgit v1.3.1