Skip to content

Commit c2e2c9e

Browse files
committed
ignore head usages of ignored candidates
1 parent db1a64c commit c2e2c9e

File tree

7 files changed

+374
-149
lines changed

7 files changed

+374
-149
lines changed

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

Lines changed: 66 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::ops::ControlFlow;
88
use derive_where::derive_where;
99
use rustc_type_ir::inherent::*;
1010
use rustc_type_ir::lang_items::TraitSolverLangItem;
11+
use rustc_type_ir::search_graph::CandidateHeadUsages;
1112
use rustc_type_ir::solve::SizedTraitKind;
1213
use rustc_type_ir::{
1314
self as ty, Interner, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable,
@@ -33,10 +34,11 @@ enum AliasBoundKind {
3334
///
3435
/// It consists of both the `source`, which describes how that goal would be proven,
3536
/// and the `result` when using the given `source`.
36-
#[derive_where(Clone, Debug; I: Interner)]
37+
#[derive_where(Debug; I: Interner)]
3738
pub(super) struct Candidate<I: Interner> {
3839
pub(super) source: CandidateSource<I>,
3940
pub(super) result: CanonicalResponse<I>,
41+
pub(super) head_usages: CandidateHeadUsages,
4042
}
4143

4244
/// Methods used to assemble candidates for either trait or projection goals.
@@ -116,8 +118,11 @@ where
116118
ecx: &mut EvalCtxt<'_, D>,
117119
goal: Goal<I, Self>,
118120
assumption: I::Clause,
119-
) -> Result<Candidate<I>, NoSolution> {
120-
Self::fast_reject_assumption(ecx, goal, assumption)?;
121+
) -> Result<Candidate<I>, CandidateHeadUsages> {
122+
match Self::fast_reject_assumption(ecx, goal, assumption) {
123+
Ok(()) => {}
124+
Err(NoSolution) => return Err(CandidateHeadUsages::default()),
125+
}
121126

122127
// Dealing with `ParamEnv` candidates is a bit of a mess as we need to lazily
123128
// check whether the candidate is global while considering normalization.
@@ -126,18 +131,23 @@ where
126131
// in `probe` even if the candidate does not apply before we get there. We handle this
127132
// by using a `Cell` here. We only ever write into it inside of `match_assumption`.
128133
let source = Cell::new(CandidateSource::ParamEnv(ParamEnvSource::Global));
129-
ecx.probe(|result: &QueryResult<I>| inspect::ProbeKind::TraitCandidate {
130-
source: source.get(),
131-
result: *result,
132-
})
133-
.enter(|ecx| {
134-
Self::match_assumption(ecx, goal, assumption, |ecx| {
135-
ecx.try_evaluate_added_goals()?;
136-
source.set(ecx.characterize_param_env_assumption(goal.param_env, assumption)?);
137-
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
134+
let (result, head_usages) = ecx
135+
.probe(|result: &QueryResult<I>| inspect::ProbeKind::TraitCandidate {
136+
source: source.get(),
137+
result: *result,
138138
})
139-
})
140-
.map(|result| Candidate { source: source.get(), result })
139+
.enter_single_candidate(|ecx| {
140+
Self::match_assumption(ecx, goal, assumption, |ecx| {
141+
ecx.try_evaluate_added_goals()?;
142+
source.set(ecx.characterize_param_env_assumption(goal.param_env, assumption)?);
143+
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
144+
})
145+
});
146+
147+
match result {
148+
Ok(result) => Ok(Candidate { source: source.get(), result, head_usages }),
149+
Err(NoSolution) => Err(head_usages),
150+
}
141151
}
142152

143153
/// Try equating an assumption predicate against a goal's predicate. If it
@@ -355,6 +365,19 @@ pub(super) enum AssembleCandidatesFrom {
355365
EnvAndBounds,
356366
}
357367

368+
/// This is currently used to track the [CandidateHeadUsages] of all failed `ParamEnv`
369+
/// candidates. This is then used to ignore their head usages in case there's another
370+
/// always applicable `ParamEnv` candidate. Look at how `param_env_head_usages` is
371+
/// used in the code for more details.
372+
///
373+
/// We could easily extend this to also ignore head usages of other ignored candidates.
374+
/// However, we currently don't have any tests where this matters and the complexity of
375+
/// doing so does not feel worth it for now.
376+
#[derive(Debug)]
377+
pub(super) struct FailedCandidateInfo {
378+
pub param_env_head_usages: CandidateHeadUsages,
379+
}
380+
358381
impl<D, I> EvalCtxt<'_, D>
359382
where
360383
D: SolverDelegate<Interner = I>,
@@ -364,16 +387,20 @@ where
364387
&mut self,
365388
goal: Goal<I, G>,
366389
assemble_from: AssembleCandidatesFrom,
367-
) -> Vec<Candidate<I>> {
390+
) -> (Vec<Candidate<I>>, FailedCandidateInfo) {
391+
let mut candidates = vec![];
392+
let mut failed_candidate_info =
393+
FailedCandidateInfo { param_env_head_usages: CandidateHeadUsages::default() };
368394
let Ok(normalized_self_ty) =
369395
self.structurally_normalize_ty(goal.param_env, goal.predicate.self_ty())
370396
else {
371-
return vec![];
397+
return (candidates, failed_candidate_info);
372398
};
373399

374400
if normalized_self_ty.is_ty_var() {
375401
debug!("self type has been normalized to infer");
376-
return self.forced_ambiguity(MaybeCause::Ambiguity).into_iter().collect();
402+
candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity));
403+
return (candidates, failed_candidate_info);
377404
}
378405

379406
let goal: Goal<I, G> = goal
@@ -382,16 +409,15 @@ where
382409
// normalizing the self type as well, since type variables are not uniquified.
383410
let goal = self.resolve_vars_if_possible(goal);
384411

385-
let mut candidates = vec![];
386-
387412
if let TypingMode::Coherence = self.typing_mode()
388413
&& let Ok(candidate) = self.consider_coherence_unknowable_candidate(goal)
389414
{
390-
return vec![candidate];
415+
candidates.push(candidate);
416+
return (candidates, failed_candidate_info);
391417
}
392418

393419
self.assemble_alias_bound_candidates(goal, &mut candidates);
394-
self.assemble_param_env_candidates(goal, &mut candidates);
420+
self.assemble_param_env_candidates(goal, &mut candidates, &mut failed_candidate_info);
395421

396422
match assemble_from {
397423
AssembleCandidatesFrom::All => {
@@ -423,7 +449,7 @@ where
423449
AssembleCandidatesFrom::EnvAndBounds => {}
424450
}
425451

426-
candidates
452+
(candidates, failed_candidate_info)
427453
}
428454

429455
pub(super) fn forced_ambiguity(
@@ -584,9 +610,15 @@ where
584610
&mut self,
585611
goal: Goal<I, G>,
586612
candidates: &mut Vec<Candidate<I>>,
613+
failed_candidate_info: &mut FailedCandidateInfo,
587614
) {
588615
for assumption in goal.param_env.caller_bounds().iter() {
589-
candidates.extend(G::probe_and_consider_param_env_candidate(self, goal, assumption));
616+
match G::probe_and_consider_param_env_candidate(self, goal, assumption) {
617+
Ok(candidate) => candidates.push(candidate),
618+
Err(head_usages) => {
619+
failed_candidate_info.param_env_head_usages.merge_usages(head_usages)
620+
}
621+
}
590622
}
591623
}
592624

@@ -661,7 +693,11 @@ where
661693
if let Ok(result) =
662694
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
663695
{
664-
candidates.push(Candidate { source: CandidateSource::AliasBound, result });
696+
candidates.push(Candidate {
697+
source: CandidateSource::AliasBound,
698+
result,
699+
head_usages: CandidateHeadUsages::default(),
700+
});
665701
}
666702
return;
667703
}
@@ -959,7 +995,7 @@ where
959995
// Even when a trait bound has been proven using a where-bound, we
960996
// still need to consider alias-bounds for normalization, see
961997
// `tests/ui/next-solver/alias-bound-shadowed-by-env.rs`.
962-
let mut candidates: Vec<_> = self
998+
let (mut candidates, _) = self
963999
.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::EnvAndBounds);
9641000

