diff --git a/Cargo.lock b/Cargo.lock index fdbb3ed3..b89c7cdf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -300,6 +300,7 @@ dependencies = [ "annotate-snippets", "anyhow", "colored-diff", + "elsa", "fs_extra", "hex", "itertools 0.9.0", @@ -373,6 +374,15 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "elsa" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0daf12e2857c9485cb2386b18a9ecd5a17c7cee2fd10afb87d436b10ced678e7" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "encoding_rs" version = "0.8.24" @@ -1456,6 +1466,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" diff --git a/dhall/Cargo.toml b/dhall/Cargo.toml index 1b4f9a30..1e9cfbce 100644 --- a/dhall/Cargo.toml +++ b/dhall/Cargo.toml @@ -21,6 +21,7 @@ path = "tests/spec.rs" [dependencies] annotate-snippets = "0.9.0" +elsa = "1.3.2" hex = "0.4.2" itertools = "0.9.0" lazy_static = "1.4.0" diff --git a/dhall/src/builtins.rs b/dhall/src/builtins.rs index cc426dd6..123e03d2 100644 --- a/dhall/src/builtins.rs +++ b/dhall/src/builtins.rs @@ -2,15 +2,13 @@ use std::collections::{BTreeMap, HashMap}; use std::convert::TryInto; use crate::operations::{BinOp, OpKind}; -use crate::semantics::{ - nze, skip_resolve_expr, typecheck, Hir, HirKind, Nir, NirKind, NzEnv, - VarEnv, -}; +use crate::semantics::{nze, Hir, HirKind, Nir, NirKind, NzEnv, VarEnv}; use crate::syntax::Const::Type; use crate::syntax::{ Const, Expr, ExprKind, InterpolatedText, InterpolatedTextContents, Label, NaiveDouble, NumKind, Span, UnspannedExpr, V, }; +use crate::{Ctxt, Parsed}; /// Built-ins #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -89,23 +87,23 @@ impl Builtin { /// A partially applied builtin. /// Invariant: the evaluation of the given args must not be able to progress further #[derive(Debug, Clone)] -pub struct BuiltinClosure { - env: NzEnv, +pub struct BuiltinClosure<'cx> { + env: NzEnv<'cx>, b: Builtin, /// Arguments applied to the closure so far. - args: Vec, + args: Vec>, } -impl BuiltinClosure { - pub fn new(b: Builtin, env: NzEnv) -> NirKind { +impl<'cx> BuiltinClosure<'cx> { + pub fn new(b: Builtin, env: NzEnv<'cx>) -> NirKind<'cx> { apply_builtin(b, Vec::new(), env) } - pub fn apply(&self, a: Nir) -> NirKind { + pub fn apply(&self, a: Nir<'cx>) -> NirKind<'cx> { use std::iter::once; let args = self.args.iter().cloned().chain(once(a)).collect(); apply_builtin(self.b, args, self.env.clone()) } - pub fn to_hirkind(&self, venv: VarEnv) -> HirKind { + pub fn to_hirkind(&self, venv: VarEnv) -> HirKind<'cx> { HirKind::Expr(self.args.iter().fold( ExprKind::Builtin(self.b), |acc, v| { @@ -178,7 +176,7 @@ macro_rules! make_type { }; } -pub fn type_of_builtin(b: Builtin) -> Hir { +pub fn type_of_builtin<'cx>(cx: Ctxt<'cx>, b: Builtin) -> Hir<'cx> { use Builtin::*; let expr = match b { Bool | Natural | Integer | Double | Text => make_type!(Type), @@ -253,7 +251,10 @@ pub fn type_of_builtin(b: Builtin) -> Hir { forall (A: Type) -> Optional A ), }; - skip_resolve_expr(&expr).unwrap() + Parsed::from_expr_without_imports(expr) + .resolve(cx) + .unwrap() + .0 } // Ad-hoc macro to help construct closures @@ -307,19 +308,28 @@ macro_rules! make_closure { } #[allow(clippy::cognitive_complexity)] -fn apply_builtin(b: Builtin, args: Vec, env: NzEnv) -> NirKind { +fn apply_builtin<'cx>( + b: Builtin, + args: Vec>, + env: NzEnv<'cx>, +) -> NirKind<'cx> { + let cx = env.cx(); use NirKind::*; use NumKind::{Bool, Double, Integer, Natural}; // Small helper enum - enum Ret { - NirKind(NirKind), - Nir(Nir), + enum Ret<'cx> { + NirKind(NirKind<'cx>), + Nir(Nir<'cx>), DoneAsIs, } let make_closure = |e| { - typecheck(&skip_resolve_expr(&e).unwrap()) + Parsed::from_expr_without_imports(e) + .resolve(cx) + .unwrap() + .typecheck(cx) .unwrap() + .as_hir() .eval(env.clone()) }; @@ -486,7 +496,7 @@ fn apply_builtin(b: Builtin, args: Vec, env: NzEnv) -> NirKind { let mut kts = HashMap::new(); kts.insert( "index".into(), - Nir::from_builtin(Builtin::Natural), + Nir::from_builtin(cx, Builtin::Natural), ); kts.insert("value".into(), t.clone()); let t = Nir::from_kind(RecordType(kts)); @@ -516,7 +526,7 @@ fn apply_builtin(b: Builtin, args: Vec, env: NzEnv) -> NirKind { } } (Builtin::ListBuild, [t, f]) => { - let list_t = Nir::from_builtin(Builtin::List).app(t.clone()); + let list_t = Nir::from_builtin(cx, Builtin::List).app(t.clone()); Ret::Nir( f.app(list_t) .app( @@ -543,7 +553,7 @@ fn apply_builtin(b: Builtin, args: Vec, env: NzEnv) -> NirKind { _ => Ret::DoneAsIs, }, (Builtin::NaturalBuild, [f]) => Ret::Nir( - f.app(Nir::from_builtin(Builtin::Natural)) + f.app(Nir::from_builtin(cx, Builtin::Natural)) .app(make_closure(make_closure!( λ(x : Natural) -> 1 + var(x) @@ -554,7 +564,7 @@ fn apply_builtin(b: Builtin, args: Vec, env: NzEnv) -> NirKind { (Builtin::NaturalFold, [n, t, succ, zero]) => match &*n.kind() { Num(Natural(0)) => Ret::Nir(zero.clone()), Num(Natural(n)) => { - let fold = Nir::from_builtin(Builtin::NaturalFold) + let fold = Nir::from_builtin(cx, Builtin::NaturalFold) .app(Num(Natural(n - 1)).into_nir()) .app(t.clone()) .app(succ.clone()) @@ -572,12 +582,12 @@ fn apply_builtin(b: Builtin, args: Vec, env: NzEnv) -> NirKind { } } -impl std::cmp::PartialEq for BuiltinClosure { +impl<'cx> std::cmp::PartialEq for BuiltinClosure<'cx> { fn eq(&self, other: &Self) -> bool { self.b == other.b && self.args == other.args } } -impl std::cmp::Eq for BuiltinClosure {} +impl<'cx> std::cmp::Eq for BuiltinClosure<'cx> {} impl std::fmt::Display for Builtin { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { diff --git a/dhall/src/ctxt.rs b/dhall/src/ctxt.rs new file mode 100644 index 00000000..aad1a1b8 --- /dev/null +++ b/dhall/src/ctxt.rs @@ -0,0 +1,211 @@ +use elsa::vec::FrozenVec; +use once_cell::sync::OnceCell; +use std::marker::PhantomData; +use std::ops::{Deref, Index}; + +use crate::semantics::{Import, ImportLocation, ImportNode}; +use crate::syntax::Span; +use crate::Typed; + +///////////////////////////////////////////////////////////////////////////////////////////////////// +// Ctxt + +/// Implementation detail. Made public for the `Index` instances. +#[derive(Default)] +pub struct CtxtS<'cx> { + imports: FrozenVec>>, + import_alternatives: FrozenVec>>, + import_results: FrozenVec>>, +} + +/// Context for the dhall compiler. Stores various global maps. +/// Access the relevant value using `cx[id]`. +#[derive(Copy, Clone)] +pub struct Ctxt<'cx>(&'cx CtxtS<'cx>); + +impl Ctxt<'_> { + pub fn with_new(f: impl for<'cx> FnOnce(Ctxt<'cx>) -> T) -> T { + let cx = CtxtS::default(); + let cx = Ctxt(&cx); + f(cx) + } +} +impl<'cx> Deref for Ctxt<'cx> { + type Target = &'cx CtxtS<'cx>; + fn deref(&self) -> &&'cx CtxtS<'cx> { + &self.0 + } +} +impl<'a, 'cx, T> Index<&'a T> for CtxtS<'cx> +where + Self: Index, + T: Copy, +{ + type Output = >::Output; + fn index(&self, id: &'a T) -> &Self::Output { + &self[*id] + } +} + +/// Empty impl, because `FrozenVec` does not implement `Debug` and I can't be bothered to do it +/// myself. +impl<'cx> std::fmt::Debug for Ctxt<'cx> { + fn fmt(&self, _: &mut std::fmt::Formatter) -> std::fmt::Result { + Ok(()) + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// +// Imports + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct ImportId<'cx>(usize, PhantomData<&'cx ()>); + +/// What's stored for each `ImportId`. Allows getting and setting a result for this import. +pub struct StoredImport<'cx> { + cx: Ctxt<'cx>, + pub base_location: ImportLocation, + pub import: Import, + pub span: Span, + result: OnceCell>, +} + +impl<'cx> StoredImport<'cx> { + /// Get the id of the result of fetching this import. Returns `None` if the result has not yet + /// been fetched. + pub fn get_resultid(&self) -> Option> { + self.result.get().copied() + } + /// Store the result of fetching this import. + pub fn set_resultid(&self, res: ImportResultId<'cx>) { + let _ = self.result.set(res); + } + /// Get the result of fetching this import. Returns `None` if the result has not yet been + /// fetched. + pub fn get_result(&self) -> Option<&'cx StoredImportResult<'cx>> { + let res = self.get_resultid()?; + Some(&self.cx[res]) + } + /// Get the result of fetching this import. Panicx if the result has not yet been + /// fetched. + pub fn unwrap_result(&self) -> &'cx StoredImportResult<'cx> { + self.get_result() + .expect("imports should all have been resolved at this stage") + } + /// Store the result of fetching this import. + pub fn set_result( + &self, + res: StoredImportResult<'cx>, + ) -> ImportResultId<'cx> { + let res = self.cx.push_import_result(res); + self.set_resultid(res); + res + } +} +impl<'cx> Ctxt<'cx> { + /// Store an import and the location relative to which it must be resolved. + pub fn push_import( + self, + base_location: ImportLocation, + import: Import, + span: Span, + ) -> ImportId<'cx> { + let stored = StoredImport { + cx: self, + base_location, + import, + span, + result: OnceCell::new(), + }; + let id = self.0.imports.len(); + self.0.imports.push(Box::new(stored)); + ImportId(id, PhantomData) + } +} +impl<'cx> Index> for CtxtS<'cx> { + type Output = StoredImport<'cx>; + fn index(&self, id: ImportId<'cx>) -> &StoredImport<'cx> { + &self.imports[id.0] + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// +// Import alternatives + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct ImportAlternativeId<'cx>(usize, PhantomData<&'cx ()>); + +/// What's stored for each `ImportAlternativeId`. +pub struct StoredImportAlternative<'cx> { + pub left_imports: Box<[ImportNode<'cx>]>, + pub right_imports: Box<[ImportNode<'cx>]>, + /// `true` for left, `false` for right. + selected: OnceCell, +} + +impl<'cx> StoredImportAlternative<'cx> { + /// Get which alternative got selected. `true` for left, `false` for right. + pub fn get_selected(&self) -> Option { + self.selected.get().copied() + } + /// Get which alternative got selected. `true` for left, `false` for right. + pub fn unwrap_selected(&self) -> bool { + self.get_selected() + .expect("imports should all have been resolved at this stage") + } + /// Set which alternative got selected. `true` for left, `false` for right. + pub fn set_selected(&self, selected: bool) { + let _ = self.selected.set(selected); + } +} +impl<'cx> Ctxt<'cx> { + pub fn push_import_alternative( + self, + left_imports: Box<[ImportNode<'cx>]>, + right_imports: Box<[ImportNode<'cx>]>, + ) -> ImportAlternativeId<'cx> { + let stored = StoredImportAlternative { + left_imports, + right_imports, + selected: OnceCell::new(), + }; + let id = self.0.import_alternatives.len(); + self.0.import_alternatives.push(Box::new(stored)); + ImportAlternativeId(id, PhantomData) + } +} +impl<'cx> Index> for CtxtS<'cx> { + type Output = StoredImportAlternative<'cx>; + fn index( + &self, + id: ImportAlternativeId<'cx>, + ) -> &StoredImportAlternative<'cx> { + &self.import_alternatives[id.0] + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// +// Import results + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct ImportResultId<'cx>(usize, PhantomData<&'cx ()>); + +type StoredImportResult<'cx> = Typed<'cx>; + +impl<'cx> Ctxt<'cx> { + /// Store the result of fetching an import. + pub fn push_import_result( + self, + res: StoredImportResult<'cx>, + ) -> ImportResultId<'cx> { + let id = self.0.import_results.len(); + self.0.import_results.push(Box::new(res)); + ImportResultId(id, PhantomData) + } +} +impl<'cx> Index> for CtxtS<'cx> { + type Output = StoredImportResult<'cx>; + fn index(&self, id: ImportResultId<'cx>) -> &StoredImportResult<'cx> { + &self.import_results[id.0] + } +} diff --git a/dhall/src/lib.rs b/dhall/src/lib.rs index 6747eff7..ad16a240 100644 --- a/dhall/src/lib.rs +++ b/dhall/src/lib.rs @@ -5,17 +5,18 @@ clippy::needless_lifetimes, clippy::new_ret_no_self, clippy::new_without_default, + clippy::try_err, clippy::useless_format )] pub mod builtins; +pub mod ctxt; pub mod error; pub mod operations; pub mod semantics; pub mod syntax; pub mod utils; -use std::fmt::Display; use std::path::Path; use url::Url; @@ -26,6 +27,8 @@ use crate::semantics::resolve::ImportLocation; use crate::semantics::{typecheck, typecheck_with, Hir, Nir, Tir, Type}; use crate::syntax::Expr; +pub use ctxt::*; + #[derive(Debug, Clone)] pub struct Parsed(Expr, ImportLocation); @@ -33,20 +36,20 @@ pub struct Parsed(Expr, ImportLocation); /// /// Invariant: there must be no `Import` nodes or `ImportAlt` operations left. #[derive(Debug, Clone)] -pub struct Resolved(Hir); +pub struct Resolved<'cx>(Hir<'cx>); /// A typed expression #[derive(Debug, Clone)] -pub struct Typed { - pub hir: Hir, - pub ty: Type, +pub struct Typed<'cx> { + pub hir: Hir<'cx>, + pub ty: Type<'cx>, } /// A normalized expression. /// /// This is actually a lie, because the expression will only get normalized on demand. #[derive(Debug, Clone)] -pub struct Normalized(Nir); +pub struct Normalized<'cx>(Nir<'cx>); /// Controls conversion from `Nir` to `Expr` #[derive(Copy, Clone, Default)] @@ -56,6 +59,11 @@ pub struct ToExprOptions { } impl Parsed { + /// Construct from an `Expr`. This `Expr` will have imports disabled. + pub fn from_expr_without_imports(e: Expr) -> Self { + Parsed(e, ImportLocation::dhall_code_without_imports()) + } + pub fn parse_file(f: &Path) -> Result { parse::parse_file(f) } @@ -73,11 +81,14 @@ impl Parsed { parse::parse_binary(data) } - pub fn resolve(self) -> Result { - resolve::resolve(self) + pub fn resolve<'cx>(self, cx: Ctxt<'cx>) -> Result, Error> { + resolve::resolve(cx, self) } - pub fn skip_resolve(self) -> Result { - resolve::skip_resolve(self) + pub fn skip_resolve<'cx>( + self, + cx: Ctxt<'cx>, + ) -> Result, Error> { + resolve::skip_resolve(cx, self) } /// Converts a value back to the corresponding AST expression. @@ -86,59 +97,66 @@ impl Parsed { } } -impl Resolved { - pub fn typecheck(&self) -> Result { - Ok(Typed::from_tir(typecheck(&self.0)?)) +impl<'cx> Resolved<'cx> { + pub fn typecheck(&self, cx: Ctxt<'cx>) -> Result, TypeError> { + Ok(Typed::from_tir(typecheck(cx, &self.0)?)) } - pub fn typecheck_with(self, ty: &Hir) -> Result { - Ok(Typed::from_tir(typecheck_with(&self.0, ty)?)) + pub fn typecheck_with( + self, + cx: Ctxt<'cx>, + ty: &Hir<'cx>, + ) -> Result, TypeError> { + Ok(Typed::from_tir(typecheck_with(cx, &self.0, ty)?)) } /// Converts a value back to the corresponding AST expression. - pub fn to_expr(&self) -> Expr { - self.0.to_expr_noopts() + pub fn to_expr(&self, cx: Ctxt<'cx>) -> Expr { + self.0.to_expr_noopts(cx) } } -impl Typed { - fn from_tir(tir: Tir<'_>) -> Self { +impl<'cx> Typed<'cx> { + fn from_tir(tir: Tir<'cx, '_>) -> Self { Typed { hir: tir.as_hir().clone(), ty: tir.ty().clone(), } } /// Reduce an expression to its normal form, performing beta reduction - pub fn normalize(&self) -> Normalized { - Normalized(self.hir.eval_closed_expr()) + pub fn normalize(&self, cx: Ctxt<'cx>) -> Normalized<'cx> { + Normalized(self.hir.eval_closed_expr(cx)) } /// Converts a value back to the corresponding AST expression. - fn to_expr(&self) -> Expr { - self.hir.to_expr(ToExprOptions { alpha: false }) + fn to_expr(&self, cx: Ctxt<'cx>) -> Expr { + self.hir.to_expr(cx, ToExprOptions { alpha: false }) } - pub fn ty(&self) -> &Type { + pub fn as_hir(&self) -> &Hir<'cx> { + &self.hir + } + pub fn ty(&self) -> &Type<'cx> { &self.ty } - pub fn get_type(&self) -> Result { + pub fn get_type(&self) -> Result, TypeError> { Ok(Normalized(self.ty.clone().into_nir())) } } -impl Normalized { +impl<'cx> Normalized<'cx> { /// Converts a value back to the corresponding AST expression. - pub fn to_expr(&self) -> Expr { - self.0.to_expr(ToExprOptions::default()) + pub fn to_expr(&self, cx: Ctxt<'cx>) -> Expr { + self.0.to_expr(cx, ToExprOptions::default()) } /// Converts a value back to the corresponding Hir expression. - pub fn to_hir(&self) -> Hir { + pub fn to_hir(&self) -> Hir<'cx> { self.0.to_hir_noenv() } - pub fn as_nir(&self) -> &Nir { + pub fn as_nir(&self) -> &Nir<'cx> { &self.0 } /// Converts a value back to the corresponding AST expression, alpha-normalizing in the process. - pub fn to_expr_alpha(&self) -> Expr { - self.0.to_expr(ToExprOptions { alpha: true }) + pub fn to_expr_alpha(&self, cx: Ctxt<'cx>) -> Expr { + self.0.to_expr(cx, ToExprOptions { alpha: true }) } } @@ -170,38 +188,10 @@ impl From for Expr { other.to_expr() } } -impl From for Expr { - fn from(other: Normalized) -> Self { - other.to_expr() - } -} - -impl Display for Resolved { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - self.to_expr().fmt(f) - } -} - -impl Eq for Typed {} -impl PartialEq for Typed { - fn eq(&self, other: &Self) -> bool { - self.normalize() == other.normalize() - } -} -impl Display for Typed { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - self.to_expr().fmt(f) - } -} -impl Eq for Normalized {} -impl PartialEq for Normalized { +impl<'cx> Eq for Normalized<'cx> {} +impl<'cx> PartialEq for Normalized<'cx> { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } -impl Display for Normalized { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - self.to_expr().fmt(f) - } -} diff --git a/dhall/src/operations/normalization.rs b/dhall/src/operations/normalization.rs index 28375b2e..6c68e7c5 100644 --- a/dhall/src/operations/normalization.rs +++ b/dhall/src/operations/normalization.rs @@ -8,7 +8,7 @@ use crate::semantics::{ }; use crate::syntax::{ExprKind, Label, NumKind}; -fn normalize_binop(o: BinOp, x: Nir, y: Nir) -> Ret { +fn normalize_binop<'cx>(o: BinOp, x: Nir<'cx>, y: Nir<'cx>) -> Ret<'cx> { use BinOp::*; use NirKind::{EmptyListLit, NEListLit, Num, RecordLit, RecordType}; use NumKind::{Bool, Natural}; @@ -119,7 +119,7 @@ fn normalize_binop(o: BinOp, x: Nir, y: Nir) -> Ret { } } -fn normalize_field(v: &Nir, field: &Label) -> Ret { +fn normalize_field<'cx>(v: &Nir<'cx>, field: &Label) -> Ret<'cx> { use self::BinOp::{RecursiveRecordMerge, RightBiasedRecordMerge}; use NirKind::{Op, RecordLit, UnionConstructor, UnionType}; use OpKind::{BinOp, Field, Projection}; @@ -187,7 +187,7 @@ fn normalize_field(v: &Nir, field: &Label) -> Ret { } } -pub fn normalize_operation(opkind: OpKind) -> Ret { +pub fn normalize_operation<'cx>(opkind: OpKind>) -> Ret<'cx> { use self::BinOp::RightBiasedRecordMerge; use NirKind::{ EmptyListLit, EmptyOptionalLit, NEListLit, NEOptionalLit, Num, Op, diff --git a/dhall/src/operations/typecheck.rs b/dhall/src/operations/typecheck.rs index bc0e8647..9b19c846 100644 --- a/dhall/src/operations/typecheck.rs +++ b/dhall/src/operations/typecheck.rs @@ -13,9 +13,9 @@ use crate::syntax::{Const, ExprKind, Span}; fn check_rectymerge( span: &Span, - env: &TyEnv, - x: Nir, - y: Nir, + env: &TyEnv<'_>, + x: Nir<'_>, + y: Nir<'_>, ) -> Result<(), TypeError> { let kts_x = match x.kind() { NirKind::RecordType(kts) => kts, @@ -44,13 +44,14 @@ fn check_rectymerge( Ok(()) } -fn typecheck_binop( - env: &TyEnv, +fn typecheck_binop<'cx>( + env: &TyEnv<'cx>, span: Span, op: BinOp, - l: Tir<'_>, - r: Tir<'_>, -) -> Result { + l: Tir<'cx, '_>, + r: Tir<'cx, '_>, +) -> Result, TypeError> { + let cx = env.cx(); let span_err = |msg: &str| mk_span_err(span.clone(), msg); use BinOp::*; use NirKind::{ListType, RecordType}; @@ -124,17 +125,20 @@ fn typecheck_binop( Type::from_const(Const::Type) } op => { - let t = Type::from_builtin(match op { - BoolAnd | BoolOr | BoolEQ | BoolNE => Builtin::Bool, - NaturalPlus | NaturalTimes => Builtin::Natural, - TextAppend => Builtin::Text, - ListAppend - | RightBiasedRecordMerge - | RecursiveRecordMerge - | RecursiveRecordTypeMerge - | Equivalence => unreachable!(), - ImportAlt => unreachable!("ImportAlt leftover in tck"), - }); + let t = Type::from_builtin( + cx, + match op { + BoolAnd | BoolOr | BoolEQ | BoolNE => Builtin::Bool, + NaturalPlus | NaturalTimes => Builtin::Natural, + TextAppend => Builtin::Text, + ListAppend + | RightBiasedRecordMerge + | RecursiveRecordMerge + | RecursiveRecordTypeMerge + | Equivalence => unreachable!(), + ImportAlt => unreachable!("ImportAlt leftover in tck"), + }, + ); if *l.ty() != t { return span_err("BinOpTypeMismatch"); @@ -149,13 +153,13 @@ fn typecheck_binop( }) } -fn typecheck_merge( - env: &TyEnv, +fn typecheck_merge<'cx>( + env: &TyEnv<'cx>, span: Span, - record: &Tir<'_>, - scrut: &Tir<'_>, - type_annot: Option<&Tir<'_>>, -) -> Result { + record: &Tir<'cx, '_>, + scrut: &Tir<'cx, '_>, + type_annot: Option<&Tir<'cx, '_>>, +) -> Result, TypeError> { let span_err = |msg: &str| mk_span_err(span.clone(), msg); use NirKind::{OptionalType, PiClosure, RecordType, UnionType}; @@ -287,11 +291,12 @@ fn typecheck_merge( }) } -pub fn typecheck_operation( - env: &TyEnv, +pub fn typecheck_operation<'cx>( + env: &TyEnv<'cx>, span: Span, - opkind: OpKind>, -) -> Result { + opkind: OpKind>, +) -> Result, TypeError> { + let cx = env.cx(); let span_err = |msg: &str| mk_span_err(span.clone(), msg); use NirKind::{ListType, PiClosure, RecordType, UnionType}; use OpKind::*; @@ -347,7 +352,7 @@ pub fn typecheck_operation( } BinOp(o, l, r) => typecheck_binop(env, span, o, l, r)?, BoolIf(x, y, z) => { - if *x.ty().kind() != NirKind::from_builtin(Builtin::Bool) { + if *x.ty().kind() != NirKind::from_builtin(cx, Builtin::Bool) { return span_err("InvalidPredicate"); } if y.ty().ty().as_const().is_none() { @@ -399,7 +404,7 @@ pub fn typecheck_operation( return span_err(err_msg); } match kts.get("mapKey") { - Some(t) if *t == Nir::from_builtin(Builtin::Text) => {} + Some(t) if *t == Nir::from_builtin(cx, Builtin::Text) => {} _ => return span_err(err_msg), } match kts.get("mapValue") { @@ -418,9 +423,12 @@ pub fn typecheck_operation( } let mut kts = HashMap::new(); - kts.insert("mapKey".into(), Nir::from_builtin(Builtin::Text)); + kts.insert( + "mapKey".into(), + Nir::from_builtin(cx, Builtin::Text), + ); kts.insert("mapValue".into(), entry_type); - let output_type: Type = Nir::from_builtin(Builtin::List) + let output_type: Type = Nir::from_builtin(cx, Builtin::List) .app(Nir::from_kind(RecordType(kts))) .to_type(Const::Type); if let Some(annot) = annot { diff --git a/dhall/src/semantics/nze/env.rs b/dhall/src/semantics/nze/env.rs index ec99dbed..30fa8afe 100644 --- a/dhall/src/semantics/nze/env.rs +++ b/dhall/src/semantics/nze/env.rs @@ -1,4 +1,5 @@ use crate::semantics::{AlphaVar, Nir, NirKind}; +use crate::Ctxt; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum NzVar { @@ -9,19 +10,20 @@ pub enum NzVar { } #[derive(Debug, Clone)] -enum EnvItem { +enum EnvItem<'cx, T> { // Variable is bound with given type - Kept(Type), + Kept(T), // Variable has been replaced by corresponding value - Replaced(Nir, Type), + Replaced(Nir<'cx>, T), } #[derive(Debug, Clone)] -pub struct ValEnv { - items: Vec>, +pub struct ValEnv<'cx, T> { + cx: Ctxt<'cx>, + items: Vec>, } -pub type NzEnv = ValEnv<()>; +pub type NzEnv<'cx> = ValEnv<'cx, ()>; impl NzVar { pub fn new(idx: usize) -> Self { @@ -46,11 +48,17 @@ impl NzVar { } } -impl ValEnv { - pub fn new() -> Self { - ValEnv { items: Vec::new() } +impl<'cx, T: Clone> ValEnv<'cx, T> { + pub fn new(cx: Ctxt<'cx>) -> Self { + ValEnv { + cx, + items: Vec::new(), + } + } + pub fn cx(&self) -> Ctxt<'cx> { + self.cx } - pub fn discard_types(&self) -> ValEnv<()> { + pub fn discard_types(&self) -> ValEnv<'cx, ()> { let items = self .items .iter() @@ -59,27 +67,27 @@ impl ValEnv { EnvItem::Replaced(val, _) => EnvItem::Replaced(val.clone(), ()), }) .collect(); - ValEnv { items } + ValEnv { cx: self.cx, items } } - pub fn insert_type(&self, ty: Type) -> Self { + pub fn insert_type(&self, ty: T) -> Self { let mut env = self.clone(); env.items.push(EnvItem::Kept(ty)); env } - pub fn insert_value(&self, e: Nir, ty: Type) -> Self { + pub fn insert_value(&self, e: Nir<'cx>, ty: T) -> Self { let mut env = self.clone(); env.items.push(EnvItem::Replaced(e, ty)); env } - pub fn lookup_val(&self, var: AlphaVar) -> NirKind { + pub fn lookup_val(&self, var: AlphaVar) -> NirKind<'cx> { let idx = self.items.len() - 1 - var.idx(); match &self.items[idx] { EnvItem::Kept(_) => NirKind::Var(NzVar::new(idx)), EnvItem::Replaced(x, _) => x.kind().clone(), } } - pub fn lookup_ty(&self, var: AlphaVar) -> Type { + pub fn lookup_ty(&self, var: AlphaVar) -> T { let idx = self.items.len() - 1 - var.idx(); match &self.items[idx] { EnvItem::Kept(ty) | EnvItem::Replaced(_, ty) => ty.clone(), @@ -87,8 +95,8 @@ impl ValEnv { } } -impl<'a> From<&'a NzEnv> for NzEnv { - fn from(x: &'a NzEnv) -> Self { +impl<'cx, 'a> From<&'a NzEnv<'cx>> for NzEnv<'cx> { + fn from(x: &'a NzEnv<'cx>) -> Self { x.clone() } } diff --git a/dhall/src/semantics/nze/nir.rs b/dhall/src/semantics/nze/nir.rs index 7bda8368..8cf06c5c 100644 --- a/dhall/src/semantics/nze/nir.rs +++ b/dhall/src/semantics/nze/nir.rs @@ -11,7 +11,7 @@ use crate::semantics::{ use crate::syntax::{ Const, Expr, ExprKind, InterpolatedTextContents, Label, NumKind, Span, }; -use crate::ToExprOptions; +use crate::{Ctxt, ToExprOptions}; /// Stores a possibly unevaluated value. Gets (partially) normalized on-demand, sharing computation /// automatically. Uses a Rc to share computation. @@ -20,31 +20,31 @@ use crate::ToExprOptions; /// normalize as needed. /// Stands for "Normalized Intermediate Representation" #[derive(Clone)] -pub struct Nir(Rc>); +pub struct Nir<'cx>(Rc, NirKind<'cx>>>); /// An unevaluated subexpression #[derive(Debug, Clone)] -pub enum Thunk { +pub enum Thunk<'cx> { /// A completely unnormalized expression. - Thunk { env: NzEnv, body: Hir }, + Thunk { env: NzEnv<'cx>, body: Hir<'cx> }, /// A partially normalized expression that may need to go through `normalize_one_layer`. - PartialExpr { env: NzEnv, expr: ExprKind }, + PartialExpr { expr: ExprKind> }, } /// An unevaluated subexpression that takes an argument. #[derive(Debug, Clone)] -pub enum Closure { +pub enum Closure<'cx> { /// Normal closure - Closure { env: NzEnv, body: Hir }, + Closure { env: NzEnv<'cx>, body: Hir<'cx> }, /// Closure that ignores the argument passed - ConstantClosure { body: Nir }, + ConstantClosure { body: Nir<'cx> }, } /// A text literal with interpolations. // Invariant: this must not contain interpolations that are themselves TextLits, and contiguous // text values must be merged. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct TextLit(Vec>); +pub struct TextLit<'cx>(Vec>>); /// This represents a value in Weak Head Normal Form (WHNF). This means that the value is /// normalized up to the first constructor, but subexpressions may not be fully normalized. @@ -54,67 +54,65 @@ pub struct TextLit(Vec>); /// In particular, this means that once we get a `NirKind`, it can be considered immutable, and /// we only need to recursively normalize its sub-`Nir`s to get to the NF. #[derive(Debug, Clone, PartialEq, Eq)] -pub enum NirKind { +pub enum NirKind<'cx> { /// Closures LamClosure { binder: Binder, - annot: Nir, - closure: Closure, + annot: Nir<'cx>, + closure: Closure<'cx>, }, PiClosure { binder: Binder, - annot: Nir, - closure: Closure, + annot: Nir<'cx>, + closure: Closure<'cx>, }, - AppliedBuiltin(BuiltinClosure), + AppliedBuiltin(BuiltinClosure<'cx>), Var(NzVar), Const(Const), Num(NumKind), // Must be a number type, Bool or Text BuiltinType(Builtin), - TextLit(TextLit), - EmptyOptionalLit(Nir), - NEOptionalLit(Nir), - OptionalType(Nir), + TextLit(TextLit<'cx>), + EmptyOptionalLit(Nir<'cx>), + NEOptionalLit(Nir<'cx>), + OptionalType(Nir<'cx>), // EmptyListLit(t) means `[] : List t`, not `[] : t` - EmptyListLit(Nir), - NEListLit(Vec), - ListType(Nir), - RecordLit(HashMap), - RecordType(HashMap), - UnionConstructor(Label, HashMap>), - UnionLit(Label, Nir, HashMap>), - UnionType(HashMap>), - Equivalence(Nir, Nir), - Assert(Nir), + EmptyListLit(Nir<'cx>), + NEListLit(Vec>), + ListType(Nir<'cx>), + RecordLit(HashMap>), + RecordType(HashMap>), + UnionConstructor(Label, HashMap>>), + UnionLit(Label, Nir<'cx>, HashMap>>), + UnionType(HashMap>>), + Equivalence(Nir<'cx>, Nir<'cx>), + Assert(Nir<'cx>), /// Invariant: evaluation must not be able to progress with `normalize_operation`. /// This is used when an operation couldn't proceed further, for example because of variables. - Op(OpKind), + Op(OpKind>), } -impl Nir { +impl<'cx> Nir<'cx> { /// Construct a Nir from a completely unnormalized expression. - pub fn new_thunk(env: NzEnv, hir: Hir) -> Nir { + pub fn new_thunk(env: NzEnv<'cx>, hir: Hir<'cx>) -> Self { Nir(Rc::new(lazy::Lazy::new(Thunk::new(env, hir)))) } /// Construct a Nir from a partially normalized expression that's not in WHNF. - pub fn from_partial_expr(e: ExprKind) -> Nir { - // TODO: env - let env = NzEnv::new(); - Nir(Rc::new(lazy::Lazy::new(Thunk::from_partial_expr(env, e)))) + pub fn from_partial_expr(e: ExprKind) -> Self { + Nir(Rc::new(lazy::Lazy::new(Thunk::from_partial_expr(e)))) } /// Make a Nir from a NirKind - pub fn from_kind(v: NirKind) -> Nir { + pub fn from_kind(v: NirKind<'cx>) -> Self { Nir(Rc::new(lazy::Lazy::new_completed(v))) } pub fn from_const(c: Const) -> Self { Self::from_kind(NirKind::Const(c)) } - pub fn from_builtin(b: Builtin) -> Self { - Self::from_builtin_env(b, &NzEnv::new()) + pub fn from_builtin(cx: Ctxt<'cx>, b: Builtin) -> Self { + Self::from_builtin_env(b, &NzEnv::new(cx)) } - pub fn from_builtin_env(b: Builtin, env: &NzEnv) -> Self { + pub fn from_builtin_env(b: Builtin, env: &NzEnv<'cx>) -> Self { Nir::from_kind(NirKind::from_builtin_env(b, env.clone())) } pub fn from_text(txt: impl ToString) -> Self { @@ -129,51 +127,54 @@ impl Nir { } /// This is what you want if you want to pattern-match on the value. - pub fn kind(&self) -> &NirKind { + pub fn kind(&self) -> &NirKind<'cx> { &*self.0 } /// The contents of a `Nir` are immutable and shared. If however we happen to be the sole /// owners, we can mutate it directly. Otherwise, this clones the internal value first. - pub fn kind_mut(&mut self) -> &mut NirKind { + pub fn kind_mut(&mut self) -> &mut NirKind<'cx> { Rc::make_mut(&mut self.0).get_mut() } /// If we are the sole owner of this Nir, we can avoid a clone. - pub fn into_kind(self) -> NirKind { + pub fn into_kind(self) -> NirKind<'cx> { match Rc::try_unwrap(self.0) { Ok(lazy) => lazy.into_inner(), Err(rc) => (**rc).clone(), } } - pub fn to_type(&self, u: impl Into) -> Type { + pub fn to_type(&self, u: impl Into) -> Type<'cx> { Type::new(self.clone(), u.into()) } /// Converts a value back to the corresponding AST expression. - pub fn to_expr(&self, opts: ToExprOptions) -> Expr { - self.to_hir_noenv().to_expr(opts) + pub fn to_expr(&self, cx: Ctxt<'cx>, opts: ToExprOptions) -> Expr { + self.to_hir_noenv().to_expr(cx, opts) } - pub fn to_expr_tyenv(&self, tyenv: &TyEnv) -> Expr { + pub fn to_expr_tyenv(&self, tyenv: &TyEnv<'cx>) -> Expr { self.to_hir(tyenv.as_varenv()).to_expr_tyenv(tyenv) } - pub fn app(&self, v: Nir) -> Nir { + pub fn app(&self, v: Self) -> Self { Nir::from_kind(self.app_to_kind(v)) } - pub fn app_to_kind(&self, v: Nir) -> NirKind { + pub fn app_to_kind(&self, v: Self) -> NirKind<'cx> { apply_any(self, v) } - pub fn to_hir(&self, venv: VarEnv) -> Hir { - let map_uniontype = |kts: &HashMap>| { - ExprKind::UnionType( - kts.iter() - .map(|(k, v)| { - (k.clone(), v.as_ref().map(|v| v.to_hir(venv))) - }) - .collect(), - ) - }; + pub fn to_hir(&self, venv: VarEnv) -> Hir<'cx> { + let map_uniontype = + |kts: &HashMap>>| -> ExprKind> { + ExprKind::UnionType( + kts.iter() + .map(|(k, v)| { + (k.clone(), v.as_ref().map(|v| v.to_hir(venv))) + }) + .collect(), + ) + }; + let builtin = + |b| Hir::new(HirKind::Expr(ExprKind::Builtin(b)), Span::Artificial); let hir = match self.kind() { NirKind::Var(v) => HirKind::Var(venv.lookup(*v)), @@ -204,21 +205,21 @@ impl Nir { NirKind::BuiltinType(b) => ExprKind::Builtin(*b), NirKind::Num(l) => ExprKind::Num(l.clone()), NirKind::OptionalType(t) => ExprKind::Op(OpKind::App( - Nir::from_builtin(Builtin::Optional).to_hir(venv), + builtin(Builtin::Optional), t.to_hir(venv), )), NirKind::EmptyOptionalLit(n) => ExprKind::Op(OpKind::App( - Nir::from_builtin(Builtin::OptionalNone).to_hir(venv), + builtin(Builtin::OptionalNone), n.to_hir(venv), )), NirKind::NEOptionalLit(n) => ExprKind::SomeLit(n.to_hir(venv)), NirKind::ListType(t) => ExprKind::Op(OpKind::App( - Nir::from_builtin(Builtin::List).to_hir(venv), + builtin(Builtin::List), t.to_hir(venv), )), NirKind::EmptyListLit(n) => ExprKind::EmptyListLit(Hir::new( HirKind::Expr(ExprKind::Op(OpKind::App( - Nir::from_builtin(Builtin::List).to_hir(venv), + builtin(Builtin::List), n.to_hir(venv), ))), Span::Artificial, @@ -276,52 +277,52 @@ impl Nir { Hir::new(hir, Span::Artificial) } - pub fn to_hir_noenv(&self) -> Hir { + pub fn to_hir_noenv(&self) -> Hir<'cx> { self.to_hir(VarEnv::new()) } } -impl NirKind { - pub fn into_nir(self) -> Nir { +impl<'cx> NirKind<'cx> { + pub fn into_nir(self) -> Nir<'cx> { Nir::from_kind(self) } - pub fn from_builtin(b: Builtin) -> NirKind { - NirKind::from_builtin_env(b, NzEnv::new()) + pub fn from_builtin(cx: Ctxt<'cx>, b: Builtin) -> Self { + NirKind::from_builtin_env(b, NzEnv::new(cx)) } - pub fn from_builtin_env(b: Builtin, env: NzEnv) -> NirKind { + pub fn from_builtin_env(b: Builtin, env: NzEnv<'cx>) -> Self { BuiltinClosure::new(b, env) } } -impl Thunk { - fn new(env: NzEnv, body: Hir) -> Self { +impl<'cx> Thunk<'cx> { + fn new(env: NzEnv<'cx>, body: Hir<'cx>) -> Self { Thunk::Thunk { env, body } } - fn from_partial_expr(env: NzEnv, expr: ExprKind) -> Self { - Thunk::PartialExpr { env, expr } + fn from_partial_expr(expr: ExprKind>) -> Self { + Thunk::PartialExpr { expr } } - fn eval(self) -> NirKind { + fn eval(self) -> NirKind<'cx> { match self { - Thunk::Thunk { env, body } => normalize_hir(&env, &body), - Thunk::PartialExpr { env, expr } => normalize_one_layer(expr, &env), + Thunk::Thunk { env, body, .. } => normalize_hir(&env, &body), + Thunk::PartialExpr { expr } => normalize_one_layer(expr), } } } -impl Closure { - pub fn new(env: &NzEnv, body: Hir) -> Self { +impl<'cx> Closure<'cx> { + pub fn new(env: &NzEnv<'cx>, body: Hir<'cx>) -> Self { Closure::Closure { env: env.clone(), body, } } /// New closure that ignores its argument - pub fn new_constant(body: Nir) -> Self { + pub fn new_constant(body: Nir<'cx>) -> Self { Closure::ConstantClosure { body } } - pub fn apply(&self, val: Nir) -> Nir { + pub fn apply(&self, val: Nir<'cx>) -> Nir<'cx> { match self { Closure::Closure { env, body, .. } => { body.eval(env.insert_value(val, ())) @@ -329,7 +330,7 @@ impl Closure { Closure::ConstantClosure { body, .. } => body.clone(), } } - fn apply_var(&self, var: NzVar) -> Nir { + fn apply_var(&self, var: NzVar) -> Nir<'cx> { match self { Closure::Closure { .. } => { self.apply(Nir::from_kind(NirKind::Var(var))) @@ -339,13 +340,13 @@ impl Closure { } /// Convert this closure to a Hir expression - pub fn to_hir(&self, venv: VarEnv) -> Hir { + pub fn to_hir(&self, venv: VarEnv) -> Hir<'cx> { self.apply_var(NzVar::new(venv.size())) .to_hir(venv.insert()) } /// If the closure variable is free in the closure, return Err. Otherwise, return the value /// with that free variable remove. - pub fn remove_binder(&self) -> Result { + pub fn remove_binder(&self) -> Result, ()> { match self { Closure::Closure { .. } => { let v = NzVar::fresh(); @@ -358,20 +359,20 @@ impl Closure { } } -impl TextLit { +impl<'cx> TextLit<'cx> { pub fn new( - elts: impl Iterator>, + elts: impl Iterator>>, ) -> Self { TextLit(squash_textlit(elts)) } - pub fn interpolate(v: Nir) -> TextLit { + pub fn interpolate(v: Nir<'cx>) -> Self { TextLit(vec![InterpolatedTextContents::Expr(v)]) } - pub fn from_text(s: String) -> TextLit { + pub fn from_text(s: String) -> Self { TextLit(vec![InterpolatedTextContents::Text(s)]) } - pub fn concat(&self, other: &TextLit) -> TextLit { + pub fn concat(&self, other: &Self) -> Self { TextLit::new(self.iter().chain(other.iter()).cloned()) } pub fn is_empty(&self) -> bool { @@ -379,7 +380,7 @@ impl TextLit { } /// If the literal consists of only one interpolation and not text, return the interpolated /// value. - pub fn as_single_expr(&self) -> Option<&Nir> { + pub fn as_single_expr(&self) -> Option<&Nir<'cx>> { use InterpolatedTextContents::Expr; if let [Expr(v)] = self.0.as_slice() { Some(v) @@ -398,43 +399,45 @@ impl TextLit { None } } - pub fn iter(&self) -> impl Iterator> { + pub fn iter( + &self, + ) -> impl Iterator>> { self.0.iter() } } -impl lazy::Eval for Thunk { - fn eval(self) -> NirKind { +impl<'cx> lazy::Eval> for Thunk<'cx> { + fn eval(self) -> NirKind<'cx> { self.eval() } } /// Compare two values for equality modulo alpha/beta-equivalence. -impl std::cmp::PartialEq for Nir { +impl<'cx> std::cmp::PartialEq for Nir<'cx> { fn eq(&self, other: &Self) -> bool { Rc::ptr_eq(&self.0, &other.0) || self.kind() == other.kind() } } -impl std::cmp::Eq for Nir {} +impl<'cx> std::cmp::Eq for Nir<'cx> {} -impl std::cmp::PartialEq for Thunk { +impl<'cx> std::cmp::PartialEq for Thunk<'cx> { fn eq(&self, _other: &Self) -> bool { unreachable!( "Trying to compare thunks but we should only compare WHNFs" ) } } -impl std::cmp::Eq for Thunk {} +impl<'cx> std::cmp::Eq for Thunk<'cx> {} -impl std::cmp::PartialEq for Closure { +impl<'cx> std::cmp::PartialEq for Closure<'cx> { fn eq(&self, other: &Self) -> bool { let v = NzVar::fresh(); self.apply_var(v) == other.apply_var(v) } } -impl std::cmp::Eq for Closure {} +impl<'cx> std::cmp::Eq for Closure<'cx> {} -impl std::fmt::Debug for Nir { +impl<'cx> std::fmt::Debug for Nir<'cx> { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if let NirKind::Const(c) = self.kind() { return write!(fmt, "{:?}", c); diff --git a/dhall/src/semantics/nze/normalize.rs b/dhall/src/semantics/nze/normalize.rs index 62efc5ff..59710d1c 100644 --- a/dhall/src/semantics/nze/normalize.rs +++ b/dhall/src/semantics/nze/normalize.rs @@ -5,7 +5,7 @@ use crate::semantics::NzEnv; use crate::semantics::{Binder, Closure, Hir, HirKind, Nir, NirKind, TextLit}; use crate::syntax::{ExprKind, InterpolatedTextContents}; -pub fn apply_any(f: &Nir, a: Nir) -> NirKind { +pub fn apply_any<'cx>(f: &Nir<'cx>, a: Nir<'cx>) -> NirKind<'cx> { match f.kind() { NirKind::LamClosure { closure, .. } => closure.apply(a).kind().clone(), NirKind::AppliedBuiltin(closure) => closure.apply(a), @@ -16,16 +16,16 @@ pub fn apply_any(f: &Nir, a: Nir) -> NirKind { } } -pub fn squash_textlit( - elts: impl Iterator>, -) -> Vec> { +pub fn squash_textlit<'cx>( + elts: impl Iterator>>, +) -> Vec>> { use std::mem::replace; use InterpolatedTextContents::{Expr, Text}; - fn inner( - elts: impl Iterator>, + fn inner<'cx>( + elts: impl Iterator>>, crnt_str: &mut String, - ret: &mut Vec>, + ret: &mut Vec>>, ) { for contents in elts { match contents { @@ -81,22 +81,22 @@ where kvs } -pub type Ret = NirKind; +pub type Ret<'cx> = NirKind<'cx>; -pub fn ret_nir(x: Nir) -> Ret { +pub fn ret_nir<'cx>(x: Nir<'cx>) -> Ret<'cx> { x.into_kind() } -pub fn ret_kind(x: NirKind) -> Ret { +pub fn ret_kind<'cx>(x: NirKind<'cx>) -> Ret<'cx> { x } -pub fn ret_ref(x: &Nir) -> Ret { +pub fn ret_ref<'cx>(x: &Nir<'cx>) -> Ret<'cx> { x.kind().clone() } -pub fn ret_op(x: OpKind) -> Ret { +pub fn ret_op<'cx>(x: OpKind>) -> Ret<'cx> { NirKind::Op(x) } -pub fn normalize_one_layer(expr: ExprKind, env: &NzEnv) -> NirKind { +pub fn normalize_one_layer<'cx>(expr: ExprKind>) -> NirKind<'cx> { use NirKind::{ Assert, Const, NEListLit, NEOptionalLit, Num, RecordLit, RecordType, UnionType, @@ -106,15 +106,13 @@ pub fn normalize_one_layer(expr: ExprKind, env: &NzEnv) -> NirKind { ExprKind::Var(..) | ExprKind::Lam(..) | ExprKind::Pi(..) - | ExprKind::Let(..) => { + | ExprKind::Let(..) + | ExprKind::Builtin(..) => { unreachable!("This case should have been handled in normalize_hir") } ExprKind::Const(c) => ret_kind(Const(c)), ExprKind::Num(l) => ret_kind(Num(l)), - ExprKind::Builtin(b) => { - ret_kind(NirKind::from_builtin_env(b, env.clone())) - } ExprKind::TextLit(elts) => { let tlit = TextLit::new(elts.into_iter()); // Simplify bare interpolation @@ -154,10 +152,22 @@ pub fn normalize_one_layer(expr: ExprKind, env: &NzEnv) -> NirKind { } /// Normalize Hir into WHNF -pub fn normalize_hir(env: &NzEnv, hir: &Hir) -> NirKind { +pub fn normalize_hir<'cx>(env: &NzEnv<'cx>, hir: &Hir<'cx>) -> NirKind<'cx> { match hir.kind() { + HirKind::MissingVar(..) => unreachable!("ruled out by typechecking"), HirKind::Var(var) => env.lookup_val(*var), - HirKind::Import(hir, _) => normalize_hir(env, hir), + HirKind::Import(import) => { + let typed = env.cx()[import].unwrap_result(); + normalize_hir(env, &typed.hir) + } + HirKind::ImportAlternative(alt, left, right) => { + let hir = if env.cx()[alt].unwrap_selected() { + left + } else { + right + }; + normalize_hir(env, hir) + } HirKind::Expr(ExprKind::Lam(binder, annot, body)) => { let annot = annot.eval(env); NirKind::LamClosure { @@ -178,9 +188,12 @@ pub fn normalize_hir(env: &NzEnv, hir: &Hir) -> NirKind { let val = val.eval(env); body.eval(env.insert_value(val, ())).kind().clone() } + HirKind::Expr(ExprKind::Builtin(b)) => { + NirKind::from_builtin_env(*b, env.clone()) + } HirKind::Expr(e) => { let e = e.map_ref(|hir| hir.eval(env)); - normalize_one_layer(e, env) + normalize_one_layer(e) } } } diff --git a/dhall/src/semantics/resolve/cache.rs b/dhall/src/semantics/resolve/cache.rs index b00a2d51..1fc8dd3a 100644 --- a/dhall/src/semantics/resolve/cache.rs +++ b/dhall/src/semantics/resolve/cache.rs @@ -5,7 +5,7 @@ use std::path::{Path, PathBuf}; use crate::error::{CacheError, Error}; use crate::parse::parse_binary; use crate::syntax::{binary, Hash}; -use crate::Typed; +use crate::{Ctxt, Typed}; use std::ffi::OsStr; use std::fs::File; @@ -52,9 +52,13 @@ impl Cache { self.cache_dir.join(filename_for_hash(hash)) } - pub fn get(&self, hash: &Hash) -> Result { + pub fn get<'cx>( + &self, + cx: Ctxt<'cx>, + hash: &Hash, + ) -> Result, Error> { let path = self.entry_path(hash); - let res = read_cache_file(&path, hash); + let res = read_cache_file(cx, &path, hash); if res.is_err() && path.exists() { // Delete cache file since it's invalid. We ignore the error. let _ = std::fs::remove_file(&path); @@ -62,14 +66,23 @@ impl Cache { res } - pub fn insert(&self, hash: &Hash, expr: &Typed) -> Result<(), Error> { + pub fn insert<'cx>( + &self, + cx: Ctxt<'cx>, + hash: &Hash, + expr: &Typed<'cx>, + ) -> Result<(), Error> { let path = self.entry_path(hash); - write_cache_file(&path, expr) + write_cache_file(cx, &path, expr) } } /// Read a file from the cache, also checking that its hash is valid. -fn read_cache_file(path: &Path, hash: &Hash) -> Result { +fn read_cache_file<'cx>( + cx: Ctxt<'cx>, + path: &Path, + hash: &Hash, +) -> Result, Error> { let data = crate::utils::read_binary_file(path)?; match hash { @@ -81,12 +94,16 @@ fn read_cache_file(path: &Path, hash: &Hash) -> Result { } } - Ok(parse_binary(&data)?.skip_resolve()?.typecheck()?) + Ok(parse_binary(&data)?.resolve(cx)?.typecheck(cx)?) } /// Write a file to the cache. -fn write_cache_file(path: &Path, expr: &Typed) -> Result<(), Error> { - let data = binary::encode(&expr.to_expr())?; +fn write_cache_file<'cx>( + cx: Ctxt<'cx>, + path: &Path, + expr: &Typed<'cx>, +) -> Result<(), Error> { + let data = binary::encode(&expr.to_expr(cx))?; File::create(path)?.write_all(data.as_slice())?; Ok(()) } diff --git a/dhall/src/semantics/resolve/env.rs b/dhall/src/semantics/resolve/env.rs index 29dd16bf..bde99d17 100644 --- a/dhall/src/semantics/resolve/env.rs +++ b/dhall/src/semantics/resolve/env.rs @@ -1,9 +1,9 @@ use std::collections::HashMap; use crate::error::{Error, ImportError}; -use crate::semantics::{AlphaVar, Cache, ImportLocation, VarEnv}; +use crate::semantics::{check_hash, AlphaVar, Cache, ImportLocation, VarEnv}; use crate::syntax::{Hash, Label, V}; -use crate::Typed; +use crate::{Ctxt, ImportId, ImportResultId, Typed}; /// Environment for resolving names. #[derive(Debug, Clone, Default)] @@ -11,14 +11,13 @@ pub struct NameEnv { names: Vec