mod annotations;
mod constraint;
mod reify;
mod subst;
#[cfg(test)]
mod tests;
mod ty;
mod util;
use std::collections::{BTreeSet, HashSet};
use symbol::Symbol;
use ast::{Decl, Type};
use typeck::{
constraint::Constraint, subst::{SubstVar, Substitution}, ty::Ty,
util::{group, toposort, AnnotEnv},
};
#[derive(Clone, Debug, Fail, PartialEq)]
pub enum TypeError {
#[fail(display = "Can't unify {} with {}", _0, _1)]
CantUnify(Ty, Ty),
#[fail(display = "Undefined variables: {:?}", _0)]
Freevars(BTreeSet<Symbol>),
#[fail(display = "Mutual recursion involving {}", _0)]
MutualRecursion(Symbol),
#[fail(display = "{} occurs within {} when solving {} ~ {}", _0, _1, _0, _1)]
Occurs(SubstVar, Ty),
}
pub fn typeck(
decls: Vec<Decl<()>>,
mut checked: Vec<Decl<Type>>,
) -> Result<Vec<Decl<Type>>, TypeError> {
let mut freevars = decls
.iter()
.flat_map(|decl| decl.freevars())
.collect::<BTreeSet<_>>();
for decl in &decls {
freevars.remove(&decl.name);
}
for decl in &checked {
freevars.remove(&decl.name);
}
if !freevars.is_empty() {
return Err(TypeError::Freevars(freevars));
}
let known = checked.iter().map(|decl| decl.name).collect::<HashSet<_>>();
let decls = group(decls, |decl| decl.name);
let decls = toposort(decls, known, |decls| {
let mut vars = BTreeSet::new();
for decl in decls {
vars.extend(decl.freevars());
}
vars.into_iter().collect()
}).map_err(TypeError::MutualRecursion)?;
for decl in decls {
let decl = typeck_decls_with(decl, &checked)?;
checked.extend(decl);
}
Ok(checked)
}
fn typeck_decls_with(
decls: Vec<Decl<()>>,
checked: &[Decl<Type>],
) -> Result<Vec<Decl<Type>>, TypeError> {
if decls.is_empty() {
return Ok(Vec::new());
}
let mut env = AnnotEnv::new();
for decl in checked {
env.put_poly(decl.name, decl.aux());
}
env.put(decls[0].name, Ty::fresh());
let mut decls = decls
.into_iter()
.map(|decl| decl.add_type_annotations(&mut env.clone()))
.collect::<Vec<_>>();
let constraints = decls
.iter()
.flat_map(|decl| decl.collect_constraints())
.collect();
debug!("decls = {:?}", decls);
debug!("constraints = {:?}", constraints);
let subst = unify(constraints)?;
for decl in &mut decls {
decl.apply_subst(&subst);
}
let decls = decls.into_iter().map(|decl| decl.reify()).collect();
Ok(decls)
}
fn unify(constraints: BTreeSet<Constraint>) -> Result<Substitution, TypeError> {
let mut constraints = constraints.into_iter().collect::<Vec<_>>();
let mut subst = Substitution::new();
while let Some(Constraint(s, t)) = constraints.pop() {
debug!("Applying constraint {} ~ {}...", s, t);
if s == t {
} else {
match (s, t) {
(Ty::Var(x), t) => {
if t.freevars().contains(&x) {
return Err(TypeError::Occurs(x, t));
}
for &mut Constraint(ref mut cs, ref mut ct) in &mut constraints {
cs.sub(x, &t);
ct.sub(x, &t);
}
subst.add(x, t);
}
(s, Ty::Var(x)) => {
if s.freevars().contains(&x) {
return Err(TypeError::Occurs(x, s));
}
for &mut Constraint(ref mut cs, ref mut ct) in &mut constraints {
cs.sub(x, &s);
ct.sub(x, &s);
}
subst.add(x, s);
}
(Ty::Func(s1, s2), Ty::Func(t1, t2)) => {
constraints.push(Constraint(*s1, *t1));
constraints.push(Constraint(*s2, *t2));
}
(Ty::List(s), Ty::List(t)) => {
constraints.push(Constraint(*s, *t));
}
(s, t) => {
return Err(TypeError::CantUnify(s, t));
}
}
}
}
Ok(subst)
}