9651001
// We still need to prefer where-bounds over alias-bounds however.
@@ -972,23 +1008,20 @@ where
9721008
return inject_normalize_to_rigid_candidate(self);
9731009
}
9741010

975-
if let Some(response) = self.try_merge_candidates(&candidates) {
1011+
if let Some((response, _)) = self.try_merge_candidates(&candidates) {
9761012
Ok(response)
9771013
} else {
9781014
self.flounder(&candidates)
9791015
}
9801016
}
9811017
TraitGoalProvenVia::Misc => {
982-
let mut candidates =
1018+
let (mut candidates, _) =
9831019
self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
9841020

9851021
// Prefer "orphaned" param-env normalization predicates, which are used
9861022
// (for example, and ideally only) when proving item bounds for an impl.
987-
let candidates_from_env: Vec<_> = candidates
988-
.extract_if(.., |c| matches!(c.source, CandidateSource::ParamEnv(_)))
989-
.collect();
990-
if let Some(response) = self.try_merge_candidates(&candidates_from_env) {
991-
return Ok(response);
1023+
if candidates.iter().any(|c| matches!(c.source, CandidateSource::ParamEnv(_))) {
1024+
candidates.retain(|c| matches!(c.source, CandidateSource::ParamEnv(_)));
9921025
}
9931026

9941027
// We drop specialized impls to allow normalization via a final impl here. In case
@@ -997,7 +1030,7 @@ where
9971030
// means we can just ignore inference constraints and don't have to special-case
9981031
// constraining the normalized-to `term`.
9991032
self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates);
1000-
if let Some(response) = self.try_merge_candidates(&candidates) {
1033+
if let Some((response, _)) = self.try_merge_candidates(&candidates) {
10011034
Ok(response)
10021035
} else {
10031036
self.flounder(&candidates)

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_type_ir::fast_reject::DeepRejectCtxt;
88
use rustc_type_ir::inherent::*;
99
use rustc_type_ir::relate::Relate;
1010
use rustc_type_ir::relate::solver_relating::RelateExt;
11-
use rustc_type_ir::search_graph::PathKind;
11+
use rustc_type_ir::search_graph::{CandidateHeadUsages, PathKind};
1212
use rustc_type_ir::{
1313
self as ty, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, TypeFolder,
1414
TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
@@ -399,6 +399,10 @@ where
399399
result
400400
}
401401

402+
pub(super) fn ignore_candidate_head_usages(&mut self, usages: CandidateHeadUsages) {
403+
self.search_graph.ignore_candidate_head_usages(usages);
404+
}
405+
402406
/// Recursively evaluates `goal`, returning whether any inference vars have
403407
/// been constrained and the certainty of the result.
404408
fn evaluate_goal(

compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::marker::PhantomData;
22

3+
use rustc_type_ir::search_graph::CandidateHeadUsages;
34
use rustc_type_ir::{InferCtxtLike, Interner};
45
use tracing::instrument;
56

@@ -25,6 +26,20 @@ where
2526
D: SolverDelegate<Interner = I>,
2627
I: Interner,
2728
{
29+
pub(in crate::solve) fn enter_single_candidate(
30+
self,
31+
f: impl FnOnce(&mut EvalCtxt<'_, D>) -> T,
32+
) -> (T, CandidateHeadUsages) {
33+
self.ecx.search_graph.enter_single_candidate();
34+
let mut candidate_usages = CandidateHeadUsages::default();
35+
let result = self.enter(|ecx| {
36+
let result = f(ecx);
37+
candidate_usages = ecx.search_graph.finish_single_candidate();
38+
result
39+
});
40+
(result, candidate_usages)
41+
}
42+
2843
pub(in crate::solve) fn enter(self, f: impl FnOnce(&mut EvalCtxt<'_, D>) -> T) -> T {
2944
let ProbeCtxt { ecx: outer, probe_kind, _result } = self;
3045

@@ -78,7 +93,8 @@ where
7893
self,
7994
f: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
8095
) -> Result<Candidate<I>, NoSolution> {
81-
self.cx.enter(|ecx| f(ecx)).map(|result| Candidate { source: self.source, result })
96+
let (result, head_usages) = self.cx.enter_single_candidate(f);
97+
result.map(|result| Candidate { source: self.source, result, head_usages })
8298
}
8399
}
84100

compiler/rustc_next_trait_solver/src/solve/mod.rs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,12 @@ where
236236
}
237237
}
238238

239+
#[derive(Debug)]
240+
enum MergeCandidateInfo {
241+
AlwaysApplicable(usize),
242+
EqualResponse,
243+
}
244+
239245
impl<D, I> EvalCtxt<'_, D>
240246
where
241247
D: SolverDelegate<Interner = I>,
@@ -248,23 +254,25 @@ where
248254
fn try_merge_candidates(
249255
&mut self,
250256
candidates: &[Candidate<I>],
251-
) -> Option<CanonicalResponse<I>> {
257+
) -> Option<(CanonicalResponse<I>, MergeCandidateInfo)> {
252258
if candidates.is_empty() {
253259
return None;
254260
}
255261

262+
let always_applicable = candidates.iter().enumerate().find(|(_, candidate)| {
263+
candidate.result.value.certainty == Certainty::Yes
264+
&& has_no_inference_or_external_constraints(candidate.result)
265+
});
266+
if let Some((i, c)) = always_applicable {
267+
return Some((c.result, MergeCandidateInfo::AlwaysApplicable(i)));
268+
}
269+
256270
let one: CanonicalResponse<I> = candidates[0].result;
257271
if candidates[1..].iter().all(|candidate| candidate.result == one) {
258-
return Some(one);
272+
return Some((one, MergeCandidateInfo::EqualResponse));
259273
}
260274

261-
candidates
262-
.iter()
263-
.find(|candidate| {
264-
candidate.result.value.certainty == Certainty::Yes
265-
&& has_no_inference_or_external_constraints(candidate.result)
266-
})
267-
.map(|candidate| candidate.result)
275+
None
268276
}
269277

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

0 commit comments

Comments
 (0)