Skip to content

Commit 08a6c93

Browse files
authored
Rollup merge of #105066 - lcnr:mv-candidate_from_obligation, r=compiler-errors
move `candidate_from_obligation` out of assembly it doesn't belong there as it also does winnowing r? `@compiler-errors`
2 parents 5726f1f + bb982df commit 08a6c93

File tree

2 files changed

+207
-210
lines changed

2 files changed

+207
-210
lines changed

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+3-210
Original file line numberDiff line numberDiff line change
@@ -9,225 +9,18 @@ use hir::LangItem;
99
use rustc_hir as hir;
1010
use rustc_infer::traits::ObligationCause;
1111
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
12-
use rustc_middle::ty::print::with_no_trimmed_paths;
1312
use rustc_middle::ty::{self, Ty, TypeVisitable};
1413
use rustc_target::spec::abi::Abi;
1514

1615
use crate::traits;
17-
use crate::traits::coherence::Conflict;
1816
use crate::traits::query::evaluate_obligation::InferCtxtExt;
19-
use crate::traits::{util, SelectionResult};
20-
use crate::traits::{ErrorReporting, Overflow, Unimplemented};
17+
use crate::traits::util;
2118

2219
use super::BuiltinImplConditions;
23-
use super::IntercrateAmbiguityCause;
24-
use super::OverflowError;
25-
use super::SelectionCandidate::{self, *};
26-
use super::{EvaluatedCandidate, SelectionCandidateSet, SelectionContext, TraitObligationStack};
20+
use super::SelectionCandidate::*;
21+
use super::{SelectionCandidateSet, SelectionContext, TraitObligationStack};
2722

