Only prefer param-env candidates if they remain non-global after norm

This commit is contained in:
Michael Goulet
2025-05-06 20:24:23 +00:00
parent df13f7c1fa
commit 1f774d74b3
8 changed files with 268 additions and 145 deletions

View File

@@ -17,7 +17,7 @@ use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidates
use crate::solve::inspect::ProbeKind;
use crate::solve::{
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
NoSolution, QueryResult,
NoSolution, ParamEnvSource,
};
impl<D, I> assembly::GoalKind<D> for TraitPredicate<I>
@@ -125,39 +125,40 @@ where
.enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
}
fn probe_and_match_goal_against_assumption(
fn fast_reject_assumption(
ecx: &mut EvalCtxt<'_, D>,
source: CandidateSource<I>,
goal: Goal<I, Self>,
assumption: I::Clause,
then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
) -> Result<Candidate<I>, NoSolution> {
) -> Result<(), NoSolution> {
if let Some(trait_clause) = assumption.as_trait_clause() {
if trait_clause.def_id() == goal.predicate.def_id()
&& trait_clause.polarity() == goal.predicate.polarity
{
if !DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
if DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
goal.predicate.trait_ref.args,
trait_clause.skip_binder().trait_ref.args,
) {
return Err(NoSolution);
return Ok(());
}
ecx.probe_trait_candidate(source).enter(|ecx| {
let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause);
ecx.eq(
goal.param_env,
goal.predicate.trait_ref,
assumption_trait_pred.trait_ref,
)?;
then(ecx)
})
} else {
Err(NoSolution)
}
} else {
Err(NoSolution)
}
Err(NoSolution)
}
fn match_assumption(
ecx: &mut EvalCtxt<'_, D>,
goal: Goal<I, Self>,
assumption: I::Clause,
) -> Result<(), NoSolution> {
let Some(trait_clause) = assumption.as_trait_clause() else {
panic!("fast_reject_assumption should have avoided this");
};
let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause);
ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?;
Ok(())
}
fn consider_auto_trait_candidate(
@@ -1253,10 +1254,9 @@ where
D: SolverDelegate<Interner = I>,
I: Interner,
{
#[instrument(level = "debug", skip(self, goal), ret)]
#[instrument(level = "debug", skip(self), ret)]
pub(super) fn merge_trait_candidates(
&mut self,
goal: Goal<I, TraitPredicate<I>>,
mut candidates: Vec<Candidate<I>>,
) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
if let TypingMode::Coherence = self.typing_mode() {
@@ -1284,21 +1284,9 @@ where
// If there are non-global where-bounds, prefer where-bounds
// (including global ones) over everything else.
let has_non_global_where_bounds = candidates.iter().any(|c| match c.source {
CandidateSource::ParamEnv(idx) => {
let where_bound = goal.param_env.caller_bounds().get(idx).unwrap();
let ty::ClauseKind::Trait(trait_pred) = where_bound.kind().skip_binder() else {
unreachable!("expected trait-bound: {where_bound:?}");
};
if trait_pred.has_bound_vars() || !trait_pred.is_global() {
return true;
}
false
}
_ => false,
});
let has_non_global_where_bounds = candidates
.iter()
.any(|c| matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::NonGlobal)));
if has_non_global_where_bounds {
let where_bounds: Vec<_> = candidates
.iter()
@@ -1331,13 +1319,16 @@ where
// is still reported as being proven-via the param-env so that rigid projections
// operate correctly. Otherwise, drop all global where-bounds before merging the
// remaining candidates.
let proven_via =
if candidates.iter().all(|c| matches!(c.source, CandidateSource::ParamEnv(_))) {
TraitGoalProvenVia::ParamEnv
} else {
candidates.retain(|c| !matches!(c.source, CandidateSource::ParamEnv(_)));
TraitGoalProvenVia::Misc
};
let proven_via = if candidates
.iter()
.all(|c| matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global)))
{
TraitGoalProvenVia::ParamEnv
} else {
candidates
.retain(|c| !matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global)));
TraitGoalProvenVia::Misc
};
let all_candidates: Vec<_> = candidates.into_iter().map(|c| c.result).collect();
if let Some(response) = self.try_merge_responses(&all_candidates) {
@@ -1353,7 +1344,7 @@ where
goal: Goal<I, TraitPredicate<I>>,
) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
let candidates = self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
self.merge_trait_candidates(goal, candidates)
self.merge_trait_candidates(candidates)
}
fn try_stall_coroutine_witness(