use core::fmt;
use crate::ty::{TyVar, Type};
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum TypeError {
Mismatch {
expected: Type,
found: Type,
},
Occurs {
var: TyVar,
ty: Type,
},
}
impl fmt::Display for TypeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Mismatch { expected, found } => {
write!(f, "type mismatch: expected `{expected}`, found `{found}`")
}
Self::Occurs { var, ty } => {
write!(
f,
"recursive type: variable `?{}` occurs in `{ty}`",
var.to_u32(),
)
}
}
}
}
impl core::error::Error for TypeError {}
#[cfg(test)]
mod tests {
extern crate alloc;
use alloc::string::ToString;
use alloc::vec;
use super::*;
use crate::ty::TyCon;
const INT: TyCon = TyCon::new(0);
const LIST: TyCon = TyCon::new(7);
#[test]
fn test_mismatch_display_names_both_types() {
let err = TypeError::Mismatch {
expected: Type::con(INT),
found: Type::app(LIST, vec![Type::con(INT)]),
};
let text = err.to_string();
assert!(text.contains("#0"), "{text}");
assert!(text.contains("#7(#0)"), "{text}");
}
#[test]
fn test_occurs_display_names_variable() {
let err = TypeError::Occurs {
var: TyVar::from_index(3),
ty: Type::app(LIST, vec![Type::var(TyVar::from_index(3))]),
};
let text = err.to_string();
assert!(text.contains("?3"), "{text}");
}
#[test]
fn test_error_is_clonable_and_equatable() {
let a = TypeError::Mismatch {
expected: Type::con(INT),
found: Type::con(LIST),
};
let b = a.clone();
assert_eq!(a, b);
}
}