diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 993e95b351484..230429d325ff1 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1252,6 +1252,10 @@ impl<'tcx> InstantiatedPredicates<'tcx> {
     pub fn is_empty(&self) -> bool {
         self.predicates.is_empty()
     }
+
+    pub fn into_iter(self) -> impl Iterator<Item = (ty::Predicate<'tcx>, Span)> {
+        std::iter::zip(self.predicates, self.spans)
+    }
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable, Lift)]
diff --git a/compiler/rustc_trait_selection/src/solve/assembly.rs b/compiler/rustc_trait_selection/src/solve/assembly.rs
index e9ddad11ff23e..67ee168bccb1d 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly.rs
@@ -1,5 +1,7 @@
 //! Code shared by trait and projection goals for candidate assembly.
 
+use crate::traits::TupleArgumentsFlag;
+
 use super::infcx_ext::InferCtxtExt;
 use super::{
     fixme_instantiate_canonical_query_response, CanonicalGoal, CanonicalResponse, Certainty,
@@ -44,6 +46,45 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy {
         goal: Goal<'tcx, Self>,
         impl_def_id: DefId,
     );
+
+    fn consider_trait_alias_candidate(
+        acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+        goal: Goal<'tcx, Self>,
+    );
+
+    fn consider_alias_bound_candidates(
+        acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+        goal: Goal<'tcx, Self>,
+        alias_ty: ty::AliasTy<'tcx>,
+    );
+
+    fn consider_object_bound_candidates(
+        acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+        goal: Goal<'tcx, Self>,
+        object_bounds: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+    );
+
+    fn consider_param_env_candidates(
+        acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+        goal: Goal<'tcx, Self>,
+    );
+
+    fn consider_auto_trait_candidate(
+        acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+        goal: Goal<'tcx, Self>,
+    );
+
+    fn consider_fn_candidate(
+        acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+        goal: Goal<'tcx, Self>,
+        bound_sig: ty::PolyFnSig<'tcx>,
+        tuple_arguments: TupleArgumentsFlag,
+    );
+
+    fn consider_builtin_trait_candidates(
+        acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+        goal: Goal<'tcx, Self>,
+    );
 }
 
 /// An abstraction which correctly deals with the canonical results for candidates.
@@ -69,6 +110,18 @@ impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> {
 
         acx.assemble_impl_candidates(goal);
 
+        acx.assemble_bound_candidates(goal);
+
+        acx.assemble_param_env_candidates(goal);
+
+        acx.assemble_auto_trait_candidates(goal);
+
+        acx.assemble_trait_alias_candidates(goal);
+
+        acx.assemble_fn_like_candidates(goal);
+
+        G::consider_builtin_trait_candidates(&mut acx, goal);
+
         acx.candidates
     }
 
@@ -111,7 +164,10 @@ impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> {
 
             // NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate.
             // This doesn't work as long as we use `CandidateSource` in both winnowing and to resolve associated items.
-            let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
+            let goal = goal.with(
+                tcx,
+                goal.predicate.with_self_ty(tcx, self.infcx.shallow_resolve(normalized_ty)),
+            );
             let mut orig_values = OriginalQueryValues::default();
             let goal = self.infcx.canonicalize_query(goal, &mut orig_values);
             let normalized_candidates =
@@ -147,4 +203,53 @@ impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> {
             |impl_def_id| G::consider_impl_candidate(self, goal, impl_def_id),
         );
     }
