Skip to content

Commit ead6920

Browse files
committed
do not prefer trivially true candidates
1 parent 8d1a764 commit ead6920

5 files changed

Lines changed: 38 additions & 171 deletions

File tree

‎compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1335,7 +1335,7 @@ where
13351335
candidates.retain(|c| matches!(c.source, CandidateSource::ParamEnv(_)));
13361336
}
13371337

1338-
if let Some((response, _)) = self.try_merge_candidates(&candidates) {
1338+
if let Some(response) = self.try_merge_candidates(&candidates) {
13391339
Ok(response)
13401340
} else {
13411341
self.flounder(&candidates).map_err(Into::into)
@@ -1357,7 +1357,7 @@ where
13571357
// means we can just ignore inference constraints and don't have to special-case
13581358
// constraining the normalized-to `term`.
13591359
self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates);
1360-
if let Some((response, _)) = self.try_merge_candidates(&candidates) {
1360+
if let Some(response) = self.try_merge_candidates(&candidates) {
13611361
Ok(response)
13621362
} else {
13631363
self.flounder(&candidates).map_err(Into::into)

‎compiler/rustc_next_trait_solver/src/solve/mod.rs‎

Lines changed: 29 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -86,23 +86,18 @@ fn equal_response_modulo_region_constraints<I: Interner>(
8686
b: &CanonicalResponse<I>,
8787
) -> bool {
8888
let CanonicalResponse {
89-
max_universe: a_max_universe,
90-
var_kinds: a_var_kinds,
91-
value:
92-
Response {
93-
var_values: a_var_values,
94-
certainty: a_certainty,
95-
external_constraints: a_external_constraints,
96-
},
89+
max_universe,
90+
var_kinds,
91+
value: Response { var_values, certainty, external_constraints },
9792
} = a;
9893

9994
let ExternalConstraintsData { region_constraints: _, opaque_types, normalization_nested_goals } =
100-
&**a_external_constraints;
95+
&**external_constraints;
10196

102-
a_max_universe == &b.max_universe
103-
&& a_var_kinds == &b.var_kinds
104-
&& a_var_values == &b.value.var_values
105-
&& a_certainty == &b.value.certainty
97+
max_universe == &b.max_universe
98+
&& var_kinds == &b.var_kinds
99+
&& var_values == &b.value.var_values
100+
&& certainty == &b.value.certainty
106101
&& opaque_types == &b.value.external_constraints.opaque_types
107102
&& normalization_nested_goals == &b.value.external_constraints.normalization_nested_goals
108103
}
@@ -319,12 +314,6 @@ where
319314
}
320315
}
321316

322-
#[derive(Debug)]
323-
enum MergeCandidateInfo {
324-
AlwaysApplicable(usize),
325-
EqualResponse,
326-
}
327-
328317
impl<D, I> EvalCtxt<'_, D>
329318
where
330319
D: SolverDelegate<Interner = I>,
@@ -337,48 +326,33 @@ where
337326
fn try_merge_candidates(
338327
&mut self,
339328
candidates: &[Candidate<I>],
340-
) -> Option<(CanonicalResponse<I>, MergeCandidateInfo)> {
341-
if candidates.is_empty() {
329+
) -> Option<CanonicalResponse<I>> {
330+
let Some((first, rest)) = candidates.split_first() else {
342331
return None;
343-
}
344-
345-
let always_applicable = candidates.iter().enumerate().find(|(_, candidate)| {
346-
candidate.result.value.certainty == Certainty::Yes
347-
&& has_no_inference_or_external_constraints(candidate.result)
348-
});
332+
};
349333

350-
if let Some((i, candidate)) = always_applicable {
351-
return Some((candidate.result, MergeCandidateInfo::AlwaysApplicable(i)));
334+
if rest.iter().any(|candidate| {
335+
!equal_response_modulo_region_constraints(&first.result, &candidate.result)
336+
}) {
337+
return None;
352338
}
353339

354-
let one: CanonicalResponse<I> = candidates[0].result;
355-
356-
if candidates[1..]
357-
.iter()
358-
.all(|candidate| equal_response_modulo_region_constraints(&one, &candidate.result))
359-
{
360-
let region_constraints = &one.value.external_constraints.region_constraints;
361-
if candidates[1..].iter().all(|candidate| {
362-
&candidate.result.value.external_constraints.region_constraints
363-
== region_constraints
364-
}) {
365-
return Some((one, MergeCandidateInfo::EqualResponse));
366-
}
367-
368-
// If candidates differ only in region constraints, their merged region
369-
// constraints are an `Or` of their respective constraints. If one of them
370-
// has no region constraints, the `Or` constraint evaluates to `true`.
371-
//
372-
// This is a special case of `-Zassumptions-on-binders` and should be
373-
// replaced eventually.
374-
if let Some(candidate) = candidates.iter().find(|candidate| {
375-
candidate.result.value.external_constraints.region_constraints.is_empty()
376-
}) {
377-
return Some((candidate.result, MergeCandidateInfo::EqualResponse));
378-
}
340+
let region_constraints = &first.result.value.external_constraints.region_constraints;
341+
if candidates[1..].iter().all(|candidate| {
342+
&candidate.result.value.external_constraints.region_constraints == region_constraints
343+
}) {
344+
return Some(first.result);
379345
}
380346

381-
None
347+
// If candidates differ only in region constraints, their merged region
348+
// constraints are an OR of their respective constraints. We don't support
349+
// OR constraints yet, so the only way we can merge differing candidates
350+
// if one of them has no region constraints as then the OR constraint
351+
// is trivially true.
352+
candidates
353+
.iter()
354+
.find(|c| c.result.value.external_constraints.region_constraints.is_empty())
355+
.map(|c| c.result)
382356
}
383357

384358
fn bail_with_ambiguity(&mut self, candidates: &[Candidate<I>]) -> CanonicalResponse<I> {

‎compiler/rustc_next_trait_solver/src/solve/trait_goals.rs‎

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ use crate::solve::assembly::{
2424
use crate::solve::inspect::ProbeKind;
2525
use crate::solve::{
2626
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
27-
MergeCandidateInfo, NoSolution, ParamEnvSource, StalledOnCoroutines,
28-
has_only_region_constraints,
27+
NoSolution, ParamEnvSource, StalledOnCoroutines, has_only_region_constraints,
2928
};
3029

3130
impl<D, I> assembly::GoalKind<D> for TraitClause<I>
@@ -1578,7 +1577,7 @@ where
15781577
.all(|candidate| matches!(candidate.source, CandidateSource::BuiltinImpl(_)));
15791578

15801579
if is_marker || all_builtin {
1581-
self.try_merge_candidates(candidates).map(|(response, _)| response)
1580+
self.try_merge_candidates(candidates)
15821581
} else if candidates.len() > 1 {
15831582
None
15841583
} else {
@@ -1591,7 +1590,7 @@ where
15911590
candidates: &[Candidate<I>],
15921591
proven_via: TraitGoalProvenVia,
15931592
) -> (CanonicalResponse<I>, Option<TraitGoalProvenVia>) {
1594-
if let Some((response, _)) = self.try_merge_candidates(candidates) {
1593+
if let Some(response) = self.try_merge_candidates(candidates) {
15951594
(response, Some(proven_via))
15961595
} else {
15971596
(self.bail_with_ambiguity(candidates), None)
@@ -1656,6 +1655,7 @@ where
16561655
let alias_bounds: Vec<_> = candidates
16571656
.extract_if(.., |c| matches!(c.source, CandidateSource::AliasBound(..)))
16581657
.collect();
1658+
candidates.into_iter().for_each(|c| self.ignore_candidate_head_usages(c.head_usages));
16591659
return Ok(self.merge_candidates_or_bail_with_ambiguity(
16601660
&alias_bounds,
16611661
TraitGoalProvenVia::AliasBound,
@@ -1671,38 +1671,10 @@ where
16711671
let where_bounds: Vec<_> = candidates
16721672
.extract_if(.., |c| matches!(c.source, CandidateSource::ParamEnv(_)))
16731673
.collect();
1674-
let Some((response, info)) = self.try_merge_candidates(&where_bounds) else {
1674+
candidates.into_iter().for_each(|c| self.ignore_candidate_head_usages(c.head_usages));
1675+
let Some(response) = self.try_merge_candidates(&where_bounds) else {
16751676
return Ok((self.bail_with_ambiguity(&where_bounds), None));
16761677
};
1677-
match info {
1678-
// If there's an always applicable candidate, the result of all
1679-
// other candidates does not matter. This means we can ignore
1680-
// them when checking whether we've reached a fixpoint.
1681-
//
1682-
// We always prefer the first always applicable candidate, even if a
1683-
// later candidate is also always applicable and would result in fewer
1684-
// reruns. We could slightly improve this by e.g. searching for another
1685-
// always applicable candidate which doesn't depend on any cycle heads.
1686-
//
1687-
// NOTE: This is optimization is observable in case there is an always
1688-
// applicable global candidate and another non-global candidate which only
1689-
// applies because of a provisional result. I can't even think of a test
1690-
// case where this would occur and even then, this would not be unsound.
1691-
// Supporting this makes the code more involved, so I am just going to
1692-
// ignore this for now.
1693-
MergeCandidateInfo::AlwaysApplicable(i) => {
1694-
for (j, c) in where_bounds.into_iter().enumerate() {
1695-
if i != j {
1696-
self.ignore_candidate_head_usages(c.head_usages)
1697-
}
1698-
}
1699-
// If a where-bound does not apply, we don't actually get a
1700-
// candidate for it. We manually track the head usages
1701-
// of all failed `ParamEnv` candidates instead.
1702-
self.ignore_candidate_head_usages(failed_candidate_info.param_env_head_usages);
1703-
}
1704-
MergeCandidateInfo::EqualResponse => {}
1705-
}
17061678
return Ok((response, Some(TraitGoalProvenVia::ParamEnv)));
17071679
}
17081680

@@ -1711,6 +1683,7 @@ where
17111683
let alias_bounds: Vec<_> = candidates
17121684
.extract_if(.., |c| matches!(c.source, CandidateSource::AliasBound(_)))
17131685
.collect();
1686+
candidates.into_iter().for_each(|c| self.ignore_candidate_head_usages(c.head_usages));
17141687
return Ok(self.merge_candidates_or_bail_with_ambiguity(
17151688
&alias_bounds,
17161689
TraitGoalProvenVia::AliasBound,

‎tests/ui/traits/next-solver/cycles/inductive-cycle-but-ok.rs‎

Lines changed: 0 additions & 44 deletions
This file was deleted.

‎tests/ui/traits/next-solver/cycles/inductive-cycle-discarded-coinductive-constraints.rs‎

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)