2823
impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
29-
#[instrument(level = "debug", skip(self), ret)]
30-
pub(super) fn candidate_from_obligation<'o>(
31-
&mut self,
32-
stack: &TraitObligationStack<'o, 'tcx>,
33-
) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
34-
// Watch out for overflow. This intentionally bypasses (and does
35-
// not update) the cache.
36-
self.check_recursion_limit(&stack.obligation, &stack.obligation)?;
37-
38-
// Check the cache. Note that we freshen the trait-ref
39-
// separately rather than using `stack.fresh_trait_ref` --
40-
// this is because we want the unbound variables to be
41-
// replaced with fresh types starting from index 0.
42-
let cache_fresh_trait_pred = self.infcx.freshen(stack.obligation.predicate);
43-
debug!(?cache_fresh_trait_pred);
44-
debug_assert!(!stack.obligation.predicate.has_escaping_bound_vars());
45-
46-
if let Some(c) =
47-
self.check_candidate_cache(stack.obligation.param_env, cache_fresh_trait_pred)
48-
{
49-
debug!("CACHE HIT");
50-
return c;
51-
}
52-
53-
// If no match, compute result and insert into cache.
54-
//
55-
// FIXME(nikomatsakis) -- this cache is not taking into
56-
// account cycles that may have occurred in forming the
57-
// candidate. I don't know of any specific problems that
58-
// result but it seems awfully suspicious.
59-
let (candidate, dep_node) =
60-
self.in_task(|this| this.candidate_from_obligation_no_cache(stack));
61-
62-
debug!("CACHE MISS");
63-
self.insert_candidate_cache(
64-
stack.obligation.param_env,
65-
cache_fresh_trait_pred,
66-
dep_node,
67-
candidate.clone(),
68-
);
69-
candidate
70-
}
71-
72-
fn candidate_from_obligation_no_cache<'o>(
73-
&mut self,
74-
stack: &TraitObligationStack<'o, 'tcx>,
75-
) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> {
76-
if let Err(conflict) = self.is_knowable(stack) {
77-
debug!("coherence stage: not knowable");
78-
if self.intercrate_ambiguity_causes.is_some() {
79-
debug!("evaluate_stack: intercrate_ambiguity_causes is some");
80-
// Heuristics: show the diagnostics when there are no candidates in crate.
81-
if let Ok(candidate_set) = self.assemble_candidates(stack) {
82-
let mut no_candidates_apply = true;
83-
84-
for c in candidate_set.vec.iter() {
85-
if self.evaluate_candidate(stack, &c)?.may_apply() {
86-
no_candidates_apply = false;
87-
break;
88-
}
89-
}
90-
91-
if !candidate_set.ambiguous && no_candidates_apply {
92-
let trait_ref = stack.obligation.predicate.skip_binder().trait_ref;
93-
let self_ty = trait_ref.self_ty();
94-
let (trait_desc, self_desc) = with_no_trimmed_paths!({
95-
let trait_desc = trait_ref.print_only_trait_path().to_string();
96-
let self_desc = if self_ty.has_concrete_skeleton() {
97-
Some(self_ty.to_string())
98-
} else {
99-
None
100-
};
101-
(trait_desc, self_desc)
102-
});
103-
let cause = if let Conflict::Upstream = conflict {
104-
IntercrateAmbiguityCause::UpstreamCrateUpdate { trait_desc, self_desc }
105-
} else {
106-
IntercrateAmbiguityCause::DownstreamCrate { trait_desc, self_desc }
107-
};
108-
debug!(?cause, "evaluate_stack: pushing cause");
109-
self.intercrate_ambiguity_causes.as_mut().unwrap().insert(cause);
110-
}
111-
}
112-
}
113-
return Ok(None);
114-
}
115-
116-
let candidate_set = self.assemble_candidates(stack)?;
117-
118-
if candidate_set.ambiguous {
119-
debug!("candidate set contains ambig");
120-
return Ok(None);
121-
}
122-
123-
let candidates = candidate_set.vec;
124-
125-
debug!(?stack, ?candidates, "assembled {} candidates", candidates.len());
126-
127-
// At this point, we know that each of the entries in the
128-
// candidate set is *individually* applicable. Now we have to
129-
// figure out if they contain mutual incompatibilities. This
130-
// frequently arises if we have an unconstrained input type --
131-
// for example, we are looking for `$0: Eq` where `$0` is some
132-
// unconstrained type variable. In that case, we'll get a
133-
// candidate which assumes $0 == int, one that assumes `$0 ==
134-
// usize`, etc. This spells an ambiguity.
135-
136-
let mut candidates = self.filter_impls(candidates, stack.obligation);
137-
138-
// If there is more than one candidate, first winnow them down
139-
// by considering extra conditions (nested obligations and so
140-
// forth). We don't winnow if there is exactly one
141-
// candidate. This is a relatively minor distinction but it
142-
// can lead to better inference and error-reporting. An
143-
// example would be if there was an impl:
144-
//
145-
// impl<T:Clone> Vec<T> { fn push_clone(...) { ... } }
146-
//
147-
// and we were to see some code `foo.push_clone()` where `boo`
148-
// is a `Vec<Bar>` and `Bar` does not implement `Clone`. If
149-
// we were to winnow, we'd wind up with zero candidates.
150-
// Instead, we select the right impl now but report "`Bar` does
151-
// not implement `Clone`".
152-
if candidates.len() == 1 {
153-
return self.filter_reservation_impls(candidates.pop().unwrap(), stack.obligation);
154-
}
155-
156-
// Winnow, but record the exact outcome of evaluation, which
157-
// is needed for specialization. Propagate overflow if it occurs.
158-
let mut candidates = candidates
159-
.into_iter()
160-
.map(|c| match self.evaluate_candidate(stack, &c) {
161-
Ok(eval) if eval.may_apply() => {
162-
Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval }))
163-
}
164-
Ok(_) => Ok(None),
165-
Err(OverflowError::Canonical) => Err(Overflow(OverflowError::Canonical)),
166-
Err(OverflowError::ErrorReporting) => Err(ErrorReporting),
167-
Err(OverflowError::Error(e)) => Err(Overflow(OverflowError::Error(e))),
168-
})
169-
.flat_map(Result::transpose)
170-
.collect::<Result<Vec<_>, _>>()?;
171-
172-
debug!(?stack, ?candidates, "winnowed to {} candidates", candidates.len());
173-
174-
let needs_infer = stack.obligation.predicate.has_non_region_infer();
175-
176-
// If there are STILL multiple candidates, we can further
177-
// reduce the list by dropping duplicates -- including
178-
// resolving specializations.
179-
if candidates.len() > 1 {
180-
let mut i = 0;
181-
while i < candidates.len() {
182-
let is_dup = (0..candidates.len()).filter(|&j| i != j).any(|j| {
183-
self.candidate_should_be_dropped_in_favor_of(
184-
&candidates[i],
185-
&candidates[j],
186-
needs_infer,
187-
)
188-
});
189-
if is_dup {
190-
debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
191-
candidates.swap_remove(i);
192-
} else {
193-
debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len());
194-
i += 1;
195-
196-
// If there are *STILL* multiple candidates, give up
197-
// and report ambiguity.
198-
if i > 1 {
199-
debug!("multiple matches, ambig");
200-
return Ok(None);
201-
}
202-
}
203-
}
204-
}
205-
206-
// If there are *NO* candidates, then there are no impls --
207-
// that we know of, anyway. Note that in the case where there
208-
// are unbound type variables within the obligation, it might
209-
// be the case that you could still satisfy the obligation
210-
// from another crate by instantiating the type variables with
211-
// a type from another crate that does have an impl. This case
212-
// is checked for in `evaluate_stack` (and hence users
213-
// who might care about this case, like coherence, should use
214-
// that function).
215-
if candidates.is_empty() {
216-
// If there's an error type, 'downgrade' our result from
217-
// `Err(Unimplemented)` to `Ok(None)`. This helps us avoid
218-
// emitting additional spurious errors, since we're guaranteed
219-
// to have emitted at least one.
220-
if stack.obligation.predicate.references_error() {
221-
debug!(?stack.obligation.predicate, "found error type in predicate, treating as ambiguous");
222-
return Ok(None);
223-
}
224-
return Err(Unimplemented);
225-
}
226-
227-
// Just one candidate left.
228-
self.filter_reservation_impls(candidates.pop().unwrap().candidate, stack.obligation)
229-
}
230-
23124
#[instrument(skip(self, stack), level = "debug")]
23225
pub(super) fn assemble_candidates<'o>(
23326
&mut self,

0 commit comments

Comments
 (0)