+
+    fn assemble_bound_candidates(&mut self, goal: Goal<'tcx, G>) {
+        match *goal.predicate.self_ty().kind() {
+            ty::Alias(_, alias_ty) => G::consider_alias_bound_candidates(self, goal, alias_ty),
+            ty::Dynamic(predicates, _, _) => {
+                G::consider_object_bound_candidates(self, goal, predicates)
+            }
+            _ => {}
+        }
+    }
+
+    fn assemble_param_env_candidates(&mut self, goal: Goal<'tcx, G>) {
+        G::consider_param_env_candidates(self, goal);
+    }
+
+    fn assemble_auto_trait_candidates(&mut self, goal: Goal<'tcx, G>) {
+        if self.cx.tcx.trait_is_auto(goal.predicate.trait_def_id(self.cx.tcx)) {
+            G::consider_auto_trait_candidate(self, goal);
+        }
+    }
+
+    fn assemble_trait_alias_candidates(&mut self, goal: Goal<'tcx, G>) {
+        if self.cx.tcx.is_trait_alias(goal.predicate.trait_def_id(self.cx.tcx)) {
+            G::consider_trait_alias_candidate(self, goal);
+        }
+    }
+
+    fn assemble_fn_like_candidates(&mut self, goal: Goal<'tcx, G>) {
+        let tcx = self.cx.tcx;
+        let trait_def_id = goal.predicate.trait_def_id(tcx);
+        if let Some(goal_kind) = tcx.fn_trait_kind_from_def_id(trait_def_id) {
+            match *goal.predicate.self_ty().kind() {
+                ty::FnDef(def_id, substs) => {
+                    G::consider_fn_candidate(self, goal, tcx.bound_fn_sig(def_id).subst(tcx, substs), TupleArgumentsFlag::Yes)
+                }
+                ty::FnPtr(sig) => {
+                    G::consider_fn_candidate(self, goal, sig, TupleArgumentsFlag::Yes)
+                }
+                ty::Closure(_, substs) => {
+                    if let Some(kind) = self.infcx.closure_kind(substs)
+                        && kind.extends(goal_kind)
+                    {
+                        G::consider_fn_candidate(self, goal, substs.as_closure().sig(), TupleArgumentsFlag::No)
+                    }
+                }
+                _ => {}
+            }
+        }
+    }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs
index 3d649bea19ddf..68cdc24204788 100644
--- a/compiler/rustc_trait_selection/src/solve/project_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs
@@ -211,6 +211,59 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
             acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty);
         })
     }
+
+    fn consider_trait_alias_candidate(
+        _acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+        _goal: Goal<'tcx, Self>,
+    ) {
+        // Trait aliases never have (their own) associated types
+    }
+
+    fn consider_alias_bound_candidates(
+        _acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+        _goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
+        _alias_ty: ty::AliasTy<'tcx>,
+    ) {
+        todo!()
+    }
+
+    fn consider_object_bound_candidates(
+        _acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+        _goal: Goal<'tcx, Self>,
+        _object_bounds: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+    ) {
+        todo!()
+    }
+
+    fn consider_param_env_candidates(
+        _acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+        _goal: Goal<'tcx, Self>,
+    ) {
+        todo!()
+    }
+
+    fn consider_auto_trait_candidate(
+        _acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+        _goal: Goal<'tcx, Self>,
+    ) {
+        // Auto traits never have associated types
+    }
+
+    fn consider_fn_candidate(
+        _acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+        _goal: Goal<'tcx, Self>,
+        _bound_sig: ty::PolyFnSig<'tcx>,
+        _tuple_arguments: crate::traits::TupleArgumentsFlag,
+    ) {
+        todo!()
+    }
+
+    fn consider_builtin_trait_candidates(
+        _acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+        _goal: Goal<'tcx, Self>,
+    ) {
+        todo!();
+    }
 }
 
 /// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code.
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index c69cc39acb53c..a15f4b2f89edf 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -2,22 +2,30 @@
 
 use std::iter;
 
+use crate::traits::TupleArgumentsFlag;
+
 use super::assembly::{self, AssemblyCtxt};
-use super::{CanonicalGoal, EvalCtxt, Goal, QueryResult};
+use super::{CanonicalGoal, Certainty, EvalCtxt, Goal, MaybeCause, QueryResult};
+
 use rustc_hir::def_id::DefId;
-use rustc_infer::infer::InferOk;
+use rustc_hir::{Movability, Mutability, Unsafety};
+use rustc_infer::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime};
 use rustc_infer::traits::query::NoSolution;
+use rustc_infer::traits::util::supertraits;
 use rustc_infer::traits::ObligationCause;
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
-use rustc_middle::ty::TraitPredicate;
 use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{TraitPredicate, TypeVisitable};
 use rustc_span::DUMMY_SP;
