//! Confirmation.
//!
//! Confirmation unifies the output type parameters of the trait
//! with the values found in the obligation, possibly yielding a
//! type error. See the [rustc dev guide] for more details.
//!
//! [rustc dev guide]:
//! https://rustc-dev-guide.rust-lang.org/traits/resolution.html#confirmation

use std::ops::ControlFlow;

use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir::lang_items::LangItem;
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk};
use rustc_infer::traits::ObligationCauseCode;
use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData};
use rustc_middle::ty::{
    self, GenericArgsRef, Region, SizedTraitKind, Ty, TyCtxt, Unnormalized, Upcast,
};
use rustc_middle::{bug, span_bug};
use rustc_span::def_id::DefId;
use thin_vec::thin_vec;
use tracing::{debug, instrument};

use super::SelectionCandidate::{self, *};
use super::{PredicateObligations, SelectionContext};
use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to};
use crate::traits::util::{self, closure_trait_ref_and_return_type};
use crate::traits::{
    ImplSource, ImplSourceUserDefinedData, Normalized, Obligation, ObligationCause,
    PolyTraitObligation, PredicateObligation, Selection, SelectionError, TraitObligation,
};

impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
    #[instrument(level = "debug", skip(self))]
    pub(super) fn confirm_candidate(
        &mut self,
        obligation: &PolyTraitObligation<'tcx>,
        candidate: SelectionCandidate<'tcx>,
    ) -> Result<Selection<'tcx>, SelectionError<'tcx>> {
        Ok(match candidate {
            SizedCandidate => {
                let data = self.confirm_builtin_candidate(obligation);
                ImplSource::Builtin(BuiltinImplSource::Misc, data)
            }

            BuiltinCandidate => {
                let data = self.confirm_builtin_candidate(obligation);
                ImplSource::Builtin(BuiltinImplSource::Misc, data)
            }

            TransmutabilityCandidate => {
                let data = self.confirm_transmutability_candidate(obligation)?;
                ImplSource::Builtin(BuiltinImplSource::Misc, data)
            }

            ParamCandidate(param) => {
                let obligations =
                    self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref));
                ImplSource::Param(obligations)
            }

            ImplCandidate(impl_def_id) => {
                ImplSource::UserDefined(self.confirm_impl_candidate(obligation, impl_def_id))
            }

            AutoImplCandidate => {
                let data = self.confirm_auto_impl_candidate(obligation)?;
                ImplSource::Builtin(BuiltinImplSource::Misc, data)
            }

            ProjectionCandidate { idx, .. } => {
                let obligations = self.confirm_projection_candidate(obligation, idx)?;
                ImplSource::Param(obligations)
            }

            ObjectCandidate(idx) => self.confirm_object_candidate(obligation, idx)?,

            ClosureCandidate { .. } => {
                let vtable_closure = self.confirm_closure_candidate(obligation)?;
                ImplSource::Builtin(BuiltinImplSource::Misc, vtable_closure)
            }

            AsyncClosureCandidate => {
                let vtable_closure = self.confirm_async_closure_candidate(obligation)?;
                ImplSource::Builtin(BuiltinImplSource::Misc, vtable_closure)
            }

            // No nested obligations or confirmation process. The checks that we do in
            // candidate assembly are sufficient.
            AsyncFnKindHelperCandidate => {
                ImplSource::Builtin(BuiltinImplSource::Misc, PredicateObligations::new())
            }

            CoroutineCandidate => {
                let vtable_coroutine = self.confirm_coroutine_candidate(obligation)?;
                ImplSource::Builtin(BuiltinImplSource::Misc, vtable_coroutine)
            }

            FutureCandidate => {
                let vtable_future = self.confirm_future_candidate(obligation)?;
                ImplSource::Builtin(BuiltinImplSource::Misc, vtable_future)
            }

            IteratorCandidate => {
                let vtable_iterator = self.confirm_iterator_candidate(obligation)?;
                ImplSource::Builtin(BuiltinImplSource::Misc, vtable_iterator)
            }

            AsyncIteratorCandidate => {
                let vtable_iterator = self.confirm_async_iterator_candidate(obligation)?;
                ImplSource::Builtin(BuiltinImplSource::Misc, vtable_iterator)
            }

            FnPointerCandidate => {
                let data = self.confirm_fn_pointer_candidate(obligation)?;
                ImplSource::Builtin(BuiltinImplSource::Misc, data)
            }

            TraitAliasCandidate => {
                let data = self.confirm_trait_alias_candidate(obligation);
                ImplSource::Builtin(BuiltinImplSource::Misc, data)
            }

            BuiltinObjectCandidate => {
                // This indicates something like `Trait + Send: Send`. In this case, we know that
                // this holds because that's what the object type is telling us, and there's really
                // no additional obligations to prove and no types in particular to unify, etc.
                ImplSource::Builtin(BuiltinImplSource::Misc, PredicateObligations::new())
            }

            BuiltinUnsizeCandidate => self.confirm_builtin_unsize_candidate(obligation)?,

            TraitUpcastingUnsizeCandidate(idx) => {
                self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?
            }

            BikeshedGuaranteedNoDropCandidate => {
                self.confirm_bikeshed_guaranteed_no_drop_candidate(obligation)
            }
        })
    }

    fn confirm_projection_candidate(
        &mut self,
        obligation: &PolyTraitObligation<'tcx>,
        idx: usize,
    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
        let placeholder_trait_predicate =
            self.infcx.enter_forall_and_leak_universe(obligation.predicate).trait_ref;
        let placeholder_self_ty = self.infcx.shallow_resolve(placeholder_trait_predicate.self_ty());
        let candidate_predicate = self
            .for_each_item_bound(
                placeholder_self_ty,
                |_, clause, clause_idx, _| {
                    if clause_idx == idx {
                        ControlFlow::Break(clause)
                    } else {
                        ControlFlow::Continue(())
                    }
                },
                || unreachable!(),
            )
            .break_value()
            .expect("expected to index into clause that exists");
        let candidate_predicate = candidate_predicate
            .as_trait_clause()
            .expect("projection candidate is not a trait predicate");
        let candidate_predicate =
            util::lazily_elaborate_sizedness_candidate(self.infcx, obligation, candidate_predicate);

        let candidate = candidate_predicate.map_bound(|t| t.trait_ref);

        let candidate = self.infcx.instantiate_binder_with_fresh_vars(
            obligation.cause.span,
            BoundRegionConversionTime::HigherRankedType,
            candidate,
        );
        let mut obligations = PredicateObligations::new();
        let candidate = normalize_with_depth_to(
            self,
            obligation.param_env,
            obligation.cause.clone(),
            obligation.recursion_depth + 1,
            candidate,
            &mut obligations,
        );

        obligations.extend(
            self.infcx
                .at(&obligation.cause, obligation.param_env)
                .eq(DefineOpaqueTypes::No, placeholder_trait_predicate, candidate)
                .map(|InferOk { obligations, .. }| obligations)
                .map_err(|_| SelectionError::Unimplemented)?,
        );

        Ok(obligations)
    }

    fn confirm_param_candidate(
        &mut self,
        obligation: &PolyTraitObligation<'tcx>,
        param: ty::PolyTraitRef<'tcx>,
    ) -> PredicateObligations<'tcx> {
        debug!(?obligation, ?param, "confirm_param_candidate");

        let param = util::lazily_elaborate_sizedness_candidate(
            self.infcx,
            obligation,
            param.upcast(self.infcx.tcx),
        )
        .map_bound(|p| p.trait_ref);

        // During evaluation, we already checked that this
        // where-clause trait-ref could be unified with the obligation
        // trait-ref. Repeat that unification now without any
        // transactional boundary; it should not fail.
        match self.match_where_clause_trait_ref(obligation, param) {
            Ok(obligations) => obligations,
            Err(()) => {
                bug!(
                    "Where clause `{:?}` was applicable to `{:?}` but now is not",
                    param,
                    obligation
                );
            }
        }
    }

    #[instrument(level = "debug", skip(self), ret)]
    fn confirm_builtin_candidate(
        &mut self,
        obligation: &PolyTraitObligation<'tcx>,
    ) -> PredicateObligations<'tcx> {
        debug!(?obligation, "confirm_builtin_candidate");
        let tcx = self.tcx();
        let trait_def = obligation.predicate.def_id();
        let self_ty = self.infcx.shallow_resolve(
            self.infcx.enter_forall_and_leak_universe(obligation.predicate.self_ty()),
        );
        let types = match tcx.as_lang_item(trait_def) {
            Some(LangItem::Sized) => self.sizedness_conditions(self_ty, SizedTraitKind::Sized),
            Some(LangItem::MetaSized) => {
                self.sizedness_conditions(self_ty, SizedTraitKind::MetaSized)
            }
            Some(LangItem::PointeeSized) => {
                bug!("`PointeeSized` is removing during lowering");
            }
            Some(LangItem::Copy | LangItem::Clone | LangItem::TrivialClone) => {
                self.copy_clone_conditions(self_ty)
            }
            Some(LangItem::FusedIterator) => {
                if self.coroutine_is_gen(self_ty) {
                    ty::Binder::dummy(vec![])
                } else {
                    unreachable!("tried to assemble `FusedIterator` for non-gen coroutine");
                }
            }
            Some(
                LangItem::Destruct
                | LangItem::DiscriminantKind
                | LangItem::Field
                | LangItem::FnPtrTrait
                | LangItem::PointeeTrait
                | LangItem::Tuple
                | LangItem::Unpin,
            ) => ty::Binder::dummy(vec![]),
            other => bug!("unexpected builtin trait {trait_def:?} ({other:?})"),
        };
        let types = self.infcx.enter_forall_and_leak_universe(types);

        let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived);
        self.collect_predicates_for_types(
            obligation.param_env,
            cause,
            obligation.recursion_depth + 1,
            trait_def,
            types,
        )
    }

    #[instrument(level = "debug", skip(self))]
    fn confirm_transmutability_candidate(
        &mut self,
        obligation: &PolyTraitObligation<'tcx>,
    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
        use rustc_transmute::{Answer, Assume, Condition};

        /// Flatten the `Condition` tree into a conjunction of obligations.
        #[instrument(level = "debug", skip(tcx, obligation))]
        fn flatten_answer_tree<'tcx>(
            tcx: TyCtxt<'tcx>,
            obligation: &PolyTraitObligation<'tcx>,
            cond: Condition<Region<'tcx>, Ty<'tcx>>,
            assume: Assume,
        ) -> PredicateObligations<'tcx> {
            match cond {
                // FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll`
                // Not possible until the trait solver supports disjunctions of obligations
                Condition::IfAll(conds) | Condition::IfAny(conds) => conds
                    .into_iter()
                    .flat_map(|cond| flatten_answer_tree(tcx, obligation, cond, assume))
                    .collect(),
                Condition::Immutable { ty } => {
                    let trait_ref = ty::TraitRef::new(
                        tcx,
                        tcx.require_lang_item(LangItem::Freeze, obligation.cause.span),
                        [ty::GenericArg::from(ty)],
                    );
                    thin_vec![Obligation::with_depth(
                        tcx,
                        obligation.cause.clone(),
                        obligation.recursion_depth + 1,
                        obligation.param_env,
                        trait_ref,
                    )]
                }
                Condition::Outlives { long, short } => {
                    let outlives = ty::OutlivesPredicate(long, short);
                    thin_vec![Obligation::with_depth(
                        tcx,
                        obligation.cause.clone(),
                        obligation.recursion_depth + 1,
                        obligation.param_env,
                        outlives,
                    )]
                }
                Condition::Transmutable { src, dst } => {
                    let transmute_trait = obligation.predicate.def_id();
                    let assume = obligation.predicate.skip_binder().trait_ref.args.const_at(2);
                    let trait_ref = ty::TraitRef::new(
                        tcx,
                        transmute_trait,
                        [
                            ty::GenericArg::from(dst),
                            ty::GenericArg::from(src),
                            ty::GenericArg::from(assume),
                        ],
                    );
                    thin_vec![Obligation::with_depth(
                        tcx,
                        obligation.cause.clone(),
                        obligation.recursion_depth + 1,
                        obligation.param_env,
                        trait_ref,
                    )]
                }
            }
        }

        let predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);

        let mut assume = predicate.trait_ref.args.const_at(2);
        if self.tcx().features().generic_const_exprs() {
            assume = crate::traits::evaluate_const(self.infcx, assume, obligation.param_env)
        }
        let Some(assume) = rustc_transmute::Assume::from_const(self.infcx.tcx, assume) else {
            return Err(SelectionError::Unimplemented);
        };

        let dst = predicate.trait_ref.args.type_at(0);
        let src = predicate.trait_ref.args.type_at(1);

        debug!(?src, ?dst);
        let mut transmute_env = rustc_transmute::TransmuteTypeEnv::new(self.infcx.tcx);
        let maybe_transmutable = transmute_env.is_transmutable(src, dst, assume);

        let fully_flattened = match maybe_transmutable {
            Answer::No(_) => Err(SelectionError::Unimplemented)?,
            Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation, cond, assume),
            Answer::Yes => PredicateObligations::new(),
        };

        debug!(?fully_flattened);
        Ok(fully_flattened)
    }

    /// This handles the case where an `auto trait Foo` impl is being used.
    /// The idea is that the impl applies to `X : Foo` if the following conditions are met:
    ///
    /// 1. For each constituent type `Y` in `X`, `Y : Foo` holds
    /// 2. For each where-clause `C` declared on `Foo`, `[Self => X] C` holds.
    fn confirm_auto_impl_candidate(
        &mut self,
        obligation: &PolyTraitObligation<'tcx>,
    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
        ensure_sufficient_stack(|| {
            assert_eq!(obligation.predicate.polarity(), ty::PredicatePolarity::Positive);

            let self_ty =
                obligation.predicate.self_ty().map_bound(|ty| self.infcx.shallow_resolve(ty));
            let self_ty = self.infcx.enter_forall_and_leak_universe(self_ty);

            let constituents = self.constituent_types_for_auto_trait(self_ty)?;
            let constituents = self.infcx.enter_forall_and_leak_universe(constituents);

            let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived);
            let mut obligations = self.collect_predicates_for_types(
                obligation.param_env,
                cause.clone(),
                obligation.recursion_depth + 1,
                obligation.predicate.def_id(),
                constituents.types,
            );

            // Only normalize these goals if `-Zhigher-ranked-assumptions` is enabled, since
            // we don't want to cause ourselves to do extra work if we're not even able to
            // take advantage of these assumption clauses.
            if self.tcx().sess.opts.unstable_opts.higher_ranked_assumptions {
                // FIXME(coroutine_clone): We could uplift this into `collect_predicates_for_types`
                // and do this for `Copy`/`Clone` too, but that's feature-gated so it doesn't really
                // matter yet.
                for assumption in constituents.assumptions {
                    let assumption = normalize_with_depth_to(
                        self,
                        obligation.param_env,
                        cause.clone(),
                        obligation.recursion_depth + 1,
                        assumption,
                        &mut obligations,
                    );
                    self.infcx.register_region_assumption(assumption);
                }
            }

            Ok(obligations)
        })
    }

    fn confirm_impl_candidate(
        &mut self,
        obligation: &PolyTraitObligation<'tcx>,
        impl_def_id: DefId,
    ) -> ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>> {
        debug!(?obligation, ?impl_def_id, "confirm_impl_candidate");

        // First, create the generic parameters by matching the impl again,
        // this time not in a probe.
        let args = self.rematch_impl(impl_def_id, obligation);
        debug!(?args, "impl args");
        ensure_sufficient_stack(|| {
            self.vtable_impl(
                impl_def_id,
                args,
                &obligation.cause,
                obligation.recursion_depth + 1,
                obligation.param_env,
                obligation.predicate,
            )
        })
    }

    fn vtable_impl(
        &mut self,
        impl_def_id: DefId,
        args: Normalized<'tcx, GenericArgsRef<'tcx>>,
        cause: &ObligationCause<'tcx>,
        recursion_depth: usize,
        param_env: ty::ParamEnv<'tcx>,
        parent_trait_pred: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
    ) -> ImplSourceUserDefinedData<'tcx, PredicateObligation<'tcx>> {
        debug!(?impl_def_id, ?args, ?recursion_depth, "vtable_impl");

        let mut impl_obligations = self.impl_or_trait_obligations(
            cause,
            recursion_depth,
            param_env,
            impl_def_id,
            args.value,
            parent_trait_pred,
        );

        debug!(?impl_obligations, "vtable_impl");

        // Because of RFC447, the impl-trait-ref and obligations
        // are sufficient to determine the impl args, without
        // relying on projections in the impl-trait-ref.
        //
        // e.g., `impl<U: Tr, V: Iterator<Item=U>> Foo<<U as Tr>::T> for V`
        impl_obligations.extend(args.obligations);

        ImplSourceUserDefinedData { impl_def_id, args: args.value, nested: impl_obligations }
    }

    fn confirm_object_candidate(
        &mut self,
        obligation: &PolyTraitObligation<'tcx>,
        index: usize,
    ) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
        let tcx = self.tcx();
        debug!(?obligation, ?index, "confirm_object_candidate");

        let trait_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
        let self_ty = self.infcx.shallow_resolve(trait_predicate.self_ty());
        let ty::Dynamic(data, ..) = *self_ty.kind() else {
            span_bug!(obligation.cause.span, "object candidate with non-object");
        };

        let object_trait_ref = data.principal().unwrap_or_else(|| {
            span_bug!(obligation.cause.span, "object candidate with no principal")
        });
        let object_trait_ref = self.infcx.instantiate_binder_with_fresh_vars(
            obligation.cause.span,
            BoundRegionConversionTime::HigherRankedType,
            object_trait_ref,
        );
        let object_trait_ref = object_trait_ref.with_self_ty(self.tcx(), self_ty);

        let mut nested = PredicateObligations::new();

        let mut supertraits = util::supertraits(tcx, ty::Binder::dummy(object_trait_ref));
        let unnormalized_upcast_trait_ref =
            supertraits.nth(index).expect("supertraits iterator no longer has as many elements");

        let upcast_trait_ref = self.infcx.instantiate_binder_with_fresh_vars(
            obligation.cause.span,
            BoundRegionConversionTime::HigherRankedType,
            unnormalized_upcast_trait_ref,
        );
        let upcast_trait_ref = normalize_with_depth_to(
            self,
            obligation.param_env,
            obligation.cause.clone(),
            obligation.recursion_depth + 1,
            upcast_trait_ref,
            &mut nested,
        );

        nested.extend(
            self.infcx
                .at(&obligation.cause, obligation.param_env)
                .eq(DefineOpaqueTypes::No, trait_predicate.trait_ref, upcast_trait_ref)
                .map(|InferOk { obligations, .. }| obligations)
                .map_err(|_| SelectionError::Unimplemented)?,
        );

        // Check supertraits hold. This is so that their associated type bounds
        // will be checked in the code below.
        for (supertrait, _) in tcx
            .explicit_super_predicates_of(trait_predicate.def_id())
            .iter_instantiated_copied(tcx, trait_predicate.trait_ref.args)
            .map(Unnormalized::skip_norm_wip)
        {
            let normalized_supertrait = normalize_with_depth_to(
                self,
                obligation.param_env,
                obligation.cause.clone(),
                obligation.recursion_depth + 1,
                supertrait,
                &mut nested,
            );
            nested.push(obligation.with(tcx, normalized_supertrait));
        }

        let assoc_types: Vec<_> = tcx
            .associated_items(trait_predicate.def_id())
            .in_definition_order()
            // Associated types that require `Self: Sized` do not show up in the built-in
            // implementation of `Trait for dyn Trait`, and can be dropped here.
            .filter(|item| !tcx.generics_require_sized_self(item.def_id))
            .filter_map(|item| if item.is_type() { Some(item.def_id) } else { None })
            .collect();

        for assoc_type in assoc_types {
            let defs: &ty::Generics = tcx.generics_of(assoc_type);

            if !defs.own_params.is_empty() {
                tcx.dcx().span_delayed_bug(
                    obligation.cause.span,
                    "GATs in trait object shouldn't have been considered",
                );
                return Err(SelectionError::TraitDynIncompatible(trait_predicate.trait_ref.def_id));
            }

            // This maybe belongs in wf, but that can't (doesn't) handle
            // higher-ranked things.
            // Prevent, e.g., `dyn Iterator<Item = str>`.
            for bound in self.tcx().item_bounds(assoc_type).transpose_iter() {
                let normalized_bound = normalize_with_depth_to(
                    self,
                    obligation.param_env,
                    obligation.cause.clone(),
                    obligation.recursion_depth + 1,
                    bound.instantiate(tcx, trait_predicate.trait_ref.args).skip_norm_wip(),
                    &mut nested,
                );
                nested.push(obligation.with(tcx, normalized_bound));
            }
        }

        debug!(?nested, "object nested obligations");

        Ok(ImplSource::Builtin(BuiltinImplSource::Object(index), nested))
    }

    fn confirm_fn_pointer_candidate(
        &mut self,
        obligation: &PolyTraitObligation<'tcx>,
    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
        debug!(?obligation, "confirm_fn_pointer_candidate");
        let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
        let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());

        let tcx = self.tcx();
        let sig = self_ty.fn_sig(tcx);
        let trait_ref = closure_trait_ref_and_return_type(
            tcx,
            obligation.predicate.def_id(),
            self_ty,
            sig,
            util::TupleArgumentsFlag::Yes,
        )
        .map_bound(|(trait_ref, _)| trait_ref);

        let mut nested =
            self.equate_trait_refs(obligation.with(tcx, placeholder_predicate), trait_ref)?;
        let cause = obligation.derived_cause(ObligationCauseCode::BuiltinDerived);

        // Confirm the `type Output: Sized;` bound that is present on `FnOnce`
        let output_ty = self.infcx.enter_forall_and_leak_universe(sig.output());
        let output_ty = normalize_with_depth_to(
            self,
            obligation.param_env,
            cause.clone(),
            obligation.recursion_depth,
            output_ty,
            &mut nested,
        );
        let tr = ty::TraitRef::new(
            self.tcx(),
            self.tcx().require_lang_item(LangItem::Sized, cause.span),
            [output_ty],
        );
        nested.push(Obligation::new(self.infcx.tcx, cause, obligation.param_env, tr));

        Ok(nested)
    }

    fn confirm_trait_alias_candidate(
        &mut self,
        obligation: &PolyTraitObligation<'tcx>,
    ) -> PredicateObligations<'tcx> {
        debug!(?obligation, "confirm_trait_alias_candidate");

        let predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
        let trait_ref = predicate.trait_ref;
        let trait_def_id = trait_ref.def_id;
        let args = trait_ref.args;

        let trait_obligations = self.impl_or_trait_obligations(
            &obligation.cause,
            obligation.recursion_depth,
            obligation.param_env,
            trait_def_id,
            args,
            obligation.predicate,
        );

        debug!(?trait_def_id, ?trait_obligations, "trait alias obligations");

        trait_obligations
    }

    fn confirm_coroutine_candidate(
        &mut self,
        obligation: &PolyTraitObligation<'tcx>,
    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
        let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
        let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
        let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
            bug!("closure candidate for non-closure {:?}", obligation);
        };

        debug!(?obligation, ?coroutine_def_id, ?args, "confirm_coroutine_candidate");

        let coroutine_sig = args.as_coroutine().sig();

        let (trait_ref, _, _) = super::util::coroutine_trait_ref_and_outputs(
            self.tcx(),
            obligation.predicate.def_id(),
            self_ty,
            coroutine_sig,
        );

        let nested = self.equate_trait_refs(
            obligation.with(self.tcx(), placeholder_predicate),
            ty::Binder::dummy(trait_ref),
        )?;
        debug!(?trait_ref, ?nested, "coroutine candidate obligations");

        Ok(nested)
    }

    fn confirm_future_candidate(
        &mut self,
        obligation: &PolyTraitObligation<'tcx>,
    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
        let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
        let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
        let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
            bug!("closure candidate for non-closure {:?}", obligation);
        };

        debug!(?obligation, ?coroutine_def_id, ?args, "confirm_future_candidate");

        let coroutine_sig = args.as_coroutine().sig();

        let (trait_ref, _) = super::util::future_trait_ref_and_outputs(
            self.tcx(),
            obligation.predicate.def_id(),
            self_ty,
            coroutine_sig,
        );

        let nested = self.equate_trait_refs(
            obligation.with(self.tcx(), placeholder_predicate),
            ty::Binder::dummy(trait_ref),
        )?;
        debug!(?trait_ref, ?nested, "future candidate obligations");

        Ok(nested)
    }

    fn confirm_iterator_candidate(
        &mut self,
        obligation: &PolyTraitObligation<'tcx>,
    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
        let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
        let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
        let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
            bug!("closure candidate for non-closure {:?}", obligation);
        };

        debug!(?obligation, ?coroutine_def_id, ?args, "confirm_iterator_candidate");

        let gen_sig = args.as_coroutine().sig();

        let (trait_ref, _) = super::util::iterator_trait_ref_and_outputs(
            self.tcx(),
            obligation.predicate.def_id(),
            self_ty,
            gen_sig,
        );

        let nested = self.equate_trait_refs(
            obligation.with(self.tcx(), placeholder_predicate),
            ty::Binder::dummy(trait_ref),
        )?;
        debug!(?trait_ref, ?nested, "iterator candidate obligations");

        Ok(nested)
    }

    fn confirm_async_iterator_candidate(
        &mut self,
        obligation: &PolyTraitObligation<'tcx>,
    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
        let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
        let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());
        let ty::Coroutine(coroutine_def_id, args) = *self_ty.kind() else {
            bug!("closure candidate for non-closure {:?}", obligation);
        };

        debug!(?obligation, ?coroutine_def_id, ?args, "confirm_async_iterator_candidate");

        let gen_sig = args.as_coroutine().sig();

        let (trait_ref, _) = super::util::async_iterator_trait_ref_and_outputs(
            self.tcx(),
            obligation.predicate.def_id(),
            self_ty,
            gen_sig,
        );

        let nested = self.equate_trait_refs(
            obligation.with(self.tcx(), placeholder_predicate),
            ty::Binder::dummy(trait_ref),
        )?;
        debug!(?trait_ref, ?nested, "iterator candidate obligations");

        Ok(nested)
    }

    #[instrument(skip(self), level = "debug")]
    fn confirm_closure_candidate(
        &mut self,
        obligation: &PolyTraitObligation<'tcx>,
    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
        let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
        let self_ty: Ty<'_> = self.infcx.shallow_resolve(placeholder_predicate.self_ty());

        let trait_ref = match *self_ty.kind() {
            ty::Closure(..) => {
                self.closure_trait_ref_unnormalized(self_ty, obligation.predicate.def_id())
            }
            ty::CoroutineClosure(_, args) => {
                args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| {
                    ty::TraitRef::new(
                        self.tcx(),
                        obligation.predicate.def_id(),
                        [self_ty, sig.tupled_inputs_ty],
                    )
                })
            }
            _ => {
                bug!("closure candidate for non-closure {:?}", obligation);
            }
        };

        self.equate_trait_refs(obligation.with(self.tcx(), placeholder_predicate), trait_ref)
    }

    #[instrument(skip(self), level = "debug")]
    fn confirm_async_closure_candidate(
        &mut self,
        obligation: &PolyTraitObligation<'tcx>,
    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
        let placeholder_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
        let self_ty = self.infcx.shallow_resolve(placeholder_predicate.self_ty());

        let tcx = self.tcx();

        let mut nested = PredicateObligations::new();
        let (trait_ref, kind_ty) = match *self_ty.kind() {
            ty::CoroutineClosure(_, args) => {
                let args = args.as_coroutine_closure();
                let trait_ref = args.coroutine_closure_sig().map_bound(|sig| {
                    ty::TraitRef::new(
                        self.tcx(),
                        obligation.predicate.def_id(),
                        [self_ty, sig.tupled_inputs_ty],
                    )
                });

                // Note that unlike below, we don't need to check `Future + Sized` for
                // the output coroutine because they are `Future + Sized` by construction.

                (trait_ref, args.kind_ty())
            }
            ty::FnDef(..) | ty::FnPtr(..) => {
                let sig = self_ty.fn_sig(tcx);
                let trait_ref = sig.map_bound(|sig| {
                    ty::TraitRef::new(
                        self.tcx(),
                        obligation.predicate.def_id(),
                        [self_ty, Ty::new_tup(tcx, sig.inputs())],
                    )
                });

                // We must additionally check that the return type impls `Future + Sized`.
                let future_trait_def_id =
                    tcx.require_lang_item(LangItem::Future, obligation.cause.span);
                nested.push(obligation.with(
                    tcx,
                    sig.output().map_bound(|output_ty| {
                        ty::TraitRef::new(tcx, future_trait_def_id, [output_ty])
                    }),
                ));
                let sized_trait_def_id =
                    tcx.require_lang_item(LangItem::Sized, obligation.cause.span);
                nested.push(obligation.with(
                    tcx,
                    sig.output().map_bound(|output_ty| {
                        ty::TraitRef::new(tcx, sized_trait_def_id, [output_ty])
                    }),
                ));

                (trait_ref, Ty::from_closure_kind(tcx, ty::ClosureKind::Fn))
            }
            ty::Closure(_, args) => {
                let args = args.as_closure();
                let sig = args.sig();
                let trait_ref = sig.map_bound(|sig| {
                    ty::TraitRef::new(
                        self.tcx(),
                        obligation.predicate.def_id(),
                        [self_ty, sig.inputs()[0]],
                    )
                });

                // We must additionally check that the return type impls `Future + Sized`.
                let future_trait_def_id =
                    tcx.require_lang_item(LangItem::Future, obligation.cause.span);
                let placeholder_output_ty = self.infcx.enter_forall_and_leak_universe(sig.output());
                nested.push(obligation.with(
                    tcx,
                    ty::TraitRef::new(tcx, future_trait_def_id, [placeholder_output_ty]),
                ));
                let sized_trait_def_id =
                    tcx.require_lang_item(LangItem::Sized, obligation.cause.span);
                nested.push(obligation.with(
                    tcx,
                    sig.output().map_bound(|output_ty| {
                        ty::TraitRef::new(tcx, sized_trait_def_id, [output_ty])
                    }),
                ));

                (trait_ref, args.kind_ty())
            }
            _ => bug!("expected callable type for AsyncFn candidate"),
        };

        nested.extend(
            self.equate_trait_refs(obligation.with(tcx, placeholder_predicate), trait_ref)?,
        );

        let goal_kind =
            self.tcx().async_fn_trait_kind_from_def_id(obligation.predicate.def_id()).unwrap();

        // If we have not yet determined the `ClosureKind` of the closure or coroutine-closure,
        // then additionally register an `AsyncFnKindHelper` goal which will fail if the kind
        // is constrained to an insufficient type later on.
        if let Some(closure_kind) = self.infcx.shallow_resolve(kind_ty).to_opt_closure_kind() {
            if !closure_kind.extends(goal_kind) {
                return Err(SelectionError::Unimplemented);
            }
        } else {
            nested.push(Obligation::new(
                self.tcx(),
                obligation.derived_cause(ObligationCauseCode::BuiltinDerived),
                obligation.param_env,
                ty::TraitRef::new(
                    self.tcx(),
                    self.tcx()
                        .require_lang_item(LangItem::AsyncFnKindHelper, obligation.cause.span),
                    [kind_ty, Ty::from_closure_kind(self.tcx(), goal_kind)],
                ),
            ));
        }

        Ok(nested)
    }

    /// In the case of closure types and fn pointers,
    /// we currently treat the input type parameters on the trait as
    /// outputs. This means that when we have a match we have only
    /// considered the self type, so we have to go back and make sure
    /// to relate the argument types too. This is kind of wrong, but
    /// since we control the full set of impls, also not that wrong,
    /// and it DOES yield better error messages (since we don't report
    /// errors as if there is no applicable impl, but rather report
    /// errors are about mismatched argument types.
    ///
    /// Here is an example. Imagine we have a closure expression
    /// and we desugared it so that the type of the expression is
    /// `Closure`, and `Closure` expects `i32` as argument. Then it
    /// is "as if" the compiler generated this impl:
    /// ```ignore (illustrative)
    /// impl Fn(i32) for Closure { ... }
    /// ```
    /// Now imagine our obligation is `Closure: Fn(usize)`. So far
    /// we have matched the self type `Closure`. At this point we'll
    /// compare the `i32` to `usize` and generate an error.
    ///
    /// Note that this checking occurs *after* the impl has selected,
    /// because these output type parameters should not affect the
    /// selection of the impl. Therefore, if there is a mismatch, we
    /// report an error to the user.
    #[instrument(skip(self), level = "trace")]
    fn equate_trait_refs(
        &mut self,
        obligation: TraitObligation<'tcx>,
        found_trait_ref: ty::PolyTraitRef<'tcx>,
    ) -> Result<PredicateObligations<'tcx>, SelectionError<'tcx>> {
        let found_trait_ref = self.infcx.instantiate_binder_with_fresh_vars(
            obligation.cause.span,
            BoundRegionConversionTime::HigherRankedType,
            found_trait_ref,
        );
        // Normalize the obligation and expected trait refs together, because why not
        let Normalized { obligations: nested, value: (obligation_trait_ref, found_trait_ref) } =
            ensure_sufficient_stack(|| {
                normalize_with_depth(
                    self,
                    obligation.param_env,
                    obligation.cause.clone(),
                    obligation.recursion_depth + 1,
                    (obligation.predicate.trait_ref, found_trait_ref),
                )
            });

        // needed to define opaque types for tests/ui/type-alias-impl-trait/assoc-projection-ice.rs
        self.infcx
            .at(&obligation.cause, obligation.param_env)
            .eq(DefineOpaqueTypes::Yes, obligation_trait_ref, found_trait_ref)
            .map(|InferOk { mut obligations, .. }| {
                obligations.extend(nested);
                obligations
            })
            .map_err(|terr| {
                SelectionError::SignatureMismatch(Box::new(SignatureMismatchData {
                    expected_trait_ref: obligation_trait_ref,
                    found_trait_ref,
                    terr,
                }))
            })
    }

    fn confirm_trait_upcasting_unsize_candidate(
        &mut self,
        obligation: &PolyTraitObligation<'tcx>,
        idx: usize,
    ) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
        let tcx = self.tcx();

        // `assemble_candidates_for_unsizing` should ensure there are no late-bound
        // regions here. See the comment there for more details.
        let predicate = obligation.predicate.no_bound_vars().unwrap();
        let a_ty = self.infcx.shallow_resolve(predicate.self_ty());
        let b_ty = self.infcx.shallow_resolve(predicate.trait_ref.args.type_at(1));

        let ty::Dynamic(a_data, a_region) = *a_ty.kind() else {
            bug!("expected `dyn` type in `confirm_trait_upcasting_unsize_candidate`")
        };
        let ty::Dynamic(b_data, b_region) = *b_ty.kind() else {
            bug!("expected `dyn` type in `confirm_trait_upcasting_unsize_candidate`")
        };

        let source_principal = a_data.principal().unwrap().with_self_ty(tcx, a_ty);
        let unnormalized_upcast_principal =
            util::supertraits(tcx, source_principal).nth(idx).unwrap();

        let nested = self
            .match_upcast_principal(
                obligation,
                unnormalized_upcast_principal,
                a_data,
                b_data,
                a_region,
                b_region,
            )?
            .expect("did not expect ambiguity during confirmation");

        Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting(idx), nested))
    }

    fn confirm_builtin_unsize_candidate(
        &mut self,
        obligation: &PolyTraitObligation<'tcx>,
    ) -> Result<ImplSource<'tcx, PredicateObligation<'tcx>>, SelectionError<'tcx>> {
        let tcx = self.tcx();

        // `assemble_candidates_for_unsizing` should ensure there are no late-bound
        // regions here. See the comment there for more details.
        let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap());
        let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1);
        let target = self.infcx.shallow_resolve(target);
        debug!(?source, ?target, "confirm_builtin_unsize_candidate");

        Ok(match (source.kind(), target.kind()) {
            // `dyn Trait + Kx + 'a` -> `dyn Trait + Ky + 'b` (auto traits and lifetime subtyping).
            (&ty::Dynamic(data_a, r_a), &ty::Dynamic(data_b, r_b)) => {
                // See `assemble_candidates_for_unsizing` for more info.
                // We already checked the compatibility of auto traits within `assemble_candidates_for_unsizing`.
                let existential_predicates = if data_b.principal().is_some() {
                    tcx.mk_poly_existential_predicates_from_iter(
                        data_a
                            .principal()
                            .map(|b| b.map_bound(ty::ExistentialPredicate::Trait))
                            .into_iter()
                            .chain(
                                data_a
                                    .projection_bounds()
                                    .map(|b| b.map_bound(ty::ExistentialPredicate::Projection)),
                            )
                            .chain(
                                data_b
                                    .auto_traits()
                                    .map(ty::ExistentialPredicate::AutoTrait)
                                    .map(ty::Binder::dummy),
                            ),
                    )
                } else {
                    // If we're unsizing to a dyn type that has no principal, then drop
                    // the principal and projections from the type. We use the auto traits
                    // from the RHS type since as we noted that we've checked for auto
                    // trait compatibility during unsizing.
                    tcx.mk_poly_existential_predicates_from_iter(
                        data_b
                            .auto_traits()
                            .map(ty::ExistentialPredicate::AutoTrait)
                            .map(ty::Binder::dummy),
                    )
                };
                let source_trait = Ty::new_dynamic(tcx, existential_predicates, r_b);

                // Require that the traits involved in this upcast are **equal**;
                // only the **lifetime bound** is changed.
                let InferOk { mut obligations, .. } = self
                    .infcx
                    .at(&obligation.cause, obligation.param_env)
                    .sup(DefineOpaqueTypes::Yes, target, source_trait)
                    .map_err(|_| SelectionError::Unimplemented)?;

                // Register one obligation for 'a: 'b.
                let outlives = ty::OutlivesPredicate(r_a, r_b);
                obligations.push(Obligation::with_depth(
                    tcx,
                    obligation.cause.clone(),
                    obligation.recursion_depth + 1,
                    obligation.param_env,
                    obligation.predicate.rebind(outlives),
                ));

                ImplSource::Builtin(BuiltinImplSource::Misc, obligations)
            }

            // `T` -> `dyn Trait`
            (_, &ty::Dynamic(data, r)) => {
                let mut object_dids = data.auto_traits().chain(data.principal_def_id());
                if let Some(did) = object_dids.find(|did| !tcx.is_dyn_compatible(*did)) {
                    return Err(SelectionError::TraitDynIncompatible(did));
                }

                let predicate_to_obligation = |predicate| {
                    Obligation::with_depth(
                        tcx,
                        obligation.cause.clone(),
                        obligation.recursion_depth + 1,
                        obligation.param_env,
                        predicate,
                    )
                };

                // Create obligations:
                //  - Casting `T` to `Trait`
                //  - For all the various builtin bounds attached to the object cast. (In other
                //  words, if the object type is `Foo + Send`, this would create an obligation for
                //  the `Send` check.)
                //  - Projection predicates
                let mut nested: PredicateObligations<'_> = data
                    .iter()
                    .map(|predicate| predicate_to_obligation(predicate.with_self_ty(tcx, source)))
                    .collect();

                // We can only make objects from sized types.
                let tr = ty::TraitRef::new(
                    tcx,
                    tcx.require_lang_item(LangItem::Sized, obligation.cause.span),
                    [source],
                );
                nested.push(predicate_to_obligation(tr.upcast(tcx)));

                // If the type is `Foo + 'a`, ensure that the type
                // being cast to `Foo + 'a` outlives `'a`:
                let outlives = ty::OutlivesPredicate(source, r);
                nested.push(predicate_to_obligation(
                    ty::ClauseKind::TypeOutlives(outlives).upcast(tcx),
                ));

                ImplSource::Builtin(BuiltinImplSource::Misc, nested)
            }

            // `[T; n]` -> `[T]`
            (&ty::Array(a, _), &ty::Slice(b)) => {
                let InferOk { obligations, .. } = self
                    .infcx
                    .at(&obligation.cause, obligation.param_env)
                    .eq(DefineOpaqueTypes::Yes, b, a)
                    .map_err(|_| SelectionError::Unimplemented)?;

                ImplSource::Builtin(BuiltinImplSource::Misc, obligations)
            }

            // `Struct<T>` -> `Struct<U>`
            (&ty::Adt(def, args_a), &ty::Adt(_, args_b)) => {
                let unsizing_params = tcx.unsizing_params_for_adt(def.did());
                if unsizing_params.is_empty() {
                    return Err(SelectionError::Unimplemented);
                }

                let tail_field = def.non_enum_variant().tail();
                let tail_field_ty = tcx.type_of(tail_field.did);

                let mut nested = PredicateObligations::new();

                // Extract `TailField<T>` and `TailField<U>` from `Struct<T>` and `Struct<U>`,
                // normalizing in the process, since `type_of` returns something directly from
                // HIR ty lowering (which means it's un-normalized).
                let source_tail = normalize_with_depth_to(
                    self,
                    obligation.param_env,
                    obligation.cause.clone(),
                    obligation.recursion_depth + 1,
                    tail_field_ty.instantiate(tcx, args_a).skip_norm_wip(),
                    &mut nested,
                );
                let target_tail = normalize_with_depth_to(
                    self,
                    obligation.param_env,
                    obligation.cause.clone(),
                    obligation.recursion_depth + 1,
                    tail_field_ty.instantiate(tcx, args_b).skip_norm_wip(),
                    &mut nested,
                );

                // Check that the source struct with the target's
                // unsizing parameters is equal to the target.
                let args =
                    tcx.mk_args_from_iter(args_a.iter().enumerate().map(|(i, k)| {
                        if unsizing_params.contains(i as u32) { args_b[i] } else { k }
                    }));
                let new_struct = Ty::new_adt(tcx, def, args);
                let InferOk { obligations, .. } = self
                    .infcx
                    .at(&obligation.cause, obligation.param_env)
                    .eq(DefineOpaqueTypes::Yes, target, new_struct)
                    .map_err(|_| SelectionError::Unimplemented)?;
                nested.extend(obligations);

                // Construct the nested `TailField<T>: Unsize<TailField<U>>` predicate.
                let tail_unsize_obligation = obligation.with(
                    tcx,
                    ty::TraitRef::new(
                        tcx,
                        obligation.predicate.def_id(),
                        [source_tail, target_tail],
                    ),
                );
                nested.push(tail_unsize_obligation);

                ImplSource::Builtin(BuiltinImplSource::Misc, nested)
            }

            _ => bug!("source: {source}, target: {target}"),
        })
    }

    /// This trait is indirectly exposed on stable, so do *not* extend the set of types that
    /// implement the trait without FCP!
    fn confirm_bikeshed_guaranteed_no_drop_candidate(
        &mut self,
        obligation: &PolyTraitObligation<'tcx>,
    ) -> ImplSource<'tcx, PredicateObligation<'tcx>> {
        let mut obligations = thin_vec![];

        let tcx = self.tcx();
        let self_ty = obligation.predicate.self_ty();
        match *self_ty.skip_binder().kind() {
            // `&mut T` and `&T` always implement `BikeshedGuaranteedNoDrop`.
            ty::Ref(..) => {}
            // `ManuallyDrop<T>` always implements `BikeshedGuaranteedNoDrop`.
            ty::Adt(def, _) if def.is_manually_drop() => {}
            // Arrays and tuples implement `BikeshedGuaranteedNoDrop` only if
            // their constituent types implement `BikeshedGuaranteedNoDrop`.
            ty::Tuple(tys) => {
                obligations.extend(tys.iter().map(|elem_ty| {
                    obligation.with(
                        tcx,
                        self_ty.rebind(ty::TraitRef::new(
                            tcx,
                            obligation.predicate.def_id(),
                            [elem_ty],
                        )),
                    )
                }));
            }
            ty::Array(elem_ty, _) => {
                obligations.push(obligation.with(
                    tcx,
                    self_ty.rebind(ty::TraitRef::new(
                        tcx,
                        obligation.predicate.def_id(),
                        [elem_ty],
                    )),
                ));
            }

            // All other types implement `BikeshedGuaranteedNoDrop` only if
            // they implement `Copy`. We could be smart here and short-circuit
            // some trivially `Copy`/`!Copy` types, but there's no benefit.
            ty::FnDef(..)
            | ty::FnPtr(..)
            | ty::Error(_)
            | ty::Uint(_)
            | ty::Int(_)
            | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
            | ty::Bool
            | ty::Float(_)
            | ty::Char
            | ty::RawPtr(..)
            | ty::Never
            | ty::Pat(..)
            | ty::Dynamic(..)
            | ty::Str
            | ty::Slice(_)
            | ty::Foreign(..)
            | ty::Adt(..)
            | ty::Alias(..)
            | ty::Param(_)
            | ty::Placeholder(..)
            | ty::Closure(..)
            | ty::CoroutineClosure(..)
            | ty::Coroutine(..)
            | ty::UnsafeBinder(_)
            | ty::CoroutineWitness(..)
            | ty::Bound(..) => {
                obligations.push(obligation.with(
                    tcx,
                    self_ty.map_bound(|ty| {
                        ty::TraitRef::new(
                            tcx,
                            tcx.require_lang_item(LangItem::Copy, obligation.cause.span),
                            [ty],
                        )
                    }),
                ));
            }

            ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
                panic!("unexpected type `{self_ty:?}`")
            }
        }

        ImplSource::Builtin(BuiltinImplSource::Misc, obligations)
    }
}
