blob: e42c282c6ac755b482a1b497b4b37df01b7f4a32 [file] [log] [blame]
use super::lib::Solution;
use tracing::debug;
use chalk_ir::interner::Interner;
use chalk_ir::{ClausePriority, DomainGoal, Fallible, GenericArg, Goal, GoalData};
pub(super) fn with_priorities_for_goal<I: Interner>(
interner: &I,
goal: &Goal<I>,
a: Fallible<Solution<I>>,
prio_a: ClausePriority,
b: Fallible<Solution<I>>,
prio_b: ClausePriority,
) -> (Fallible<Solution<I>>, ClausePriority) {
let domain_goal = match goal.data(interner) {
GoalData::DomainGoal(domain_goal) => domain_goal,
_ => {
// non-domain goals currently have no priorities, so we always take the new solution here
return (b, prio_b);
}
};
match (a, b) {
(Ok(a), Ok(b)) => {
let (solution, prio) = with_priorities(interner, domain_goal, a, prio_a, b, prio_b);
(Ok(solution), prio)
}
(Ok(solution), Err(_)) => (Ok(solution), prio_a),
(Err(_), Ok(solution)) => (Ok(solution), prio_b),
(Err(_), Err(e)) => (Err(e), prio_b),
}
}
pub(super) fn with_priorities<I: Interner>(
interner: &I,
domain_goal: &DomainGoal<I>,
a: Solution<I>,
prio_a: ClausePriority,
b: Solution<I>,
prio_b: ClausePriority,
) -> (Solution<I>, ClausePriority) {
match (prio_a, prio_b, a, b) {
(ClausePriority::High, ClausePriority::Low, higher, lower)
| (ClausePriority::Low, ClausePriority::High, lower, higher) => {
// if we have a high-priority solution and a low-priority solution,
// the high-priority solution overrides *if* they are both for the
// same inputs -- we don't want a more specific high-priority
// solution overriding a general low-priority one. Currently inputs
// only matter for projections; in a goal like `AliasEq(<?0 as
// Trait>::Type = ?1)`, ?0 is the input.
let inputs_higher = calculate_inputs(interner, domain_goal, &higher);
let inputs_lower = calculate_inputs(interner, domain_goal, &lower);
if inputs_higher == inputs_lower {
debug!(
"preferring solution: {:?} over {:?} because of higher prio",
higher, lower
);
(higher, ClausePriority::High)
} else {
(higher.combine(lower, interner), ClausePriority::High)
}
}
(_, _, a, b) => (a.combine(b, interner), prio_a),
}
}
fn calculate_inputs<I: Interner>(
interner: &I,
domain_goal: &DomainGoal<I>,
solution: &Solution<I>,
) -> Vec<GenericArg<I>> {
if let Some(subst) = solution.constrained_subst() {
let subst_goal = subst.value.subst.apply(&domain_goal, interner);
subst_goal.inputs(interner)
} else {
domain_goal.inputs(interner)
}
}