consistently handle global where-bounds
This commit is contained in:
parent
5f548890b8
commit
3350b9faad
6 changed files with 253 additions and 346 deletions
|
@ -23,6 +23,7 @@
|
||||||
#![feature(extract_if)]
|
#![feature(extract_if)]
|
||||||
#![feature(if_let_guard)]
|
#![feature(if_let_guard)]
|
||||||
#![feature(iter_intersperse)]
|
#![feature(iter_intersperse)]
|
||||||
|
#![feature(iterator_try_reduce)]
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
#![feature(never_type)]
|
#![feature(never_type)]
|
||||||
#![feature(rustdoc_internals)]
|
#![feature(rustdoc_internals)]
|
||||||
|
|
|
@ -445,7 +445,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
|
|
||||||
// Winnow, but record the exact outcome of evaluation, which
|
// Winnow, but record the exact outcome of evaluation, which
|
||||||
// is needed for specialization. Propagate overflow if it occurs.
|
// is needed for specialization. Propagate overflow if it occurs.
|
||||||
let mut candidates = candidates
|
let candidates = candidates
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|c| match self.evaluate_candidate(stack, &c) {
|
.map(|c| match self.evaluate_candidate(stack, &c) {
|
||||||
Ok(eval) if eval.may_apply() => {
|
Ok(eval) if eval.may_apply() => {
|
||||||
|
@ -458,40 +458,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
.flat_map(Result::transpose)
|
.flat_map(Result::transpose)
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
debug!(?stack, ?candidates, "winnowed to {} candidates", candidates.len());
|
debug!(?stack, ?candidates, "{} potentially applicable candidates", candidates.len());
|
||||||
|
|
||||||
let has_non_region_infer = stack.obligation.predicate.has_non_region_infer();
|
|
||||||
|
|
||||||
// If there are STILL multiple candidates, we can further
|
|
||||||
// reduce the list by dropping duplicates -- including
|
|
||||||
// resolving specializations.
|
|
||||||
if candidates.len() > 1 {
|
|
||||||
let mut i = 0;
|
|
||||||
while i < candidates.len() {
|
|
||||||
let should_drop_i = (0..candidates.len()).filter(|&j| i != j).any(|j| {
|
|
||||||
self.candidate_should_be_dropped_in_favor_of(
|
|
||||||
&candidates[i],
|
|
||||||
&candidates[j],
|
|
||||||
has_non_region_infer,
|
|
||||||
) == DropVictim::Yes
|
|
||||||
});
|
|
||||||
if should_drop_i {
|
|
||||||
debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
|
|
||||||
candidates.swap_remove(i);
|
|
||||||
} else {
|
|
||||||
debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len());
|
|
||||||
i += 1;
|
|
||||||
|
|
||||||
// If there are *STILL* multiple candidates, give up
|
|
||||||
// and report ambiguity.
|
|
||||||
if i > 1 {
|
|
||||||
debug!("multiple matches, ambig");
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there are *NO* candidates, then there are no impls --
|
// If there are *NO* candidates, then there are no impls --
|
||||||
// that we know of, anyway. Note that in the case where there
|
// that we know of, anyway. Note that in the case where there
|
||||||
// are unbound type variables within the obligation, it might
|
// are unbound type variables within the obligation, it might
|
||||||
|
@ -508,13 +475,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
// to have emitted at least one.
|
// to have emitted at least one.
|
||||||
if stack.obligation.predicate.references_error() {
|
if stack.obligation.predicate.references_error() {
|
||||||
debug!(?stack.obligation.predicate, "found error type in predicate, treating as ambiguous");
|
debug!(?stack.obligation.predicate, "found error type in predicate, treating as ambiguous");
|
||||||
return Ok(None);
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Err(Unimplemented)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let has_non_region_infer = stack.obligation.predicate.has_non_region_infer();
|
||||||
|
if let Some(candidate) = self.winnow_candidates(has_non_region_infer, candidates) {
|
||||||
|
self.filter_reservation_impls(candidate)
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
}
|
}
|
||||||
return Err(Unimplemented);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just one candidate left.
|
|
||||||
self.filter_reservation_impls(candidates.pop().unwrap().candidate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1803,18 +1775,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
||||||
enum DropVictim {
|
|
||||||
Yes,
|
|
||||||
No,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DropVictim {
|
|
||||||
fn drop_if(should_drop: bool) -> DropVictim {
|
|
||||||
if should_drop { DropVictim::Yes } else { DropVictim::No }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ## Winnowing
|
/// ## Winnowing
|
||||||
///
|
///
|
||||||
/// Winnowing is the process of attempting to resolve ambiguity by
|
/// Winnowing is the process of attempting to resolve ambiguity by
|
||||||
|
@ -1822,61 +1782,149 @@ impl DropVictim {
|
||||||
/// type variables and then we also attempt to evaluate recursive
|
/// type variables and then we also attempt to evaluate recursive
|
||||||
/// bounds to see if they are satisfied.
|
/// bounds to see if they are satisfied.
|
||||||
impl<'tcx> SelectionContext<'_, 'tcx> {
|
impl<'tcx> SelectionContext<'_, 'tcx> {
|
||||||
/// Returns `DropVictim::Yes` if `victim` should be dropped in favor of
|
/// If there are multiple ways to prove a trait goal, we make some
|
||||||
/// `other`. Generally speaking we will drop duplicate
|
/// *fairly arbitrary* choices about which candidate is actually used.
|
||||||
/// candidates and prefer where-clause candidates.
|
|
||||||
///
|
///
|
||||||
/// See the comment for "SelectionCandidate" for more details.
|
/// For more details, look at the implementation of this method :)
|
||||||
#[instrument(level = "debug", skip(self))]
|
#[instrument(level = "debug", skip(self), ret)]
|
||||||
fn candidate_should_be_dropped_in_favor_of(
|
fn winnow_candidates(
|
||||||
&mut self,
|
&mut self,
|
||||||
victim: &EvaluatedCandidate<'tcx>,
|
|
||||||
other: &EvaluatedCandidate<'tcx>,
|
|
||||||
has_non_region_infer: bool,
|
has_non_region_infer: bool,
|
||||||
) -> DropVictim {
|
mut candidates: Vec<EvaluatedCandidate<'tcx>>,
|
||||||
if victim.candidate == other.candidate {
|
) -> Option<SelectionCandidate<'tcx>> {
|
||||||
return DropVictim::Yes;
|
if candidates.len() == 1 {
|
||||||
|
return Some(candidates.pop().unwrap().candidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a bound would previously have been removed when normalizing
|
// We prefer trivial builtin candidates, i.e. builtin impls without any nested
|
||||||
// the param_env so that it can be given the lowest priority. See
|
// requirements, over all others. This is a fix for #53123 and prevents winnowing
|
||||||
// #50825 for the motivation for this.
|
// from accidentally extending the lifetime of a variable.
|
||||||
let is_global =
|
let mut trivial_builtin = candidates
|
||||||
|cand: ty::PolyTraitPredicate<'tcx>| cand.is_global() && !cand.has_bound_vars();
|
.iter()
|
||||||
|
.filter(|c| matches!(c.candidate, BuiltinCandidate { has_nested: false }));
|
||||||
|
if let Some(_trivial) = trivial_builtin.next() {
|
||||||
|
// There should only ever be a single trivial builtin candidate
|
||||||
|
// as they would otherwise overlap.
|
||||||
|
debug_assert_eq!(trivial_builtin.next(), None);
|
||||||
|
return Some(BuiltinCandidate { has_nested: false });
|
||||||
|
}
|
||||||
|
|
||||||
// (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`,
|
// Before we consider where-bounds, we have to deduplicate them here and also
|
||||||
// or `DiscriminantKindCandidate` to anything else.
|
// drop where-bounds in case the same where-bound exists without bound vars.
|
||||||
|
// This is necessary as elaborating super-trait bounds may result in duplicates.
|
||||||
|
'search_victim: loop {
|
||||||
|
for (i, this) in candidates.iter().enumerate() {
|
||||||
|
let ParamCandidate(this) = this.candidate else { continue };
|
||||||
|
for (j, other) in candidates.iter().enumerate() {
|
||||||
|
if i == j {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ParamCandidate(other) = other.candidate else { continue };
|
||||||
|
if this == other {
|
||||||
|
candidates.remove(j);
|
||||||
|
continue 'search_victim;
|
||||||
|
}
|
||||||
|
|
||||||
|
if this.skip_binder().trait_ref == other.skip_binder().trait_ref
|
||||||
|
&& this.skip_binder().polarity == other.skip_binder().polarity
|
||||||
|
&& !this.skip_binder().trait_ref.has_escaping_bound_vars()
|
||||||
|
{
|
||||||
|
candidates.remove(j);
|
||||||
|
continue 'search_victim;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The next highest priority is for non-global where-bounds. However, while we don't
|
||||||
|
// prefer global where-clauses here, we do bail with ambiguity when encountering both
|
||||||
|
// a global and a non-global where-clause.
|
||||||
//
|
//
|
||||||
// This is a fix for #53123 and prevents winnowing from accidentally extending the
|
// Our handling of where-bounds is generally fairly messy but necessary for backwards
|
||||||
// lifetime of a variable.
|
// compatability, see #50825 for why we need to handle global where-bounds like this.
|
||||||
match (&other.candidate, &victim.candidate) {
|
let is_global = |c: ty::PolyTraitPredicate<'tcx>| c.is_global() && !c.has_bound_vars();
|
||||||
// FIXME(@jswrenn): this should probably be more sophisticated
|
let param_candidates = candidates
|
||||||
(TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => DropVictim::No,
|
.iter()
|
||||||
|
.filter_map(|c| if let ParamCandidate(p) = c.candidate { Some(p) } else { None });
|
||||||
|
let mut has_global_bounds = false;
|
||||||
|
let mut param_candidate = None;
|
||||||
|
for c in param_candidates {
|
||||||
|
if is_global(c) {
|
||||||
|
has_global_bounds = true;
|
||||||
|
} else if param_candidate.replace(c).is_some() {
|
||||||
|
// Ambiguity, two potentially different where-clauses
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(predicate) = param_candidate {
|
||||||
|
// Ambiguity, a global and a non-global where-bound.
|
||||||
|
if has_global_bounds {
|
||||||
|
return None;
|
||||||
|
} else {
|
||||||
|
return Some(ParamCandidate(predicate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// (*)
|
// Prefer alias-bounds over blanket impls for rigid associated types. This is
|
||||||
(BuiltinCandidate { has_nested: false }, _) => DropVictim::Yes,
|
// fairly arbitrary but once again necessary for backwards compatibility.
|
||||||
(_, BuiltinCandidate { has_nested: false }) => DropVictim::No,
|
// If there are multiple applicable candidates which don't affect type inference,
|
||||||
|
// choose the one with the lowest index.
|
||||||
|
let alias_bound = candidates
|
||||||
|
.iter()
|
||||||
|
.filter_map(|c| if let ProjectionCandidate(i) = c.candidate { Some(i) } else { None })
|
||||||
|
.try_reduce(|c1, c2| if has_non_region_infer { None } else { Some(c1.min(c2)) });
|
||||||
|
match alias_bound {
|
||||||
|
Some(Some(index)) => return Some(ProjectionCandidate(index)),
|
||||||
|
Some(None) => {}
|
||||||
|
None => return None,
|
||||||
|
}
|
||||||
|
|
||||||
(ParamCandidate(other), ParamCandidate(victim)) => {
|
// Need to prioritize builtin trait object impls as `<dyn Any as Any>::type_id`
|
||||||
let same_except_bound_vars = other.skip_binder().trait_ref
|
// should use the vtable method and not the method provided by the user-defined
|
||||||
== victim.skip_binder().trait_ref
|
// impl `impl<T: ?Sized> Any for T { .. }`. This really shouldn't exist but is
|
||||||
&& other.skip_binder().polarity == victim.skip_binder().polarity
|
// necessary due to #57893. We again arbitrarily prefer the applicable candidate
|
||||||
&& !other.skip_binder().trait_ref.has_escaping_bound_vars();
|
// with the lowest index.
|
||||||
if same_except_bound_vars {
|
let object_bound = candidates
|
||||||
// See issue #84398. In short, we can generate multiple ParamCandidates which are
|
.iter()
|
||||||
// the same except for unused bound vars. Just pick the one with the fewest bound vars
|
.filter_map(|c| if let ObjectCandidate(i) = c.candidate { Some(i) } else { None })
|
||||||
// or the current one if tied (they should both evaluate to the same answer). This is
|
.try_reduce(|c1, c2| if has_non_region_infer { None } else { Some(c1.min(c2)) });
|
||||||
// probably best characterized as a "hack", since we might prefer to just do our
|
match object_bound {
|
||||||
// best to *not* create essentially duplicate candidates in the first place.
|
Some(Some(index)) => return Some(ObjectCandidate(index)),
|
||||||
DropVictim::drop_if(other.bound_vars().len() <= victim.bound_vars().len())
|
Some(None) => {}
|
||||||
|
None => return None,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, handle overlapping user-written impls.
|
||||||
|
let impls = candidates.iter().filter_map(|c| {
|
||||||
|
if let ImplCandidate(def_id) = c.candidate {
|
||||||
|
Some((def_id, c.evaluation))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let mut impl_candidate = None;
|
||||||
|
for c in impls {
|
||||||
|
if let Some(prev) = impl_candidate.replace(c) {
|
||||||
|
if self.prefer_lhs_over_victim(has_non_region_infer, c, prev) {
|
||||||
|
// Ok, prefer `c` over the previous entry
|
||||||
|
} else if self.prefer_lhs_over_victim(has_non_region_infer, prev, c) {
|
||||||
|
// Ok, keep `prev` instead of the new entry
|
||||||
|
impl_candidate = Some(prev);
|
||||||
} else {
|
} else {
|
||||||
DropVictim::No
|
// Ambiguity, two potentially different where-clauses
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
(
|
if let Some((def_id, _evaluation)) = impl_candidate {
|
||||||
ParamCandidate(other_cand),
|
// Don't use impl candidates which overlap with other candidates.
|
||||||
ImplCandidate(..)
|
// This should pretty much only ever happen with malformed impls.
|
||||||
|
if candidates.iter().all(|c| match c.candidate {
|
||||||
|
BuiltinCandidate { has_nested: _ }
|
||||||
|
| TransmutabilityCandidate
|
||||||
| AutoImplCandidate
|
| AutoImplCandidate
|
||||||
| ClosureCandidate { .. }
|
| ClosureCandidate { .. }
|
||||||
| AsyncClosureCandidate
|
| AsyncClosureCandidate
|
||||||
|
@ -1885,225 +1933,113 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
||||||
| FutureCandidate
|
| FutureCandidate
|
||||||
| IteratorCandidate
|
| IteratorCandidate
|
||||||
| AsyncIteratorCandidate
|
| AsyncIteratorCandidate
|
||||||
| FnPointerCandidate { .. }
|
| FnPointerCandidate
|
||||||
| BuiltinObjectCandidate
|
|
||||||
| BuiltinUnsizeCandidate
|
|
||||||
| TraitUpcastingUnsizeCandidate(_)
|
|
||||||
| BuiltinCandidate { .. }
|
|
||||||
| TraitAliasCandidate
|
| TraitAliasCandidate
|
||||||
| ObjectCandidate(_)
|
| TraitUpcastingUnsizeCandidate(_)
|
||||||
| ProjectionCandidate(_),
|
| BuiltinObjectCandidate
|
||||||
) => {
|
| BuiltinUnsizeCandidate => false,
|
||||||
// We have a where clause so don't go around looking
|
// Non-global param candidates have already been handled, global
|
||||||
// for impls. Arbitrarily give param candidates priority
|
// where-bounds get ignored.
|
||||||
// over projection and object candidates.
|
ParamCandidate(_) | ImplCandidate(_) => true,
|
||||||
|
ProjectionCandidate(_) | ObjectCandidate(_) => unreachable!(),
|
||||||
|
}) {
|
||||||
|
return Some(ImplCandidate(def_id));
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if candidates.len() == 1 {
|
||||||
|
Some(candidates.pop().unwrap().candidate)
|
||||||
|
} else {
|
||||||
|
// Also try ignoring all global where-bounds and check whether we end
|
||||||
|
// with a unique candidate in this case.
|
||||||
|
let mut not_a_global_where_bound = candidates
|
||||||
|
.into_iter()
|
||||||
|
.filter(|c| !matches!(c.candidate, ParamCandidate(p) if is_global(p)));
|
||||||
|
not_a_global_where_bound
|
||||||
|
.next()
|
||||||
|
.map(|c| c.candidate)
|
||||||
|
.filter(|_| not_a_global_where_bound.next().is_none())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prefer_lhs_over_victim(
|
||||||
|
&self,
|
||||||
|
has_non_region_infer: bool,
|
||||||
|
(lhs, lhs_evaluation): (DefId, EvaluationResult),
|
||||||
|
(victim, victim_evaluation): (DefId, EvaluationResult),
|
||||||
|
) -> bool {
|
||||||
|
let tcx = self.tcx();
|
||||||
|
// See if we can toss out `victim` based on specialization.
|
||||||
|
//
|
||||||
|
// While this requires us to know *for sure* that the `lhs` impl applies
|
||||||
|
// we still use modulo regions here. This is fine as specialization currently
|
||||||
|
// assumes that specializing impls have to be always applicable, meaning that
|
||||||
|
// the only allowed region constraints may be constraints also present on the default impl.
|
||||||
|
if lhs_evaluation.must_apply_modulo_regions() {
|
||||||
|
if tcx.specializes((lhs, victim)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match tcx.impls_are_allowed_to_overlap(lhs, victim) {
|
||||||
|
// For #33140 the impl headers must be exactly equal, the trait must not have
|
||||||
|
// any associated items and there are no where-clauses.
|
||||||
|
//
|
||||||
|
// We can just arbitrarily drop one of the impls.
|
||||||
|
Some(ty::ImplOverlapKind::FutureCompatOrderDepTraitObjects) => {
|
||||||
|
assert_eq!(lhs_evaluation, victim_evaluation);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
// For candidates which already reference errors it doesn't really
|
||||||
|
// matter what we do 🤷
|
||||||
|
Some(ty::ImplOverlapKind::Permitted { marker: false }) => {
|
||||||
|
lhs_evaluation.must_apply_considering_regions()
|
||||||
|
}
|
||||||
|
Some(ty::ImplOverlapKind::Permitted { marker: true }) => {
|
||||||
|
// Subtle: If the predicate we are evaluating has inference
|
||||||
|
// variables, do *not* allow discarding candidates due to
|
||||||
|
// marker trait impls.
|
||||||
//
|
//
|
||||||
// Global bounds from the where clause should be ignored
|
// Without this restriction, we could end up accidentally
|
||||||
// here (see issue #50825).
|
// constraining inference variables based on an arbitrarily
|
||||||
DropVictim::drop_if(!is_global(*other_cand))
|
// chosen trait impl.
|
||||||
}
|
|
||||||
(ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(victim_cand)) => {
|
|
||||||
// Prefer these to a global where-clause bound
|
|
||||||
// (see issue #50825).
|
|
||||||
if is_global(*victim_cand) { DropVictim::Yes } else { DropVictim::No }
|
|
||||||
}
|
|
||||||
(
|
|
||||||
ImplCandidate(_)
|
|
||||||
| AutoImplCandidate
|
|
||||||
| ClosureCandidate { .. }
|
|
||||||
| AsyncClosureCandidate
|
|
||||||
| AsyncFnKindHelperCandidate
|
|
||||||
| CoroutineCandidate
|
|
||||||
| FutureCandidate
|
|
||||||
| IteratorCandidate
|
|
||||||
| AsyncIteratorCandidate
|
|
||||||
| FnPointerCandidate { .. }
|
|
||||||
| BuiltinObjectCandidate
|
|
||||||
| BuiltinUnsizeCandidate
|
|
||||||
| TraitUpcastingUnsizeCandidate(_)
|
|
||||||
| BuiltinCandidate { has_nested: true }
|
|
||||||
| TraitAliasCandidate,
|
|
||||||
ParamCandidate(victim_cand),
|
|
||||||
) => {
|
|
||||||
// Prefer these to a global where-clause bound
|
|
||||||
// (see issue #50825).
|
|
||||||
DropVictim::drop_if(
|
|
||||||
is_global(*victim_cand) && other.evaluation.must_apply_modulo_regions(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
(ProjectionCandidate(i), ProjectionCandidate(j))
|
|
||||||
| (ObjectCandidate(i), ObjectCandidate(j)) => {
|
|
||||||
// Arbitrarily pick the lower numbered candidate for backwards
|
|
||||||
// compatibility reasons. Don't let this affect inference.
|
|
||||||
DropVictim::drop_if(i < j && !has_non_region_infer)
|
|
||||||
}
|
|
||||||
(ObjectCandidate(_), ProjectionCandidate(_))
|
|
||||||
| (ProjectionCandidate(_), ObjectCandidate(_)) => {
|
|
||||||
bug!("Have both object and projection candidate")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arbitrarily give projection and object candidates priority.
|
|
||||||
(
|
|
||||||
ObjectCandidate(_) | ProjectionCandidate(_),
|
|
||||||
ImplCandidate(..)
|
|
||||||
| AutoImplCandidate
|
|
||||||
| ClosureCandidate { .. }
|
|
||||||
| AsyncClosureCandidate
|
|
||||||
| AsyncFnKindHelperCandidate
|
|
||||||
| CoroutineCandidate
|
|
||||||
| FutureCandidate
|
|
||||||
| IteratorCandidate
|
|
||||||
| AsyncIteratorCandidate
|
|
||||||
| FnPointerCandidate { .. }
|
|
||||||
| BuiltinObjectCandidate
|
|
||||||
| BuiltinUnsizeCandidate
|
|
||||||
| TraitUpcastingUnsizeCandidate(_)
|
|
||||||
| BuiltinCandidate { .. }
|
|
||||||
| TraitAliasCandidate,
|
|
||||||
) => DropVictim::Yes,
|
|
||||||
|
|
||||||
(
|
|
||||||
ImplCandidate(..)
|
|
||||||
| AutoImplCandidate
|
|
||||||
| ClosureCandidate { .. }
|
|
||||||
| AsyncClosureCandidate
|
|
||||||
| AsyncFnKindHelperCandidate
|
|
||||||
| CoroutineCandidate
|
|
||||||
| FutureCandidate
|
|
||||||
| IteratorCandidate
|
|
||||||
| AsyncIteratorCandidate
|
|
||||||
| FnPointerCandidate { .. }
|
|
||||||
| BuiltinObjectCandidate
|
|
||||||
| BuiltinUnsizeCandidate
|
|
||||||
| TraitUpcastingUnsizeCandidate(_)
|
|
||||||
| BuiltinCandidate { .. }
|
|
||||||
| TraitAliasCandidate,
|
|
||||||
ObjectCandidate(_) | ProjectionCandidate(_),
|
|
||||||
) => DropVictim::No,
|
|
||||||
|
|
||||||
(&ImplCandidate(other_def), &ImplCandidate(victim_def)) => {
|
|
||||||
// See if we can toss out `victim` based on specialization.
|
|
||||||
// While this requires us to know *for sure* that the `other` impl applies
|
|
||||||
// we still use modulo regions here.
|
|
||||||
//
|
//
|
||||||
// This is fine as specialization currently assumes that specializing
|
// Imagine we have the following code:
|
||||||
// impls have to be always applicable, meaning that the only allowed
|
//
|
||||||
// region constraints may be constraints also present on the default impl.
|
// ```rust
|
||||||
let tcx = self.tcx();
|
// #[marker] trait MyTrait {}
|
||||||
if other.evaluation.must_apply_modulo_regions()
|
// impl MyTrait for u8 {}
|
||||||
&& tcx.specializes((other_def, victim_def))
|
// impl MyTrait for bool {}
|
||||||
{
|
// ```
|
||||||
return DropVictim::Yes;
|
//
|
||||||
}
|
// And we are evaluating the predicate `<_#0t as MyTrait>`.
|
||||||
|
//
|
||||||
match tcx.impls_are_allowed_to_overlap(other_def, victim_def) {
|
// During selection, we will end up with one candidate for each
|
||||||
// For #33140 the impl headers must be exactly equal, the trait must not have
|
// impl of `MyTrait`. If we were to discard one impl in favor
|
||||||
// any associated items and there are no where-clauses.
|
// of the other, we would be left with one candidate, causing
|
||||||
//
|
// us to "successfully" select the predicate, unifying
|
||||||
// We can just arbitrarily drop one of the impls.
|
// _#0t with (for example) `u8`.
|
||||||
Some(ty::ImplOverlapKind::FutureCompatOrderDepTraitObjects) => {
|
//
|
||||||
assert_eq!(other.evaluation, victim.evaluation);
|
// However, we have no reason to believe that this unification
|
||||||
DropVictim::Yes
|
// is correct - we've essentially just picked an arbitrary
|
||||||
}
|
// *possibility* for _#0t, and required that this be the *only*
|
||||||
// For candidates which already reference errors it doesn't really
|
// possibility.
|
||||||
// matter what we do 🤷
|
//
|
||||||
Some(ty::ImplOverlapKind::Permitted { marker: false }) => {
|
// Eventually, we will either:
|
||||||
DropVictim::drop_if(other.evaluation.must_apply_considering_regions())
|
// 1) Unify all inference variables in the predicate through
|
||||||
}
|
// some other means (e.g. type-checking of a function). We will
|
||||||
Some(ty::ImplOverlapKind::Permitted { marker: true }) => {
|
// then be in a position to drop marker trait candidates
|
||||||
// Subtle: If the predicate we are evaluating has inference
|
// without constraining inference variables (since there are
|
||||||
// variables, do *not* allow discarding candidates due to
|
// none left to constrain)
|
||||||
// marker trait impls.
|
// 2) Be left with some unconstrained inference variables. We
|
||||||
//
|
// will then correctly report an inference error, since the
|
||||||
// Without this restriction, we could end up accidentally
|
// existence of multiple marker trait impls tells us nothing
|
||||||
// constraining inference variables based on an arbitrarily
|
// about which one should actually apply.
|
||||||
// chosen trait impl.
|
!has_non_region_infer && lhs_evaluation.must_apply_considering_regions()
|
||||||
//
|
|
||||||
// Imagine we have the following code:
|
|
||||||
//
|
|
||||||
// ```rust
|
|
||||||
// #[marker] trait MyTrait {}
|
|
||||||
// impl MyTrait for u8 {}
|
|
||||||
// impl MyTrait for bool {}
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// And we are evaluating the predicate `<_#0t as MyTrait>`.
|
|
||||||
//
|
|
||||||
// During selection, we will end up with one candidate for each
|
|
||||||
// impl of `MyTrait`. If we were to discard one impl in favor
|
|
||||||
// of the other, we would be left with one candidate, causing
|
|
||||||
// us to "successfully" select the predicate, unifying
|
|
||||||
// _#0t with (for example) `u8`.
|
|
||||||
//
|
|
||||||
// However, we have no reason to believe that this unification
|
|
||||||
// is correct - we've essentially just picked an arbitrary
|
|
||||||
// *possibility* for _#0t, and required that this be the *only*
|
|
||||||
// possibility.
|
|
||||||
//
|
|
||||||
// Eventually, we will either:
|
|
||||||
// 1) Unify all inference variables in the predicate through
|
|
||||||
// some other means (e.g. type-checking of a function). We will
|
|
||||||
// then be in a position to drop marker trait candidates
|
|
||||||
// without constraining inference variables (since there are
|
|
||||||
// none left to constrain)
|
|
||||||
// 2) Be left with some unconstrained inference variables. We
|
|
||||||
// will then correctly report an inference error, since the
|
|
||||||
// existence of multiple marker trait impls tells us nothing
|
|
||||||
// about which one should actually apply.
|
|
||||||
DropVictim::drop_if(
|
|
||||||
!has_non_region_infer
|
|
||||||
&& other.evaluation.must_apply_considering_regions(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
None => DropVictim::No,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
None => false,
|
||||||
(AutoImplCandidate, ImplCandidate(_)) | (ImplCandidate(_), AutoImplCandidate) => {
|
|
||||||
DropVictim::No
|
|
||||||
}
|
|
||||||
|
|
||||||
(AutoImplCandidate, _) | (_, AutoImplCandidate) => {
|
|
||||||
bug!(
|
|
||||||
"default implementations shouldn't be recorded \
|
|
||||||
when there are other global candidates: {:?} {:?}",
|
|
||||||
other,
|
|
||||||
victim
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Everything else is ambiguous
|
|
||||||
(
|
|
||||||
ImplCandidate(_)
|
|
||||||
| ClosureCandidate { .. }
|
|
||||||
| AsyncClosureCandidate
|
|
||||||
| AsyncFnKindHelperCandidate
|
|
||||||
| CoroutineCandidate
|
|
||||||
| FutureCandidate
|
|
||||||
| IteratorCandidate
|
|
||||||
| AsyncIteratorCandidate
|
|
||||||
| FnPointerCandidate { .. }
|
|
||||||
| BuiltinObjectCandidate
|
|
||||||
| BuiltinUnsizeCandidate
|
|
||||||
| TraitUpcastingUnsizeCandidate(_)
|
|
||||||
| BuiltinCandidate { has_nested: true }
|
|
||||||
| TraitAliasCandidate,
|
|
||||||
ImplCandidate(_)
|
|
||||||
| ClosureCandidate { .. }
|
|
||||||
| AsyncClosureCandidate
|
|
||||||
| AsyncFnKindHelperCandidate
|
|
||||||
| CoroutineCandidate
|
|
||||||
| FutureCandidate
|
|
||||||
| IteratorCandidate
|
|
||||||
| AsyncIteratorCandidate
|
|
||||||
| FnPointerCandidate { .. }
|
|
||||||
| BuiltinObjectCandidate
|
|
||||||
| BuiltinUnsizeCandidate
|
|
||||||
| TraitUpcastingUnsizeCandidate(_)
|
|
||||||
| BuiltinCandidate { has_nested: true }
|
|
||||||
| TraitAliasCandidate,
|
|
||||||
) => DropVictim::No,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//@ check-pass
|
||||||
|
|
||||||
// A regression test for an edge case of candidate selection
|
// A regression test for an edge case of candidate selection
|
||||||
// in the old trait solver, see #132325 for more details. Unlike
|
// in the old trait solver, see #132325 for more details. Unlike
|
||||||
// the first test, this one has two impl candidates.
|
// the first test, this one has two impl candidates.
|
||||||
|
@ -12,7 +14,7 @@ where
|
||||||
(): Trait<u32>,
|
(): Trait<u32>,
|
||||||
(): Trait<T>,
|
(): Trait<T>,
|
||||||
{
|
{
|
||||||
impls_trait(()) //~ ERROR mismatched types
|
impls_trait(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
error[E0308]: mismatched types
|
|
||||||
--> $DIR/global-non-global-env-2.rs:15:5
|
|
||||||
|
|
|
||||||
LL | fn foo<T>() -> u32
|
|
||||||
| - --- expected `u32` because of return type
|
|
||||||
| |
|
|
||||||
| found this type parameter
|
|
||||||
...
|
|
||||||
LL | impls_trait(())
|
|
||||||
| ^^^^^^^^^^^^^^^ expected `u32`, found type parameter `T`
|
|
||||||
|
|
|
||||||
= note: expected type `u32`
|
|
||||||
found type parameter `T`
|
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0308`.
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//@ check-pass
|
||||||
|
|
||||||
// A regression test for an edge case of candidate selection
|
// A regression test for an edge case of candidate selection
|
||||||
// in the old trait solver, see #132325 for more details. Unlike
|
// in the old trait solver, see #132325 for more details. Unlike
|
||||||
// the third test, this one has 3 impl candidates.
|
// the third test, this one has 3 impl candidates.
|
||||||
|
@ -13,7 +15,7 @@ where
|
||||||
(): Trait<T>,
|
(): Trait<T>,
|
||||||
(): Trait<u32>,
|
(): Trait<u32>,
|
||||||
{
|
{
|
||||||
impls_trait(()) //~ ERROR mismatched types
|
impls_trait(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
error[E0308]: mismatched types
|
|
||||||
--> $DIR/global-non-global-env-4.rs:16:5
|
|
||||||
|
|
|
||||||
LL | fn foo<T>() -> u32
|
|
||||||
| - --- expected `u32` because of return type
|
|
||||||
| |
|
|
||||||
| found this type parameter
|
|
||||||
...
|
|
||||||
LL | impls_trait(())
|
|
||||||
| ^^^^^^^^^^^^^^^ expected `u32`, found type parameter `T`
|
|
||||||
|
|
|
||||||
= note: expected type `u32`
|
|
||||||
found type parameter `T`
|
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0308`.
|
|
Loading…
Add table
Add a link
Reference in a new issue