From c785b7c0c6cd8b3b1cc15eb79caf982a757020ba Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 6 Dec 2020 23:55:21 +0000 Subject: Thread cx through normalization --- dhall/src/semantics/nze/env.rs | 42 ++++--- dhall/src/semantics/nze/nir.rs | 197 +++++++++++++++++---------------- dhall/src/semantics/nze/normalize.rs | 39 +++---- dhall/src/semantics/resolve/cache.rs | 24 +++- dhall/src/semantics/resolve/env.rs | 13 ++- dhall/src/semantics/resolve/hir.rs | 42 +++---- dhall/src/semantics/resolve/resolve.rs | 32 ++++-- dhall/src/semantics/tck/env.rs | 16 +-- dhall/src/semantics/tck/tir.rs | 60 +++++----- dhall/src/semantics/tck/typecheck.rs | 60 +++++----- 10 files changed, 287 insertions(+), 238 deletions(-) (limited to 'dhall/src/semantics') diff --git a/dhall/src/semantics/nze/env.rs b/dhall/src/semantics/nze/env.rs index ec99dbe..30fa8af 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 7bda836..538b8ea 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_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 62efc5f..cce421d 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,7 +152,7 @@ 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::Var(var) => env.lookup_val(*var), HirKind::Import(hir, _) => normalize_hir(env, hir), @@ -178,9 +176,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 b00a2d5..59463dd 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,22 @@ impl Cache { res } - pub fn insert(&self, hash: &Hash, expr: &Typed) -> Result<(), Error> { + pub fn insert<'cx>( + &self, + hash: &Hash, + expr: &Typed<'cx>, + ) -> Result<(), Error> { let path = self.entry_path(hash); write_cache_file(&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,7 +93,7 @@ fn read_cache_file(path: &Path, hash: &Hash) -> Result { } } - Ok(parse_binary(&data)?.skip_resolve()?.typecheck()?) + Ok(parse_binary(&data)?.skip_resolve()?.typecheck(cx)?) } /// Write a file to the cache. diff --git a/dhall/src/semantics/resolve/env.rs b/dhall/src/semantics/resolve/env.rs index 82f21e8..2b998f8 100644 --- a/dhall/src/semantics/resolve/env.rs +++ b/dhall/src/semantics/resolve/env.rs @@ -86,9 +86,12 @@ impl<'cx> ImportEnv<'cx> { Some(*self.mem_cache.get(location)?) } - pub fn get_from_disk_cache(&self, hash: &Option) -> Option { + pub fn get_from_disk_cache( + &self, + hash: &Option, + ) -> Option> { let hash = hash.as_ref()?; - let expr = self.disk_cache.as_ref()?.get(hash).ok()?; + let expr = self.disk_cache.as_ref()?.get(self.cx(), hash).ok()?; Some(expr) } @@ -100,7 +103,7 @@ impl<'cx> ImportEnv<'cx> { self.mem_cache.insert(location, result); } - pub fn write_to_disk_cache(&self, hash: &Option, expr: &Typed) { + pub fn write_to_disk_cache(&self, hash: &Option, expr: &Typed<'cx>) { if let Some(disk_cache) = self.disk_cache.as_ref() { if let Some(hash) = hash { let _ = disk_cache.insert(hash, &expr); @@ -111,8 +114,8 @@ impl<'cx> ImportEnv<'cx> { pub fn with_cycle_detection( &mut self, location: ImportLocation, - do_resolve: impl FnOnce(&mut Self) -> Result, - ) -> Result { + do_resolve: impl FnOnce(&mut Self) -> Result, Error>, + ) -> Result, Error> { if self.stack.contains(&location) { return Err( ImportError::ImportCycle(self.stack.clone(), location).into() diff --git a/dhall/src/semantics/resolve/hir.rs b/dhall/src/semantics/resolve/hir.rs index c2d1883..c67ab75 100644 --- a/dhall/src/semantics/resolve/hir.rs +++ b/dhall/src/semantics/resolve/hir.rs @@ -10,19 +10,19 @@ pub struct AlphaVar { } #[derive(Debug, Clone, PartialEq, Eq)] -pub enum HirKind { +pub enum HirKind<'cx> { /// A resolved variable (i.e. a DeBruijn index) Var(AlphaVar), /// Result of resolving an import. - Import(Hir, Type), + Import(Hir<'cx>, Type<'cx>), // Forbidden ExprKind variants: Var, Import, Completion - Expr(ExprKind), + Expr(ExprKind>), } // An expression with resolved variables and imports. #[derive(Debug, Clone)] -pub struct Hir { - kind: Box, +pub struct Hir<'cx> { + kind: Box>, span: Span, } @@ -35,15 +35,15 @@ impl AlphaVar { } } -impl Hir { - pub fn new(kind: HirKind, span: Span) -> Self { +impl<'cx> Hir<'cx> { + pub fn new(kind: HirKind<'cx>, span: Span) -> Self { Hir { kind: Box::new(kind), span, } } - pub fn kind(&self) -> &HirKind { + pub fn kind(&self) -> &HirKind<'cx> { &*self.kind } pub fn span(&self) -> Span { @@ -63,7 +63,7 @@ impl Hir { let opts = ToExprOptions { alpha: true }; self.to_expr(opts) } - pub fn to_expr_tyenv(&self, env: &TyEnv) -> Expr { + pub fn to_expr_tyenv(&self, env: &TyEnv<'cx>) -> Expr { let opts = ToExprOptions { alpha: false }; let mut env = env.as_nameenv().clone(); hir_to_expr(self, opts, &mut env) @@ -72,29 +72,33 @@ impl Hir { /// Typecheck the Hir. pub fn typecheck<'hir>( &'hir self, - env: &TyEnv, - ) -> Result, TypeError> { + env: &TyEnv<'cx>, + ) -> Result, TypeError> { type_with(env, self, None) } pub fn typecheck_noenv<'hir>( &'hir self, - cx: Ctxt<'_>, - ) -> Result, TypeError> { + cx: Ctxt<'cx>, + ) -> Result, TypeError> { self.typecheck(&TyEnv::new(cx)) } /// Eval the Hir. It will actually get evaluated only as needed on demand. - pub fn eval(&self, env: impl Into) -> Nir { + pub fn eval(&self, env: impl Into>) -> Nir<'cx> { Nir::new_thunk(env.into(), self.clone()) } /// Eval a closed Hir (i.e. without free variables). It will actually get evaluated only as /// needed on demand. - pub fn eval_closed_expr(&self) -> Nir { - self.eval(NzEnv::new()) + pub fn eval_closed_expr(&self, cx: Ctxt<'cx>) -> Nir<'cx> { + self.eval(NzEnv::new(cx)) } } -fn hir_to_expr(hir: &Hir, opts: ToExprOptions, env: &mut NameEnv) -> Expr { +fn hir_to_expr<'cx>( + hir: &Hir<'cx>, + opts: ToExprOptions, + env: &mut NameEnv, +) -> Expr { let kind = match hir.kind() { HirKind::Var(v) if opts.alpha => ExprKind::Var(V("_".into(), v.idx())), HirKind::Var(v) => ExprKind::Var(env.label_var(*v)), @@ -127,9 +131,9 @@ fn hir_to_expr(hir: &Hir, opts: ToExprOptions, env: &mut NameEnv) -> Expr { Expr::new(kind, hir.span()) } -impl std::cmp::PartialEq for Hir { +impl<'cx> std::cmp::PartialEq for Hir<'cx> { fn eq(&self, other: &Self) -> bool { self.kind == other.kind } } -impl std::cmp::Eq for Hir {} +impl<'cx> std::cmp::Eq for Hir<'cx> {} diff --git a/dhall/src/semantics/resolve/resolve.rs b/dhall/src/semantics/resolve/resolve.rs index cf72f80..488c516 100644 --- a/dhall/src/semantics/resolve/resolve.rs +++ b/dhall/src/semantics/resolve/resolve.rs @@ -231,12 +231,13 @@ impl ImportLocation { &self, env: &mut ImportEnv<'cx>, span: Span, - ) -> Result { + ) -> Result, Error> { let (hir, ty) = match self.mode { ImportMode::Code => { let parsed = self.kind.fetch_dhall()?; - let typed = resolve_with_env(env, parsed)?.typecheck()?; - let hir = typed.normalize().to_hir(); + let typed = + resolve_with_env(env, parsed)?.typecheck(env.cx())?; + let hir = typed.normalize(env.cx()).to_hir(); (hir, typed.ty) } ImportMode::RawText => { @@ -245,7 +246,7 @@ impl ImportLocation { HirKind::Expr(ExprKind::TextLit(text.into())), span, ); - (hir, Type::from_builtin(Builtin::Text)) + (hir, Type::from_builtin(env.cx(), Builtin::Text)) } ImportMode::Location => { let expr = self.kind.to_location(); @@ -286,7 +287,11 @@ fn make_aslocation_uniontype() -> Expr { mkexpr(ExprKind::UnionType(union)) } -fn check_hash(import: &Import, typed: &Typed, span: Span) -> Result<(), Error> { +fn check_hash<'cx>( + import: &Import, + typed: &Typed<'cx>, + span: Span, +) -> Result<(), Error> { if let (ImportMode::Code, Some(Hash::SHA256(hash))) = (import.mode, &import.hash) { @@ -338,11 +343,11 @@ fn desugar(expr: &Expr) -> Cow<'_, Expr> { /// Traverse the expression, handling import alternatives and passing /// found imports to the provided function. Also resolving names. -fn traverse_resolve_expr( +fn traverse_resolve_expr<'cx>( name_env: &mut NameEnv, expr: &Expr, - f: &mut impl FnMut(Import, Span) -> Result, -) -> Result { + f: &mut impl FnMut(Import, Span) -> Result, Error>, +) -> Result, Error> { let expr = desugar(expr); Ok(match expr.kind() { ExprKind::Var(var) => match name_env.unlabel_var(&var) { @@ -447,7 +452,7 @@ fn fetch_import<'cx>( fn resolve_with_env<'cx>( env: &mut ImportEnv<'cx>, parsed: Parsed, -) -> Result { +) -> Result, Error> { let Parsed(expr, base_location) = parsed; let resolved = traverse_resolve_expr( &mut NameEnv::new(), @@ -463,17 +468,20 @@ fn resolve_with_env<'cx>( Ok(Resolved(resolved)) } -pub fn resolve(cx: Ctxt<'_>, parsed: Parsed) -> Result { +pub fn resolve<'cx>( + cx: Ctxt<'cx>, + parsed: Parsed, +) -> Result, Error> { resolve_with_env(&mut ImportEnv::new(cx), parsed) } -pub fn skip_resolve_expr(expr: &Expr) -> Result { +pub fn skip_resolve_expr<'cx>(expr: &Expr) -> Result, Error> { traverse_resolve_expr(&mut NameEnv::new(), expr, &mut |import, _span| { Err(ImportError::UnexpectedImport(import).into()) }) } -pub fn skip_resolve(parsed: Parsed) -> Result { +pub fn skip_resolve<'cx>(parsed: Parsed) -> Result, Error> { let Parsed(expr, _) = parsed; let resolved = skip_resolve_expr(&expr)?; Ok(Resolved(resolved)) diff --git a/dhall/src/semantics/tck/env.rs b/dhall/src/semantics/tck/env.rs index fc0ce9f..fdcc62d 100644 --- a/dhall/src/semantics/tck/env.rs +++ b/dhall/src/semantics/tck/env.rs @@ -13,7 +13,7 @@ pub struct VarEnv { pub struct TyEnv<'cx> { cx: Ctxt<'cx>, names: NameEnv, - items: ValEnv, + items: ValEnv<'cx, Type<'cx>>, } impl VarEnv { @@ -45,7 +45,7 @@ impl<'cx> TyEnv<'cx> { TyEnv { cx, names: NameEnv::new(), - items: ValEnv::new(), + items: ValEnv::new(cx), } } pub fn cx(&self) -> Ctxt<'cx> { @@ -54,34 +54,34 @@ impl<'cx> TyEnv<'cx> { pub fn as_varenv(&self) -> VarEnv { self.names.as_varenv() } - pub fn to_nzenv(&self) -> NzEnv { + pub fn to_nzenv(&self) -> NzEnv<'cx> { self.items.discard_types() } pub fn as_nameenv(&self) -> &NameEnv { &self.names } - pub fn insert_type(&self, x: &Label, ty: Type) -> Self { + pub fn insert_type(&self, x: &Label, ty: Type<'cx>) -> Self { TyEnv { cx: self.cx, names: self.names.insert(x), items: self.items.insert_type(ty), } } - pub fn insert_value(&self, x: &Label, e: Nir, ty: Type) -> Self { + pub fn insert_value(&self, x: &Label, e: Nir<'cx>, ty: Type<'cx>) -> Self { TyEnv { cx: self.cx, names: self.names.insert(x), items: self.items.insert_value(e, ty), } } - pub fn lookup(&self, var: AlphaVar) -> Type { + pub fn lookup(&self, var: AlphaVar) -> Type<'cx> { self.items.lookup_ty(var) } } -impl<'a, 'cx> From<&'a TyEnv<'cx>> for NzEnv { - fn from(x: &'a TyEnv) -> Self { +impl<'a, 'cx> From<&'a TyEnv<'cx>> for NzEnv<'cx> { + fn from(x: &'a TyEnv<'cx>) -> Self { x.to_nzenv() } } diff --git a/dhall/src/semantics/tck/tir.rs b/dhall/src/semantics/tck/tir.rs index f34802c..7b04f57 100644 --- a/dhall/src/semantics/tck/tir.rs +++ b/dhall/src/semantics/tck/tir.rs @@ -2,6 +2,7 @@ use crate::builtins::Builtin; use crate::error::{ErrorBuilder, TypeError}; use crate::semantics::{mkerr, Hir, Nir, NirKind, NzEnv, TyEnv, VarEnv}; use crate::syntax::{Const, Expr, Span}; +use crate::Ctxt; /// The type of a type. 0 is `Type`, 1 is `Kind`, etc... #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)] @@ -9,17 +10,17 @@ pub struct Universe(u8); /// An expression representing a type #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Type { - val: Nir, +pub struct Type<'cx> { + val: Nir<'cx>, univ: Universe, } /// A hir expression plus its inferred type. /// Stands for "Typed intermediate representation" #[derive(Debug, Clone)] -pub struct Tir<'hir> { - hir: &'hir Hir, - ty: Type, +pub struct Tir<'cx, 'hir> { + hir: &'hir Hir<'cx>, + ty: Type<'cx>, } impl Universe { @@ -43,16 +44,16 @@ impl Universe { } } -impl Type { - pub fn new(val: Nir, univ: Universe) -> Self { +impl<'cx> Type<'cx> { + pub fn new(val: Nir<'cx>, univ: Universe) -> Self { Type { val, univ } } /// Creates a new Type and infers its universe by re-typechecking its value. /// TODO: ideally avoid this function altogether. Would need to store types in RecordType and /// PiClosure. pub fn new_infer_universe( - env: &TyEnv, - val: Nir, + env: &TyEnv<'cx>, + val: Nir<'cx>, ) -> Result { let c = val.to_hir(env.as_varenv()).typecheck(env)?.ty().as_const(); let u = match c { @@ -67,14 +68,14 @@ impl Type { pub fn from_const(c: Const) -> Self { Self::new(Nir::from_const(c), c.to_universe().next()) } - pub fn from_builtin(b: Builtin) -> Self { + pub fn from_builtin(cx: Ctxt<'cx>, b: Builtin) -> Self { use Builtin::*; match b { Bool | Natural | Integer | Double | Text => {} _ => unreachable!("this builtin is not a type: {}", b), } - Self::new(Nir::from_builtin(b), Universe::from_const(Const::Type)) + Self::new(Nir::from_builtin(cx, b), Universe::from_const(Const::Type)) } /// Get the type of this type @@ -82,60 +83,60 @@ impl Type { self.univ } - pub fn to_nir(&self) -> Nir { + pub fn to_nir(&self) -> Nir<'cx> { self.val.clone() } - pub fn as_nir(&self) -> &Nir { + pub fn as_nir(&self) -> &Nir<'cx> { &self.val } - pub fn into_nir(self) -> Nir { + pub fn into_nir(self) -> Nir<'cx> { self.val } pub fn as_const(&self) -> Option { self.val.as_const() } - pub fn kind(&self) -> &NirKind { + pub fn kind(&self) -> &NirKind<'cx> { self.val.kind() } - pub fn to_hir(&self, venv: VarEnv) -> Hir { + pub fn to_hir(&self, venv: VarEnv) -> Hir<'cx> { self.val.to_hir(venv) } - pub fn to_expr_tyenv(&self, tyenv: &TyEnv) -> Expr { + pub fn to_expr_tyenv(&self, tyenv: &TyEnv<'cx>) -> Expr { self.val.to_hir(tyenv.as_varenv()).to_expr_tyenv(tyenv) } } -impl<'hir> Tir<'hir> { - pub fn from_hir(hir: &'hir Hir, ty: Type) -> Self { +impl<'cx, 'hir> Tir<'cx, 'hir> { + pub fn from_hir(hir: &'hir Hir<'cx>, ty: Type<'cx>) -> Self { Tir { hir, ty } } pub fn span(&self) -> Span { self.as_hir().span() } - pub fn ty(&self) -> &Type { + pub fn ty(&self) -> &Type<'cx> { &self.ty } - pub fn into_ty(self) -> Type { + pub fn into_ty(self) -> Type<'cx> { self.ty } - pub fn to_hir(&self) -> Hir { + pub fn to_hir(&self) -> Hir<'cx> { self.as_hir().clone() } - pub fn as_hir(&self) -> &Hir { - &self.hir + pub fn as_hir(&self) -> &'hir Hir<'cx> { + self.hir } - pub fn to_expr_tyenv(&self, env: &TyEnv) -> Expr { + pub fn to_expr_tyenv(&self, env: &TyEnv<'cx>) -> Expr { self.as_hir().to_expr_tyenv(env) } /// Eval the Tir. It will actually get evaluated only as needed on demand. - pub fn eval(&self, env: impl Into) -> Nir { + pub fn eval(&self, env: impl Into>) -> Nir<'cx> { self.as_hir().eval(env.into()) } - pub fn ensure_is_type(&self, env: &TyEnv) -> Result<(), TypeError> { + pub fn ensure_is_type(&self, env: &TyEnv<'cx>) -> Result<(), TypeError> { if self.ty().as_const().is_none() { return mkerr( ErrorBuilder::new(format!( @@ -159,7 +160,10 @@ impl<'hir> Tir<'hir> { Ok(()) } /// Evaluate to a Type. - pub fn eval_to_type(&self, env: &TyEnv) -> Result { + pub fn eval_to_type( + &self, + env: &TyEnv<'cx>, + ) -> Result, TypeError> { self.ensure_is_type(env)?; Ok(Type::new( self.eval(env), diff --git a/dhall/src/semantics/tck/typecheck.rs b/dhall/src/semantics/tck/typecheck.rs index 7e8c0e1..8218368 100644 --- a/dhall/src/semantics/tck/typecheck.rs +++ b/dhall/src/semantics/tck/typecheck.rs @@ -29,11 +29,12 @@ pub fn mk_span_err(span: Span, msg: S) -> Result { /// When all sub-expressions have been typed, check the remaining toplevel /// layer. -fn type_one_layer( - env: &TyEnv<'_>, - ekind: ExprKind>, +fn type_one_layer<'cx>( + env: &TyEnv<'cx>, + ekind: ExprKind>, span: Span, -) -> Result { +) -> Result, TypeError> { + let cx = env.cx(); let span_err = |msg: &str| mk_span_err(span.clone(), msg); Ok(match ekind { @@ -51,18 +52,21 @@ fn type_one_layer( ExprKind::Const(Const::Type) => Type::from_const(Const::Kind), ExprKind::Const(Const::Kind) => Type::from_const(Const::Sort), - ExprKind::Num(num) => Type::from_builtin(match num { - NumKind::Bool(_) => Builtin::Bool, - NumKind::Natural(_) => Builtin::Natural, - NumKind::Integer(_) => Builtin::Integer, - NumKind::Double(_) => Builtin::Double, - }), + ExprKind::Num(num) => Type::from_builtin( + cx, + match num { + NumKind::Bool(_) => Builtin::Bool, + NumKind::Natural(_) => Builtin::Natural, + NumKind::Integer(_) => Builtin::Integer, + NumKind::Double(_) => Builtin::Double, + }, + ), ExprKind::Builtin(b) => { let t_hir = type_of_builtin(b); typecheck(env.cx(), &t_hir)?.eval_to_type(env)? } ExprKind::TextLit(interpolated) => { - let text_type = Type::from_builtin(Builtin::Text); + let text_type = Type::from_builtin(cx, Builtin::Text); for contents in interpolated.iter() { use InterpolatedTextContents::Expr; if let Expr(x) = contents { @@ -79,7 +83,7 @@ fn type_one_layer( } let t = x.ty().to_nir(); - Nir::from_builtin(Builtin::Optional) + Nir::from_builtin(cx, Builtin::Optional) .app(t) .to_type(Const::Type) } @@ -104,7 +108,9 @@ fn type_one_layer( } let t = x.ty().to_nir(); - Nir::from_builtin(Builtin::List).app(t).to_type(Const::Type) + Nir::from_builtin(cx, Builtin::List) + .app(t) + .to_type(Const::Type) } ExprKind::RecordLit(kvs) => { // An empty record type has type Type @@ -171,11 +177,11 @@ fn type_one_layer( /// to compare with. // We pass the annotation to avoid duplicating the annot checking logic. I hope one day we can use // it to handle the annotations in merge/toMap/etc. uniformly. -pub fn type_with<'hir>( - env: &TyEnv<'_>, - hir: &'hir Hir, - annot: Option, -) -> Result, TypeError> { +pub fn type_with<'cx, 'hir>( + env: &TyEnv<'cx>, + hir: &'hir Hir<'cx>, + annot: Option>, +) -> Result, TypeError> { let tir = match hir.kind() { HirKind::Var(var) => Tir::from_hir(hir, env.lookup(*var)), HirKind::Import(_, ty) => Tir::from_hir(hir, ty.clone()), @@ -268,19 +274,19 @@ pub fn type_with<'hir>( /// Typecheck an expression and return the expression annotated with its type if type-checking /// succeeded, or an error if type-checking failed. -pub fn typecheck<'hir>( - cx: Ctxt<'_>, - hir: &'hir Hir, -) -> Result, TypeError> { +pub fn typecheck<'cx, 'hir>( + cx: Ctxt<'cx>, + hir: &'hir Hir<'cx>, +) -> Result, TypeError> { type_with(&TyEnv::new(cx), hir, None) } /// Like `typecheck`, but additionally checks that the expression's type matches the provided type. -pub fn typecheck_with<'hir>( - cx: Ctxt<'_>, - hir: &'hir Hir, - ty: &Hir, -) -> Result, TypeError> { +pub fn typecheck_with<'cx, 'hir>( + cx: Ctxt<'cx>, + hir: &'hir Hir<'cx>, + ty: &Hir<'cx>, +) -> Result, TypeError> { let ty = typecheck(cx, ty)?.eval_to_type(&TyEnv::new(cx))?; type_with(&TyEnv::new(cx), hir, Some(ty)) } -- cgit v1.2.3