+use rustc_target::spec::abi::Abi;
 
 #[allow(dead_code)] // FIXME: implement and use all variants.
 #[derive(Debug, Clone, Copy)]
 pub(super) enum CandidateSource {
     /// Some user-defined impl with the given `DefId`.
     Impl(DefId),
+    /// The automatic implementation of a trait alias.
+    TraitAlias,
     /// The n-th caller bound in the `param_env` of our goal.
     ///
     /// This is pretty much always a bound from the `where`-clauses of the
@@ -36,6 +44,11 @@ pub(super) enum CandidateSource {
     /// We know that `<Whatever as Trait>::Assoc: OtherTrait` holds by looking at
     /// the bounds on `Trait::Assoc`.
     AliasBound(usize),
+    /// Implementation of `Trait` or its supertraits for a `dyn Trait + Send + Sync`.
+    ObjectBound(usize),
+    /// Implementation of `Send` or other explicitly listed *auto* traits for
+    /// a `dyn Trait + Send + Sync`
+    ObjectAutoBound,
     /// A builtin implementation for some specific traits, used in cases
     /// where we cannot rely an ordinary library implementations.
     ///
@@ -47,6 +60,9 @@ pub(super) enum CandidateSource {
     /// at the constituent types of the `self_ty` to check whether the auto trait
     /// is implemented for those.
     AutoImpl,
+    /// An automatic impl for `Fn`/`FnMut`/`FnOnce` for fn pointers, fn items,
+    /// and closures.
+    Fn,
 }
 
 type Candidate<'tcx> = assembly::Candidate<'tcx, TraitPredicate<'tcx>>;
@@ -68,7 +84,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
 
     fn consider_impl_candidate(
         acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
-        goal: Goal<'tcx, TraitPredicate<'tcx>>,
+        goal: Goal<'tcx, Self>,
         impl_def_id: DefId,
     ) {
         let tcx = acx.cx.tcx;
@@ -108,6 +124,474 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
             acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty);
         })
     }
