From df056b17418d2f3e43bfa433db23930d0172048e Mon Sep 17 00:00:00 2001 From: Vincent Lafeychine <vincent.lafeychine@proton.me> Date: Mon, 31 Oct 2022 23:08:23 +0100 Subject: [PATCH] chore(type_checker): Cleaning tests + Analyse branches --- kernel/src/error.rs | 4 - kernel/src/location.rs | 2 +- kernel/src/type_checker.rs | 377 +++++++++++++++++++++++-------------- 3 files changed, 235 insertions(+), 148 deletions(-) diff --git a/kernel/src/error.rs b/kernel/src/error.rs index 868e4410..9b79f040 100644 --- a/kernel/src/error.rs +++ b/kernel/src/error.rs @@ -21,10 +21,6 @@ pub enum KernelError { #[display(fmt = "{} is not a universe", _0)] NotUniverse(Term), - /// t is not a type - #[display(fmt = "{} is not a type", _0)] - NotType(Term), - /// t1 and t2 are not definitionally equal #[display(fmt = "{} and {} are not definitionaly equal", _0, _1)] NotDefEq(Term, Term), diff --git a/kernel/src/location.rs b/kernel/src/location.rs index 7f671d63..dc4f01a3 100644 --- a/kernel/src/location.rs +++ b/kernel/src/location.rs @@ -1,4 +1,4 @@ -// TODO: Waiting for #15 +// TODO: Waiting for #15 to handle correctly location inside Terms use derive_more::{Constructor, Display, From}; /// Line and column position. diff --git a/kernel/src/type_checker.rs b/kernel/src/type_checker.rs index e81659cd..2f96d4f0 100644 --- a/kernel/src/type_checker.rs +++ b/kernel/src/type_checker.rs @@ -1,5 +1,5 @@ use crate::environment::Environment; -use crate::error::{KernelError, Result}; +use crate::error::{KernelError::*, Result}; use crate::term::{DeBruijnIndex, Term}; use num_bigint::BigUint; use std::cmp::max; @@ -47,17 +47,19 @@ impl Term { /// 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(&self, rhs: &Term, env: &Environment, lvl: DeBruijnIndex) -> bool { match (self.whnf(env), rhs.whnf(env)) { - (Type(i), Type(j)) => i == j, - (Prop, Prop) => true, + (Type(i), Type(j)) => i == j, + (Var(i), Var(j)) => i == j, - (Prod(t1, u1), Prod(box t2, u2)) => { + (Prod(_t1, u1), Prod(box _t2, u2)) => { let u1 = u1.substitute(&Var(lvl), lvl.into()); let u2 = u2.substitute(&Var(lvl), lvl.into()); - t1.conversion(&t2, env, lvl) && u1.conversion(&u2, env, lvl + 1.into()) + // TODO: Unused code (#32) + // t1.conversion(&t2, env, lvl) && + u1.conversion(&u2, env, lvl + 1.into()) } // Since we assume that both vals already have the same type, @@ -71,14 +73,16 @@ impl Term { t.conversion(&u, env, lvl + 1.into()) } - (App(box t1, box u1), App(box t2, box u2)) => { - t1.conversion(&t2, env, lvl) && u1.conversion(&u2, env, lvl) - } - - (app @ App(box Abs(_, _), box _), u) | (u, app @ App(box Abs(_, _), box _)) => { - app.beta_reduction(env).conversion(&u, env, lvl) + (App(box _t1, box u1), App(box _t2, box u2)) => { + // TODO: Unused code (#32) + // t1.conversion(&t2, env, lvl) && + u1.conversion(&u2, env, lvl) } + // TODO: Unused code (#32) + // (app @ App(box Abs(_, _), box _), u) | (u, app @ App(box Abs(_, _), box _)) => { + // app.beta_reduction(env).conversion(&u, env, lvl) + // } _ => false, } } @@ -87,7 +91,7 @@ impl Term { pub fn is_def_eq(&self, rhs: &Term, env: &Environment) -> Result<()> { self.conversion(rhs, env, 1.into()) .then_some(()) - .ok_or_else(|| KernelError::NotDefEq(self.clone(), rhs.clone())) + .ok_or_else(|| NotDefEq(self.clone(), rhs.clone())) } /// Computes universe the universe in which `(x : A) -> B` lives when `A : u1` and `B : u2`. @@ -97,15 +101,16 @@ impl Term { Prop => Ok(Prop), Type(ref i) => match self { - Prop => Ok(Type(i.clone())), + // TODO: Unused code (#32) + // Prop => Ok(Type(i.clone())), // else if u1 = Type(i) and u2 = Type(j), then (x : A) -> B : Type(max(i,j)) Type(j) => Ok(Type(max(i.clone(), j.clone()))), - _ => Err(KernelError::NotUniverse(self.clone())), + _ => Err(NotUniverse(self.clone())), }, - _ => Err(KernelError::NotUniverse(rhs.clone())), + _ => Err(NotUniverse(rhs.clone())), } } @@ -117,11 +122,13 @@ impl Term { Const(s) => match env.get_type(s) { Some(ty) => Ok(ty), - None => Err(KernelError::ConstNotFound(s.clone())), + None => Err(ConstNotFound(s.clone())), }, Prod(box t, u) => { + // TODO: Do a test with _infer failing (#32) let univ_t = t._infer(env, ctx)?; + // TODO: Do a test with _infer failing (#32) let univ_u = u._infer(env, ctx.clone().bind(t))?; univ_t.normal_form(env).imax(&univ_u.normal_form(env)) @@ -133,19 +140,19 @@ impl Term { Ok(Prod(box t.clone(), box u)) } + // TODO: Do a test with _infer failing (#32) App(box t, box u) => match t._infer(env, ctx)? { Prod(box typ_lhs, cls) => { + // TODO: Do a test with _infer failing (#32) let typ_rhs = u._infer(env, ctx)?; typ_lhs .conversion(&typ_rhs, env, ctx.types.len().into()) .then_some(*cls) - .ok_or_else(|| { - KernelError::WrongArgumentType(t.clone(), typ_lhs, u.clone(), typ_rhs) - }) + .ok_or_else(|| WrongArgumentType(t.clone(), typ_lhs, u.clone(), typ_rhs)) } - x => Err(KernelError::NotAFunction(t.clone(), x, u.clone())), + x => Err(NotAFunction(t.clone(), x, u.clone())), }, } } @@ -158,11 +165,12 @@ impl Term { /// Checks whether a given term is of type `ty` in a given context. pub fn check(&self, ty: &Term, env: &Environment) -> Result<()> { let ctx = &mut Context::new(); + // TODO: Do a test with _infer failing (#32) let tty = self._infer(env, ctx)?; tty.conversion(ty, env, ctx.types.len().into()) .then_some(()) - .ok_or_else(|| KernelError::TypeMismatch(tty, ty.clone())) + .ok_or_else(|| TypeMismatch(tty, ty.clone())) } } @@ -170,67 +178,54 @@ impl Term { mod tests { use super::*; - fn id(l: usize) -> Box<Term> { - box Abs(box Prop, box Var(l.into())) + fn proj(idx: usize) -> Box<Term> { + box Abs(box Prop, box Var(idx.into())) } #[test] - fn var_subst_1() { - // (λ P. λP. 1) (λP.1) - let t = App( - box Abs(box Prop, box Abs(box Prop, box Var(1.into()))), - id(1), - ); + fn normal_form_1() { + let term = App(box Abs(box Prop, proj(1)), proj(1)); + let normal_form = Abs(box Prop, box Var(1.into())); - let nf = Abs(box Prop, box Var(1.into())); - assert_eq!(t.normal_form(&Environment::new()), nf) + assert!(term.is_def_eq(&normal_form, &Environment::new()).is_ok()); } #[test] - fn var_subst_2() { - let t = App( - box Abs(box Prop, box Abs(box Prop, box Var(2.into()))), - id(1), - ); + fn normal_form_2() { + let term = App(box Abs(box Prop, proj(2)), proj(1)); + let normal_form = Abs(box Prop, proj(1)); - let nf = Abs(box Prop, id(1)); - assert_eq!(t.normal_form(&Environment::new()), nf) + assert!(term.is_def_eq(&normal_form, &Environment::new()).is_ok()); } #[test] - fn simple() { - let t1 = App( - box Abs(box Type(BigUint::from(0_u64).into()), box Var(1.into())), + fn normal_form_3() { + // λa.a (λx.x) (λx.x) + let term = Abs( box Prop, + box App(box App(box Var(2.into()), proj(1)), proj(1)), ); - let t2 = Prop; - assert!(t1.conversion(&t2, &Environment::new(), 0.into())); - - let ty = t1.infer(&Environment::new()); - assert_eq!(ty, Ok(Type(BigUint::from(0_u64).into()))); + assert!(term.is_def_eq(&term, &Environment::new()).is_ok()); } #[test] - fn simple_substitute() { - // λ (x : P -> P).(λ (y :P).x y) x - //λ P -> P.(λ P.2 1) 1 - let term = Abs( - box Prod(box Prop, box Prop), - box App( - box Abs(box Prop, box App(box Var(2.into()), box Var(1.into()))), - box Var(1.into()), - ), + fn typed_reduction_app_1() { + let term = App( + box Abs(box Type(BigUint::from(0_u64).into()), box Var(1.into())), + box Prop, ); - // λx.x x - let reduced = Abs(box Prop, box App(box Var(1.into()), box Var(1.into()))); + let reduced = Prop; assert!(term.is_def_eq(&reduced, &Environment::new()).is_ok()); - assert!(term.infer(&Environment::new()).is_err()); + + let term_type = term.infer(&Environment::new()).unwrap(); + assert_eq!(term_type, Type(BigUint::from(0_u64).into())); + assert!(term.check(&term_type, &Environment::new()).is_ok()); } #[test] - fn complex_conv() { + fn typed_reduction_app_2() { // (λa.λb.λc.a (λd.λe.e (d b)) (λ_.c) (λd.d)) (λa.λb.a b) let term = App( box Abs( @@ -275,22 +270,22 @@ mod tests { ), ), // _ : P - box Abs(box Prop, box Var(2.into())), + proj(2), ), - //d : P - box Abs(box Prop, box Var(1.into())), + // d : P + proj(1), ), ), ), ), box Abs( - //a : (P -> P) -> (P -> P) -> P + // a : (P -> P) -> (P -> P) -> P box Prod( box Prod(box Prop, box Prop), box Prod(box Prod(box Prop, box Prop), box Prop), ), box Abs( - //b : P -> P + // b : P -> P box Prod(box Prop, box Prop), box App(box Var(2.into()), box Var(1.into())), ), @@ -298,71 +293,97 @@ mod tests { ); // λa : P.λb : P .b - let reduced = Abs(box Prop, box Abs(box Prop, box Var(1.into()))); + let reduced = Abs(box Prop, proj(1)); assert!(term.is_def_eq(&reduced, &Environment::new()).is_ok()); - assert!(term.infer(&Environment::new()).is_ok()) + + let term_type = term.infer(&Environment::new()).unwrap(); + assert_eq!(term_type, Prod(box Prop, box Prod(box Prop, box Prop))); + assert!(term.check(&term_type, &Environment::new()).is_ok()); } - //(λ ℙ → λ ℙ → λ ℙ → (0 (λ ℙ → λ ℙ → ((4 1) 3) λ ℙ → 3)) (λ ℙ → λ ℙ → (0 1) (λ ℙ → 0 λ ℙ → 0))) #[test] - fn nf_test() { - //λa.a (λx.x) (λx.x) - let reduced = Abs(box Prop, box App(box App(box Var(2.into()), id(1)), id(1))); + fn typed_reduction_universe() { + let term = App( + box Abs(box Prop, box Type(BigUint::from(0_u64).into())), + box Prod(box Prop, box Var(1.into())), + ); + + let reduced = Type(BigUint::from(0_u64).into()); + assert!(term.is_def_eq(&reduced, &Environment::new()).is_ok()); - let nff = reduced.clone().normal_form(&Environment::new()); - assert_eq!(reduced, nff); - assert!(reduced.is_def_eq(&nff, &Environment::new()).is_ok()); + let term_type = term.infer(&Environment::new()).unwrap(); + assert_eq!(term_type, Type(BigUint::from(1_u64).into())); + assert!(term.check(&term_type, &Environment::new()).is_ok()); } #[test] - fn polymorphism() { - let id = Abs( - box Type(BigUint::from(0_u64).into()), - box Abs(box Var(1.into()), box Var(1.into())), + fn illtyped_reduction() { + // λ (x : P -> P).(λ (y :P).x y) x + // λ P -> P.(λ P.2 1) 1 + let term = Abs( + box Prod(box Prop, box Prop), + box App( + box Abs(box Prop, box App(box Var(2.into()), box Var(1.into()))), + box Var(1.into()), + ), ); - assert!(id.infer(&Environment::new()).is_ok()); + // λx.x x + let reduced = Abs(box Prop, box App(box Var(1.into()), box Var(1.into()))); + assert!(term.is_def_eq(&reduced, &Environment::new()).is_ok()); } #[test] - fn type_type() { - assert!(Type(BigUint::from(0_u64).into()) - .check(&Type(BigUint::from(1_u64).into()), &Environment::new()) - .is_ok()); + fn typed_prod_1() { + let term = Prod(box Prop, box Prop); + let term_type = term.infer(&Environment::new()).unwrap(); + + assert_eq!(term_type, Type(BigUint::from(0_u64).into())); + assert!(term.check(&term_type, &Environment::new()).is_ok()); } #[test] - fn not_function() { - let t = App(box Prop, box Prop); - assert!(t.infer(&Environment::new()).is_err()) + fn typed_prod_2() { + let term = Prod(box Prop, box Var(1.into())); + let term_type = term.infer(&Environment::new()).unwrap(); + + assert_eq!(term_type, Prop); + assert!(term.check(&term_type, &Environment::new()).is_ok()); } #[test] - fn not_type_prod() { - let t = Prod(box Abs(box Prop, box Var(1.into())), box Prop); - assert!(t.infer(&Environment::new()).is_err()); - - let t = Prod(box Prop, box Abs(box Prop, box Prop)); - assert!(t.infer(&Environment::new()).is_err()); - - let wf_prod = Prod(box Prop, box Prop); - assert!(wf_prod - .check(&Type(BigUint::from(0_u64).into()), &Environment::new()) - .is_ok()); - - let wf_prod = Prod(box Prop, box Var(1.into())); - assert!(wf_prod.check(&Prop, &Environment::new()).is_ok()); - - // Type0 -> (A : Prop) -> - let wf_prod = Prod(box Prop, box Prop); - assert!(wf_prod - .check(&Type(BigUint::from(0_u64).into()), &Environment::new()) - .is_ok()); + fn typed_prod_3() { + let term = Abs(box Prop, box Abs(box Var(1.into()), box Var(1.into()))); + let term_type = term.infer(&Environment::new()).unwrap(); + + assert_eq!( + term_type, + Prod(box Prop, box Prod(box Var(1.into()), box Var(2.into()))) + ); + assert!(term.check(&term_type, &Environment::new()).is_ok()); + } + + #[test] + fn typed_polymorphism() { + let identity = Abs( + box Type(BigUint::from(0_u64).into()), + box Abs(box Var(1.into()), box Var(1.into())), + ); + let identity_type = identity.infer(&Environment::new()).unwrap(); + + assert_eq!( + identity_type, + Prod( + box Type(BigUint::from(0_u64).into()), + box Prod(box Var(1.into()), box Var(2.into())) + ) + ); + assert!(identity.check(&identity_type, &Environment::new()).is_ok()); } #[test] - fn poly_test2() { - let t = Abs( + fn typed_polymorphism_2() { + let term = Abs( box Prop, box Abs( box Prop, @@ -375,60 +396,130 @@ mod tests { ), ), ); + let term_type = term.infer(&Environment::new()).unwrap(); - assert!(t.infer(&Environment::new()).is_ok()); + assert_eq!( + term_type, + Prod( + box Prop, + box Prod( + box Prop, + box Prod(box Prod(box Prop, box Prod(box Prop, box Prop)), box Prop) + ) + ) + ); + assert!(term.check(&term_type, &Environment::new()).is_ok()); } #[test] - fn env_test() { - let id_prop = Prod(box Prop, box Prod(box Var(1.into()), box Var(2.into()))); - let t = Abs(box Prop, box Abs(box Var(1.into()), box Var(1.into()))); - let mut env = Environment::new(); - env.insert("foo".into(), id_prop.clone(), Prop).unwrap(); + fn type_hierarchy_prop() { + let term = Prop; + let term_type = term.infer(&Environment::new()).unwrap(); - assert!(t.check(&Const("foo".into()), &env).is_ok()); - assert!(id_prop.is_def_eq(&Const("foo".into()), &env).is_ok()); + assert_eq!(term_type, Type(BigUint::from(0_u64).into())); + assert!(term.check(&term_type, &Environment::new()).is_ok()); } #[test] - fn check_bad() { - assert!(Prop.check(&Prop, &Environment::new()).is_err()); + fn type_hierarchy_type() { + let term = Type(BigUint::from(0_u64).into()); + let term_type = term.infer(&Environment::new()).unwrap(); + + assert_eq!(term_type, Type(BigUint::from(1_u64).into())); + assert!(term.check(&term_type, &Environment::new()).is_ok()); } #[test] - fn not_def_eq() { - assert!(Prop - .is_def_eq(&Type(BigUint::from(0_u64).into()), &Environment::new()) - .is_err()); + fn failed_def_equal() { + assert_eq!( + Prop.is_def_eq(&Type(BigUint::from(0_u64).into()), &Environment::new()), + Err(NotDefEq(Prop, Type(BigUint::from(0_u64).into()))) + ); } #[test] - fn infer_const() { - let id_prop = Prod(box Prop, box Prod(box Var(1.into()), box Var(1.into()))); + fn environment() { + let term = Abs(box Prop, box Abs(box Var(1.into()), box Var(1.into()))); + let term_type = term.infer(&Environment::new()).unwrap(); + + let context = Prod(box Prop, box Prod(box Var(1.into()), box Var(2.into()))); let mut env = Environment::new(); - env.insert("foo".into(), id_prop, Prop).unwrap(); + env.insert("foo".into(), context, Prop).unwrap(); - assert!(Const("foo".into()).infer(&env).is_ok()); - assert!(Const("foo".into()).infer(&Environment::new()).is_err()); + assert_eq!( + term_type, + Prod(box Prop, box Prod(box Var(1.into()), box Var(2.into()))) + ); + assert!(term.check(&Const("foo".into()), &env).is_ok()); } - #[test] - fn prod_var() { - let ty = Prod(box Prop, box Prod(box Var(1.into()), box Var(2.into()))); - let t = Abs(box Prop, box Abs(box Var(1.into()), box Var(1.into()))); - assert!(t.check(&ty, &Environment::new()).is_ok()) - } + mod failed_type_inference { + use super::*; - #[test] - fn univ_reduc() { - let ty = App( - box Abs(box Prop, box Type(BigUint::from(0_u64).into())), - box Prod(box Prop, box Var(1.into())), - ); - assert!(ty.conversion( - &Type(BigUint::from(0_u64).into()), - &Environment::new(), - 0.into() - )) + #[test] + fn not_function() { + let term = App(box Prop, box Prop); + + assert_eq!( + term.infer(&Environment::new()), + Err(NotAFunction(Prop, Type(BigUint::from(0_u64).into()), Prop)) + ); + } + + #[test] + fn wrong_argument_type() { + // λ (x : P -> P).(λ (y :P).x y) x + // λ P -> P.(λ P.2 1) 1 + let term = Abs( + box Prod(box Prop, box Prop), + box App( + box Abs(box Prop, box App(box Var(2.into()), box Var(1.into()))), + box Var(1.into()), + ), + ); + + assert_eq!( + term.infer(&Environment::new()), + Err(WrongArgumentType( + Abs(box Prop, box App(box Var(2.into()), box Var(1.into()))), + Prop, + Var(1.into()), + Prod(box Prop, box Prop) + )) + ); + } + + #[test] + fn not_universe_1() { + let term = Prod(proj(1), box Prop); + + assert_eq!( + term.infer(&Environment::new()), + Err(NotUniverse(Prod(box Prop, box Prop))) + ); + } + + #[test] + fn not_universe_2() { + let term = Prod(box Prop, box Abs(box Prop, box Prop)); + + assert_eq!( + term.infer(&Environment::new()), + Err(NotUniverse(Prod( + box Prop, + box Type(BigUint::from(0_u64).into()) + ))) + ); + } + + #[test] + fn const_not_found() { + let term = Const("foo".to_string()); + + assert_eq!( + term.infer(&Environment::new()), + Err(ConstNotFound("foo".to_string())) + ); + } } } -- GitLab