use indexmap::IndexMap; use petgraph::prelude::*; use rustc_hash::FxHashMap; use crate::solve::Solver; use crate::RustIrDatabase; use chalk_ir::interner::Interner; use chalk_ir::{self, ImplId, TraitId}; use std::fmt; use std::sync::Arc; pub mod orphan; mod solve; pub struct CoherenceSolver<'a, I: Interner> { db: &'a dyn RustIrDatabase, solver_builder: &'a dyn Fn() -> Box>, trait_id: TraitId, } #[derive(Debug)] pub enum CoherenceError { OverlappingImpls(TraitId), FailedOrphanCheck(TraitId), } impl fmt::Display for CoherenceError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { CoherenceError::OverlappingImpls(id) => { write!(f, "overlapping impls of trait `{:?}`", id) } CoherenceError::FailedOrphanCheck(id) => { write!(f, "impl for trait `{:?}` violates the orphan rules", id) } } } } impl std::error::Error for CoherenceError {} /// Stores the specialization priorities for a set of impls. /// This basically encodes which impls specialize one another. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct SpecializationPriorities { map: IndexMap, SpecializationPriority>, } impl SpecializationPriorities { pub fn new() -> Self { Self { map: IndexMap::new(), } } /// Lookup the priority of an impl in the set (panics if impl is not in set). pub fn priority(&self, impl_id: ImplId) -> SpecializationPriority { self.map[&impl_id] } /// Store the priority of an impl (used during construction). /// Panics if we have already stored the priority for this impl. fn insert(&mut self, impl_id: ImplId, p: SpecializationPriority) { let old_value = self.map.insert(impl_id, p); assert!(old_value.is_none()); } } /// Impls with higher priority take precedence over impls with lower /// priority (if both apply to the same types). Impls with equal /// priority should never apply to the same set of input types. #[derive(Copy, Clone, Default, PartialOrd, Ord, PartialEq, Eq, Debug)] pub struct SpecializationPriority(usize); impl<'a, I> CoherenceSolver<'a, I> where I: Interner, { /// Constructs a new `CoherenceSolver`. pub fn new( db: &'a dyn RustIrDatabase, solver_builder: &'a dyn Fn() -> Box>, trait_id: TraitId, ) -> Self { Self { db, solver_builder, trait_id, } } pub fn specialization_priorities( &self, ) -> Result>, CoherenceError> { let mut result = SpecializationPriorities::::new(); let forest = self.build_specialization_forest()?; // Visit every root in the forest & set specialization // priority for the tree that is the root of. for root_idx in forest.externals(Direction::Incoming) { self.set_priorities(root_idx, &forest, 0, &mut result); } Ok(Arc::new(result)) } // Build the forest of specialization relationships. fn build_specialization_forest(&self) -> Result, ()>, CoherenceError> { let mut forest = DiGraph::new(); let mut node_map = FxHashMap::default(); // Find all specializations. Record them in the forest // by adding an edge from the less special to the more special. self.visit_specializations_of_trait(|less_special, more_special| { let less_special_node = *node_map .entry(less_special) .or_insert_with(|| forest.add_node(less_special)); let more_special_node = *node_map .entry(more_special) .or_insert_with(|| forest.add_node(more_special)); forest.update_edge(less_special_node, more_special_node, ()); })?; Ok(forest) } // Recursively set priorities for those node and all of its children. fn set_priorities( &self, idx: NodeIndex, forest: &Graph, ()>, p: usize, map: &mut SpecializationPriorities, ) { // Get the impl datum recorded at this node and reset its priority { let impl_id = forest .node_weight(idx) .expect("index should be a valid index into graph"); map.insert(*impl_id, SpecializationPriority(p)); } // Visit all children of this node, setting their priority to this + 1 for child_idx in forest.neighbors(idx) { self.set_priorities(child_idx, forest, p + 1, map); } } }