+
+    fn consider_trait_alias_candidate(
+        acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+        goal: Goal<'tcx, Self>,
+    ) {
+        let tcx = acx.cx.tcx;
+        acx.infcx.probe(|_| {
+            let nested_goals = tcx
+                .predicates_of(goal.predicate.def_id())
+                .instantiate_own(tcx, goal.predicate.trait_ref.substs)
+                .predicates
+                .into_iter()
+                .map(|pred| goal.with(tcx, pred))
+                .collect();
+            let Ok(certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return };
+            acx.try_insert_candidate(CandidateSource::TraitAlias, certainty);
+        })
+    }
+
+    fn consider_alias_bound_candidates(
+        acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+        goal: Goal<'tcx, Self>,
+        alias_ty: ty::AliasTy<'tcx>,
+    ) {
+        for (idx, (predicate, _)) in acx
+            .cx
+            .tcx
+            .bound_explicit_item_bounds(alias_ty.def_id)
+            .subst_iter_copied(acx.cx.tcx, alias_ty.substs)
+            .enumerate()
+        {
+            let Some(poly_trait_pred) = predicate.to_opt_poly_trait_pred() else { continue };
+            if poly_trait_pred.skip_binder().def_id() != goal.predicate.def_id() {
+                continue;
+            };
+            // FIXME: constness? polarity?
+            let poly_trait_ref = poly_trait_pred.map_bound(|trait_pred| trait_pred.trait_ref);
+            // FIXME: Faster to do a filter first with a rejection context?
+            match_poly_trait_ref_against_goal(
+                acx,
+                goal,
+                poly_trait_ref,
+                CandidateSource::AliasBound(idx),
+            );
+        }
+    }
+
+    fn consider_object_bound_candidates(
+        acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+        goal: Goal<'tcx, Self>,
+        object_bounds: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+    ) {
+        if let Some(principal_trait_ref) = object_bounds.principal() {
+            let principal_trait_ref =
+                principal_trait_ref.with_self_ty(acx.cx.tcx, goal.predicate.self_ty());
+
+            for (idx, poly_trait_ref) in supertraits(acx.cx.tcx, principal_trait_ref).enumerate() {
+                if poly_trait_ref.skip_binder().def_id != goal.predicate.def_id() {
+                    continue;
+                };
+                match_poly_trait_ref_against_goal(
+                    acx,
+                    goal,
+                    poly_trait_ref,
+                    CandidateSource::ObjectBound(idx),
+                );
+            }
+        }
+
+        if object_bounds.auto_traits().any(|def_id| def_id == goal.predicate.def_id()) {
+            acx.try_insert_candidate(CandidateSource::ObjectAutoBound, Certainty::Yes);
+        }
+    }
+
+    fn consider_param_env_candidates(
+        acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+        goal: Goal<'tcx, Self>,
+    ) {
+        for (idx, predicate) in goal.param_env.caller_bounds().iter().enumerate() {
+            let Some(poly_trait_pred) = predicate.to_opt_poly_trait_pred() else { continue };
+            if poly_trait_pred.skip_binder().def_id() != goal.predicate.def_id() {
+                continue;
+            };
+            // FIXME: constness? polarity?
+            let poly_trait_ref = poly_trait_pred.map_bound(|trait_pred| trait_pred.trait_ref);
+            // FIXME: Faster to do a filter first with a rejection context?
+
+            match_poly_trait_ref_against_goal(
+                acx,
+                goal,
+                poly_trait_ref,
+                CandidateSource::ParamEnv(idx),
+            );
+        }
+    }
+
+    fn consider_auto_trait_candidate(
+        acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+        goal: Goal<'tcx, Self>,
+    ) {
+        // FIXME: We need to give auto trait candidates less precedence than impl candidates?
+        acx.infcx.probe(|_| {
+            let components =
+                instantiate_constituent_tys_for_auto_trait(acx.infcx, goal.predicate.self_ty());
+            evaluate_goal_for_components(acx, goal, components, CandidateSource::AutoImpl);
+        })
+    }
+
+    fn consider_fn_candidate(
+        acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+        goal: Goal<'tcx, Self>,
+        bound_sig: ty::PolyFnSig<'tcx>,
+        tuple_args_flag: TupleArgumentsFlag,
+    ) {
+        if bound_sig.unsafety() != Unsafety::Normal || bound_sig.c_variadic() {
+            return;
+        }
+
+        // Binder skipped here (*)
+        let (arguments_tuple, expected_abi) = match tuple_args_flag {
+            TupleArgumentsFlag::No => (bound_sig.skip_binder().inputs()[0], Abi::RustCall),
+            TupleArgumentsFlag::Yes => {
+                (acx.cx.tcx.intern_tup(bound_sig.skip_binder().inputs()), Abi::Rust)
+            }
+        };
+        if expected_abi != bound_sig.abi() {
+            return;
+        }
+        // (*) Rebound here
+        let found_trait_ref = bound_sig.rebind(
+            acx.cx
+                .tcx
+                .mk_trait_ref(goal.predicate.def_id(), [goal.predicate.self_ty(), arguments_tuple]),
+        );
+
+        acx.infcx.probe(|_| {
+            // FIXME: This needs to validate that `fn() -> TY` has `TY: Sized`.
+            match_poly_trait_ref_against_goal(acx, goal, found_trait_ref, CandidateSource::Fn);
+        })
+    }
+
+    fn consider_builtin_trait_candidates(
+        acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+        goal: Goal<'tcx, Self>,
+    ) {
+        let lang_items = acx.cx.tcx.lang_items();
+        let trait_def_id = goal.predicate.def_id();
+        let self_ty = goal.predicate.self_ty();
+
+        if Some(trait_def_id) == lang_items.sized_trait() {
+            acx.infcx.probe(|_| {
+                let components = instantiate_constituent_tys_for_sized_trait(acx.infcx, self_ty);
+                evaluate_goal_for_components(acx, goal, components, CandidateSource::Builtin);
+            })
+        } else if Some(trait_def_id) == lang_items.copy_trait()
+            || Some(trait_def_id) == lang_items.clone_trait()
+        {
+            acx.infcx.probe(|_| {
+                let components =
+                    instantiate_constituent_tys_for_copy_clone_trait(acx.infcx, self_ty);
+                evaluate_goal_for_components(acx, goal, components, CandidateSource::Builtin);
+            })
+        } else if Some(trait_def_id) == lang_items.discriminant_kind_trait()
+            || Some(trait_def_id) == lang_items.pointee_trait()
+        {
+            // `Pointee` and `DiscriminantKind` are implemented by all traits unconditionally
+            acx.try_insert_candidate(CandidateSource::Builtin, Certainty::Yes);
+        } else if Some(trait_def_id) == lang_items.tuple_trait() {
+            match *self_ty.kind() {
+                ty::Infer(ty::TyVar(_)) => acx.try_insert_candidate(
+                    CandidateSource::Builtin,
+                    Certainty::Maybe(MaybeCause::Ambiguity),
+                ),
+                ty::Tuple(_) => acx.try_insert_candidate(CandidateSource::Builtin, Certainty::Yes),
+                _ => {}
+            }
+        } else if Some(trait_def_id) == lang_items.pointer_sized() {
+            let erased_self_ty = acx.cx.tcx.erase_regions(self_ty);
+            if erased_self_ty.has_non_region_infer() {
+                acx.try_insert_candidate(
+                    CandidateSource::Builtin,
+                    Certainty::Maybe(MaybeCause::Ambiguity),
+                )
+            } else {
+                let usize_layout = acx
+                    .cx
+                    .tcx
+                    .layout_of(ty::ParamEnv::empty().and(acx.cx.tcx.types.usize))
+                    .unwrap();
+                if let Ok(layout) = acx.cx.tcx.layout_of(goal.param_env.and(self_ty))
+                    && layout.layout.size() == usize_layout.layout.size()
+                    && layout.layout.align().abi == usize_layout.layout.align().abi
+                {
+                    acx.try_insert_candidate(CandidateSource::Builtin, Certainty::Yes);
+                }
+            }
+        } else if Some(trait_def_id) == lang_items.coerce_unsized_trait()
+            || Some(trait_def_id) == lang_items.unsize_trait()
+        {
+            // FIXME
+        }
+    }
+}
+
+fn match_poly_trait_ref_against_goal<'tcx>(
+    acx: &mut AssemblyCtxt<'_, 'tcx, TraitPredicate<'tcx>>,
+    goal: Goal<'tcx, TraitPredicate<'tcx>>,
+    trait_ref: ty::PolyTraitRef<'tcx>,
+    candidate: CandidateSource,
+) {
+    acx.infcx.probe(|_| {
+        let trait_ref = acx.infcx.replace_bound_vars_with_fresh_vars(
+            DUMMY_SP,
+            LateBoundRegionConversionTime::HigherRankedType,
+            trait_ref,
+        );
+
+        let Ok(InferOk { obligations, .. }) = acx
+            .infcx
+            .at(&ObligationCause::dummy(), goal.param_env)
+            .define_opaque_types(false)
+            .sup(goal.predicate.trait_ref, trait_ref)
+            .map_err(|e| debug!("failed to equate trait refs: {e:?}"))
+        else {
+            return
+        };
+
+        let nested_goals = obligations.into_iter().map(|o| o.into()).collect();
+
+        let Ok(certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return };
+        acx.try_insert_candidate(candidate, certainty);
+    })
+}
+
+// Evaluate the goal with a new set of self types, combined with a certainty.
+fn evaluate_goal_for_components<'tcx>(
+    acx: &mut AssemblyCtxt<'_, 'tcx, TraitPredicate<'tcx>>,
+    goal: Goal<'tcx, TraitPredicate<'tcx>>,
+    components: ComponentsAndCertainty<'tcx>,
+    candidate: CandidateSource,
+) {
+    let components = match components {
+        ComponentsAndCertainty::Yes(components) => components,
+        ComponentsAndCertainty::Maybe => {
+            acx.try_insert_candidate(candidate, Certainty::Maybe(MaybeCause::Ambiguity));
+            return;
+        }
+        ComponentsAndCertainty::No => {
+            return;
+        }
+    };
+
+    let nested_goals = components
+        .into_iter()
+        .map(|ty| {
+            Goal::new(
+                acx.cx.tcx,
+                goal.param_env,
+                ty::Binder::dummy(goal.predicate.with_self_ty(acx.cx.tcx, ty)),
+            )
+        })
+        .collect();
+
+    let Ok(certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return };
+    acx.try_insert_candidate(candidate, certainty);
+}
+
+enum ComponentsAndCertainty<'tcx> {
+    Yes(Vec<Ty<'tcx>>),
+    Maybe,
+    No,
+}
+
+// Calculates the constituent types of a type for `auto trait` purposes.
+//
+// For types with an "existential" binder, i.e. generator witnesses, we also
+// instantiate the binder with placeholders eagerly.
+fn instantiate_constituent_tys_for_auto_trait<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    ty: Ty<'tcx>,
+) -> ComponentsAndCertainty<'tcx> {
+    let tcx = infcx.tcx;
+    match *ty.kind() {
+        ty::Uint(_)
+        | ty::Int(_)
+        | ty::Bool
+        | ty::Float(_)
+        | ty::FnDef(..)
+        | ty::FnPtr(_)
+        | ty::Str
+        | ty::Error(_)
+        | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
+        | ty::Never
+        | ty::Char => ComponentsAndCertainty::Yes(vec![]),
+
+        ty::Placeholder(..)
+        | ty::Dynamic(..)
+        | ty::Param(..)
+        | ty::Foreign(..)
+        | ty::Alias(ty::Projection, ..)
+        | ty::Bound(..) => ComponentsAndCertainty::No,
+
+        ty::Infer(ty::TyVar(_)) => ComponentsAndCertainty::Maybe,
+
+        ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(),
+
+        ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => {
+            ComponentsAndCertainty::Yes(vec![element_ty])
+        }
+
+        ty::Array(element_ty, _) | ty::Slice(element_ty) => {
+            ComponentsAndCertainty::Yes(vec![element_ty])
+        }
+
+        ty::Tuple(ref tys) => {
+            // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet
+            ComponentsAndCertainty::Yes(tys.iter().collect())
+        }
+
+        ty::Closure(_, ref substs) => {
+            ComponentsAndCertainty::Yes(vec![substs.as_closure().tupled_upvars_ty()])
+        }
+
+        ty::Generator(_, ref substs, _) => {
+            let generator_substs = substs.as_generator();
+            ComponentsAndCertainty::Yes(vec![
+                generator_substs.tupled_upvars_ty(),
+                generator_substs.witness(),
+            ])
+        }
+
+        ty::GeneratorWitness(types) => {
+            ComponentsAndCertainty::Yes(infcx.replace_bound_vars_with_placeholders(types).to_vec())
+        }
+
+        // For `PhantomData<T>`, we pass `T`.
+        ty::Adt(def, substs) if def.is_phantom_data() => {
+            ComponentsAndCertainty::Yes(vec![substs.type_at(0)])
+        }
+
+        ty::Adt(def, substs) => {
+            ComponentsAndCertainty::Yes(def.all_fields().map(|f| f.ty(tcx, substs)).collect())
+        }
+
+        ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
+            // We can resolve the `impl Trait` to its concrete type,
+            // which enforces a DAG between the functions requiring
+            // the auto trait bounds in question.
+            ComponentsAndCertainty::Yes(vec![tcx.bound_type_of(def_id).subst(tcx, substs)])
+        }
+    }
+}
+
+fn instantiate_constituent_tys_for_sized_trait<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    ty: Ty<'tcx>,
+) -> ComponentsAndCertainty<'tcx> {
+    match *ty.kind() {
+        ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
+        | ty::Uint(_)
+        | ty::Int(_)
+        | ty::Bool
+        | ty::Float(_)
+        | ty::FnDef(..)
+        | ty::FnPtr(_)
+        | ty::RawPtr(..)
+        | ty::Char
+        | ty::Ref(..)
+        | ty::Generator(..)
+        | ty::GeneratorWitness(..)
+        | ty::Array(..)
+        | ty::Closure(..)
+        | ty::Never
+        | ty::Dynamic(_, _, ty::DynStar)
+        | ty::Error(_) => ComponentsAndCertainty::Yes(vec![]),
+
+        ty::Str
+        | ty::Slice(_)
+        | ty::Dynamic(..)
+        | ty::Foreign(..)
+        | ty::Alias(..)
+        | ty::Param(_) => ComponentsAndCertainty::No,
+
+        ty::Infer(ty::TyVar(_)) => ComponentsAndCertainty::Maybe,
+
+        ty::Placeholder(..)
+        | ty::Bound(..)
+        | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(),
+
+        ty::Tuple(tys) => ComponentsAndCertainty::Yes(tys.to_vec()),
+
+        ty::Adt(def, substs) => {
+            let sized_crit = def.sized_constraint(infcx.tcx);
+            ComponentsAndCertainty::Yes(
+                sized_crit
+                    .0
+                    .iter()
+                    .map(|ty| sized_crit.rebind(*ty).subst(infcx.tcx, substs))
+                    .collect(),
+            )
+        }
+    }
+}
+
+fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    ty: Ty<'tcx>,
+) -> ComponentsAndCertainty<'tcx> {
+    match *ty.kind() {
+        ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
+        | ty::FnDef(..)
+        | ty::FnPtr(_)
+        | ty::Error(_) => ComponentsAndCertainty::Yes(vec![]),
+
+        // Implementations are provided in core
+        ty::Uint(_)
+        | ty::Int(_)
+        | ty::Bool
+        | ty::Float(_)
+        | ty::Char
+        | ty::RawPtr(..)
+        | ty::Never
+        | ty::Ref(_, _, Mutability::Not)
+        | ty::Array(..) => ComponentsAndCertainty::No,
+
+        ty::Dynamic(..)
+        | ty::Str
+        | ty::Slice(_)
+        | ty::Generator(_, _, Movability::Static)
+        | ty::Foreign(..)
+        | ty::Ref(_, _, Mutability::Mut)
+        | ty::Adt(_, _)
+        | ty::Alias(_, _)
+        | ty::Param(_) => ComponentsAndCertainty::No,
+
+        ty::Infer(ty::TyVar(_)) => ComponentsAndCertainty::Maybe,
+
+        ty::Placeholder(..)
+        | ty::Bound(..)
+        | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(),
+
+        ty::Tuple(tys) => ComponentsAndCertainty::Yes(tys.to_vec()),
+
+        ty::Closure(_, substs) => match *substs.as_closure().tupled_upvars_ty().kind() {
+            ty::Tuple(tys) => ComponentsAndCertainty::Yes(tys.to_vec()),
+            ty::Infer(ty::TyVar(_)) => ComponentsAndCertainty::Maybe,
+            _ => bug!(),
+        },
+
+        ty::Generator(_, substs, Movability::Movable) => {
+            if infcx.tcx.features().generator_clone {
+                let generator = substs.as_generator();
+                match *generator.tupled_upvars_ty().kind() {
+                    ty::Tuple(tys) => ComponentsAndCertainty::Yes(
+                        tys.iter().chain([generator.witness()]).collect(),
+                    ),
+                    ty::Infer(ty::TyVar(_)) => ComponentsAndCertainty::Maybe,
+                    _ => bug!(),
+                }
+            } else {
+                ComponentsAndCertainty::No
+            }
+        }
+
+        ty::GeneratorWitness(types) => {
+            ComponentsAndCertainty::Yes(infcx.replace_bound_vars_with_placeholders(types).to_vec())
+        }
+    }
 }
 
 impl<'tcx> EvalCtxt<'tcx> {
@@ -167,10 +651,14 @@ impl<'tcx> EvalCtxt<'tcx> {
         // FIXME: implement this
         match (candidate.source, other.source) {
             (CandidateSource::Impl(_), _)
+            | (CandidateSource::TraitAlias, _)
             | (CandidateSource::ParamEnv(_), _)
             | (CandidateSource::AliasBound(_), _)
+            | (CandidateSource::ObjectBound(_), _)
+            | (CandidateSource::ObjectAutoBound, _)
             | (CandidateSource::Builtin, _)
-            | (CandidateSource::AutoImpl, _) => unimplemented!(),
+            | (CandidateSource::AutoImpl, _)
+            | (CandidateSource::Fn, _) => unimplemented!(),
         }
     }
 
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 37b40a2f75adc..340922220384c 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -60,6 +60,7 @@ pub use self::specialize::{specialization_graph, translate_substs, OverlapError}
 pub use self::structural_match::{
     search_for_adt_const_param_violation, search_for_structural_match_violation,
 };
+pub use self::util::TupleArgumentsFlag;
 pub use self::util::{
     elaborate_obligations, elaborate_predicates, elaborate_predicates_with_span,
     elaborate_trait_ref, elaborate_trait_refs,