diff --git a/kernel/src/calculus/term.rs b/kernel/src/calculus/term.rs index 14698eda19699c24c419a5d42d437e8212fdad4c..d23443807bbf3113dc67f1cd5ddbcc445ed16416 100644 --- a/kernel/src/calculus/term.rs +++ b/kernel/src/calculus/term.rs @@ -10,119 +10,119 @@ use crate::memory::declaration::InstantiatedDeclaration; use crate::memory::level::Level; use crate::memory::term::{Payload, Term}; -impl<'arena> Arena<'arena> { +impl<'arena> Term<'arena> { /// Apply one step of β-reduction, using the leftmost-outermost evaluation strategy. - pub fn beta_reduction(&mut self, t: Term<'arena>) -> Term<'arena> { - match *t { + pub fn beta_reduction(self, arena: &mut Arena<'arena>) -> Self { + match *self { App(t1, t2) => match *t1 { - Abs(_, t1) => self.substitute(t1, t2, 1), + Abs(_, t1) => t1.substitute(t2, 1, arena), _ => { - let t1_new = self.beta_reduction(t1); + let t1_new = t1.beta_reduction(arena); if t1_new == t1 { - let t2_new = self.beta_reduction(t2); - self.app(t1, t2_new) + let t2_new = t2.beta_reduction(arena); + t1.app(t2_new, arena) } else { - self.app(t1_new, t2) + t1_new.app(t2, arena) } }, }, Abs(arg_type, body) => { - let body = self.beta_reduction(body); - self.abs(arg_type, body) + let body = body.beta_reduction(arena); + arg_type.abs(body, arena) }, Prod(arg_type, body) => { - let body = self.beta_reduction(body); - self.prod(arg_type, body) + let body = body.beta_reduction(arena); + arg_type.prod(body, arena) }, - _ => t, + _ => self, } } /// Returns the term `t` where all variables with de Bruijn index larger than `depth` are offset /// by `offset`. - pub(crate) fn shift(&mut self, t: Term<'arena>, offset: usize, depth: usize) -> Term<'arena> { - match *t { - Var(i, type_) if i > depth.into() => self.var(i + offset.into(), type_), + pub(crate) fn shift(self, offset: usize, depth: usize, arena: &mut Arena<'arena>) -> Self { + match *self { + Var(i, type_) if i > depth.into() => Term::var(i + offset.into(), type_, arena), App(t1, t2) => { - let t1 = self.shift(t1, offset, depth); - let t2 = self.shift(t2, offset, depth); - self.app(t1, t2) + let t1 = t1.shift(offset, depth, arena); + let t2 = t2.shift(offset, depth, arena); + t1.app(t2, arena) }, Abs(arg_type, body) => { - let arg_type = self.shift(arg_type, offset, depth); - let body = self.shift(body, offset, depth + 1); - self.abs(arg_type, body) + let arg_type = arg_type.shift(offset, depth, arena); + let body = body.shift(offset, depth + 1, arena); + arg_type.abs(body, arena) }, Prod(arg_type, body) => { - let arg_type = self.shift(arg_type, offset, depth); - let body = self.shift(body, offset, depth + 1); - self.prod(arg_type, body) + let arg_type = arg_type.shift(offset, depth, arena); + let body = body.shift(offset, depth + 1, arena); + arg_type.prod(body, arena) }, - _ => t, + _ => self, } } /// Returns the term `t` where all instances of the variable tracked by `depth` are substituted /// with `sub`. - pub(crate) fn substitute(&mut self, t: Term<'arena>, sub: Term<'arena>, depth: usize) -> Term<'arena> { - self.get_subst_or_init(&(t, sub, depth), |arena| match *t { - Var(i, _) if i == depth.into() => arena.shift(sub, depth - 1, 0), - Var(i, type_) if i > depth.into() => arena.var(i - 1.into(), type_), + pub(crate) fn substitute(self, sub: Self, depth: usize, arena: &mut Arena<'arena>) -> Self { + arena.get_subst_or_init(&(self, sub, depth), |arena| match *self { + Var(i, _) if i == depth.into() => sub.shift(depth - 1, 0, arena), + Var(i, type_) if i > depth.into() => Term::var(i - 1.into(), type_, arena), App(l, r) => { - let l = arena.substitute(l, sub, depth); - let r = arena.substitute(r, sub, depth); - arena.app(l, r) + let l = l.substitute(sub, depth, arena); + let r = r.substitute(sub, depth, arena); + l.app(r, arena) }, Abs(arg_type, body) => { - let arg_type = arena.substitute(arg_type, sub, depth); - let body = arena.substitute(body, sub, depth + 1); - arena.abs(arg_type, body) + let arg_type = arg_type.substitute(sub, depth, arena); + let body = body.substitute(sub, depth + 1, arena); + arg_type.abs(body, arena) }, Prod(arg_type, body) => { - let arg_type = arena.substitute(arg_type, sub, depth); - let body = arena.substitute(body, sub, depth + 1); - arena.prod(arg_type, body) + let arg_type = arg_type.substitute(sub, depth, arena); + let body = body.substitute(sub, depth + 1, arena); + arg_type.prod(body, arena) }, - _ => t, + _ => self, }) } /// TODO(docs) - pub(crate) fn substitute_univs(&mut self, t: Term<'arena>, univs: &[Level<'arena>]) -> Term<'arena> { - match *t { + pub(crate) fn substitute_univs(self, univs: &[Level<'arena>], arena: &mut Arena<'arena>) -> Self { + match *self { Var(i, ty) => { - let ty = self.substitute_univs(ty, univs); - self.var(i, ty) + let ty = ty.substitute_univs(univs, arena); + Term::var(i, ty, arena) }, Sort(level) => { - let subst = level.substitute(univs, self); - self.sort(subst) + let subst = level.substitute(univs, arena); + Term::sort(subst, arena) }, App(u1, u2) => { - let u1 = self.substitute_univs(u1, univs); - let u2 = self.substitute_univs(u2, univs); - self.app(u1, u2) + let u1 = u1.substitute_univs(univs, arena); + let u2 = u2.substitute_univs(univs, arena); + u1.app(u2, arena) }, Abs(u1, u2) => { - let u1 = self.substitute_univs(u1, univs); - let u2 = self.substitute_univs(u2, univs); - self.abs(u1, u2) + let u1 = u1.substitute_univs(univs, arena); + let u2 = u2.substitute_univs(univs, arena); + u1.abs(u2, arena) }, Prod(u1, u2) => { - let u1 = self.substitute_univs(u1, univs); - let u2 = self.substitute_univs(u2, univs); - self.prod(u1, u2) + let u1 = u1.substitute_univs(univs, arena); + let u2 = u2.substitute_univs(univs, arena); + u1.prod(u2, arena) }, Decl(decl) => { // TODO this can be slightly optimised. Certainly the substitute mapping can be // performed in place while allocating the slice with store_level_slice. This // function thus has to be made with templates. - let params = &*decl.params.into_iter().map(|level| level.substitute(univs, self)).collect::<Vec<Level>>(); - let params = self.store_level_slice(params); - let inst = InstantiatedDeclaration::instantiate((*decl).decl, params, self); - self.decl(inst) + let params = &*decl.params.into_iter().map(|level| level.substitute(univs, arena)).collect::<Vec<Level>>(); + let params = arena.store_level_slice(params); + let inst = InstantiatedDeclaration::instantiate((*decl).decl, params, arena); + Term::decl(inst, arena) }, } } @@ -130,28 +130,28 @@ impl<'arena> Arena<'arena> { /// Returns the normal form of a term. /// /// This function is computationally expensive and should only be used for reduce/eval commands, not when type-checking. - pub fn normal_form(&mut self, t: Term<'arena>) -> Term<'arena> { - let mut temp = t; - let mut res = self.beta_reduction(t); + pub fn normal_form(self, arena: &mut Arena<'arena>) -> Self { + let mut temp = self; + let mut res = self.beta_reduction(arena); while res != temp { temp = res; - res = self.beta_reduction(res); + res = res.beta_reduction(arena); } res } /// Returns the weak-head normal form of a term. - pub fn whnf(&mut self, t: Term<'arena>) -> Term<'arena> { - t.get_whnf_or_init(|| match *t { - App(t1, t2) => match *self.whnf(t1) { + pub fn whnf(self, arena: &mut Arena<'arena>) -> Self { + self.get_whnf_or_init(|| match *self { + App(t1, t2) => match *t1.whnf(arena) { Abs(_, t1) => { - let subst = self.substitute(t1, t2, 1); - self.whnf(subst) + let subst = t1.substitute(t2, 1, arena); + subst.whnf(arena) }, - _ => t, + _ => self, }, - _ => t, + _ => self, }) } } @@ -171,7 +171,7 @@ mod tests { // λx.x x let reduced = arena.build_term_raw(abs(prop(), app(var(1.into(), prop()), var(1.into(), prop())))); - assert_eq!(arena.beta_reduction(term), reduced); + assert_eq!(term.beta_reduction(arena), reduced); }) } @@ -290,14 +290,14 @@ mod tests { // λa.λb.b let term_step_7 = arena.build_term_raw(abs(prop(), abs(prop(), var(1.into(), prop())))); - assert_eq!(arena.beta_reduction(term), term_step_1); - assert_eq!(arena.beta_reduction(term_step_1), term_step_2); - assert_eq!(arena.beta_reduction(term_step_2), term_step_3); - assert_eq!(arena.beta_reduction(term_step_3), term_step_4); - assert_eq!(arena.beta_reduction(term_step_4), term_step_5); - assert_eq!(arena.beta_reduction(term_step_5), term_step_6); - assert_eq!(arena.beta_reduction(term_step_6), term_step_7); - assert_eq!(arena.beta_reduction(term_step_7), term_step_7); + assert_eq!(term.beta_reduction(arena), term_step_1); + assert_eq!(term_step_1.beta_reduction(arena), term_step_2); + assert_eq!(term_step_2.beta_reduction(arena), term_step_3); + assert_eq!(term_step_3.beta_reduction(arena), term_step_4); + assert_eq!(term_step_4.beta_reduction(arena), term_step_5); + assert_eq!(term_step_5.beta_reduction(arena), term_step_6); + assert_eq!(term_step_6.beta_reduction(arena), term_step_7); + assert_eq!(term_step_7.beta_reduction(arena), term_step_7); }) } @@ -307,7 +307,7 @@ mod tests { let reduced = arena.build_term_raw(prod(prop(), var(1.into(), prop()))); let term = arena.build_term_raw(app(abs(prop(), reduced.into()), prop())); - assert_eq!(arena.beta_reduction(term), reduced) + assert_eq!(term.beta_reduction(arena), reduced) }) } @@ -317,7 +317,7 @@ mod tests { let term = arena.build_term_raw(prod(prop(), app(abs(prop(), var(1.into(), prop())), var(1.into(), prop())))); let reduced = arena.build_term_raw(prod(prop(), var(1.into(), prop()))); - assert_eq!(arena.beta_reduction(term), reduced); + assert_eq!(term.beta_reduction(arena), reduced); }) } @@ -328,7 +328,7 @@ mod tests { .build_term_raw(abs(prop(), app(var(1.into(), prop()), app(abs(prop(), var(1.into(), prop())), var(1.into(), prop()))))); let reduced = arena.build_term_raw(abs(prop(), app(var(1.into(), prop()), var(1.into(), prop())))); - assert_eq!(arena.beta_reduction(term), reduced); + assert_eq!(term.beta_reduction(arena), reduced); }) } @@ -339,7 +339,7 @@ mod tests { .build_term_raw(app(app(app(abs(prop(), abs(prop(), abs(prop(), var(1.into(), prop())))), prop()), prop()), prop())); let normal_form = arena.build_term_raw(prop()); - assert_eq!(arena.normal_form(term), normal_form); + assert_eq!(term.normal_form(arena), normal_form); }) } } diff --git a/kernel/src/memory/builders.rs b/kernel/src/memory/builders.rs index 27ff0fda98f5264f9d031536b9f3954766db76ec..e45f7570b7db6846e44001cc30a21059d377055d 100644 --- a/kernel/src/memory/builders.rs +++ b/kernel/src/memory/builders.rs @@ -64,8 +64,8 @@ pub const fn var<'build, 'arena>(name: &'build str) -> impl BuilderTrait<'build, .map(|(bind_depth, term)| { // This is arguably an eager computation, it could be worth making it lazy, // or at least memoizing it so as to not compute it again - let var_type = arena.shift(*term, usize::from(depth - *bind_depth), 0); - arena.var(depth - *bind_depth, var_type) + let var_type = term.shift(usize::from(depth - *bind_depth), 0, arena); + Term::var(depth - *bind_depth, var_type, arena) }) .or_else(|| arena.get_binding(name)) .ok_or(Error { @@ -77,36 +77,28 @@ pub const fn var<'build, 'arena>(name: &'build str) -> impl BuilderTrait<'build, /// Returns a closure building the Prop term. #[inline] pub const fn prop<'build, 'arena>() -> impl BuilderTrait<'build, 'arena> { - |arena: &mut Arena<'arena>, _: &Environment<'build, 'arena>, _| Ok(arena.prop()) + |arena: &mut Arena<'arena>, _: &Environment<'build, 'arena>, _| Ok(Term::prop(arena)) } -/// Returns a closure building the Type `level` term. -#[inline] -pub const fn type_<'build, 'arena>(level: Level<'arena>) -> impl BuilderTrait<'build, 'arena> { - move |arena: &mut Arena<'arena>, _: &Environment<'build, 'arena>, _| Ok(arena.type_(level)) -} - -/// Returns a closure building the Type `level` term (indirection from `usize`). +/// Returns a closure building the Type `i` term, where `i` is an integer #[inline] pub const fn type_usize<'build, 'arena>(level: usize) -> impl BuilderTrait<'build, 'arena> { move |arena: &mut Arena<'arena>, _: &Environment<'build, 'arena>, _| { - let lvl = Level::from(level, arena); - Ok(arena.type_(lvl)) + Ok(Term::type_usize(level, arena)) } } -/// Returns a closure building the Type `level` term. +/// Returns a closure building the Sort `level` term. #[inline] pub const fn sort<'build, 'arena>(level: Level<'arena>) -> impl BuilderTrait<'build, 'arena> { - move |arena: &mut Arena<'arena>, _: &Environment<'build, 'arena>, _| Ok(arena.sort(level)) + move |arena: &mut Arena<'arena>, _: &Environment<'build, 'arena>, _| Ok(Term::sort(level, arena)) } -/// Returns a closure building the Type `level` term (indirection from `usize`). +/// Returns a closure building the Sort `level` term (indirection from `usize`). #[inline] pub const fn sort_usize<'build, 'arena>(level: usize) -> impl BuilderTrait<'build, 'arena> { move |arena: &mut Arena<'arena>, _: &Environment<'build, 'arena>, _| { - let lvl = Level::from(level, arena); - Ok(arena.sort(lvl)) + Ok(Term::sort_usize(level, arena)) } } @@ -121,7 +113,7 @@ pub const fn app<'build, 'arena, F1: BuilderTrait<'build, 'arena>, F2: BuilderTr |arena: &mut Arena<'arena>, env: &Environment<'build, 'arena>, depth| { let u1 = u1(arena, env, depth)?; let u2 = u2(arena, env, depth)?; - Ok(arena.app(u1, u2)) + Ok(u1.app(u2, arena)) } } @@ -138,7 +130,7 @@ pub const fn abs<'build, 'arena, F1: BuilderTrait<'build, 'arena>, F2: BuilderTr let arg_type = arg_type(arena, env, depth)?; let env = env.update(name, (depth, arg_type)); let body = body(arena, &env, depth + 1.into())?; - Ok(arena.abs(arg_type, body)) + Ok(arg_type.abs(body, arena)) } } @@ -155,7 +147,7 @@ pub const fn prod<'build, 'arena, F1: BuilderTrait<'build, 'arena>, F2: BuilderT let arg_type = arg_type(arena, env, depth)?; let env = env.update(name, (depth, arg_type)); let body = body(arena, &env, depth + 1.into())?; - Ok(arena.prod(arg_type, body)) + Ok(arg_type.prod(body, arena)) } } @@ -231,41 +223,41 @@ pub(crate) mod raw { } pub const fn var<'arena, F: BuilderTrait<'arena>>(index: DeBruijnIndex, type_: F) -> impl BuilderTrait<'arena> { - move |env: &mut Arena<'arena>| { - let ty = type_(env); - env.var(index, ty) + move |arena: &mut Arena<'arena>| { + let ty = type_(arena); + Term::var(index, ty, arena) } } pub const fn prop<'arena>() -> impl BuilderTrait<'arena> { - |env: &mut Arena<'arena>| env.prop() + |arena: &mut Arena<'arena>| Term::prop(arena) } - pub const fn type_<'arena>(level: Level<'arena>) -> impl BuilderTrait<'arena> { - move |env: &mut Arena<'arena>| env.type_(level) + pub const fn sort_<'arena>(level: Level<'arena>) -> impl BuilderTrait<'arena> { + move |arena: &mut Arena<'arena>| Term::sort(level, arena) } pub const fn app<'arena, F1: BuilderTrait<'arena>, F2: BuilderTrait<'arena>>(u1: F1, u2: F2) -> impl BuilderTrait<'arena> { - |env: &mut Arena<'arena>| { - let u1 = u1(env); - let u2 = u2(env); - env.app(u1, u2) + |arena: &mut Arena<'arena>| { + let u1 = u1(arena); + let u2 = u2(arena); + u1.app(u1, arena) } } pub const fn abs<'arena, F1: BuilderTrait<'arena>, F2: BuilderTrait<'arena>>(u1: F1, u2: F2) -> impl BuilderTrait<'arena> { - |env: &mut Arena<'arena>| { - let u1 = u1(env); - let u2 = u2(env); - env.abs(u1, u2) + |arena: &mut Arena<'arena>| { + let u1 = u1(arena); + let u2 = u2(arena); + u1.abs(u2, arena) } } pub const fn prod<'arena, F1: BuilderTrait<'arena>, F2: BuilderTrait<'arena>>(u1: F1, u2: F2) -> impl BuilderTrait<'arena> { - |env: &mut Arena<'arena>| { - let u1 = u1(env); - let u2 = u2(env); - env.prod(u1, u2) + |arena: &mut Arena<'arena>| { + let u1 = u1(arena); + let u2 = u2(arena); + u1.prod(u2, arena) } } } diff --git a/kernel/src/memory/term.rs b/kernel/src/memory/term.rs index 0277c820b7ce440feced831256f1f70a67a194d5..25cca8f90baaf1a5f3b07900dcaf9910c5afe58e 100644 --- a/kernel/src/memory/term.rs +++ b/kernel/src/memory/term.rs @@ -10,6 +10,7 @@ use derive_more::{Add, Display, From, Into, Sub}; use super::declaration::InstantiatedDeclaration; use super::level::Level; +use crate::memory::arena::Arena; use crate::error::ResultTerm; /// An index used to designate bound variables. @@ -86,92 +87,101 @@ impl<'arena> fmt::Display for Term<'arena> { use Payload::*; -// TODO make this a Term-impl. This will impact most files from the project, but should not be so -// hard. -impl<'arena> super::arena::Arena<'arena> { +impl<'arena> Term<'arena> { /// This function is the base low-level function for creating terms. /// /// It enforces the uniqueness property of terms in the arena. - fn hashcons_term(&mut self, n: Payload<'arena>) -> Term<'arena> { + fn hashcons(payload: Payload<'arena>, arena: &mut Arena<'arena>) -> Self { // There are concurrent designs here. hashcons could also take a node, which gives // subsequent function some liberty in providing the other objects of the header: WHNF, // type_ (unlikely, because not always desirable), is_certainly_closed. let new_node = Node { - payload: n, + payload, header: Header { head_normal_form: OnceCell::new(), type_: OnceCell::new(), }, }; - match self.hashcons_terms.get(&new_node) { + match arena.hashcons_terms.get(&new_node) { Some(addr) => Term::new(addr), None => { - let addr = self.alloc.alloc(new_node); - self.hashcons_terms.insert(addr); + let addr = arena.alloc.alloc(new_node); + arena.hashcons_terms.insert(addr); Term::new(addr) }, } } /// Returns a variable term with the given index and type - pub(crate) fn var(&mut self, index: DeBruijnIndex, type_: Term<'arena>) -> Term<'arena> { - self.hashcons_term(Var(index, type_)) + pub(crate) fn var(index: DeBruijnIndex, type_: Term<'arena>, arena: &mut Arena<'arena>) -> Self { + Self::hashcons(Var(index, type_), arena) } /// Returns the term corresponding to a proposition - pub(crate) fn prop(&mut self) -> Term<'arena> { - let zero = Level::zero(self); - self.hashcons_term(Sort(zero)) - } - - pub(crate) fn type_(&mut self, level: Level<'arena>) -> Term<'arena> { - let succ = level.succ(self); - self.hashcons_term(Sort(succ)) + pub(crate) fn prop(arena: &mut Arena<'arena>) -> Self { + Self::hashcons(Sort(Level::zero(arena)), arena) } - pub(crate) fn sort(&mut self, level: Level<'arena>) -> Term<'arena> { - self.hashcons_term(Sort(level)) + /// Returns the term associated to the sort of the given level + pub(crate) fn sort(level: Level<'arena>, arena: &mut Arena<'arena>) -> Self { + Self::hashcons(Sort(level), arena) } /// Returns the term corresponding to Type(level), casting level appropriately first - pub(crate) fn type_usize(&mut self, level: usize) -> Term<'arena> { - let level = Level::from(level + 1, self); - self.hashcons_term(Sort(level)) + pub(crate) fn type_usize(level: usize, arena: &mut Arena<'arena>) -> Self { + Self::hashcons(Sort(Level::from(level + 1, arena)), arena) } - /// Returns the term corresponding to Type(level), casting level appropriately first - pub(crate) fn sort_usize(&mut self, level: usize) -> Term<'arena> { - let level = Level::from(level, self); - self.hashcons_term(Sort(level)) + /// Returns the term corresponding to Sort(level), casting level appropriately first + pub(crate) fn sort_usize(level: usize, arena: &mut Arena<'arena>) -> Self { + Self::hashcons(Sort(Level::from(level, arena)), arena) } /// Returns the application of one term to the other - pub(crate) fn app(&mut self, func: Term<'arena>, arg: Term<'arena>) -> Term<'arena> { - self.hashcons_term(App(func, arg)) + pub(crate) fn app(self, arg: Self, arena: &mut Arena<'arena>) -> Self { + Self::hashcons(App(self, arg), arena) } /// Returns the lambda-abstraction of the term `body`, with an argument of type `arg_type`. /// /// Please note that no verification is done that occurrences of this variable in `body` have /// the same type. - pub(crate) fn abs(&mut self, arg_type: Term<'arena>, body: Term<'arena>) -> Term<'arena> { - self.hashcons_term(Abs(arg_type, body)) + pub(crate) fn abs(self, body: Self, arena: &mut Arena<'arena>) -> Self { + Self::hashcons(Abs(self, body), arena) } /// Returns the dependant product of the term `body`, over elements of `arg_type`. /// /// Please note that no verification is done that occurrences of this variable in `body` have /// the same type. - pub(crate) fn prod(&mut self, arg_type: Term<'arena>, body: Term<'arena>) -> Term<'arena> { - self.hashcons_term(Prod(arg_type, body)) + pub(crate) fn prod(self, body: Self, arena: &mut Arena<'arena>) -> Self { + Self::hashcons(Prod(self, body), arena) } - /// TODO(doc) - pub(crate) fn decl(&mut self, decl: InstantiatedDeclaration<'arena>) -> Term<'arena> { - self.hashcons_term(Decl(decl)) + /// Returns the term associated to the given instantiated declaration. + pub(crate) fn decl(decl: InstantiatedDeclaration<'arena>, arena: &mut Arena<'arena>) -> Self { + Self::hashcons(Decl(decl), arena) + } + + /// Returns the weak head normal form of the term, lazily computing the closure `f`. + pub(crate) fn get_whnf_or_init<F>(self, f: F) -> Self + where + F: FnOnce() -> Self, + { + *self.0.header.head_normal_form.get_or_init(f) } + /// Returns the type of the term, lazily computing the closure `f`. + pub(crate) fn get_type_or_try_init<F>(self, f: F) -> ResultTerm<'arena> + where + F: FnOnce() -> ResultTerm<'arena>, + { + self.0.header.type_.get_or_try_init(f).copied() + } +} + +impl<'arena> Arena<'arena> { /// Returns the result of the substitution described by the key, lazily computing the closure `f`. pub(crate) fn get_subst_or_init<F>(&mut self, key: &(Term<'arena>, Term<'arena>, usize), f: F) -> Term<'arena> where @@ -187,21 +197,3 @@ impl<'arena> super::arena::Arena<'arena> { } } } - -impl<'arena> Term<'arena> { - /// Returns the weak head normal form of the term, lazily computing the closure `f`. - pub(crate) fn get_whnf_or_init<F>(self, f: F) -> Term<'arena> - where - F: FnOnce() -> Term<'arena>, - { - *self.0.header.head_normal_form.get_or_init(f) - } - - /// Returns the type of the term, lazily computing the closure `f`. - pub(crate) fn get_type_or_try_init<F>(self, f: F) -> ResultTerm<'arena> - where - F: FnOnce() -> ResultTerm<'arena>, - { - self.0.header.type_.get_or_try_init(f).copied() - } -} diff --git a/kernel/src/type_checker.rs b/kernel/src/type_checker.rs index df6c755a9bc74c714dfa3b91fc66d0de5394aeb2..5c2ae1fa7f45010a135ca3b7599f79a47b027305 100644 --- a/kernel/src/type_checker.rs +++ b/kernel/src/type_checker.rs @@ -38,18 +38,18 @@ pub enum TypeCheckerError<'arena> { TypeMismatch(Term<'arena>, Term<'arena>), } -impl<'arena> Arena<'arena> { +impl<'arena> Term<'arena> { /// Conversion function, checks whether two terms are definitionally equal. /// /// The conversion is untyped, meaning that it should **only** be called during type-checking /// when the two `Term`s are already known to be of the same type and in the same context. - fn conversion(&mut self, lhs: Term<'arena>, rhs: Term<'arena>) -> bool { - if lhs == rhs { + fn conversion(self, rhs: Self, arena: &mut Arena<'arena>) -> bool { + if self == rhs { return true; } - let lhs = self.whnf(lhs); - let rhs = self.whnf(rhs); + let lhs = self.whnf(arena); + let rhs = rhs.whnf(arena); if lhs == rhs { return true; @@ -62,69 +62,69 @@ impl<'arena> Arena<'arena> { // again. (Var(i, _), Var(j, _)) => i == j, - (&Prod(t1, u1), &Prod(t2, u2)) => self.conversion(t1, t2) && self.conversion(u1, u2), + (&Prod(t1, u1), &Prod(t2, u2)) => t1.conversion(t2, arena) && u1.conversion(u2, arena), // Since we assume that both values already have the same type, // checking conversion over the argument type is useless. // However, this doesn't mean we can simply remove the arg type // from the type constructor in the enum, it is needed to quote back to terms. - (&Abs(_, t), &Abs(_, u)) => self.conversion(t, u), + (&Abs(_, t), &Abs(_, u)) => t.conversion(u, arena), - (&App(t1, u1), &App(t2, u2)) => self.conversion(t1, t2) && self.conversion(u1, u2), + (&App(t1, u1), &App(t2, u2)) => t1.conversion(t2, arena) && u1.conversion(u2, arena), _ => false, } } /// Checks whether two terms are definitionally equal. - pub fn is_def_eq(&mut self, lhs: Term<'arena>, rhs: Term<'arena>) -> Result<'arena, ()> { - self.conversion(lhs, rhs).then_some(()).ok_or(Error { - kind: TypeCheckerError::NotDefEq(lhs, rhs).into(), + pub fn is_def_eq(self, rhs: Self, arena: &mut Arena<'arena>) -> Result<'arena, ()> { + self.conversion(rhs, arena).then_some(()).ok_or(Error { + kind: TypeCheckerError::NotDefEq(self, rhs).into(), }) } /// Computes the universe in which `(x: A) -> B` lives when `A: lhs` and `B: rhs`. - fn imax(&mut self, lhs: Term<'arena>, rhs: Term<'arena>) -> ResultTerm<'arena> { - match *lhs { + fn imax(self, rhs: Self, arena: &mut Arena<'arena>) -> ResultTerm<'arena> { + match *self { Sort(l1) => match *rhs { Sort(l2) => { - let lvl = l1.imax(l2, self); - Ok(self.sort(lvl)) + let lvl = l1.imax(l2, arena); + Ok(Term::sort(lvl, arena)) }, _ => Err(Error { kind: TypeCheckerError::NotUniverse(rhs).into(), }), }, _ => Err(Error { - kind: TypeCheckerError::NotUniverse(lhs).into(), + kind: TypeCheckerError::NotUniverse(self).into(), }), } } - /// Infers the type of the term `t`, living in arena `self`. - pub fn infer(&mut self, t: Term<'arena>) -> ResultTerm<'arena> { - t.get_type_or_try_init(|| match *t { + /// Infers the type of the term `t`, living in arena `arena`. + pub fn infer(self, arena: &mut Arena<'arena>) -> ResultTerm<'arena> { + self.get_type_or_try_init(|| match *self { Sort(lvl) => { - let lvl = lvl.succ(self); - Ok(self.sort(lvl)) + let lvl = lvl.succ(arena); + Ok(Term::sort(lvl, arena)) }, Var(_, type_) => Ok(type_), Prod(t, u) => { - let univ_t = self.infer(t)?; - let univ_u = self.infer(u)?; + let univ_t = t.infer(arena)?; + let univ_u = u.infer(arena)?; - let univ_t = self.whnf(univ_t); - let univ_u = self.whnf(univ_u); - self.imax(univ_t, univ_u) + let univ_t = univ_t.whnf(arena); + let univ_u = univ_u.whnf(arena); + univ_t.imax(univ_u, arena) }, Abs(t, u) => { - let type_t = self.infer(t)?; + let type_t = t.infer(arena)?; match *type_t { Sort(_) => { - let type_u = self.infer(u)?; - Ok(self.prod(t, type_u)) + let type_u = u.infer(arena)?; + Ok(t.prod(type_u, arena)) }, _ => Err(Error { kind: TypeCheckerError::NotUniverse(type_t).into(), @@ -133,14 +133,14 @@ impl<'arena> Arena<'arena> { }, App(t, u) => { - let type_t = self.infer(t)?; - let type_t = self.whnf(type_t); + let type_t = t.infer(arena)?; + let type_t = type_t.whnf(arena); match *type_t { Prod(arg_type, cls) => { - let type_u = self.infer(u)?; + let type_u = u.infer(arena)?; - if self.conversion(type_u, arg_type) { - Ok(self.substitute(cls, u, 1)) + if type_u.conversion(arg_type, arena) { + Ok(cls.substitute(u, 1, arena)) } else { Err(Error { kind: TypeCheckerError::WrongArgumentType(t, arg_type, TypedTerm(u, type_u)).into(), @@ -158,11 +158,11 @@ impl<'arena> Arena<'arena> { }) } - /// Checks whether the term `t` living in `self` is of type `ty`. - pub fn check(&mut self, t: Term<'arena>, ty: Term<'arena>) -> Result<'arena, ()> { - let tty = self.infer(t)?; + /// Checks whether the term `t` living in `arena` is of type `ty`. + pub fn check(self, ty: Self, arena: &mut Arena<'arena>) -> Result<'arena, ()> { + let tty = self.infer(arena)?; - self.conversion(tty, ty).then_some(()).ok_or(Error { + tty.conversion(ty, arena).then_some(()).ok_or(Error { kind: TypeCheckerError::TypeMismatch(tty, ty).into(), }) } @@ -185,7 +185,7 @@ mod tests { let term = arena.build_term_raw(app(abs(prop(), id.into()), id.into())); let normal_form = arena.build_term_raw(abs(prop(), var(1.into(), prop()))); - assert!(arena.is_def_eq(term, normal_form).is_ok()) + assert!(term.is_def_eq(normal_form, arena).is_ok()) }) } @@ -196,7 +196,7 @@ mod tests { let term = arena.build_term_raw(app(abs(prop(), abs(prop(), var(2.into(), prop()))), id.into())); let normal_form = arena.build_term_raw(abs(prop(), id.into())); - assert!(arena.is_def_eq(term, normal_form).is_ok()) + assert!(term.is_def_eq(normal_form, arena).is_ok()) }) } @@ -207,18 +207,18 @@ mod tests { // λa.a (λx.x) (λx.x) let term = arena.build_term_raw(abs(prop(), app(app(var(2.into(), prop()), id.into()), id.into()))); - assert!(arena.is_def_eq(term, term).is_ok()); + assert!(term.is_def_eq(term, arena).is_ok()); }) } #[test] fn failed_def_equal() { use_arena(|arena| { - let term_lhs = arena.prop(); - let term_rhs = arena.type_usize(0); + let term_lhs = Term::prop(arena); + let term_rhs = Term::type_usize(0, arena); assert_eq!( - arena.is_def_eq(term_lhs, term_rhs), + term_lhs.is_def_eq(term_rhs, arena), Err(Error { kind: TypeCheckerError::NotDefEq(term_lhs, term_rhs).into() }) @@ -229,13 +229,13 @@ mod tests { #[test] fn failed_prod_binder_conversion() { use_arena(|arena| { - let type_0 = arena.type_usize(0); + let type_0 = Term::type_usize(0, arena); let term_lhs = arena.build_term_raw(prod(prop(), prop())); let term_rhs = arena.build_term_raw(prod(type_0.into(), prop())); assert_eq!( - arena.is_def_eq(term_lhs, term_rhs), + term_lhs.is_def_eq(term_rhs, arena), Err(Error { kind: TypeCheckerError::NotDefEq(term_lhs, term_rhs).into() }) @@ -246,13 +246,13 @@ mod tests { #[test] fn failed_app_head_conversion() { use_arena(|arena| { - let type_0 = arena.type_usize(0); + let type_0 = Term::type_usize(0, arena); let term_lhs = arena.build_term_raw(abs(type_0.into(), abs(type_0.into(), app(var(1.into(), prop()), prop())))); let term_rhs = arena.build_term_raw(abs(type_0.into(), abs(type_0.into(), app(var(2.into(), prop()), prop())))); assert_eq!( - arena.is_def_eq(term_lhs, term_rhs), + term_lhs.is_def_eq(term_rhs, arena), Err(Error { kind: TypeCheckerError::NotDefEq(term_lhs, term_rhs).into() }) @@ -263,15 +263,15 @@ mod tests { #[test] fn typed_reduction_app_1() { use_arena(|arena| { - let type_0 = arena.type_usize(0); + let type_0 = Term::type_usize(0, arena); let term = arena.build_term_raw(app(abs(type_0.into(), var(1.into(), type_0.into())), prop())); let reduced = arena.build_term_raw(prop()); - assert!(arena.is_def_eq(term, reduced).is_ok()); + assert!(term.is_def_eq(reduced, arena).is_ok()); - let term_type = arena.infer(term).unwrap(); + let term_type = term.infer(arena).unwrap(); assert_eq!(term_type, type_0); - assert!(arena.check(term, term_type).is_ok()) + assert!(term.check(term_type, arena).is_ok()) }) } @@ -347,42 +347,42 @@ mod tests { // λa: P. λb: P. b let reduced = arena.build(abs("_", prop(), abs("x", prop(), var("x")))).unwrap(); - assert!(arena.is_def_eq(term, reduced).is_ok()); + assert!(term.is_def_eq(reduced, arena).is_ok()); - let term_type = arena.infer(term).unwrap(); + let term_type = term.infer(arena).unwrap(); let expected_type = arena.build(prod("_", prop(), prod("_", prop(), prop()))).unwrap(); assert_eq!(term_type, expected_type); - assert!(arena.check(term, term_type).is_ok()) + assert!(term.check(term_type, arena).is_ok()) }) } #[test] fn typed_reduction_universe() { use_arena(|arena| { - let type_0 = arena.type_usize(0); - let type_1 = arena.type_usize(1); + let type_0 = Term::type_usize(0, arena); + let type_1 = Term::type_usize(1, arena); let term = arena.build_term_raw(app(abs(prop(), type_0.into()), prod(prop(), var(1.into(), prop())))); - assert!(arena.is_def_eq(term, type_0).is_ok()); + assert!(term.is_def_eq(type_0, arena).is_ok()); - let term_type = arena.infer(term).unwrap(); + let term_type = term.infer(arena).unwrap(); assert_eq!(term_type, type_1); - assert!(arena.check(term, term_type).is_ok()) + assert!(term.check(term_type, arena).is_ok()) }) } #[test] fn escape_from_prop() { use_arena(|arena| { - let type_0 = arena.type_usize(0); - let type_1 = arena.type_usize(1); + let type_0 = Term::type_usize(0, arena); + let type_1 = Term::type_usize(1, arena); let term = arena.build_term_raw(abs(prop(), prod(var(1.into(), prop()), type_0.into()))); - let term_type = arena.infer(term).unwrap(); + let term_type = term.infer(arena).unwrap(); let expected_type = arena.build_term_raw(prod(prop(), type_1.into())); assert_eq!(term_type, expected_type); - assert!(arena.check(term, term_type).is_ok()) + assert!(term.check(term_type, arena).is_ok()) }) } @@ -402,19 +402,19 @@ mod tests { // λx.x x let reduced = arena.build_term_raw(abs(prop(), app(var(1.into(), prop()), var(1.into(), prop())))); - assert!(arena.is_def_eq(term, reduced).is_ok()) + assert!(term.is_def_eq(reduced, arena).is_ok()) }) } #[test] fn typed_prod_1() { use_arena(|arena| { - let type_0 = arena.type_usize(0); + let type_0 = Term::type_usize(0, arena); let term = arena.build_term_raw(prod(prop(), prop())); - let term_type = arena.infer(term).unwrap(); + let term_type = term.infer(arena).unwrap(); assert_eq!(term_type, type_0); - assert!(arena.check(term, term_type).is_ok()) + assert!(term.check(term_type, arena).is_ok()) }) } @@ -422,10 +422,10 @@ mod tests { fn typed_prod_2() { use_arena(|arena| { let term = arena.build_term_raw(prod(prop(), var(1.into(), prop()))); - let term_type = arena.infer(term).unwrap(); + let term_type = term.infer(arena).unwrap(); - assert_eq!(term_type, arena.prop()); - assert!(arena.check(term, term_type).is_ok()); + assert_eq!(term_type, Term::prop(arena)); + assert!(term.check(term_type, arena).is_ok()); }) } @@ -433,28 +433,28 @@ mod tests { fn typed_prod_3() { use_arena(|arena| { let term = arena.build_term_raw(abs(prop(), abs(var(1.into(), prop()), var(1.into(), var(2.into(), prop()))))); - let term_type = arena.infer(term).unwrap(); + let term_type = term.infer(arena).unwrap(); let expected_type = arena.build_term_raw(prod(prop(), prod(var(1.into(), prop()), var(2.into(), prop())))); assert_eq!(term_type, expected_type); - assert!(arena.check(term, term_type).is_ok()); + assert!(term.check(term_type, arena).is_ok()); }) } #[test] fn typed_polymorphism() { use_arena(|arena| { - let type_0 = arena.type_usize(0); + let type_0 = Term::type_usize(0, arena); let identity = arena.build_term_raw(abs(type_0.into(), abs(var(1.into(), type_0.into()), var(1.into(), var(2.into(), type_0.into()))))); - let identity_type = arena.infer(identity).unwrap(); + let identity_type = identity.infer(arena).unwrap(); let expected_type = arena.build_term_raw(prod(type_0.into(), prod(var(1.into(), type_0.into()), var(2.into(), type_0.into())))); assert_eq!(identity_type, expected_type); - assert!(arena.check(identity, identity_type).is_ok()); + assert!(identity.check(identity_type, arena).is_ok()); }) } @@ -471,32 +471,32 @@ mod tests { ), ), )); - let term_type = arena.infer(term).unwrap(); + let term_type = term.infer(arena).unwrap(); assert_eq!(term_type, arena.build_term_raw(prod(prop(), prod(prop(), prod(prod(prop(), prod(prop(), prop())), prop()))))); - assert!(arena.check(term, term_type).is_ok()); + assert!(term.check(term_type, arena).is_ok()); }) } #[test] fn type_hierarchy_prop() { use_arena(|arena| { - let term = arena.prop(); - let term_type = arena.infer(term).unwrap(); + let term = Term::prop(arena); + let term_type = term.infer(arena).unwrap(); - assert_eq!(term_type, arena.type_usize(0)); - assert!(arena.check(term, term_type).is_ok()); + assert_eq!(term_type, Term::type_usize(0, arena)); + assert!(term.check(term_type, arena).is_ok()); }) } #[test] fn type_hierarchy_type() { use_arena(|arena| { - let term = arena.type_usize(0); - let term_type = arena.infer(term).unwrap(); + let term = Term::type_usize(0, arena); + let term_type = term.infer(arena).unwrap(); - assert_eq!(term_type, arena.type_usize(1)); - assert!(arena.check(term, term_type).is_ok()); + assert_eq!(term_type, Term::type_usize(1, arena)); + assert!(term.check(term_type, arena).is_ok()); }) } @@ -509,9 +509,9 @@ mod tests { let term = arena.build_term_raw(abs(app(prop(), prop()), prop())); assert_eq!( - arena.infer(term), + term.infer(arena), Err(Error { - kind: TypeCheckerError::NotAFunction(TypedTerm(arena.prop(), arena.type_usize(0)), arena.prop()).into() + kind: TypeCheckerError::NotAFunction(TypedTerm(Term::prop(arena), Term::type_usize(0, arena)), Term::prop(arena)).into() }) ); }) @@ -523,9 +523,9 @@ mod tests { let term = arena.build_term_raw(prod(prop(), app(prop(), prop()))); assert_eq!( - arena.infer(term), + term.infer(arena), Err(Error { - kind: TypeCheckerError::NotAFunction(TypedTerm(arena.prop(), arena.type_usize(0)), arena.prop()).into() + kind: TypeCheckerError::NotAFunction(TypedTerm(Term::prop(arena), Term::type_usize(0, arena)), Term::prop(arena)).into() }) ); }) @@ -537,9 +537,9 @@ mod tests { let term = arena.build_term_raw(prod(app(prop(), prop()), prop())); assert_eq!( - arena.infer(term), + term.infer(arena), Err(Error { - kind: TypeCheckerError::NotAFunction(TypedTerm(arena.prop(), arena.type_usize(0)), arena.prop()).into() + kind: TypeCheckerError::NotAFunction(TypedTerm(Term::prop(arena), Term::type_usize(0, arena)), Term::prop(arena)).into() }) ); }) @@ -551,9 +551,9 @@ mod tests { let term = arena.build_term_raw(app(prop(), prop())); assert_eq!( - arena.infer(term), + term.infer(arena), Err(Error { - kind: TypeCheckerError::NotAFunction(TypedTerm(arena.prop(), arena.type_usize(0)), arena.prop()).into() + kind: TypeCheckerError::NotAFunction(TypedTerm(Term::prop(arena), Term::type_usize(0, arena)), Term::prop(arena)).into() }) ); }) @@ -565,9 +565,9 @@ mod tests { let term = arena.build_term_raw(app(app(prop(), prop()), prop())); assert_eq!( - arena.infer(term), + term.infer(arena), Err(Error { - kind: TypeCheckerError::NotAFunction(TypedTerm(arena.prop(), arena.type_usize(0)), arena.prop()).into() + kind: TypeCheckerError::NotAFunction(TypedTerm(Term::prop(arena), Term::type_usize(0, arena)), Term::prop(arena)).into() }) ); }) @@ -579,9 +579,9 @@ mod tests { let term = arena.build_term_raw(app(abs(prop(), prop()), app(prop(), prop()))); assert_eq!( - arena.infer(term), + term.infer(arena), Err(Error { - kind: TypeCheckerError::NotAFunction(TypedTerm(arena.prop(), arena.type_usize(0)), arena.prop()).into() + kind: TypeCheckerError::NotAFunction(TypedTerm(Term::prop(arena), Term::type_usize(0, arena)), Term::prop(arena)).into() }) ); }) @@ -601,11 +601,11 @@ mod tests { )); assert_eq!( - arena.infer(term), + term.infer(arena), Err(Error { kind: TypeCheckerError::WrongArgumentType( arena.build_term_raw(abs(prop(), app(var(2.into(), prod(prop(), prop())), var(1.into(), prop())))), - arena.prop(), + Term::prop(arena), TypedTerm(arena.build_term_raw(var(1.into(), prod(prop(), prop()))), arena.build_term_raw(prod(prop(), prop()))) ) .into() @@ -623,7 +623,7 @@ mod tests { )); assert_eq!( - arena.infer(term), + term.infer(arena), Err(Error { kind: TypeCheckerError::NotUniverse(arena.build_term_raw(var(2.into(), prop()))).into() }) @@ -637,7 +637,7 @@ mod tests { let term = arena.build_term_raw(prod(id(), prop())); assert_eq!( - arena.infer(term), + term.infer(arena), Err(Error { kind: TypeCheckerError::NotUniverse(arena.build_term_raw(prod(prop(), prop()))).into() }) @@ -650,12 +650,12 @@ mod tests { use_arena(|arena| { let term = arena.build_term_raw(prod(prop(), abs(prop(), prop()))); - let prop = arena.prop(); - let type_ = arena.type_usize(0); + let prop = Term::prop(arena); + let type_ = Term::type_usize(0, arena); assert_eq!( - arena.infer(term), + term.infer(arena), Err(Error { - kind: TypeCheckerError::NotUniverse(arena.prod(prop, type_)).into() + kind: TypeCheckerError::NotUniverse(prop.prod(type_, arena)).into() }) ); }) @@ -665,12 +665,12 @@ mod tests { fn check_fail_1() { use_arena(|arena| { let term = arena.build_term_raw(app(prop(), prop())); - let expected_type = arena.prop(); + let expected_type = Term::prop(arena); assert_eq!( - arena.check(term, expected_type), + term.check(expected_type, arena), Err(Error { - kind: TypeCheckerError::NotAFunction(TypedTerm(arena.prop(), arena.type_usize(0)), arena.prop()).into(), + kind: TypeCheckerError::NotAFunction(TypedTerm(Term::prop(arena), Term::type_usize(0, arena)), Term::prop(arena)).into(), }) ); }) @@ -679,11 +679,11 @@ mod tests { #[test] fn check_fail_2() { use_arena(|arena| { - let prop = arena.prop(); + let prop = Term::prop(arena); assert_eq!( - arena.check(prop, prop), + prop.check(prop, arena), Err(Error { - kind: TypeCheckerError::TypeMismatch(arena.type_usize(0), prop).into(), + kind: TypeCheckerError::TypeMismatch(Term::type_usize(0, arena), prop).into(), }) ); })