Skip to content

we only merge candidates for trait and normalizes-to goals #144948

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 13 additions & 30 deletions compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand All @@ -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);
}

Expand All @@ -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)
}
}
}
Expand Down
52 changes: 27 additions & 25 deletions compiler/rustc_next_trait_solver/src/solve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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))
}
}

Expand Down
22 changes: 8 additions & 14 deletions compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
};
}

Expand All @@ -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))
Expand All @@ -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))
Expand All @@ -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))
}
}

Expand Down
Loading