diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index c93087a18cf..d819740b596 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -59,6 +59,7 @@ pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind;
pub use self::specialize::{specialization_graph, translate_substs, OverlapError};
pub use self::structural_match::search_for_structural_match_violation;
pub use self::structural_match::NonStructuralMatchTy;
+pub use self::util::subst_assoc_item_bound;
pub use self::util::{elaborate_predicates, elaborate_trait_ref, elaborate_trait_refs};
pub use self::util::{expand_trait_aliases, TraitAliasExpander};
pub use self::util::{
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index f626bb0b7e3..3d52453a5c7 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -11,6 +11,8 @@ use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness};
use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext};
pub use rustc_infer::traits::util::*;
+use std::iter;
+
///////////////////////////////////////////////////////////////////////////
// `TraitAliasExpander` iterator
///////////////////////////////////////////////////////////////////////////
@@ -357,6 +359,59 @@ pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool {
assoc_item.defaultness.is_final() && tcx.impl_defaultness(assoc_item.container.id()).is_final()
}
+/// Map a bound from an associated item to apply to some other type.
+/// For example, given the following trait
+///
+/// trait X { type Y<'a>: PartialEq }
+///
+/// Say that we know that `<() as X>::Y<'c> = i32` and we need to check that
+/// the `PartialEq` bound applies. This function would return
+/// `i32: PartialEq`.
+pub fn subst_assoc_item_bound<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ bound: ty::Predicate<'tcx>,
+ normalized_projection_ty: Ty<'tcx>,
+ assoc_item_substs: &[GenericArg<'tcx>],
+) -> ty::Predicate<'tcx> {
+ let translate_predicate_substs = move |predicate_substs: SubstsRef<'tcx>| {
+ tcx.mk_substs(
+ iter::once(normalized_projection_ty.into())
+ .chain(predicate_substs[1..].iter().map(|s| s.subst(tcx, assoc_item_substs))),
+ )
+ };
+
+ match bound.kind() {
+ ty::PredicateKind::Trait(poly_tr, c) => poly_tr
+ .map_bound(|tr| {
+ let trait_substs = translate_predicate_substs(tr.trait_ref.substs);
+ ty::TraitRef { def_id: tr.def_id(), substs: trait_substs }
+ })
+ .with_constness(*c)
+ .to_predicate(tcx),
+ ty::PredicateKind::Projection(poly_projection) => poly_projection
+ .map_bound(|projection| {
+ let projection_substs = translate_predicate_substs(projection.projection_ty.substs);
+ ty::ProjectionPredicate {
+ projection_ty: ty::ProjectionTy {
+ substs: projection_substs,
+ item_def_id: projection.projection_ty.item_def_id,
+ },
+ ty: projection.ty.subst(tcx, assoc_item_substs),
+ }
+ })
+ .to_predicate(tcx),
+ ty::PredicateKind::TypeOutlives(poly_outlives) => poly_outlives
+ .map_bound(|outlives| {
+ ty::OutlivesPredicate(
+ normalized_projection_ty,
+ outlives.1.subst(tcx, assoc_item_substs),
+ )
+ })
+ .to_predicate(tcx),
+ _ => bug!("unexepected projection bound: `{:?}`", bound),
+ }
+}
+
pub enum TupleArgumentsFlag {
Yes,
No,