use std::collections::BTreeMap;
use pedant_types::ResolutionGap;
use crate::resolution::go::limits::GoResolutionLimits;
use super::corpus::Corpus;
use super::error::GoResolutionError;
use super::index::{Index, Slot};
use super::interfaces::{self, Expansion, Requirement};
use super::methods::{Member, MethodSet};
use super::signatures::Signatures;
use super::types::Receiver;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(super) struct ConcreteSlot(usize);
impl ConcreteSlot {
pub(super) fn slot(self) -> usize {
self.0
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub(super) struct InterfaceSlot(usize);
impl InterfaceSlot {
pub(super) fn new(slot: usize) -> Self {
Self(slot)
}
pub(super) fn slot(self) -> usize {
self.0
}
}
pub(super) struct Relation {
pub(super) concrete: ConcreteSlot,
pub(super) interface: InterfaceSlot,
pub(super) pointer: bool,
pub(super) gap: Option<ResolutionGap>,
}
#[derive(Clone, Copy)]
struct Match {
pointer: bool,
gap: Option<ResolutionGap>,
}
pub(super) struct Implementations {
relations: Box<[Relation]>,
implementors: BTreeMap<InterfaceSlot, Box<[ConcreteSlot]>>,
declared: BTreeMap<usize, Box<[usize]>>,
sets: BTreeMap<ConcreteSlot, MethodSet>,
}
impl Implementations {
pub(super) fn of(
index: &Index,
corpus: &Corpus<'_>,
limits: GoResolutionLimits,
) -> Result<Self, GoResolutionError> {
let signatures = Signatures::of(index, corpus)?;
let expansions = expansions(index, &signatures);
let sets = method_sets(index);
let mut draft = Draft::default();
let mut spent = 0_u64;
for (concrete, set) in sets.iter() {
draft.compare_type(
(index, &signatures),
((*concrete, set), &expansions),
(&mut spent, limits),
)?;
}
Ok(draft.seal(index, sets))
}
pub(super) fn of_unit(&self, unit: usize) -> impl Iterator<Item = &Relation> {
self.declared
.get(&unit)
.map(|held| &**held)
.unwrap_or_default()
.iter()
.filter_map(|at| self.relations.get(*at))
}
pub(super) fn implementors(&self, interface: InterfaceSlot) -> &[ConcreteSlot] {
self.implementors
.get(&interface)
.map(|found| &**found)
.unwrap_or_default()
}
pub(super) fn answering(&self, concrete: ConcreteSlot, member: &str) -> &[Member] {
self.sets
.get(&concrete)
.map(|set| set.named(member))
.unwrap_or_default()
}
}
#[derive(Default)]
struct Draft {
relations: Vec<Relation>,
implementors: BTreeMap<InterfaceSlot, Vec<ConcreteSlot>>,
}
impl Draft {
fn compare_type(
&mut self,
read: (&Index, &Signatures),
against: (
(ConcreteSlot, &MethodSet),
&BTreeMap<InterfaceSlot, Expansion>,
),
budget: (&mut u64, GoResolutionLimits),
) -> Result<(), GoResolutionError> {
let (concrete, expansions) = against;
let (spent, limits) = budget;
for (interface, expansion) in expansions.iter() {
self.compare_pair(read, (concrete, (*interface, expansion)), (spent, limits))?;
}
Ok(())
}
fn compare_pair(
&mut self,
read: (&Index, &Signatures),
pair: ((ConcreteSlot, &MethodSet), (InterfaceSlot, &Expansion)),
budget: (&mut u64, GoResolutionLimits),
) -> Result<(), GoResolutionError> {
let (spent, limits) = budget;
check_comparison_capacity(*spent, limits)?;
*spent += 1;
let (index, signatures) = read;
let ((concrete, set), (interface, expansion)) = pair;
let found = matched(index, signatures, (set, expansion));
self.retain_relation((concrete, interface), found);
Ok(())
}
fn retain_relation(&mut self, pair: (ConcreteSlot, InterfaceSlot), found: Option<Match>) {
let Some(found) = found else {
return;
};
let (concrete, interface) = pair;
self.relations.push(Relation {
concrete,
interface,
pointer: found.pointer,
gap: found.gap,
});
self.implementors
.entry(interface)
.or_default()
.push(concrete);
}
fn seal(self, index: &Index, sets: BTreeMap<ConcreteSlot, MethodSet>) -> Implementations {
let declared = declared_by_unit(index, &self.relations);
Implementations {
relations: self.relations.into_boxed_slice(),
implementors: self
.implementors
.into_iter()
.map(|(interface, concrete)| (interface, concrete.into_boxed_slice()))
.collect(),
declared,
sets,
}
}
}
fn check_comparison_capacity(
spent: u64,
limits: GoResolutionLimits,
) -> Result<(), GoResolutionError> {
let ceiling = limits.max_interface_comparisons;
match spent < u64::from(ceiling) {
true => Ok(()),
false => Err(GoResolutionError::InterfaceComparisonLimitExceeded { limit: ceiling }),
}
}
fn expansions(index: &Index, signatures: &Signatures) -> BTreeMap<InterfaceSlot, Expansion> {
index
.stated()
.iter()
.enumerate()
.filter(|(_, slot)| slot.is_interface())
.map(|(slot, _)| {
(
InterfaceSlot(slot),
interfaces::expand(index, signatures, slot),
)
})
.collect()
}
fn method_sets(index: &Index) -> BTreeMap<ConcreteSlot, MethodSet> {
index
.stated()
.iter()
.enumerate()
.filter(|(_, slot)| slot.is_concrete_type())
.map(|(slot, _)| (ConcreteSlot(slot), MethodSet::of(index, Receiver { slot })))
.collect()
}
fn declared_by_unit(index: &Index, relations: &[Relation]) -> BTreeMap<usize, Box<[usize]>> {
let mut declared: BTreeMap<usize, Vec<usize>> = BTreeMap::new();
let stated = relations
.iter()
.enumerate()
.filter_map(|(at, relation)| declaring_unit(index, relation).map(|unit| (unit, at)));
for (unit, at) in stated {
declared.entry(unit).or_default().push(at);
}
declared
.into_iter()
.map(|(unit, held)| (unit, held.into_boxed_slice()))
.collect()
}
fn declaring_unit(index: &Index, relation: &Relation) -> Option<usize> {
index.slot(relation.concrete.slot()).map(|slot| slot.unit)
}
fn matched(
index: &Index,
signatures: &Signatures,
asked: (&MethodSet, &Expansion),
) -> Option<Match> {
let (set, expansion) = asked;
if expansion.required().is_empty() {
return None;
}
let mut answer = Match {
pointer: false,
gap: expansion.gap(),
};
for requirement in expansion.required() {
answer = answered(index, signatures, (set, requirement), answer)?;
}
Some(answer)
}
fn answered(
index: &Index,
signatures: &Signatures,
asked: (&MethodSet, &Requirement),
answer: Match,
) -> Option<Match> {
let (set, requirement) = asked;
let found: Box<[Member]> = set
.named(requirement.name())
.iter()
.copied()
.filter(|member| index.slot(member.slot).is_some_and(answers))
.collect();
match *found {
[only] if !only.multiples => compared(index, signatures, (requirement, only), answer),
[] => None,
_ => Some(Match {
gap: answer.gap.or(Some(ResolutionGap::Ambiguous)),
..answer
}),
}
}
fn answers(slot: &Slot) -> bool {
slot.is_concrete_method() || slot.is_interface_method()
}
fn compared(
index: &Index,
signatures: &Signatures,
found: (&Requirement, Member),
answer: Match,
) -> Option<Match> {
let (requirement, member) = found;
let pointer = answer.pointer || needs_address(index, member);
match (requirement.signature(), signatures.of_slot(member.slot)) {
(Some(required), Some(stated)) => {
(required == stated).then_some(Match { pointer, ..answer })
}
_ => Some(Match {
pointer,
gap: answer.gap.or(Some(ResolutionGap::UnsupportedSyntax)),
}),
}
}
fn needs_address(index: &Index, member: Member) -> bool {
index
.slot(member.slot)
.is_some_and(|slot| slot.pointer_receiver)
&& !member.through_pointer
}