1
Fork 0

Deduplicate item bounds after normalization

This commit is contained in:
Matthew Jasper 2020-08-15 20:31:07 +01:00
parent e42c97919c
commit 852073a7d2

View file

@ -1192,6 +1192,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
}; };
let bounds = tcx.item_bounds(def_id).subst(tcx, substs); let bounds = tcx.item_bounds(def_id).subst(tcx, substs);
// The bounds returned by `item_bounds` may contain duplicates after
// normalization, so try to deduplicate when possible to avoid
// unnecessary ambiguity.
let mut distinct_normalized_bounds = FxHashSet::default();
let matching_bounds = bounds let matching_bounds = bounds
.iter() .iter()
.enumerate() .enumerate()
@ -1199,12 +1204,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
if let ty::PredicateAtom::Trait(pred, _) = bound.skip_binders() { if let ty::PredicateAtom::Trait(pred, _) = bound.skip_binders() {
let bound = ty::Binder::bind(pred.trait_ref); let bound = ty::Binder::bind(pred.trait_ref);
if self.infcx.probe(|_| { if self.infcx.probe(|_| {
self.match_projection( if let Ok(normalized_trait) = self.match_projection(
obligation, obligation,
bound, bound,
placeholder_trait_predicate.trait_ref, placeholder_trait_predicate.trait_ref,
) ) {
.is_ok() match normalized_trait {
None => true,
Some(normalized_trait)
if distinct_normalized_bounds.insert(normalized_trait) =>
{
true
}
_ => false,
}
} else {
false
}
}) { }) {
return Some(idx); return Some(idx);
} }
@ -1221,20 +1237,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
matching_bounds matching_bounds
} }
/// Equates the trait in `obligation` with trait bound. If the two traits
/// can be equated and the normalized trait bound doesn't contain inference
/// variables or placeholders, the normalized bound is returned.
fn match_projection( fn match_projection(
&mut self, &mut self,
obligation: &TraitObligation<'tcx>, obligation: &TraitObligation<'tcx>,
trait_bound: ty::PolyTraitRef<'tcx>, trait_bound: ty::PolyTraitRef<'tcx>,
placeholder_trait_ref: ty::TraitRef<'tcx>, placeholder_trait_ref: ty::TraitRef<'tcx>,
) -> Result<Vec<PredicateObligation<'tcx>>, ()> { ) -> Result<Option<ty::PolyTraitRef<'tcx>>, ()> {
debug_assert!(!placeholder_trait_ref.has_escaping_bound_vars()); debug_assert!(!placeholder_trait_ref.has_escaping_bound_vars());
if placeholder_trait_ref.def_id != trait_bound.def_id() { if placeholder_trait_ref.def_id != trait_bound.def_id() {
// Avoid unnecessary normalization // Avoid unnecessary normalization
return Err(()); return Err(());
} }
let Normalized { value: trait_bound, obligations: mut nested_obligations } = let Normalized { value: trait_bound, obligations: _ } = ensure_sufficient_stack(|| {
ensure_sufficient_stack(|| {
project::normalize_with_depth( project::normalize_with_depth(
self, self,
obligation.param_env, obligation.param_env,
@ -1246,9 +1264,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
self.infcx self.infcx
.at(&obligation.cause, obligation.param_env) .at(&obligation.cause, obligation.param_env)
.sup(ty::Binder::dummy(placeholder_trait_ref), trait_bound) .sup(ty::Binder::dummy(placeholder_trait_ref), trait_bound)
.map(|InferOk { obligations, .. }| { .map(|InferOk { obligations: _, value: () }| {
nested_obligations.extend(obligations); // This method is called within a probe, so we can't have
nested_obligations // inference variables and placeholders escape.
if !trait_bound.needs_infer() && !trait_bound.has_placeholders() {
Some(trait_bound)
} else {
None
}
}) })
.map_err(|_| ()) .map_err(|_| ())
} }