use std::collections::HashMap;
use super::Checker;
use super::model::Type;
use crate::ast::Generic;
pub(super) type Bound = HashMap<String, Type>;
impl Checker<'_, '_, '_> {
pub(super) fn with_types<R>(&mut self, bound: Bound, f: impl FnOnce(&mut Self) -> R) -> R {
self.type_params.push(bound);
let out = f(self);
self.type_params.pop();
out
}
pub(super) fn type_param(&self, name: &str) -> Option<Type> {
self.type_params
.iter()
.rev()
.find_map(|b| b.get(name))
.cloned()
}
pub(super) fn bound_of(&self, name: &str, args: &[Type]) -> Bound {
let generics = self
.own
.types
.get(name)
.map(|t| t.generics.as_slice())
.unwrap_or_default();
generics
.iter()
.enumerate()
.map(|(i, g)| {
(
g.clone(),
args.get(i)
.cloned()
.unwrap_or_else(|| Type::Param(g.clone())),
)
})
.collect()
}
pub(super) fn unify(
&self,
param: &Type,
given: &Type,
free: &[String],
found: &mut Bound,
) -> bool {
match (param, given) {
(Type::Param(p), _) if free.contains(p) => match found.get(p) {
Some(t) => {
let t = t.clone();
if self.converts(given, &t) {
true
} else if self.converts(&t, given) {
found.insert(p.clone(), given.clone());
true
} else {
false
}
}
None => {
found.insert(p.clone(), given.clone());
true
}
},
(Type::Own(a, xs), Type::Own(b, ys)) if a == b && xs.len() == ys.len() => xs
.iter()
.zip(ys)
.all(|(x, y)| self.unify(x, y, free, found)),
(Type::List(x), Type::List(y)) | (Type::Nullable(x), Type::Nullable(y)) => {
self.unify(x, y, free, found)
}
(Type::Nullable(x), y) => self.unify(x, y, free, found),
(Type::Map(k, v), Type::Map(k2, v2)) => {
self.unify(k, k2, free, found) && self.unify(v, v2, free, found)
}
_ => self.converts(given, param),
}
}
}
pub(super) fn params_of(generics: &[Generic]) -> Bound {
generics
.iter()
.filter(|g| !g.lifetime)
.map(|g| (g.name.text.clone(), Type::Param(g.name.text.clone())))
.collect()
}
impl Type {
pub(super) fn substitute(&self, bound: &Bound) -> Type {
let sub = |t: &Type| t.substitute(bound);
match self {
Type::Param(p) => bound.get(p).cloned().unwrap_or_else(|| self.clone()),
Type::Own(n, args) => Type::Own(n.clone(), args.iter().map(sub).collect()),
Type::List(t) => Type::List(Box::new(sub(t))),
Type::Nullable(t) => Type::Nullable(Box::new(sub(t))),
Type::Map(k, v) => Type::Map(Box::new(sub(k)), Box::new(sub(v))),
Type::Tuple(items) => Type::Tuple(items.iter().map(sub).collect()),
Type::Fn(params, ret) => Type::Fn(params.iter().map(sub).collect(), Box::new(sub(ret))),
_ => self.clone(),
}
}
}