use crate::{query, Constraint, RefID, TEnv, TraitIndex, Type, TypeData, TypeKind, Types};
pub struct TypeContext<'a, D: TypeData> {
pub(crate) tenv: &'a mut TEnv<D>,
pub(crate) traits: &'a TraitIndex<D>,
ehandler: ErrorHandler,
}
#[derive(Clone)]
pub enum ErrorHandler {
PanicOnError,
Cheap,
Expensive,
}
#[derive(Debug, Clone)]
pub enum Error<D: TypeData> {
Missmatch {
left: Type<D>,
right: Type<D>,
},
ParamAmountMissmatch {
left: Type<D>,
right: Type<D>,
},
ConstraintNotMet(Type<D>, Constraint<D>, Vec<query::Contender>),
Disgarded,
}
type CheckResult<D> = Result<(), Error<D>>;
impl<'a, D: TypeData> TypeContext<'a, D> {
pub fn new(tenv: &'a mut TEnv<D>, traits: &'a TraitIndex<D>, ehandler: ErrorHandler) -> Self {
Self { tenv, traits, ehandler }
}
pub fn check(&mut self, left: &Type<D>, right: &Type<D>) -> CheckResult<D> {
match (&left.constr, &right.constr) {
(TypeKind::Ref(lrid), TypeKind::Ref(rrid)) => {
if *lrid == *rrid {
self.params(left, right)
} else {
match (
self.tenv.get_type(*lrid).cloned(),
self.tenv.get_type(*rrid).cloned(),
) {
(Some(l), Some(r)) => self.check(&l, &r),
(Some(l), None) => self.check(&l, right),
(None, Some(r)) => self.check(left, &r),
(None, None) => self.assign_refs_bidir(*lrid, left, *rrid, right),
}
}
}
(TypeKind::Object(ltrid), TypeKind::Object(rtrid)) if ltrid == rtrid => {
self.params(left, right)
}
(TypeKind::Concrete(lspec), TypeKind::Concrete(rspec)) if lspec == rspec => {
self.params(left, right)
}
(TypeKind::Generic(lgid), TypeKind::Generic(rgid)) if lgid == rgid => {
self.params(left, right)
}
(TypeKind::Ref(lrid), _) => match self.tenv.get_type(*lrid).cloned() {
Some(assigned) => self.check(&assigned, right),
None => self.assign_to_ref(*lrid, &left.params, right.clone()),
},
(_, TypeKind::Ref(rrid)) => match self.tenv.get_type(*rrid).cloned() {
Some(assigned) => self.check(left, &assigned),
None => self.assign_to_ref(*rrid, &right.params, left.clone()),
},
(TypeKind::Self_, TypeKind::Self_) => todo!(),
(TypeKind::Self_, _) => todo!(),
(_, TypeKind::Self_) => todo!(),
_ => Err(self.ehandler.missmatch(self.tenv, left, right)),
}
}
pub fn check_types(&mut self, left: &Types<D>, right: &Types<D>) -> Result<(), Error<D>> {
left.iter()
.zip(right)
.try_for_each(|(l, r)| self.check(l, r))
}
fn assign_to_ref(
&mut self,
rid: RefID,
rid_params: &Types<D>,
given: Type<D>,
) -> CheckResult<D> {
self.check_constraints_then_assign(rid, rid_params, given)
}
fn assign_refs_bidir(
&mut self,
lrid: RefID,
left: &Type<D>,
rrid: RefID,
right: &Type<D>,
) -> CheckResult<D> {
let apply_assignments = |self_: &mut Self| {
let merged = self_
.tenv
.constraints(lrid)
.iter()
.chain(self_.tenv.constraints(rrid))
.cloned()
.collect();
let mrid = self_.tenv.spawn_with_cons(merged);
let type_ = Type::reference(left.meta.clone(), mrid, vec![]);
self_.tenv.assign(lrid, type_.clone());
self_.tenv.assign(rrid, type_);
};
match (left.params.len(), right.params.len()) {
(x, y) if x == y => {
apply_assignments(self);
self.params(left, right)
}
(_, 0) => todo!("what to do with left.params"),
(0, _) => todo!("what to do with right.params"),
_ => Err(self.ehandler.param_amount_missmatch(self.tenv, left, right)),
}
}
fn params(&mut self, left: &Type<D>, right: &Type<D>) -> CheckResult<D> {
if left.params.len() != right.params.len() {
Err(self.ehandler.param_amount_missmatch(self.tenv, left, right))
} else {
self.check_types(&left.params, &right.params)
}
}
pub(crate) fn check_constraints_then_assign(
&mut self,
rid: RefID,
rid_params: &Types<D>,
mut given: Type<D>,
) -> CheckResult<D> {
if !rid_params.is_empty() {
let at = given.params.len() - rid_params.len();
let corresponding_of_higher_kinded = given.params.split_off(at);
self.check_types(&corresponding_of_higher_kinded, rid_params)?;
}
let constrs = self.tenv.constraints(rid).to_vec();
for con in constrs {
let compatible = self
.traits
.select(self.tenv, con.trid.clone(), &con.params, &given);
match compatible {
Err(contendors) => {
return Err(Error::ConstraintNotMet(
given.clone(),
con.clone(),
contendors,
))
}
Ok(mut matches) => {
let query::QuerySuccess { impl_, tenv, .. } = matches.remove(0);
if !matches.is_empty() {
todo!("ET: conflicting implementations/inference error??");
}
*self.tenv = tenv;
if !impl_.associated.is_empty() {
unimplemented!(
"we need to port this association instantiation to the new api"
);
};
}
}
}
self.tenv.assign(rid, given);
Ok(())
}
}
impl ErrorHandler {
pub fn missmatch<D: TypeData>(
&mut self,
tenv: &TEnv<D>,
left: &Type<D>,
right: &Type<D>,
) -> Error<D> {
match self {
ErrorHandler::Cheap => Error::Disgarded,
ErrorHandler::PanicOnError => panic!(
"type missmatch: {} ⊑ {}",
tenv.concretify_type(left),
tenv.concretify_type(right)
),
ErrorHandler::Expensive => Error::Missmatch {
left: left.clone(),
right: right.clone(),
},
}
}
pub fn param_amount_missmatch<D: TypeData>(
&mut self,
tenv: &TEnv<D>,
left: &Type<D>,
right: &Type<D>,
) -> Error<D> {
match self {
ErrorHandler::Cheap => Error::Disgarded,
ErrorHandler::PanicOnError => panic!(
"type missmatch: {} ⊑ {}",
tenv.concretify_type(left),
tenv.concretify_type(right)
),
ErrorHandler::Expensive => Error::ParamAmountMissmatch {
left: left.clone(),
right: right.clone(),
},
}
}
}