Skip to content

Commit 5c09e88

Browse files
Auto merge of #144991 - lcnr:ignore-usages-from-ignored-candidates, r=<try>
ignore usages from ignored candidates
2 parents dc0bae1 + d49499e commit 5c09e88

File tree

8 files changed

+524
-326
lines changed

8 files changed

+524
-326
lines changed

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

Lines changed: 95 additions & 70 deletions
Large diffs are not rendered by default.

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::{CandidateUsages, 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_usages(&mut self, usages: CandidateUsages) {
403+
self.search_graph.ignore_candidate_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::CandidateUsages;
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_unique_candidate(
30+
self,
31+
f: impl FnOnce(&mut EvalCtxt<'_, D>) -> T,
32+
) -> (T, CandidateUsages) {
33+
self.ecx.search_graph.enter_unique_candidate();
34+
let mut candidate_usages = CandidateUsages::default();
35+
let result = self.enter(|ecx| {
36+
let result = f(ecx);
37+
candidate_usages = ecx.search_graph.finish_unique_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, candidate_usages) = self.cx.enter_unique_candidate(f);
97+
result.map(|result| Candidate { source: self.source, result, candidate_usages })
8298
}
8399
}
84100

compiler/rustc_next_trait_solver/src/solve/mod.rs

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@ mod trait_goals;
2323

2424
use derive_where::derive_where;
2525
use rustc_type_ir::inherent::*;
26+
use rustc_type_ir::search_graph::CandidateUsages;
2627
pub use rustc_type_ir::solve::*;
2728
use rustc_type_ir::{self as ty, Interner, TypingMode};
2829
use tracing::instrument;
2930

3031
pub use self::eval_ctxt::{EvalCtxt, GenerateProofTree, SolverDelegateEvalExt};
3132
use crate::delegate::SolverDelegate;
33+
use crate::solve::assembly::{Candidate, Candidates};
3234

3335
/// How many fixpoint iterations we should attempt inside of the solver before bailing
3436
/// with overflow.
@@ -235,6 +237,12 @@ where
235237
}
236238
}
237239

240+
#[derive(Debug)]
241+
pub enum MergeCandidateKind {
242+
AlwaysApplicable(usize),
243+
ConsiderAll,
244+
}
245+
238246
impl<D, I> EvalCtxt<'_, D>
239247
where
240248
D: SolverDelegate<Interner = I>,
@@ -244,50 +252,66 @@ where
244252
///
245253
/// In this case we tend to flounder and return ambiguity by calling `[EvalCtxt::flounder]`.
246254
#[instrument(level = "trace", skip(self), ret)]
247-
fn try_merge_responses(
255+
fn try_merge_candidates(
248256
&mut self,
249-
responses: &[CanonicalResponse<I>],
250-
) -> Option<CanonicalResponse<I>> {
251-
if responses.is_empty() {
252-
return None;
253-
}
254-
255-
let one = responses[0];
256-
if responses[1..].iter().all(|&resp| resp == one) {
257-
return Some(one);
257+
candidates: Vec<Candidate<I>>,
258+
not_applicable_candidates: Vec<CandidateUsages>,
259+
) -> Result<CanonicalResponse<I>, Vec<Candidate<I>>> {
260+
if candidates.is_empty() {
261+
return Err(candidates);
258262
}
259263

260-
responses
264+
let always_applicable = candidates
261265
.iter()
262-
.find(|response| {
263-
response.value.certainty == Certainty::Yes
264-
&& has_no_inference_or_external_constraints(**response)
266+
.enumerate()
267+
.find(|(_, candidate)| {
268+
candidate.result.value.certainty == Certainty::Yes
269+
&& has_no_inference_or_external_constraints(candidate.result)
265270
})
266-
.copied()
271+
.map(|(i, c)| (i, c.result));
272+
if let Some((i, result)) = always_applicable {
273+
for (j, c) in candidates.into_iter().enumerate() {
274+
if i != j {
275+
self.ignore_candidate_usages(c.candidate_usages);
276+
}
277+
}
278+
for candidate_usages in not_applicable_candidates {
279+
self.ignore_candidate_usages(candidate_usages);
280+
}
281+
return Ok(result);
282+
}
283+
284+
let one: CanonicalResponse<I> = candidates[0].result;
285+
if candidates[1..].iter().all(|candidate| candidate.result == one) {
286+
return Ok(one);
287+
}
288+
289+
Err(candidates)
267290
}
268291

269-
fn bail_with_ambiguity(&mut self, responses: &[CanonicalResponse<I>]) -> CanonicalResponse<I> {
270-
debug_assert!(responses.len() > 1);
271-
let maybe_cause = responses.iter().fold(MaybeCause::Ambiguity, |maybe_cause, response| {
272-
// Pull down the certainty of `Certainty::Yes` to ambiguity when combining
273-
// these responses, b/c we're combining more than one response and this we
274-
// don't know which one applies.
275-
let candidate = match response.value.certainty {
276-
Certainty::Yes => MaybeCause::Ambiguity,
277-
Certainty::Maybe(candidate) => candidate,
278-
};
279-
maybe_cause.or(candidate)
280-
});
292+
fn bail_with_ambiguity(&mut self, candidates: &[Candidate<I>]) -> CanonicalResponse<I> {
293+
debug_assert!(candidates.len() > 1);
294+
let maybe_cause =
295+
candidates.iter().fold(MaybeCause::Ambiguity, |maybe_cause, candidates| {
296+
// Pull down the certainty of `Certainty::Yes` to ambiguity when combining
297+
// these responses, b/c we're combining more than one response and this we
298+
// don't know which one applies.
299+
let candidate = match candidates.result.value.certainty {
300+
Certainty::Yes => MaybeCause::Ambiguity,
301+
Certainty::Maybe(candidate) => candidate,
302+
};
303+
maybe_cause.or(candidate)
304+
});
281305
self.make_ambiguous_response_no_constraints(maybe_cause)
282306
}
283307

284308
/// If we fail to merge responses we flounder and return overflow or ambiguity.
285309
#[instrument(level = "trace", skip(self), ret)]
286-
fn flounder(&mut self, responses: &[CanonicalResponse<I>]) -> QueryResult<I> {
287-
if responses.is_empty() {
310+
fn flounder(&mut self, candidates: &[Candidate<I>]) -> QueryResult<I> {
311+
if candidates.is_empty() {
288312
return Err(NoSolution);
289313
} else {
290-
Ok(self.bail_with_ambiguity(responses))
314+
Ok(self.bail_with_ambiguity(candidates))
291315
}
292316
}
293317

compiler/rustc_next_trait_solver/src/solve/search_graph.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ where
4848
) -> QueryResult<I> {
4949
match kind {
5050
PathKind::Coinductive => response_no_constraints(cx, input, Certainty::Yes),
51-
PathKind::Unknown => response_no_constraints(cx, input, Certainty::overflow(false)),
51+
PathKind::Unknown | PathKind::ForcedAmbiguity => {
52+
response_no_constraints(cx, input, Certainty::overflow(false))
53+
}
5254
// Even though we know these cycles to be unproductive, we still return
5355
// overflow during coherence. This is both as we are not 100% confident in
5456
// the implementation yet and any incorrect errors would be unsound there.

compiler/rustc_next_trait_solver/src/solve/trait_goals.rs

Lines changed: 33 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes
1616
use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidatesFrom, Candidate};
1717
use crate::solve::inspect::ProbeKind;
1818
use crate::solve::{
19-
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
20-
NoSolution, ParamEnvSource, QueryResult, has_only_region_constraints,
19+
BuiltinImplSource, CandidateSource, Candidates, Certainty, EvalCtxt, Goal, GoalSource,
20+
MaybeCause, NoSolution, ParamEnvSource, QueryResult, has_only_region_constraints,
2121
};
2222

2323
impl<D, I> assembly::GoalKind<D> for TraitPredicate<I>
@@ -1343,22 +1343,20 @@ where
13431343
#[instrument(level = "debug", skip(self), ret)]
13441344
pub(super) fn merge_trait_candidates(
13451345
&mut self,
1346-
mut candidates: Vec<Candidate<I>>,
1346+
mut candidates: Candidates<I>,
13471347
) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
13481348
if let TypingMode::Coherence = self.typing_mode() {
1349-
let all_candidates: Vec<_> = candidates.into_iter().map(|c| c.result).collect();
1350-
return if let Some(response) = self.try_merge_responses(&all_candidates) {
1351-
Ok((response, Some(TraitGoalProvenVia::Misc)))
1352-
} else {
1353-
self.flounder(&all_candidates).map(|r| (r, None))
1354-
};
1349+
match self.try_merge_candidates(candidates.applicable, vec![]) {
1350+
Ok(response) => return Ok((response, Some(TraitGoalProvenVia::Misc))),
1351+
Err(candidates) => return self.flounder(&candidates).map(|r| (r, None)),
1352+
}
13551353
}
13561354

13571355
// We prefer trivial builtin candidates, i.e. builtin impls without any
13581356
// nested requirements, over all others. This is a fix for #53123 and
13591357
// prevents where-bounds from accidentally extending the lifetime of a
13601358
// variable.
1361-
let mut trivial_builtin_impls = candidates.iter().filter(|c| {
1359+
let mut trivial_builtin_impls = candidates.applicable.iter().filter(|c| {
13621360
matches!(c.source, CandidateSource::BuiltinImpl(BuiltinImplSource::Trivial))
13631361
});
13641362
if let Some(candidate) = trivial_builtin_impls.next() {
@@ -1371,57 +1369,56 @@ where
13711369
// If there are non-global where-bounds, prefer where-bounds
13721370
// (including global ones) over everything else.
13731371
let has_non_global_where_bounds = candidates
1372+
.applicable
13741373
.iter()
13751374
.any(|c| matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::NonGlobal)));
13761375
if has_non_global_where_bounds {
1377-
let where_bounds: Vec<_> = candidates
1378-
.iter()
1379-
.filter(|c| matches!(c.source, CandidateSource::ParamEnv(_)))
1380-
.map(|c| c.result)
1381-
.collect();
1382-
return if let Some(response) = self.try_merge_responses(&where_bounds) {
1383-
Ok((response, Some(TraitGoalProvenVia::ParamEnv)))
1384-
} else {
1385-
Ok((self.bail_with_ambiguity(&where_bounds), None))
1386-
};
1376+
let mut where_bounds = candidates.applicable;
1377+
for not_where_bound in
1378+
where_bounds.extract_if(.., |c| !matches!(c.source, CandidateSource::ParamEnv(_)))
1379+
{
1380+
self.ignore_candidate_usages(not_where_bound.candidate_usages);
1381+
}
1382+
match self.try_merge_candidates(where_bounds, candidates.not_applicable_param_env) {
1383+
Ok(response) => return Ok((response, Some(TraitGoalProvenVia::ParamEnv))),
1384+
Err(where_bounds) => return Ok((self.bail_with_ambiguity(&where_bounds), None)),
1385+
}
13871386
}
13881387

1389-
if candidates.iter().any(|c| matches!(c.source, CandidateSource::AliasBound)) {
1388+
if candidates.applicable.iter().any(|c| matches!(c.source, CandidateSource::AliasBound)) {
13901389
let alias_bounds: Vec<_> = candidates
1391-
.iter()
1392-
.filter(|c| matches!(c.source, CandidateSource::AliasBound))
1393-
.map(|c| c.result)
1390+
.applicable
1391+
.extract_if(.., |c| matches!(c.source, CandidateSource::AliasBound))
13941392
.collect();
1395-
return if let Some(response) = self.try_merge_responses(&alias_bounds) {
1396-
Ok((response, Some(TraitGoalProvenVia::AliasBound)))
1397-
} else {
1398-
Ok((self.bail_with_ambiguity(&alias_bounds), None))
1399-
};
1393+
match self.try_merge_candidates(alias_bounds, vec![]) {
1394+
Ok(response) => return Ok((response, Some(TraitGoalProvenVia::AliasBound))),
1395+
Err(alias_bounds) => return Ok((self.bail_with_ambiguity(&alias_bounds), None)),
1396+
}
14001397
}
14011398

1402-
self.filter_specialized_impls(AllowInferenceConstraints::No, &mut candidates);
1403-
self.unsound_prefer_builtin_dyn_impl(&mut candidates);
1399+
self.filter_specialized_impls(AllowInferenceConstraints::No, &mut candidates.applicable);
1400+
self.unsound_prefer_builtin_dyn_impl(&mut candidates.applicable);
14041401

14051402
// If there are *only* global where bounds, then make sure to return that this
14061403
// is still reported as being proven-via the param-env so that rigid projections
14071404
// operate correctly. Otherwise, drop all global where-bounds before merging the
14081405
// remaining candidates.
14091406
let proven_via = if candidates
1407+
.applicable
14101408
.iter()
14111409
.all(|c| matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global)))
14121410
{
14131411
TraitGoalProvenVia::ParamEnv
14141412
} else {
14151413
candidates
1414+
.applicable
14161415
.retain(|c| !matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global)));
14171416
TraitGoalProvenVia::Misc
14181417
};
14191418

1420-
let all_candidates: Vec<_> = candidates.into_iter().map(|c| c.result).collect();
1421-
if let Some(response) = self.try_merge_responses(&all_candidates) {
1422-
Ok((response, Some(proven_via)))
1423-
} else {
1424-
self.flounder(&all_candidates).map(|r| (r, None))
1419+
match self.try_merge_candidates(candidates.applicable, vec![]) {
1420+
Ok(response) => Ok((response, Some(proven_via))),
1421+
Err(candidates) => self.flounder(&candidates).map(|r| (r, None)),
14251422
}
14261423
}
14271424

0 commit comments

Comments
 (0)