diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index c3cf7038bbe1..e37fe3476ed8 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -461,7 +461,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .flat_map(Result::transpose) .collect::, _>>()?; - debug!(?stack, ?candidates, "winnowed to {} candidates", candidates.len()); + debug!(?stack, ?candidates, "{} potentially applicable candidates", candidates.len()); // If there are *NO* candidates, then there are no impls -- // that we know of, anyway. Note that in the case where there // are unbound type variables within the obligation, it might @@ -1896,7 +1896,33 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } } if let Some((def_id, _evaluation)) = impl_candidate { - return Some(ImplCandidate(def_id)); + // Don't use impl candidates which overlap with other candidates. + // This should pretty much only ever happen with malformed impls. + if candidates.iter().all(|c| match c.candidate { + BuiltinCandidate { has_nested: _ } + | TransmutabilityCandidate + | AutoImplCandidate + | ClosureCandidate { .. } + | AsyncClosureCandidate + | AsyncFnKindHelperCandidate + | CoroutineCandidate + | FutureCandidate + | IteratorCandidate + | AsyncIteratorCandidate + | FnPointerCandidate + | TraitAliasCandidate + | TraitUpcastingUnsizeCandidate(_) + | BuiltinObjectCandidate + | BuiltinUnsizeCandidate => false, + // Non-global param candidates have already been handled, global + // where-bounds get ignored. + ParamCandidate(_) | ImplCandidate(_) => true, + ProjectionCandidate(_) | ObjectCandidate(_) => unreachable!(), + }) { + return Some(ImplCandidate(def_id)); + } else { + return None; + } } // Also try ignoring all global where-bounds and check whether we end