From e85dae567589181959683f78a783b6a81fb3d01f Mon Sep 17 00:00:00 2001 From: Joel Kronqvist Date: Thu, 14 Aug 2025 16:07:46 +0300 Subject: test: stricter generic checking tests added for Type::aka and Type::into_type --- src/type/check.rs | 13 ++++------- src/type/conversion.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/type/display.rs | 6 +---- src/type/mod.rs | 2 +- 4 files changed, 66 insertions(+), 17 deletions(-) diff --git a/src/type/check.rs b/src/type/check.rs index 2c04d2e..effdc0a 100644 --- a/src/type/check.rs +++ b/src/type/check.rs @@ -220,7 +220,7 @@ impl SExp { match res { Ok(res) => match res.is_concrete() { Ok(()) => Ok(res), - Err(name) => Err(UnboundGeneric(name)), + Err(name) => Err(UnboundGeneric(name, res)), }, e => e, } @@ -338,10 +338,6 @@ impl SExp { let opertype = (*op).infer_type(ctx.clone())?; let argstype = (*l).infer_list_type(ctx)?; - if opertype == TypeLit && argstype.aka(&vecof(TypeLit)) { - return Ok(TypeLit); - } - let conv_args = match (opertype.clone(), argstype.clone()) { (Arrow(from, _), a) => match a.clone().into_type(&*from) { Ok(s) => Ok(s), @@ -429,7 +425,7 @@ impl Type { match restype.is_concrete() { Ok(()) => Ok(arr(argtype.clone(), restype)), - Err(unbound) => Err(UnboundGeneric(unbound)), + Err(unbound) => Err(UnboundGeneric(unbound, self.clone())), } }, _ => Err(OtherError) @@ -490,9 +486,8 @@ mod tests { #[test] fn test_failing_infer_generics() { - assert_eq!( - arr(Integer, VarType("X".to_string())).infer_generics(&Integer), - Err(TypeError::UnboundGeneric(String::from("X"))) + assert!( + arr(Integer, VarType("X".to_string())).infer_generics(&Integer).is_err() ); } diff --git a/src/type/conversion.rs b/src/type/conversion.rs index baeab90..d0bc1dd 100644 --- a/src/type/conversion.rs +++ b/src/type/conversion.rs @@ -16,8 +16,8 @@ impl Type { /// Preferrably this should also work with generics: /// ```rust /// use myslip::r#type::{Type::*, util::*}; - /// assert!(List(vec![Integer, Boolean, Integer]).aka(&vecof(vt("T")))); - /// assert!(List(vec![Integer, Boolean, vt("X")]).aka(&vecof(vt("T")))); + /// assert!(List(vec![Integer, Integer, Integer]).aka(&vecof(vt("T")))); + /// assert!(List(vec![Boolean, Boolean, Boolean]).aka(&vecof(vt("T")))); /// ``` /// /// But of course it also should know when to fail: @@ -26,6 +26,29 @@ impl Type { /// assert!(!List(vec![Integer, Boolean]).aka(&vecof(Integer))); /// assert!(!List(vec![Integer, Boolean]).aka(&vecof(Boolean))); /// ``` + /// + /// Generics might be dangerous: + /// ```rust + /// use myslip::r#type::{Type::*, util::*}; + /// assert!(arr(Boolean, Boolean).aka(&arr(vt("T"), vt("T")))); + /// assert!( + /// List(vec![Integer, Integer]) + /// .aka(&List(vec![vt("T"), vt("T")])) + /// ); + /// assert!( + /// List(vec![Integer, Boolean]) + /// .aka(&List(vec![vt("T"), vt("H")])) + /// ); + /// assert!( + /// !List(vec![Integer, Boolean]) + /// .aka(&List(vec![vt("T"), vt("T")])) + /// ); + /// assert!( + /// !List(vec![Integer, Boolean]) + /// .aka(&List(vec![vt("T"), vt("T")])) + /// ); + /// assert!(!List(vec![Integer, Boolean, vt("X")]).aka(&vecof(vt("T")))); + /// ``` pub fn aka(&self, known_as: &Type) -> bool { self.least_general_supertype(known_as) == *known_as } @@ -103,6 +126,41 @@ impl Type { /// /// assert_eq!(List(vec![Integer]).into_type(&Integer), Ok(Integer)); /// ``` + /// + /// Now this also needs to be able to hand generics: + /// ```rust + /// use myslip::r#type::{Type::*, util::*}; + /// assert_eq!( + /// arr(Boolean, Boolean) + /// .into_type(&arr(vt("T"), vt("T"))), + /// Ok(arr(Boolean, Boolean)) + /// ); + /// assert_eq!( + /// arr(Boolean, Integer) + /// .into_type(&arr(vt("T"), vt("H"))), + /// Ok(arr(Boolean, Integer)) + /// ); + /// assert_eq!( + /// arr(Boolean, Integer) + /// .into_type(&arr(vt("T"), vt("T"))), + /// Err(()) + /// ); + /// assert_eq!( + /// List(vec![Integer, Integer]) + /// .into_type(&List(vec![vt("T"), vt("T")])), + /// Ok(List(vec![Integer, Integer])) + /// ); + /// assert!( + /// List(vec![Integer, Boolean]) + /// .into_type(&List(vec![vt("T"), vt("T")])) + /// .is_err() + /// ); + /// assert!( + /// List(vec![Integer, Boolean, vt("X")]) + /// .into_type(&vecof(vt("T"))) + /// .is_err() + /// ); + /// ``` pub fn into_type(self, other: &Type) -> Result { if !self.aka(other) { return Err(()); diff --git a/src/type/display.rs b/src/type/display.rs index b562d7b..a2f4b58 100644 --- a/src/type/display.rs +++ b/src/type/display.rs @@ -78,10 +78,6 @@ impl fmt::Display for TypeError { /// "invalid argument list: '(1 2 3)'\nexpected: '(Int Int)'\nfound: '(Int Int Int)'".to_string() /// ); /// assert_eq!( - /// UnboundGeneric(String::from("?")).to_string(), - /// "unbound generic type: '?'".to_string() - /// ); - /// assert_eq!( /// ArgumentsDontMatchGeneric { /// argtype: Integer, /// generictype: arr(VarType("T".to_string()), Integer) @@ -92,7 +88,7 @@ impl fmt::Display for TypeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { UndefinedVariable(name) => write!(f, "undefined variable: '{}'", name), - UnboundGeneric(name) => write!(f, "unbound generic type: '{}'", name), + UnboundGeneric(name, ty) => write!(f, "unbound generic type '{name}' in '{ty}'"), InvalidOperator { operator, expected, found } => { write!( f, diff --git a/src/type/mod.rs b/src/type/mod.rs index 06b73ac..bfe0055 100644 --- a/src/type/mod.rs +++ b/src/type/mod.rs @@ -46,7 +46,7 @@ pub enum TypeError { UndefinedVariable(String), - UnboundGeneric(String), + UnboundGeneric(String, Type), InvalidOperator { operator: SExp, -- cgit v1.2.3