use crate::constraint_graph::ConstraintGraph;
use crate::types::{Constructable, PreliminaryTypeTable, TypeTable, Variant};
use crate::{
keys::{Constraint, TcKey},
types::Preliminary,
};
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;
pub trait TcVar: Debug + Eq + Hash + Clone {}
#[derive(Debug, Clone)]
pub struct TypeChecker<V: Variant, Var: TcVar> {
variables: HashMap<Var, TcKey>,
graph: ConstraintGraph<V>,
}
impl<V: Variant, Var: TcVar> Default for TypeChecker<V, Var> {
fn default() -> Self {
TypeChecker::new()
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct NoVars {
unit: (), }
impl TcVar for NoVars {}
pub type VarlessTypeChecker<V> = TypeChecker<V, NoVars>;
impl<V: Variant> TypeChecker<V, NoVars> {
pub fn without_vars() -> VarlessTypeChecker<V> {
TypeChecker::new()
}
}
impl<V: Variant, Var: TcVar> TypeChecker<V, Var> {
pub fn new() -> Self {
TypeChecker { variables: HashMap::new(), graph: ConstraintGraph::new() }
}
pub fn new_term_key(&mut self) -> TcKey {
self.graph.create_vertex()
}
pub fn get_var_key(&mut self, var: &Var) -> TcKey {
if let Some(tck) = self.variables.get(var) {
*tck
} else {
let key = self.new_term_key();
let _ = self.variables.insert(var.clone(), key);
key
}
}
pub fn get_child_key(&mut self, parent: TcKey, nth: usize) -> Result<TcKey, TcErr<V>> {
self.graph.nth_child(parent, nth)
}
pub fn impose(&mut self, constr: Constraint<V>) -> Result<(), TcErr<V>> {
match constr {
Constraint::Conjunction(cs) => cs.into_iter().try_for_each(|c| self.impose(c))?,
Constraint::Equal(a, b) => self.graph.equate(a, b)?,
Constraint::MoreConc { target, bound } => self.graph.add_upper_bound(target, bound),
Constraint::MoreConcExplicit(target, bound) => self.graph.explicit_bound(target, bound)?,
}
Ok(())
}
pub fn lift_into(&mut self, variant: V, mut sub_types: Vec<TcKey>) -> TcKey {
self.lift_partially(variant, sub_types.drain(..).map(Some).collect())
}
pub fn lift_partially(&mut self, variant: V, sub_types: Vec<Option<TcKey>>) -> TcKey {
self.graph.lift(variant, sub_types)
}
pub fn all_keys(&self) -> impl Iterator<Item = TcKey> + '_ {
self.graph.all_keys()
}
pub fn type_check_preliminary(self) -> Result<PreliminaryTypeTable<V>, TcErr<V>> {
self.graph.solve_preliminary()
}
}
impl<V, Var: TcVar> TypeChecker<V, Var>
where
V: Constructable,
{
pub fn type_check(self) -> Result<TypeTable<V>, TcErr<V>> {
self.graph.solve()
}
}
#[derive(Debug, Clone)]
pub enum TcErr<V: Variant> {
KeyEquation(TcKey, TcKey, V::Err),
Bound(TcKey, Option<TcKey>, V::Err),
ChildAccessOutOfBound(TcKey, V, usize),
ArityMismatch {
key: TcKey,
variant: V,
inferred_arity: usize,
reported_arity: usize,
},
Construction(TcKey, Preliminary<V>, V::Err),
ChildConstruction(TcKey, usize, Preliminary<V>, V::Err),
}