aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Kronqvist <joel.kronqvist@iki.fi>2025-08-14 16:07:46 +0300
committerJoel Kronqvist <joel.kronqvist@iki.fi>2025-08-14 17:28:04 +0300
commite85dae567589181959683f78a783b6a81fb3d01f (patch)
tree9c5831c4429b2458936cbb9122f4a954543672f0
parent540a84fb9288699b534e98c8c5d0e649aaa1c2ba (diff)
downloadmyslip-e85dae567589181959683f78a783b6a81fb3d01f.tar.gz
myslip-e85dae567589181959683f78a783b6a81fb3d01f.zip
test: stricter generic checking tests added for Type::aka and Type::into_type
-rw-r--r--src/type/check.rs13
-rw-r--r--src/type/conversion.rs62
-rw-r--r--src/type/display.rs6
-rw-r--r--src/type/mod.rs2
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<Type, ()> {
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,