use std::cmp::Ordering;
use super::Checker;
use super::generics::Bound;
use super::members::Signature;
use super::model::Type;
pub(super) type Fit = (usize, Bound, Option<Signature>);
impl Checker<'_, '_, '_> {
pub(super) fn best(&self, sigs: &[Signature], fits: Vec<Fit>, given: &[Type]) -> Option<Fit> {
let mut fits = fits.into_iter();
let mut best = fits.next()?;
for f in fits {
if self.better(sigs, (&f, &best), given) == Ordering::Greater {
best = f;
}
}
Some(best)
}
fn better(&self, sigs: &[Signature], (a, b): (&Fit, &Fit), given: &[Type]) -> Ordering {
let (pa, pb) = (taken(sigs, a), taken(sigs, b));
let mut out = Ordering::Equal;
for (g, (x, y)) in given.iter().zip(pa.iter().zip(&pb)) {
match (out, self.better_conversion(g, x, y)) {
(_, Ordering::Equal) => {}
(Ordering::Equal, o) => out = o,
(o, p) if o != p => return Ordering::Equal,
_ => {}
}
}
if out != Ordering::Equal {
return out;
}
let generic = |f: &Fit| !sigs[f.0].generics.is_empty();
match (generic(a), generic(b)) {
(false, true) => Ordering::Greater,
(true, false) => Ordering::Less,
_ => Ordering::Equal,
}
}
fn better_conversion(&self, g: &Type, x: &Type, y: &Type) -> Ordering {
if x == y || !g.known() {
return Ordering::Equal;
}
if g == x {
return Ordering::Greater;
}
if g == y {
return Ordering::Less;
}
match (self.converts_plainly(x, y), self.converts_plainly(y, x)) {
(true, false) => Ordering::Greater,
(false, true) => Ordering::Less,
_ => Ordering::Equal,
}
}
}
fn taken(sigs: &[Signature], f: &Fit) -> Vec<Type> {
let s = f.2.as_ref().unwrap_or(&sigs[f.0]);
s.params.iter().map(|p| p.substitute(&f.1)).collect()
}