Rollup merge of #144948 - lcnr:change-candidate-handling, r=compiler-errors

we only merge candidates for trait and normalizes-to goals

so change `fn try_merge_responses` to `fn try_merge_candidates` and just use candidates everywhere.

Potentially slightly faster than the alternative :3

r? ``@compiler-errors`` ``@BoxyUwU``
This commit is contained in:
Guillaume Gomez 2025-08-06 21:29:29 +02:00 committed by GitHub
commit f7520353ab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 48 additions and 69 deletions

View file

@ -959,36 +959,23 @@ where
// Even when a trait bound has been proven using a where-bound, we
// still need to consider alias-bounds for normalization, see
// `tests/ui/next-solver/alias-bound-shadowed-by-env.rs`.
let candidates_from_env_and_bounds: Vec<_> = self
let mut candidates: Vec<_> = self
.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::EnvAndBounds);
// We still need to prefer where-bounds over alias-bounds however.
// See `tests/ui/winnowing/norm-where-bound-gt-alias-bound.rs`.
let mut considered_candidates: Vec<_> = if candidates_from_env_and_bounds
.iter()
.any(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
{
candidates_from_env_and_bounds
.into_iter()
.filter(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
.map(|c| c.result)
.collect()
} else {
candidates_from_env_and_bounds.into_iter().map(|c| c.result).collect()
};
// If the trait goal has been proven by using the environment, we want to treat
// aliases as rigid if there are no applicable projection bounds in the environment.
if considered_candidates.is_empty() {
if let Ok(response) = inject_normalize_to_rigid_candidate(self) {
considered_candidates.push(response);
}
if candidates.iter().any(|c| matches!(c.source, CandidateSource::ParamEnv(_))) {
candidates.retain(|c| matches!(c.source, CandidateSource::ParamEnv(_)));
} else if candidates.is_empty() {
// If the trait goal has been proven by using the environment, we want to treat
// aliases as rigid if there are no applicable projection bounds in the environment.
return inject_normalize_to_rigid_candidate(self);
}
if let Some(response) = self.try_merge_responses(&considered_candidates) {
if let Some(response) = self.try_merge_candidates(&candidates) {
Ok(response)
} else {
self.flounder(&considered_candidates)
self.flounder(&candidates)
}
}
TraitGoalProvenVia::Misc => {
@ -998,11 +985,9 @@ where
// Prefer "orphaned" param-env normalization predicates, which are used
// (for example, and ideally only) when proving item bounds for an impl.
let candidates_from_env: Vec<_> = candidates
.iter()
.filter(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
.map(|c| c.result)
.extract_if(.., |c| matches!(c.source, CandidateSource::ParamEnv(_)))
.collect();
if let Some(response) = self.try_merge_responses(&candidates_from_env) {
if let Some(response) = self.try_merge_candidates(&candidates_from_env) {
return Ok(response);
}
@ -1012,12 +997,10 @@ where
// means we can just ignore inference constraints and don't have to special-case
// constraining the normalized-to `term`.
self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates);
let responses: Vec<_> = candidates.iter().map(|c| c.result).collect();
if let Some(response) = self.try_merge_responses(&responses) {
if let Some(response) = self.try_merge_candidates(&candidates) {
Ok(response)
} else {
self.flounder(&responses)
self.flounder(&candidates)
}
}
}

View file

@ -29,6 +29,7 @@ use tracing::instrument;
pub use self::eval_ctxt::{EvalCtxt, GenerateProofTree, SolverDelegateEvalExt};
use crate::delegate::SolverDelegate;
use crate::solve::assembly::Candidate;
/// How many fixpoint iterations we should attempt inside of the solver before bailing
/// with overflow.
@ -244,50 +245,51 @@ where
///
/// In this case we tend to flounder and return ambiguity by calling `[EvalCtxt::flounder]`.
#[instrument(level = "trace", skip(self), ret)]
fn try_merge_responses(
fn try_merge_candidates(
&mut self,
responses: &[CanonicalResponse<I>],
candidates: &[Candidate<I>],
) -> Option<CanonicalResponse<I>> {
if responses.is_empty() {
if candidates.is_empty() {
return None;
}
let one = responses[0];
if responses[1..].iter().all(|&resp| resp == one) {
let one: CanonicalResponse<I> = candidates[0].result;
if candidates[1..].iter().all(|candidate| candidate.result == one) {
return Some(one);
}
responses
candidates
.iter()
.find(|response| {
response.value.certainty == Certainty::Yes
&& has_no_inference_or_external_constraints(**response)
.find(|candidate| {
candidate.result.value.certainty == Certainty::Yes
&& has_no_inference_or_external_constraints(candidate.result)
})
.copied()
.map(|candidate| candidate.result)
}
fn bail_with_ambiguity(&mut self, responses: &[CanonicalResponse<I>]) -> CanonicalResponse<I> {
debug_assert!(responses.len() > 1);
let maybe_cause = responses.iter().fold(MaybeCause::Ambiguity, |maybe_cause, response| {
// Pull down the certainty of `Certainty::Yes` to ambiguity when combining
// these responses, b/c we're combining more than one response and this we
// don't know which one applies.
let candidate = match response.value.certainty {
Certainty::Yes => MaybeCause::Ambiguity,
Certainty::Maybe(candidate) => candidate,
};
maybe_cause.or(candidate)
});
fn bail_with_ambiguity(&mut self, candidates: &[Candidate<I>]) -> CanonicalResponse<I> {
debug_assert!(candidates.len() > 1);
let maybe_cause =
candidates.iter().fold(MaybeCause::Ambiguity, |maybe_cause, candidates| {
// Pull down the certainty of `Certainty::Yes` to ambiguity when combining
// these responses, b/c we're combining more than one response and this we
// don't know which one applies.
let candidate = match candidates.result.value.certainty {
Certainty::Yes => MaybeCause::Ambiguity,
Certainty::Maybe(candidate) => candidate,
};
maybe_cause.or(candidate)
});
self.make_ambiguous_response_no_constraints(maybe_cause)
}
/// If we fail to merge responses we flounder and return overflow or ambiguity.
#[instrument(level = "trace", skip(self), ret)]
fn flounder(&mut self, responses: &[CanonicalResponse<I>]) -> QueryResult<I> {
if responses.is_empty() {
fn flounder(&mut self, candidates: &[Candidate<I>]) -> QueryResult<I> {
if candidates.is_empty() {
return Err(NoSolution);
} else {
Ok(self.bail_with_ambiguity(responses))
Ok(self.bail_with_ambiguity(candidates))
}
}

View file

@ -1346,11 +1346,10 @@ where
mut candidates: Vec<Candidate<I>>,
) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
if let TypingMode::Coherence = self.typing_mode() {
let all_candidates: Vec<_> = candidates.into_iter().map(|c| c.result).collect();
return if let Some(response) = self.try_merge_responses(&all_candidates) {
return if let Some(response) = self.try_merge_candidates(&candidates) {
Ok((response, Some(TraitGoalProvenVia::Misc)))
} else {
self.flounder(&all_candidates).map(|r| (r, None))
self.flounder(&candidates).map(|r| (r, None))
};
}
@ -1375,11 +1374,9 @@ where
.any(|c| matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::NonGlobal)));
if has_non_global_where_bounds {
let where_bounds: Vec<_> = candidates
.iter()
.filter(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
.map(|c| c.result)
.extract_if(.., |c| matches!(c.source, CandidateSource::ParamEnv(_)))
.collect();
return if let Some(response) = self.try_merge_responses(&where_bounds) {
return if let Some(response) = self.try_merge_candidates(&where_bounds) {
Ok((response, Some(TraitGoalProvenVia::ParamEnv)))
} else {
Ok((self.bail_with_ambiguity(&where_bounds), None))
@ -1388,11 +1385,9 @@ where
if candidates.iter().any(|c| matches!(c.source, CandidateSource::AliasBound)) {
let alias_bounds: Vec<_> = candidates
.iter()
.filter(|c| matches!(c.source, CandidateSource::AliasBound))
.map(|c| c.result)
.extract_if(.., |c| matches!(c.source, CandidateSource::AliasBound))
.collect();
return if let Some(response) = self.try_merge_responses(&alias_bounds) {
return if let Some(response) = self.try_merge_candidates(&alias_bounds) {
Ok((response, Some(TraitGoalProvenVia::AliasBound)))
} else {
Ok((self.bail_with_ambiguity(&alias_bounds), None))
@ -1417,11 +1412,10 @@ where
TraitGoalProvenVia::Misc
};
let all_candidates: Vec<_> = candidates.into_iter().map(|c| c.result).collect();
if let Some(response) = self.try_merge_responses(&all_candidates) {
if let Some(response) = self.try_merge_candidates(&candidates) {
Ok((response, Some(proven_via)))
} else {
self.flounder(&all_candidates).map(|r| (r, None))
self.flounder(&candidates).map(|r| (r, None))
}
}