use crate::coherence::{CoherenceError, CoherenceSolver}; use crate::debug_span; use crate::ext::*; use crate::rust_ir::*; use crate::{goal_builder::GoalBuilder, Solution}; use chalk_ir::cast::*; use chalk_ir::fold::shift::Shift; use chalk_ir::interner::Interner; use chalk_ir::*; use itertools::Itertools; use tracing::{debug, instrument}; impl CoherenceSolver<'_, I> { pub(super) fn visit_specializations_of_trait( &self, mut record_specialization: impl FnMut(ImplId, ImplId), ) -> Result<(), CoherenceError> { // Ignore impls for marker traits as they are allowed to overlap. let trait_datum = self.db.trait_datum(self.trait_id); if trait_datum.flags.marker { return Ok(()); } // Iterate over every pair of impls for the same trait. let impls = self.db.local_impls_to_coherence_check(self.trait_id); for (l_id, r_id) in impls.into_iter().tuple_combinations() { let lhs = &self.db.impl_datum(l_id); let rhs = &self.db.impl_datum(r_id); // Two negative impls never overlap. if !lhs.is_positive() && !rhs.is_positive() { continue; } // Check if the impls overlap, then if they do, check if one specializes // the other. Note that specialization can only run one way - if both // specialization checks return *either* true or false, that's an error. if !self.disjoint(lhs, rhs) { match (self.specializes(l_id, r_id), self.specializes(r_id, l_id)) { (true, false) => record_specialization(l_id, r_id), (false, true) => record_specialization(r_id, l_id), (_, _) => { return Err(CoherenceError::OverlappingImpls(self.trait_id)); } } } } Ok(()) } // Test if the set of types that these two impls apply to overlap. If the test succeeds, these // two impls are disjoint. // // We combine the binders of the two impls & treat them as existential quantifiers. Then we // attempt to unify the input types to the trait provided by each impl, as well as prove that // the where clauses from both impls all hold. At the end, we apply the `compatible` modality // and negate the query. Negating the query means that we are asking chalk to prove that no // such overlapping impl exists. By applying `compatible { G }`, chalk attempts to prove that // "there exists a compatible world where G is provable." When we negate compatible, it turns // into the statement "for all compatible worlds, G is not provable." This is exactly what we // want since we want to ensure that there is no overlap in *all* compatible worlds, not just // that there is no overlap in *some* compatible world. // // Examples: // // Impls: // impl Foo for T { } // rhs // impl Foo for i32 { } // lhs // Generates: // not { compatible { exists { exists<> { T = i32 } } } } // // Impls: // impl Foo for Vec { } // rhs // impl Foo for Vec { } // lhs // Generates: // not { compatible { exists { exists { Vec = Vec, T1 = T2 } } } } // // Impls: // impl Foo for Vec where T: Bar { } // impl Foo for Vec where U: Baz { } // Generates: // not { compatible { exists { exists { Vec = Vec, T: Bar, U: Baz } } } } // #[instrument(level = "debug", skip(self))] fn disjoint(&self, lhs: &ImplDatum, rhs: &ImplDatum) -> bool { let interner = self.db.interner(); let (lhs_binders, lhs_bound) = lhs.binders.as_ref().into(); let (rhs_binders, rhs_bound) = rhs.binders.as_ref().into(); // Upshift the rhs variables in params to account for the joined binders let lhs_params = lhs_bound .trait_ref .substitution .as_slice(interner) .iter() .cloned(); let rhs_params = rhs_bound .trait_ref .substitution .as_slice(interner) .iter() .map(|param| param.clone().shifted_in(interner)); // Create an equality goal for every input type the trait, attempting // to unify the inputs to both impls with one another let params_goals = lhs_params .zip(rhs_params) .map(|(a, b)| GoalData::EqGoal(EqGoal { a, b }).intern(interner)); // Upshift the rhs variables in where clauses let lhs_where_clauses = lhs_bound.where_clauses.iter().cloned(); let rhs_where_clauses = rhs_bound .where_clauses .iter() .map(|wc| wc.clone().shifted_in(interner)); // Create a goal for each clause in both where clauses let wc_goals = lhs_where_clauses .chain(rhs_where_clauses) .map(|wc| wc.cast(interner)); // Join all the goals we've created together with And, then quantify them // over the joined binders. This is our query. let goal = Box::new(Goal::all(interner, params_goals.chain(wc_goals))) .quantify(interner, QuantifierKind::Exists, lhs_binders) .quantify(interner, QuantifierKind::Exists, rhs_binders) .compatible(interner) .negate(interner); let canonical_goal = &goal.into_closed_goal(interner); let mut fresh_solver = (self.solver_builder)(); let solution = fresh_solver.solve(self.db, canonical_goal); let result = match solution { // Goal was proven with a unique solution, so no impl was found that causes these two // to overlap Some(Solution::Unique(_)) => true, // Goal was ambiguous, so there *may* be overlap Some(Solution::Ambig(_)) | // Goal cannot be proven, so there is some impl that causes overlap None => false, }; debug!("overlaps: result = {:?}", result); result } // Creates a goal which, if provable, means "more special" impl specializes the "less special" one. // // # General rule // // Given the more special impl: // // ```ignore // impl SomeTrait for T0 where WC_more // ``` // // and less special impl // // ```ignore // impl SomeTrait for U0 where WC_less // ``` // // create the goal: // // ```ignore // forall { // if (WC_more) {} // exists { // T0 = U0, ..., Tm = Um, // WC_less // } // } // } // ``` // // # Example // // Given: // // * more: `impl Foo for Vec` // * less: `impl Foo for U` // // Resulting goal: // // ```ignore // forall { // if (T: Clone) { // exists { // Vec = U, U: Clone // } // } // } // ``` #[instrument(level = "debug", skip(self))] fn specializes(&self, less_special_id: ImplId, more_special_id: ImplId) -> bool { let more_special = &self.db.impl_datum(more_special_id); let less_special = &self.db.impl_datum(less_special_id); debug_span!("specializes", ?less_special, ?more_special); let interner = self.db.interner(); let gb = &mut GoalBuilder::new(self.db); // forall { ... } let goal = gb.forall( &more_special.binders, less_special_id, |gb, _, more_special_impl, less_special_id| { // if (WC_more) { ... } gb.implies(more_special_impl.where_clauses.iter().cloned(), |gb| { let less_special = &gb.db().impl_datum(less_special_id); // exists { ... } gb.exists( &less_special.binders, more_special_impl.trait_ref.clone(), |gb, _, less_special_impl, more_special_trait_ref| { let interner = gb.interner(); // T0 = U0, ..., Tm = Um let params_goals = more_special_trait_ref .substitution .as_slice(interner) .iter() .cloned() .zip( less_special_impl .trait_ref .substitution .as_slice(interner) .iter() .cloned(), ) .map(|(a, b)| GoalData::EqGoal(EqGoal { a, b }).intern(interner)); // = where clauses from the less special impl let less_special_wc_goals = less_special_impl .where_clauses .iter() .cloned() .casted(interner); // && WC_less gb.all(params_goals.chain(less_special_wc_goals)) }, ) }) }, ); let canonical_goal = &goal.into_closed_goal(interner); let mut fresh_solver = (self.solver_builder)(); let result = fresh_solver.has_unique_solution(self.db, canonical_goal); debug!("specializes: result = {:?}", result); result } }