summaryrefslogtreecommitdiff
path: root/dhall/src/semantics/resolve/resolve.rs
diff options
context:
space:
mode:
authorNadrieril2020-11-01 18:50:09 +0000
committerGitHub2020-11-01 18:50:09 +0000
commit2ae979a22ee4b79590f74110d61164383c7b5182 (patch)
tree5286606d6d59f5d5d4b9ecb7f651c97d201a092e /dhall/src/semantics/resolve/resolve.rs
parent48037367933085ca9c1c67c8c59f311e2b21be6d (diff)
parente5381c9b76f1d88dedb4a453cd026c8e98be5533 (diff)
Merge pull request #192 from Nadrieril/rework-caching
Diffstat (limited to 'dhall/src/semantics/resolve/resolve.rs')
-rw-r--r--dhall/src/semantics/resolve/resolve.rs131
1 files changed, 74 insertions, 57 deletions
diff --git a/dhall/src/semantics/resolve/resolve.rs b/dhall/src/semantics/resolve/resolve.rs
index 2b401dc..614ea22 100644
--- a/dhall/src/semantics/resolve/resolve.rs
+++ b/dhall/src/semantics/resolve/resolve.rs
@@ -10,20 +10,17 @@ use crate::builtins::Builtin;
use crate::error::ErrorBuilder;
use crate::error::{Error, ImportError};
use crate::operations::{BinOp, OpKind};
-use crate::semantics::{mkerr, Cache, Hir, HirKind, ImportEnv, NameEnv, Type};
+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,
};
-use crate::{Parsed, Resolved};
+use crate::{Parsed, Resolved, Typed};
// TODO: evaluate import headers
pub type Import = syntax::Import<()>;
-/// Owned Hir with a type. Different from Tir because the Hir is owned.
-pub type TypedHir = (Hir, Type);
-
/// The location of some data, usually some dhall code.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ImportLocation {
@@ -220,65 +217,55 @@ fn make_aslocation_uniontype() -> Expr {
mkexpr(ExprKind::UnionType(union))
}
+fn check_hash(import: &Import, typed: &Typed, span: Span) -> Result<(), Error> {
+ match (import.mode, &import.hash) {
+ (ImportMode::Code, Some(Hash::SHA256(hash))) => {
+ let actual_hash = typed.hir.to_expr_alpha().sha256_hash()?;
+ if hash[..] != actual_hash[..] {
+ mkerr(
+ ErrorBuilder::new("hash mismatch")
+ .span_err(span, "hash mismatch")
+ .note(format!("Expected sha256:{}", hex::encode(hash)))
+ .note(format!(
+ "Found sha256:{}",
+ hex::encode(actual_hash)
+ ))
+ .format(),
+ )?
+ }
+ }
+ _ => {}
+ }
+ Ok(())
+}
+
fn resolve_one_import(
env: &mut ImportEnv,
- cache: &Cache,
import: &Import,
- location: &ImportLocation,
+ location: ImportLocation,
span: Span,
-) -> Result<TypedHir, Error> {
- let do_sanity_check = import.mode != ImportMode::Location;
- let location = location.chain(&import.location, do_sanity_check)?;
- env.handle_import(location.clone(), |env| match import.mode {
+) -> Result<Typed, Error> {
+ let (hir, ty) = match import.mode {
ImportMode::Code => {
- let (hir, ty) = cache.caching_import(
- import,
- || location.fetch_dhall(),
- |parsed| {
- let typed =
- resolve_with_env(env, cache, parsed)?.typecheck()?;
- let hir = typed.normalize().to_hir();
- Ok((hir, typed.ty))
- },
- )?;
- match &import.hash {
- Some(Hash::SHA256(hash)) => {
- let actual_hash = hir.to_expr_alpha().hash()?;
- if hash[..] != actual_hash[..] {
- mkerr(
- ErrorBuilder::new("hash mismatch")
- .span_err(span, "hash mismatch")
- .note(format!(
- "Expected sha256:{}",
- hex::encode(hash)
- ))
- .note(format!(
- "Found sha256:{}",
- hex::encode(actual_hash)
- ))
- .format(),
- )?
- }
- }
- None => {}
- }
- Ok((hir, ty))
+ let parsed = location.fetch_dhall()?;
+ let typed = resolve_with_env(env, parsed)?.typecheck()?;
+ let hir = typed.normalize().to_hir();
+ (hir, typed.ty)
}
ImportMode::RawText => {
let text = location.fetch_text()?;
- let hir = Hir::new(
- HirKind::Expr(ExprKind::TextLit(text.into())),
- Span::Artificial,
- );
- Ok((hir, Type::from_builtin(Builtin::Text)))
+ let hir =
+ Hir::new(HirKind::Expr(ExprKind::TextLit(text.into())), span);
+ (hir, Type::from_builtin(Builtin::Text))
}
ImportMode::Location => {
let expr = location.into_location();
let hir = skip_resolve_expr(&expr)?;
let ty = hir.typecheck_noenv()?.ty().clone();
- Ok((hir, ty))
+ (hir, ty)
}
- })
+ };
+ Ok(Typed { hir, ty })
}
/// Desugar a `with` expression.
@@ -342,7 +329,7 @@ fn desugar(expr: &Expr) -> Cow<'_, Expr> {
fn traverse_resolve_expr(
name_env: &mut NameEnv,
expr: &Expr,
- f: &mut impl FnMut(Import, Span) -> Result<TypedHir, Error>,
+ f: &mut impl FnMut(Import, Span) -> Result<Typed, Error>,
) -> Result<Hir, Error> {
let expr = desugar(expr);
Ok(match expr.kind() {
@@ -382,7 +369,7 @@ fn traverse_resolve_expr(
// TODO: evaluate import headers
let import = import.traverse_ref(|_| Ok::<_, Error>(()))?;
let imported = f(import, expr.span())?;
- HirKind::Import(imported.0, imported.1)
+ HirKind::Import(imported.hir, imported.ty)
}
kind => HirKind::Expr(kind),
};
@@ -393,23 +380,53 @@ fn traverse_resolve_expr(
fn resolve_with_env(
env: &mut ImportEnv,
- cache: &Cache,
parsed: Parsed,
) -> Result<Resolved, Error> {
- let Parsed(expr, location) = parsed;
+ let Parsed(expr, base_location) = parsed;
let resolved = traverse_resolve_expr(
&mut NameEnv::new(),
&expr,
&mut |import, span| {
- resolve_one_import(env, cache, &import, &location, span)
+ 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(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())
+ })?;
+
+ // Add the resolved import to the caches
+ check_hash(&import, &typed, span)?;
+ env.write_to_disk_cache(&import.hash, &typed);
+ env.write_to_mem_cache(location, typed.clone());
+ Ok(typed)
},
)?;
Ok(Resolved(resolved))
}
pub fn resolve(parsed: Parsed) -> Result<Resolved, Error> {
- let cache = Cache::new();
- resolve_with_env(&mut ImportEnv::new(), &cache, parsed)
+ resolve_with_env(&mut ImportEnv::new(), parsed)
}
pub fn skip_resolve_expr(expr: &Expr) -> Result<Hir, Error> {