about summary refs log tree commit diff
path: root/compiler/rustc_trait_selection/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_trait_selection/src')
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs2201
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs1314
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/different_lifetimes.rs160
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs238
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs128
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mod.rs94
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs94
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs495
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs87
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs618
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs163
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs167
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/note.rs422
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs954
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/region.rs1430
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/sub_relations.rs81
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs900
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/mod.rs72
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs86
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs518
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs244
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs218
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs53
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs17
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs303
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs1635
-rw-r--r--compiler/rustc_trait_selection/src/errors/note_and_explain.rs185
-rw-r--r--compiler/rustc_trait_selection/src/infer.rs15
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs8
-rw-r--r--compiler/rustc_trait_selection/src/regions.rs3
-rw-r--r--compiler/rustc_trait_selection/src/solve/delegate.rs5
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs10
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/analyse.rs17
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalize.rs18
-rw-r--r--compiler/rustc_trait_selection/src/traits/auto_trait.rs20
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs26
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/engine.rs28
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs40
-rw-r--r--compiler/rustc_trait_selection/src/traits/misc.rs116
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs63
-rw-r--r--compiler/rustc_trait_selection/src/traits/normalize.rs25
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs338
-rw-r--r--compiler/rustc_trait_selection/src/traits/outlives_bounds.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs53
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs24
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs31
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs9
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs12
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs33
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs54
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs9
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs30
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/_match.rs20
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs45
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs206
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs143
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs25
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_match.rs173
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs29
-rw-r--r--compiler/rustc_trait_selection/src/traits/vtable.rs38
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs30
66 files changed, 13200 insertions, 1414 deletions
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
new file mode 100644
index 00000000000..bff2a184b19
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
@@ -0,0 +1,2201 @@
+//! Error Reporting Code for the inference engine
+//!
+//! Because of the way inference, and in particular region inference,
+//! works, it often happens that errors are not detected until far after
+//! the relevant line of code has been type-checked. Therefore, there is
+//! an elaborate system to track why a particular constraint in the
+//! inference graph arose so that we can explain to the user what gave
+//! rise to a particular error.
+//!
+//! The system is based around a set of "origin" types. An "origin" is the
+//! reason that a constraint or inference variable arose. There are
+//! different "origin" enums for different kinds of constraints/variables
+//! (e.g., `TypeOrigin`, `RegionVariableOrigin`). An origin always has
+//! a span, but also more information so that we can generate a meaningful
+//! error message.
+//!
+//! Having a catalog of all the different reasons an error can arise is
+//! also useful for other reasons, like cross-referencing FAQs etc, though
+//! we are not really taking advantage of this yet.
+//!
+//! # Region Inference
+//!
+//! Region inference is particularly tricky because it always succeeds "in
+//! the moment" and simply registers a constraint. Then, at the end, we
+//! can compute the full graph and report errors, so we need to be able to
+//! store and later report what gave rise to the conflicting constraints.
+//!
+//! # Subtype Trace
+//!
+//! Determining whether `T1 <: T2` often involves a number of subtypes and
+//! subconstraints along the way. A "TypeTrace" is an extended version
+//! of an origin that traces the types and other values that were being
+//! compared. It is not necessarily comprehensive (in fact, at the time of
+//! this writing it only tracks the root values being compared) but I'd
+//! like to extend it to include significant "waypoints". For example, if
+//! you are comparing `(T1, T2) <: (T3, T4)`, and the problem is that `T2
+//! <: T4` fails, I'd like the trace to include enough information to say
+//! "in the 2nd element of the tuple". Similarly, failures when comparing
+//! arguments or return types in fn types should be able to cite the
+//! specific position, etc.
+//!
+//! # Reality vs plan
+//!
+//! Of course, there is still a LOT of code in typeck that has yet to be
+//! ported to this system, and which relies on string concatenation at the
+//! time of error detection.
+
+use std::borrow::Cow;
+use std::ops::ControlFlow;
+use std::path::PathBuf;
+use std::{cmp, fmt, iter};
+
+use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
+use rustc_errors::{pluralize, Applicability, Diag, DiagStyledString, IntoDiagArg, StringPart};
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::lang_items::LangItem;
+use rustc_hir::{self as hir};
+use rustc_macros::extension;
+use rustc_middle::bug;
+use rustc_middle::dep_graph::DepContext;
+use rustc_middle::ty::error::{ExpectedFound, TypeError, TypeErrorToStringExt};
+use rustc_middle::ty::print::{with_forced_trimmed_paths, PrintError, PrintTraitRefExt as _};
+use rustc_middle::ty::{
+    self, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
+    TypeVisitableExt,
+};
+use rustc_span::{sym, BytePos, DesugaringKind, Pos, Span};
+use rustc_target::spec::abi;
+use tracing::{debug, instrument};
+
+use crate::error_reporting::TypeErrCtxt;
+use crate::errors::{ObligationCauseFailureCode, TypeErrorAdditionalDiags};
+use crate::infer;
+use crate::infer::relate::{self, RelateResult, TypeRelation};
+use crate::infer::{InferCtxt, TypeTrace, ValuePairs};
+use crate::traits::{
+    IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
+};
+
+mod note_and_explain;
+mod suggest;
+
+pub mod need_type_info;
+pub mod nice_region_error;
+pub mod region;
+pub mod sub_relations;
+
+/// Makes a valid string literal from a string by escaping special characters (" and \),
+/// unless they are already escaped.
+fn escape_literal(s: &str) -> String {
+    let mut escaped = String::with_capacity(s.len());
+    let mut chrs = s.chars().peekable();
+    while let Some(first) = chrs.next() {
+        match (first, chrs.peek()) {
+            ('\\', Some(&delim @ '"') | Some(&delim @ '\'')) => {
+                escaped.push('\\');
+                escaped.push(delim);
+                chrs.next();
+            }
+            ('"' | '\'', _) => {
+                escaped.push('\\');
+                escaped.push(first)
+            }
+            (c, _) => escaped.push(c),
+        };
+    }
+    escaped
+}
+
+impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
+    // [Note-Type-error-reporting]
+    // An invariant is that anytime the expected or actual type is Error (the special
+    // error type, meaning that an error occurred when typechecking this expression),
+    // this is a derived error. The error cascaded from another error (that was already
+    // reported), so it's not useful to display it to the user.
+    // The following methods implement this logic.
+    // They check if either the actual or expected type is Error, and don't print the error
+    // in this case. The typechecker should only ever report type errors involving mismatched
+    // types using one of these methods, and should not call span_err directly for such
+    // errors.
+    pub fn type_error_struct_with_diag<M>(
+        &self,
+        sp: Span,
+        mk_diag: M,
+        actual_ty: Ty<'tcx>,
+    ) -> Diag<'a>
+    where
+        M: FnOnce(String) -> Diag<'a>,
+    {
+        let actual_ty = self.resolve_vars_if_possible(actual_ty);
+        debug!("type_error_struct_with_diag({:?}, {:?})", sp, actual_ty);
+
+        let mut err = mk_diag(self.ty_to_string(actual_ty));
+
+        // Don't report an error if actual type is `Error`.
+        if actual_ty.references_error() {
+            err.downgrade_to_delayed_bug();
+        }
+
+        err
+    }
+
+    pub fn report_mismatched_types(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        expected: Ty<'tcx>,
+        actual: Ty<'tcx>,
+        err: TypeError<'tcx>,
+    ) -> Diag<'a> {
+        self.report_and_explain_type_error(TypeTrace::types(cause, true, expected, actual), err)
+    }
+
+    pub fn report_mismatched_consts(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        expected: ty::Const<'tcx>,
+        actual: ty::Const<'tcx>,
+        err: TypeError<'tcx>,
+    ) -> Diag<'a> {
+        self.report_and_explain_type_error(TypeTrace::consts(cause, true, expected, actual), err)
+    }
+
+    pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
+        let (def_id, args) = match *ty.kind() {
+            ty::Alias(_, ty::AliasTy { def_id, args, .. })
+                if matches!(self.tcx.def_kind(def_id), DefKind::OpaqueTy) =>
+            {
+                (def_id, args)
+            }
+            ty::Alias(_, ty::AliasTy { def_id, args, .. })
+                if self.tcx.is_impl_trait_in_trait(def_id) =>
+            {
+                (def_id, args)
+            }
+            _ => return None,
+        };
+
+        let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
+        let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
+
+        self.tcx
+            .explicit_item_super_predicates(def_id)
+            .iter_instantiated_copied(self.tcx, args)
+            .find_map(|(predicate, _)| {
+                predicate
+                    .kind()
+                    .map_bound(|kind| match kind {
+                        ty::ClauseKind::Projection(projection_predicate)
+                            if projection_predicate.projection_term.def_id == item_def_id =>
+                        {
+                            projection_predicate.term.as_type()
+                        }
+                        _ => None,
+                    })
+                    .no_bound_vars()
+                    .flatten()
+            })
+    }
+
+    /// Adds a note if the types come from similarly named crates
+    fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) {
+        use hir::def_id::CrateNum;
+        use rustc_hir::definitions::DisambiguatedDefPathData;
+        use ty::print::Printer;
+        use ty::GenericArg;
+
+        struct AbsolutePathPrinter<'tcx> {
+            tcx: TyCtxt<'tcx>,
+            segments: Vec<String>,
+        }
+
+        impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
+            fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
+                self.tcx
+            }
+
+            fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> {
+                Err(fmt::Error)
+            }
+
+            fn print_type(&mut self, _ty: Ty<'tcx>) -> Result<(), PrintError> {
+                Err(fmt::Error)
+            }
+
+            fn print_dyn_existential(
+                &mut self,
+                _predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
+            ) -> Result<(), PrintError> {
+                Err(fmt::Error)
+            }
+
+            fn print_const(&mut self, _ct: ty::Const<'tcx>) -> Result<(), PrintError> {
+                Err(fmt::Error)
+            }
+
+            fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
+                self.segments = vec![self.tcx.crate_name(cnum).to_string()];
+                Ok(())
+            }
+            fn path_qualified(
+                &mut self,
+                _self_ty: Ty<'tcx>,
+                _trait_ref: Option<ty::TraitRef<'tcx>>,
+            ) -> Result<(), PrintError> {
+                Err(fmt::Error)
+            }
+
+            fn path_append_impl(
+                &mut self,
+                _print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
+                _disambiguated_data: &DisambiguatedDefPathData,
+                _self_ty: Ty<'tcx>,
+                _trait_ref: Option<ty::TraitRef<'tcx>>,
+            ) -> Result<(), PrintError> {
+                Err(fmt::Error)
+            }
+            fn path_append(
+                &mut self,
+                print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
+                disambiguated_data: &DisambiguatedDefPathData,
+            ) -> Result<(), PrintError> {
+                print_prefix(self)?;
+                self.segments.push(disambiguated_data.to_string());
+                Ok(())
+            }
+            fn path_generic_args(
+                &mut self,
+                print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
+                _args: &[GenericArg<'tcx>],
+            ) -> Result<(), PrintError> {
+                print_prefix(self)
+            }
+        }
+
+        let report_path_match = |err: &mut Diag<'_>, did1: DefId, did2: DefId| {
+            // Only report definitions from different crates. If both definitions
+            // are from a local module we could have false positives, e.g.
+            // let _ = [{struct Foo; Foo}, {struct Foo; Foo}];
+            if did1.krate != did2.krate {
+                let abs_path = |def_id| {
+                    let mut printer = AbsolutePathPrinter { tcx: self.tcx, segments: vec![] };
+                    printer.print_def_path(def_id, &[]).map(|_| printer.segments)
+                };
+
+                // We compare strings because DefPath can be different
+                // for imported and non-imported crates
+                let same_path = || -> Result<_, PrintError> {
+                    Ok(self.tcx.def_path_str(did1) == self.tcx.def_path_str(did2)
+                        || abs_path(did1)? == abs_path(did2)?)
+                };
+                if same_path().unwrap_or(false) {
+                    let crate_name = self.tcx.crate_name(did1.krate);
+                    let msg = if did1.is_local() || did2.is_local() {
+                        format!(
+                            "the crate `{crate_name}` is compiled multiple times, possibly with different configurations"
+                        )
+                    } else {
+                        format!(
+                            "perhaps two different versions of crate `{crate_name}` are being used?"
+                        )
+                    };
+                    err.note(msg);
+                }
+            }
+        };
+        match terr {
+            TypeError::Sorts(ref exp_found) => {
+                // if they are both "path types", there's a chance of ambiguity
+                // due to different versions of the same crate
+                if let (&ty::Adt(exp_adt, _), &ty::Adt(found_adt, _)) =
+                    (exp_found.expected.kind(), exp_found.found.kind())
+                {
+                    report_path_match(err, exp_adt.did(), found_adt.did());
+                }
+            }
+            TypeError::Traits(ref exp_found) => {
+                report_path_match(err, exp_found.expected, exp_found.found);
+            }
+            _ => (), // FIXME(#22750) handle traits and stuff
+        }
+    }
+
+    fn note_error_origin(
+        &self,
+        err: &mut Diag<'_>,
+        cause: &ObligationCause<'tcx>,
+        exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>,
+        terr: TypeError<'tcx>,
+    ) {
+        match *cause.code() {
+            ObligationCauseCode::Pattern { origin_expr: true, span: Some(span), root_ty } => {
+                let ty = self.resolve_vars_if_possible(root_ty);
+                if !matches!(ty.kind(), ty::Infer(ty::InferTy::TyVar(_) | ty::InferTy::FreshTy(_)))
+                {
+                    // don't show type `_`
+                    if span.desugaring_kind() == Some(DesugaringKind::ForLoop)
+                        && let ty::Adt(def, args) = ty.kind()
+                        && Some(def.did()) == self.tcx.get_diagnostic_item(sym::Option)
+                    {
+                        err.span_label(
+                            span,
+                            format!("this is an iterator with items of type `{}`", args.type_at(0)),
+                        );
+                    } else {
+                        err.span_label(span, format!("this expression has type `{ty}`"));
+                    }
+                }
+                if let Some(ty::error::ExpectedFound { found, .. }) = exp_found
+                    && ty.is_box()
+                    && ty.boxed_ty() == found
+                    && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
+                {
+                    err.span_suggestion(
+                        span,
+                        "consider dereferencing the boxed value",
+                        format!("*{snippet}"),
+                        Applicability::MachineApplicable,
+                    );
+                }
+            }
+            ObligationCauseCode::Pattern { origin_expr: false, span: Some(span), .. } => {
+                err.span_label(span, "expected due to this");
+            }
+            ObligationCauseCode::BlockTailExpression(
+                _,
+                hir::MatchSource::TryDesugar(scrut_hir_id),
+            ) => {
+                if let Some(ty::error::ExpectedFound { expected, .. }) = exp_found {
+                    let scrut_expr = self.tcx.hir().expect_expr(scrut_hir_id);
+                    let scrut_ty = if let hir::ExprKind::Call(_, args) = &scrut_expr.kind {
+                        let arg_expr = args.first().expect("try desugaring call w/out arg");
+                        self.typeck_results
+                            .as_ref()
+                            .and_then(|typeck_results| typeck_results.expr_ty_opt(arg_expr))
+                    } else {
+                        bug!("try desugaring w/out call expr as scrutinee");
+                    };
+
+                    match scrut_ty {
+                        Some(ty) if expected == ty => {
+                            let source_map = self.tcx.sess.source_map();
+                            err.span_suggestion(
+                                source_map.end_point(cause.span()),
+                                "try removing this `?`",
+                                "",
+                                Applicability::MachineApplicable,
+                            );
+                        }
+                        _ => {}
+                    }
+                }
+            }
+            ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
+                arm_block_id,
+                arm_span,
+                arm_ty,
+                prior_arm_block_id,
+                prior_arm_span,
+                prior_arm_ty,
+                source,
+                ref prior_non_diverging_arms,
+                scrut_span,
+                ..
+            }) => match source {
+                hir::MatchSource::TryDesugar(scrut_hir_id) => {
+                    if let Some(ty::error::ExpectedFound { expected, .. }) = exp_found {
+                        let scrut_expr = self.tcx.hir().expect_expr(scrut_hir_id);
+                        let scrut_ty = if let hir::ExprKind::Call(_, args) = &scrut_expr.kind {
+                            let arg_expr = args.first().expect("try desugaring call w/out arg");
+                            self.typeck_results
+                                .as_ref()
+                                .and_then(|typeck_results| typeck_results.expr_ty_opt(arg_expr))
+                        } else {
+                            bug!("try desugaring w/out call expr as scrutinee");
+                        };
+
+                        match scrut_ty {
+                            Some(ty) if expected == ty => {
+                                let source_map = self.tcx.sess.source_map();
+                                err.span_suggestion(
+                                    source_map.end_point(cause.span()),
+                                    "try removing this `?`",
+                                    "",
+                                    Applicability::MachineApplicable,
+                                );
+                            }
+                            _ => {}
+                        }
+                    }
+                }
+                _ => {
+                    // `prior_arm_ty` can be `!`, `expected` will have better info when present.
+                    let t = self.resolve_vars_if_possible(match exp_found {
+                        Some(ty::error::ExpectedFound { expected, .. }) => expected,
+                        _ => prior_arm_ty,
+                    });
+                    let source_map = self.tcx.sess.source_map();
+                    let mut any_multiline_arm = source_map.is_multiline(arm_span);
+                    if prior_non_diverging_arms.len() <= 4 {
+                        for sp in prior_non_diverging_arms {
+                            any_multiline_arm |= source_map.is_multiline(*sp);
+                            err.span_label(*sp, format!("this is found to be of type `{t}`"));
+                        }
+                    } else if let Some(sp) = prior_non_diverging_arms.last() {
+                        any_multiline_arm |= source_map.is_multiline(*sp);
+                        err.span_label(
+                            *sp,
+                            format!("this and all prior arms are found to be of type `{t}`"),
+                        );
+                    }
+                    let outer = if any_multiline_arm || !source_map.is_multiline(cause.span) {
+                        // Cover just `match` and the scrutinee expression, not
+                        // the entire match body, to reduce diagram noise.
+                        cause.span.shrink_to_lo().to(scrut_span)
+                    } else {
+                        cause.span
+                    };
+                    let msg = "`match` arms have incompatible types";
+                    err.span_label(outer, msg);
+                    if let Some(subdiag) = self.suggest_remove_semi_or_return_binding(
+                        prior_arm_block_id,
+                        prior_arm_ty,
+                        prior_arm_span,
+                        arm_block_id,
+                        arm_ty,
+                        arm_span,
+                    ) {
+                        err.subdiagnostic(subdiag);
+                    }
+                }
+            },
+            ObligationCauseCode::IfExpression(box IfExpressionCause {
+                then_id,
+                else_id,
+                then_ty,
+                else_ty,
+                outer_span,
+                ..
+            }) => {
+                let then_span = self.find_block_span_from_hir_id(then_id);
+                let else_span = self.find_block_span_from_hir_id(else_id);
+                err.span_label(then_span, "expected because of this");
+                if let Some(sp) = outer_span {
+                    err.span_label(sp, "`if` and `else` have incompatible types");
+                }
+                if let Some(subdiag) = self.suggest_remove_semi_or_return_binding(
+                    Some(then_id),
+                    then_ty,
+                    then_span,
+                    Some(else_id),
+                    else_ty,
+                    else_span,
+                ) {
+                    err.subdiagnostic(subdiag);
+                }
+            }
+            ObligationCauseCode::LetElse => {
+                err.help("try adding a diverging expression, such as `return` or `panic!(..)`");
+                err.help("...or use `match` instead of `let...else`");
+            }
+            _ => {
+                if let ObligationCauseCode::WhereClause(_, span)
+                | ObligationCauseCode::WhereClauseInExpr(_, span, ..) =
+                    cause.code().peel_derives()
+                    && !span.is_dummy()
+                    && let TypeError::RegionsPlaceholderMismatch = terr
+                {
+                    err.span_note(*span, "the lifetime requirement is introduced here");
+                }
+            }
+        }
+    }
+
+    /// Given that `other_ty` is the same as a type argument for `name` in `sub`, populate `value`
+    /// highlighting `name` and every type argument that isn't at `pos` (which is `other_ty`), and
+    /// populate `other_value` with `other_ty`.
+    ///
+    /// ```text
+    /// Foo<Bar<Qux>>
+    /// ^^^^--------^ this is highlighted
+    /// |   |
+    /// |   this type argument is exactly the same as the other type, not highlighted
+    /// this is highlighted
+    /// Bar<Qux>
+    /// -------- this type is the same as a type argument in the other type, not highlighted
+    /// ```
+    fn highlight_outer(
+        &self,
+        value: &mut DiagStyledString,
+        other_value: &mut DiagStyledString,
+        name: String,
+        sub: ty::GenericArgsRef<'tcx>,
+        pos: usize,
+        other_ty: Ty<'tcx>,
+    ) {
+        // `value` and `other_value` hold two incomplete type representation for display.
+        // `name` is the path of both types being compared. `sub`
+        value.push_highlighted(name);
+        let len = sub.len();
+        if len > 0 {
+            value.push_highlighted("<");
+        }
+
+        // Output the lifetimes for the first type
+        let lifetimes = sub
+            .regions()
+            .map(|lifetime| {
+                let s = lifetime.to_string();
+                if s.is_empty() { "'_".to_string() } else { s }
+            })
+            .collect::<Vec<_>>()
+            .join(", ");
+        if !lifetimes.is_empty() {
+            if sub.regions().count() < len {
+                value.push_normal(lifetimes + ", ");
+            } else {
+                value.push_normal(lifetimes);
+            }
+        }
+
+        // Highlight all the type arguments that aren't at `pos` and compare the type argument at
+        // `pos` and `other_ty`.
+        for (i, type_arg) in sub.types().enumerate() {
+            if i == pos {
+                let values = self.cmp(type_arg, other_ty);
+                value.0.extend((values.0).0);
+                other_value.0.extend((values.1).0);
+            } else {
+                value.push_highlighted(type_arg.to_string());
+            }
+
+            if len > 0 && i != len - 1 {
+                value.push_normal(", ");
+            }
+        }
+        if len > 0 {
+            value.push_highlighted(">");
+        }
+    }
+
+    /// If `other_ty` is the same as a type argument present in `sub`, highlight `path` in `t1_out`,
+    /// as that is the difference to the other type.
+    ///
+    /// For the following code:
+    ///
+    /// ```ignore (illustrative)
+    /// let x: Foo<Bar<Qux>> = foo::<Bar<Qux>>();
+    /// ```
+    ///
+    /// The type error output will behave in the following way:
+    ///
+    /// ```text
+    /// Foo<Bar<Qux>>
+    /// ^^^^--------^ this is highlighted
+    /// |   |
+    /// |   this type argument is exactly the same as the other type, not highlighted
+    /// this is highlighted
+    /// Bar<Qux>
+    /// -------- this type is the same as a type argument in the other type, not highlighted
+    /// ```
+    fn cmp_type_arg(
+        &self,
+        t1_out: &mut DiagStyledString,
+        t2_out: &mut DiagStyledString,
+        path: String,
+        sub: &'tcx [ty::GenericArg<'tcx>],
+        other_path: String,
+        other_ty: Ty<'tcx>,
+    ) -> Option<()> {
+        // FIXME/HACK: Go back to `GenericArgsRef` to use its inherent methods,
+        // ideally that shouldn't be necessary.
+        let sub = self.tcx.mk_args(sub);
+        for (i, ta) in sub.types().enumerate() {
+            if ta == other_ty {
+                self.highlight_outer(t1_out, t2_out, path, sub, i, other_ty);
+                return Some(());
+            }
+            if let ty::Adt(def, _) = ta.kind() {
+                let path_ = self.tcx.def_path_str(def.did());
+                if path_ == other_path {
+                    self.highlight_outer(t1_out, t2_out, path, sub, i, other_ty);
+                    return Some(());
+                }
+            }
+        }
+        None
+    }
+
+    /// Adds a `,` to the type representation only if it is appropriate.
+    fn push_comma(
+        &self,
+        value: &mut DiagStyledString,
+        other_value: &mut DiagStyledString,
+        len: usize,
+        pos: usize,
+    ) {
+        if len > 0 && pos != len - 1 {
+            value.push_normal(", ");
+            other_value.push_normal(", ");
+        }
+    }
+
+    /// Given two `fn` signatures highlight only sub-parts that are different.
+    fn cmp_fn_sig(
+        &self,
+        sig1: &ty::PolyFnSig<'tcx>,
+        sig2: &ty::PolyFnSig<'tcx>,
+    ) -> (DiagStyledString, DiagStyledString) {
+        let sig1 = &(self.normalize_fn_sig)(*sig1);
+        let sig2 = &(self.normalize_fn_sig)(*sig2);
+
+        let get_lifetimes = |sig| {
+            use rustc_hir::def::Namespace;
+            let (sig, reg) = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS)
+                .name_all_regions(sig)
+                .unwrap();
+            let lts: Vec<String> =
+                reg.into_items().map(|(_, kind)| kind.to_string()).into_sorted_stable_ord();
+            (if lts.is_empty() { String::new() } else { format!("for<{}> ", lts.join(", ")) }, sig)
+        };
+
+        let (lt1, sig1) = get_lifetimes(sig1);
+        let (lt2, sig2) = get_lifetimes(sig2);
+
+        // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+        let mut values =
+            (DiagStyledString::normal("".to_string()), DiagStyledString::normal("".to_string()));
+
+        // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+        // ^^^^^^
+        values.0.push(sig1.safety.prefix_str(), sig1.safety != sig2.safety);
+        values.1.push(sig2.safety.prefix_str(), sig1.safety != sig2.safety);
+
+        // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+        //        ^^^^^^^^^^
+        if sig1.abi != abi::Abi::Rust {
+            values.0.push(format!("extern {} ", sig1.abi), sig1.abi != sig2.abi);
+        }
+        if sig2.abi != abi::Abi::Rust {
+            values.1.push(format!("extern {} ", sig2.abi), sig1.abi != sig2.abi);
+        }
+
+        // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+        //                   ^^^^^^^^
+        let lifetime_diff = lt1 != lt2;
+        values.0.push(lt1, lifetime_diff);
+        values.1.push(lt2, lifetime_diff);
+
+        // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+        //                           ^^^
+        values.0.push_normal("fn(");
+        values.1.push_normal("fn(");
+
+        // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+        //                              ^^^^^
+        let len1 = sig1.inputs().len();
+        let len2 = sig2.inputs().len();
+        if len1 == len2 {
+            for (i, (l, r)) in iter::zip(sig1.inputs(), sig2.inputs()).enumerate() {
+                let (x1, x2) = self.cmp(*l, *r);
+                (values.0).0.extend(x1.0);
+                (values.1).0.extend(x2.0);
+                self.push_comma(&mut values.0, &mut values.1, len1, i);
+            }
+        } else {
+            for (i, l) in sig1.inputs().iter().enumerate() {
+                values.0.push_highlighted(l.to_string());
+                if i != len1 - 1 {
+                    values.0.push_highlighted(", ");
+                }
+            }
+            for (i, r) in sig2.inputs().iter().enumerate() {
+                values.1.push_highlighted(r.to_string());
+                if i != len2 - 1 {
+                    values.1.push_highlighted(", ");
+                }
+            }
+        }
+
+        if sig1.c_variadic {
+            if len1 > 0 {
+                values.0.push_normal(", ");
+            }
+            values.0.push("...", !sig2.c_variadic);
+        }
+        if sig2.c_variadic {
+            if len2 > 0 {
+                values.1.push_normal(", ");
+            }
+            values.1.push("...", !sig1.c_variadic);
+        }
+
+        // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+        //                                   ^
+        values.0.push_normal(")");
+        values.1.push_normal(")");
+
+        // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+        //                                     ^^^^^^^^
+        let output1 = sig1.output();
+        let output2 = sig2.output();
+        let (x1, x2) = self.cmp(output1, output2);
+        let output_diff = x1 != x2;
+        if !output1.is_unit() || output_diff {
+            values.0.push_normal(" -> ");
+            (values.0).0.extend(x1.0);
+        }
+        if !output2.is_unit() || output_diff {
+            values.1.push_normal(" -> ");
+            (values.1).0.extend(x2.0);
+        }
+
+        values
+    }
+
+    /// Compares two given types, eliding parts that are the same between them and highlighting
+    /// relevant differences, and return two representation of those types for highlighted printing.
+    pub fn cmp(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> (DiagStyledString, DiagStyledString) {
+        debug!("cmp(t1={}, t1.kind={:?}, t2={}, t2.kind={:?})", t1, t1.kind(), t2, t2.kind());
+
+        // helper functions
+        let recurse = |t1, t2, values: &mut (DiagStyledString, DiagStyledString)| {
+            let (x1, x2) = self.cmp(t1, t2);
+            (values.0).0.extend(x1.0);
+            (values.1).0.extend(x2.0);
+        };
+
+        fn fmt_region<'tcx>(region: ty::Region<'tcx>) -> String {
+            let mut r = region.to_string();
+            if r == "'_" {
+                r.clear();
+            } else {
+                r.push(' ');
+            }
+            format!("&{r}")
+        }
+
+        fn push_ref<'tcx>(
+            region: ty::Region<'tcx>,
+            mutbl: hir::Mutability,
+            s: &mut DiagStyledString,
+        ) {
+            s.push_highlighted(fmt_region(region));
+            s.push_highlighted(mutbl.prefix_str());
+        }
+
+        fn maybe_highlight<T: Eq + ToString>(
+            t1: T,
+            t2: T,
+            (buf1, buf2): &mut (DiagStyledString, DiagStyledString),
+            tcx: TyCtxt<'_>,
+        ) {
+            let highlight = t1 != t2;
+            let (t1, t2) = if highlight || tcx.sess.opts.verbose {
+                (t1.to_string(), t2.to_string())
+            } else {
+                // The two types are the same, elide and don't highlight.
+                ("_".into(), "_".into())
+            };
+            buf1.push(t1, highlight);
+            buf2.push(t2, highlight);
+        }
+
+        fn cmp_ty_refs<'tcx>(
+            r1: ty::Region<'tcx>,
+            mut1: hir::Mutability,
+            r2: ty::Region<'tcx>,
+            mut2: hir::Mutability,
+            ss: &mut (DiagStyledString, DiagStyledString),
+        ) {
+            let (r1, r2) = (fmt_region(r1), fmt_region(r2));
+            if r1 != r2 {
+                ss.0.push_highlighted(r1);
+                ss.1.push_highlighted(r2);
+            } else {
+                ss.0.push_normal(r1);
+                ss.1.push_normal(r2);
+            }
+
+            if mut1 != mut2 {
+                ss.0.push_highlighted(mut1.prefix_str());
+                ss.1.push_highlighted(mut2.prefix_str());
+            } else {
+                ss.0.push_normal(mut1.prefix_str());
+                ss.1.push_normal(mut2.prefix_str());
+            }
+        }
+
+        // process starts here
+        match (t1.kind(), t2.kind()) {
+            (&ty::Adt(def1, sub1), &ty::Adt(def2, sub2)) => {
+                let did1 = def1.did();
+                let did2 = def2.did();
+
+                let generics1 = self.tcx.generics_of(did1);
+                let generics2 = self.tcx.generics_of(did2);
+
+                let non_default_after_default = generics1
+                    .check_concrete_type_after_default(self.tcx, sub1)
+                    || generics2.check_concrete_type_after_default(self.tcx, sub2);
+                let sub_no_defaults_1 = if non_default_after_default {
+                    generics1.own_args(sub1)
+                } else {
+                    generics1.own_args_no_defaults(self.tcx, sub1)
+                };
+                let sub_no_defaults_2 = if non_default_after_default {
+                    generics2.own_args(sub2)
+                } else {
+                    generics2.own_args_no_defaults(self.tcx, sub2)
+                };
+                let mut values = (DiagStyledString::new(), DiagStyledString::new());
+                let path1 = self.tcx.def_path_str(did1);
+                let path2 = self.tcx.def_path_str(did2);
+                if did1 == did2 {
+                    // Easy case. Replace same types with `_` to shorten the output and highlight
+                    // the differing ones.
+                    //     let x: Foo<Bar, Qux> = y::<Foo<Quz, Qux>>();
+                    //     Foo<Bar, _>
+                    //     Foo<Quz, _>
+                    //         ---  ^ type argument elided
+                    //         |
+                    //         highlighted in output
+                    values.0.push_normal(path1);
+                    values.1.push_normal(path2);
+
+                    // Avoid printing out default generic parameters that are common to both
+                    // types.
+                    let len1 = sub_no_defaults_1.len();
+                    let len2 = sub_no_defaults_2.len();
+                    let common_len = cmp::min(len1, len2);
+                    let remainder1: Vec<_> = sub1.types().skip(common_len).collect();
+                    let remainder2: Vec<_> = sub2.types().skip(common_len).collect();
+                    let common_default_params =
+                        iter::zip(remainder1.iter().rev(), remainder2.iter().rev())
+                            .filter(|(a, b)| a == b)
+                            .count();
+                    let len = sub1.len() - common_default_params;
+                    let consts_offset = len - sub1.consts().count();
+
+                    // Only draw `<...>` if there are lifetime/type arguments.
+                    if len > 0 {
+                        values.0.push_normal("<");
+                        values.1.push_normal("<");
+                    }
+
+                    fn lifetime_display(lifetime: Region<'_>) -> String {
+                        let s = lifetime.to_string();
+                        if s.is_empty() { "'_".to_string() } else { s }
+                    }
+                    // At one point we'd like to elide all lifetimes here, they are irrelevant for
+                    // all diagnostics that use this output
+                    //
+                    //     Foo<'x, '_, Bar>
+                    //     Foo<'y, '_, Qux>
+                    //         ^^  ^^  --- type arguments are not elided
+                    //         |   |
+                    //         |   elided as they were the same
+                    //         not elided, they were different, but irrelevant
+                    //
+                    // For bound lifetimes, keep the names of the lifetimes,
+                    // even if they are the same so that it's clear what's happening
+                    // if we have something like
+                    //
+                    // for<'r, 's> fn(Inv<'r>, Inv<'s>)
+                    // for<'r> fn(Inv<'r>, Inv<'r>)
+                    let lifetimes = sub1.regions().zip(sub2.regions());
+                    for (i, lifetimes) in lifetimes.enumerate() {
+                        let l1 = lifetime_display(lifetimes.0);
+                        let l2 = lifetime_display(lifetimes.1);
+                        if lifetimes.0 != lifetimes.1 {
+                            values.0.push_highlighted(l1);
+                            values.1.push_highlighted(l2);
+                        } else if lifetimes.0.is_bound() || self.tcx.sess.opts.verbose {
+                            values.0.push_normal(l1);
+                            values.1.push_normal(l2);
+                        } else {
+                            values.0.push_normal("'_");
+                            values.1.push_normal("'_");
+                        }
+                        self.push_comma(&mut values.0, &mut values.1, len, i);
+                    }
+
+                    // We're comparing two types with the same path, so we compare the type
+                    // arguments for both. If they are the same, do not highlight and elide from the
+                    // output.
+                    //     Foo<_, Bar>
+                    //     Foo<_, Qux>
+                    //         ^ elided type as this type argument was the same in both sides
+                    let type_arguments = sub1.types().zip(sub2.types());
+                    let regions_len = sub1.regions().count();
+                    let num_display_types = consts_offset - regions_len;
+                    for (i, (ta1, ta2)) in type_arguments.take(num_display_types).enumerate() {
+                        let i = i + regions_len;
+                        if ta1 == ta2 && !self.tcx.sess.opts.verbose {
+                            values.0.push_normal("_");
+                            values.1.push_normal("_");
+                        } else {
+                            recurse(ta1, ta2, &mut values);
+                        }
+                        self.push_comma(&mut values.0, &mut values.1, len, i);
+                    }
+
+                    // Do the same for const arguments, if they are equal, do not highlight and
+                    // elide them from the output.
+                    let const_arguments = sub1.consts().zip(sub2.consts());
+                    for (i, (ca1, ca2)) in const_arguments.enumerate() {
+                        let i = i + consts_offset;
+                        maybe_highlight(ca1, ca2, &mut values, self.tcx);
+                        self.push_comma(&mut values.0, &mut values.1, len, i);
+                    }
+
+                    // Close the type argument bracket.
+                    // Only draw `<...>` if there are lifetime/type arguments.
+                    if len > 0 {
+                        values.0.push_normal(">");
+                        values.1.push_normal(">");
+                    }
+                    values
+                } else {
+                    // Check for case:
+                    //     let x: Foo<Bar<Qux> = foo::<Bar<Qux>>();
+                    //     Foo<Bar<Qux>
+                    //         ------- this type argument is exactly the same as the other type
+                    //     Bar<Qux>
+                    if self
+                        .cmp_type_arg(
+                            &mut values.0,
+                            &mut values.1,
+                            path1.clone(),
+                            sub_no_defaults_1,
+                            path2.clone(),
+                            t2,
+                        )
+                        .is_some()
+                    {
+                        return values;
+                    }
+                    // Check for case:
+                    //     let x: Bar<Qux> = y:<Foo<Bar<Qux>>>();
+                    //     Bar<Qux>
+                    //     Foo<Bar<Qux>>
+                    //         ------- this type argument is exactly the same as the other type
+                    if self
+                        .cmp_type_arg(
+                            &mut values.1,
+                            &mut values.0,
+                            path2,
+                            sub_no_defaults_2,
+                            path1,
+                            t1,
+                        )
+                        .is_some()
+                    {
+                        return values;
+                    }
+
+                    // We can't find anything in common, highlight relevant part of type path.
+                    //     let x: foo::bar::Baz<Qux> = y:<foo::bar::Bar<Zar>>();
+                    //     foo::bar::Baz<Qux>
+                    //     foo::bar::Bar<Zar>
+                    //               -------- this part of the path is different
+
+                    let t1_str = t1.to_string();
+                    let t2_str = t2.to_string();
+                    let min_len = t1_str.len().min(t2_str.len());
+
+                    const SEPARATOR: &str = "::";
+                    let separator_len = SEPARATOR.len();
+                    let split_idx: usize =
+                        iter::zip(t1_str.split(SEPARATOR), t2_str.split(SEPARATOR))
+                            .take_while(|(mod1_str, mod2_str)| mod1_str == mod2_str)
+                            .map(|(mod_str, _)| mod_str.len() + separator_len)
+                            .sum();
+
+                    debug!(?separator_len, ?split_idx, ?min_len, "cmp");
+
+                    if split_idx >= min_len {
+                        // paths are identical, highlight everything
+                        (
+                            DiagStyledString::highlighted(t1_str),
+                            DiagStyledString::highlighted(t2_str),
+                        )
+                    } else {
+                        let (common, uniq1) = t1_str.split_at(split_idx);
+                        let (_, uniq2) = t2_str.split_at(split_idx);
+                        debug!(?common, ?uniq1, ?uniq2, "cmp");
+
+                        values.0.push_normal(common);
+                        values.0.push_highlighted(uniq1);
+                        values.1.push_normal(common);
+                        values.1.push_highlighted(uniq2);
+
+                        values
+                    }
+                }
+            }
+
+            // When finding `&T != &T`, compare the references, then recurse into pointee type
+            (&ty::Ref(r1, ref_ty1, mutbl1), &ty::Ref(r2, ref_ty2, mutbl2)) => {
+                let mut values = (DiagStyledString::new(), DiagStyledString::new());
+                cmp_ty_refs(r1, mutbl1, r2, mutbl2, &mut values);
+                recurse(ref_ty1, ref_ty2, &mut values);
+                values
+            }
+            // When finding T != &T, highlight the borrow
+            (&ty::Ref(r1, ref_ty1, mutbl1), _) => {
+                let mut values = (DiagStyledString::new(), DiagStyledString::new());
+                push_ref(r1, mutbl1, &mut values.0);
+                recurse(ref_ty1, t2, &mut values);
+                values
+            }
+            (_, &ty::Ref(r2, ref_ty2, mutbl2)) => {
+                let mut values = (DiagStyledString::new(), DiagStyledString::new());
+                push_ref(r2, mutbl2, &mut values.1);
+                recurse(t1, ref_ty2, &mut values);
+                values
+            }
+
+            // When encountering tuples of the same size, highlight only the differing types
+            (&ty::Tuple(args1), &ty::Tuple(args2)) if args1.len() == args2.len() => {
+                let mut values = (DiagStyledString::normal("("), DiagStyledString::normal("("));
+                let len = args1.len();
+                for (i, (left, right)) in args1.iter().zip(args2).enumerate() {
+                    recurse(left, right, &mut values);
+                    self.push_comma(&mut values.0, &mut values.1, len, i);
+                }
+                if len == 1 {
+                    // Keep the output for single element tuples as `(ty,)`.
+                    values.0.push_normal(",");
+                    values.1.push_normal(",");
+                }
+                values.0.push_normal(")");
+                values.1.push_normal(")");
+                values
+            }
+
+            (ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => {
+                let sig1 = self.tcx.fn_sig(*did1).instantiate(self.tcx, args1);
+                let sig2 = self.tcx.fn_sig(*did2).instantiate(self.tcx, args2);
+                let mut values = self.cmp_fn_sig(&sig1, &sig2);
+                let path1 = format!(" {{{}}}", self.tcx.def_path_str_with_args(*did1, args1));
+                let path2 = format!(" {{{}}}", self.tcx.def_path_str_with_args(*did2, args2));
+                let same_path = path1 == path2;
+                values.0.push(path1, !same_path);
+                values.1.push(path2, !same_path);
+                values
+            }
+
+            (ty::FnDef(did1, args1), ty::FnPtr(sig_tys2, hdr2)) => {
+                let sig1 = self.tcx.fn_sig(*did1).instantiate(self.tcx, args1);
+                let mut values = self.cmp_fn_sig(&sig1, &sig_tys2.with(*hdr2));
+                values.0.push_highlighted(format!(
+                    " {{{}}}",
+                    self.tcx.def_path_str_with_args(*did1, args1)
+                ));
+                values
+            }
+
+            (ty::FnPtr(sig_tys1, hdr1), ty::FnDef(did2, args2)) => {
+                let sig2 = self.tcx.fn_sig(*did2).instantiate(self.tcx, args2);
+                let mut values = self.cmp_fn_sig(&sig_tys1.with(*hdr1), &sig2);
+                values
+                    .1
+                    .push_normal(format!(" {{{}}}", self.tcx.def_path_str_with_args(*did2, args2)));
+                values
+            }
+
+            (ty::FnPtr(sig_tys1, hdr1), ty::FnPtr(sig_tys2, hdr2)) => {
+                self.cmp_fn_sig(&sig_tys1.with(*hdr1), &sig_tys2.with(*hdr2))
+            }
+
+            _ => {
+                let mut strs = (DiagStyledString::new(), DiagStyledString::new());
+                maybe_highlight(t1, t2, &mut strs, self.tcx);
+                strs
+            }
+        }
+    }
+
+    /// Extend a type error with extra labels pointing at "non-trivial" types, like closures and
+    /// the return type of `async fn`s.
+    ///
+    /// `secondary_span` gives the caller the opportunity to expand `diag` with a `span_label`.
+    ///
+    /// `swap_secondary_and_primary` is used to make projection errors in particular nicer by using
+    /// the message in `secondary_span` as the primary label, and apply the message that would
+    /// otherwise be used for the primary label on the `secondary_span` `Span`. This applies on
+    /// E0271, like `tests/ui/issues/issue-39970.stderr`.
+    #[instrument(
+        level = "debug",
+        skip(self, diag, secondary_span, swap_secondary_and_primary, prefer_label)
+    )]
+    pub fn note_type_err(
+        &self,
+        diag: &mut Diag<'_>,
+        cause: &ObligationCause<'tcx>,
+        secondary_span: Option<(Span, Cow<'static, str>)>,
+        mut values: Option<ValuePairs<'tcx>>,
+        terr: TypeError<'tcx>,
+        swap_secondary_and_primary: bool,
+        prefer_label: bool,
+    ) {
+        let span = cause.span();
+
+        // For some types of errors, expected-found does not make
+        // sense, so just ignore the values we were given.
+        if let TypeError::CyclicTy(_) = terr {
+            values = None;
+        }
+        struct OpaqueTypesVisitor<'tcx> {
+            types: FxIndexMap<TyCategory, FxIndexSet<Span>>,
+            expected: FxIndexMap<TyCategory, FxIndexSet<Span>>,
+            found: FxIndexMap<TyCategory, FxIndexSet<Span>>,
+            ignore_span: Span,
+            tcx: TyCtxt<'tcx>,
+        }
+
+        impl<'tcx> OpaqueTypesVisitor<'tcx> {
+            fn visit_expected_found(
+                tcx: TyCtxt<'tcx>,
+                expected: impl TypeVisitable<TyCtxt<'tcx>>,
+                found: impl TypeVisitable<TyCtxt<'tcx>>,
+                ignore_span: Span,
+            ) -> Self {
+                let mut types_visitor = OpaqueTypesVisitor {
+                    types: Default::default(),
+                    expected: Default::default(),
+                    found: Default::default(),
+                    ignore_span,
+                    tcx,
+                };
+                // The visitor puts all the relevant encountered types in `self.types`, but in
+                // here we want to visit two separate types with no relation to each other, so we
+                // move the results from `types` to `expected` or `found` as appropriate.
+                expected.visit_with(&mut types_visitor);
+                std::mem::swap(&mut types_visitor.expected, &mut types_visitor.types);
+                found.visit_with(&mut types_visitor);
+                std::mem::swap(&mut types_visitor.found, &mut types_visitor.types);
+                types_visitor
+            }
+
+            fn report(&self, err: &mut Diag<'_>) {
+                self.add_labels_for_types(err, "expected", &self.expected);
+                self.add_labels_for_types(err, "found", &self.found);
+            }
+
+            fn add_labels_for_types(
+                &self,
+                err: &mut Diag<'_>,
+                target: &str,
+                types: &FxIndexMap<TyCategory, FxIndexSet<Span>>,
+            ) {
+                for (kind, values) in types.iter() {
+                    let count = values.len();
+                    for &sp in values {
+                        err.span_label(
+                            sp,
+                            format!(
+                                "{}{} {:#}{}",
+                                if count == 1 { "the " } else { "one of the " },
+                                target,
+                                kind,
+                                pluralize!(count),
+                            ),
+                        );
+                    }
+                }
+            }
+        }
+
+        impl<'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for OpaqueTypesVisitor<'tcx> {
+            fn visit_ty(&mut self, t: Ty<'tcx>) {
+                if let Some((kind, def_id)) = TyCategory::from_ty(self.tcx, t) {
+                    let span = self.tcx.def_span(def_id);
+                    // Avoid cluttering the output when the "found" and error span overlap:
+                    //
+                    // error[E0308]: mismatched types
+                    //   --> $DIR/issue-20862.rs:2:5
+                    //    |
+                    // LL |     |y| x + y
+                    //    |     ^^^^^^^^^
+                    //    |     |
+                    //    |     the found closure
+                    //    |     expected `()`, found closure
+                    //    |
+                    //    = note: expected unit type `()`
+                    //                 found closure `{closure@$DIR/issue-20862.rs:2:5: 2:14 x:_}`
+                    //
+                    // Also ignore opaque `Future`s that come from async fns.
+                    if !self.ignore_span.overlaps(span)
+                        && !span.is_desugaring(DesugaringKind::Async)
+                    {
+                        self.types.entry(kind).or_default().insert(span);
+                    }
+                }
+                t.super_visit_with(self)
+            }
+        }
+
+        debug!("note_type_err(diag={:?})", diag);
+        enum Mismatch<'a> {
+            Variable(ty::error::ExpectedFound<Ty<'a>>),
+            Fixed(&'static str),
+        }
+        let (expected_found, exp_found, is_simple_error, values) = match values {
+            None => (None, Mismatch::Fixed("type"), false, None),
+            Some(values) => {
+                let values = self.resolve_vars_if_possible(values);
+                let (is_simple_error, exp_found) = match values {
+                    ValuePairs::Terms(ExpectedFound { expected, found }) => {
+                        match (expected.unpack(), found.unpack()) {
+                            (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => {
+                                let is_simple_err = expected.is_simple_text(self.tcx)
+                                    && found.is_simple_text(self.tcx);
+                                OpaqueTypesVisitor::visit_expected_found(
+                                    self.tcx, expected, found, span,
+                                )
+                                .report(diag);
+
+                                (
+                                    is_simple_err,
+                                    Mismatch::Variable(ExpectedFound { expected, found }),
+                                )
+                            }
+                            (ty::TermKind::Const(_), ty::TermKind::Const(_)) => {
+                                (false, Mismatch::Fixed("constant"))
+                            }
+                            _ => (false, Mismatch::Fixed("type")),
+                        }
+                    }
+                    ValuePairs::PolySigs(ExpectedFound { expected, found }) => {
+                        OpaqueTypesVisitor::visit_expected_found(self.tcx, expected, found, span)
+                            .report(diag);
+                        (false, Mismatch::Fixed("signature"))
+                    }
+                    ValuePairs::TraitRefs(_) => (false, Mismatch::Fixed("trait")),
+                    ValuePairs::Aliases(ExpectedFound { expected, .. }) => {
+                        (false, Mismatch::Fixed(self.tcx.def_descr(expected.def_id)))
+                    }
+                    ValuePairs::Regions(_) => (false, Mismatch::Fixed("lifetime")),
+                    ValuePairs::ExistentialTraitRef(_) => {
+                        (false, Mismatch::Fixed("existential trait ref"))
+                    }
+                    ValuePairs::ExistentialProjection(_) => {
+                        (false, Mismatch::Fixed("existential projection"))
+                    }
+                    ValuePairs::Dummy => {
+                        bug!("do not expect to report a type error from a ValuePairs::Dummy")
+                    }
+                };
+                let Some(vals) = self.values_str(values) else {
+                    // Derived error. Cancel the emitter.
+                    // NOTE(eddyb) this was `.cancel()`, but `diag`
+                    // is borrowed, so we can't fully defuse it.
+                    diag.downgrade_to_delayed_bug();
+                    return;
+                };
+                (Some(vals), exp_found, is_simple_error, Some(values))
+            }
+        };
+
+        let mut label_or_note = |span: Span, msg: Cow<'static, str>| {
+            if (prefer_label && is_simple_error) || &[span] == diag.span.primary_spans() {
+                diag.span_label(span, msg);
+            } else {
+                diag.span_note(span, msg);
+            }
+        };
+        if let Some((sp, msg)) = secondary_span {
+            if swap_secondary_and_primary {
+                let terr = if let Some(infer::ValuePairs::Terms(ExpectedFound {
+                    expected, ..
+                })) = values
+                {
+                    Cow::from(format!("expected this to be `{expected}`"))
+                } else {
+                    terr.to_string(self.tcx)
+                };
+                label_or_note(sp, terr);
+                label_or_note(span, msg);
+            } else {
+                label_or_note(span, terr.to_string(self.tcx));
+                label_or_note(sp, msg);
+            }
+        } else {
+            if let Some(values) = values
+                && let Some((e, f)) = values.ty()
+                && let TypeError::ArgumentSorts(..) | TypeError::Sorts(_) = terr
+            {
+                let e = self.tcx.erase_regions(e);
+                let f = self.tcx.erase_regions(f);
+                let expected = with_forced_trimmed_paths!(e.sort_string(self.tcx));
+                let found = with_forced_trimmed_paths!(f.sort_string(self.tcx));
+                if expected == found {
+                    label_or_note(span, terr.to_string(self.tcx));
+                } else {
+                    label_or_note(span, Cow::from(format!("expected {expected}, found {found}")));
+                }
+            } else {
+                label_or_note(span, terr.to_string(self.tcx));
+            }
+        }
+
+        if let Some((expected, found, path)) = expected_found {
+            let (expected_label, found_label, exp_found) = match exp_found {
+                Mismatch::Variable(ef) => (
+                    ef.expected.prefix_string(self.tcx),
+                    ef.found.prefix_string(self.tcx),
+                    Some(ef),
+                ),
+                Mismatch::Fixed(s) => (s.into(), s.into(), None),
+            };
+
+            enum Similar<'tcx> {
+                Adts { expected: ty::AdtDef<'tcx>, found: ty::AdtDef<'tcx> },
+                PrimitiveFound { expected: ty::AdtDef<'tcx>, found: Ty<'tcx> },
+                PrimitiveExpected { expected: Ty<'tcx>, found: ty::AdtDef<'tcx> },
+            }
+
+            let similarity = |ExpectedFound { expected, found }: ExpectedFound<Ty<'tcx>>| {
+                if let ty::Adt(expected, _) = expected.kind()
+                    && let Some(primitive) = found.primitive_symbol()
+                {
+                    let path = self.tcx.def_path(expected.did()).data;
+                    let name = path.last().unwrap().data.get_opt_name();
+                    if name == Some(primitive) {
+                        return Some(Similar::PrimitiveFound { expected: *expected, found });
+                    }
+                } else if let Some(primitive) = expected.primitive_symbol()
+                    && let ty::Adt(found, _) = found.kind()
+                {
+                    let path = self.tcx.def_path(found.did()).data;
+                    let name = path.last().unwrap().data.get_opt_name();
+                    if name == Some(primitive) {
+                        return Some(Similar::PrimitiveExpected { expected, found: *found });
+                    }
+                } else if let ty::Adt(expected, _) = expected.kind()
+                    && let ty::Adt(found, _) = found.kind()
+                {
+                    if !expected.did().is_local() && expected.did().krate == found.did().krate {
+                        // Most likely types from different versions of the same crate
+                        // are in play, in which case this message isn't so helpful.
+                        // A "perhaps two different versions..." error is already emitted for that.
+                        return None;
+                    }
+                    let f_path = self.tcx.def_path(found.did()).data;
+                    let e_path = self.tcx.def_path(expected.did()).data;
+
+                    if let (Some(e_last), Some(f_last)) = (e_path.last(), f_path.last())
+                        && e_last == f_last
+                    {
+                        return Some(Similar::Adts { expected: *expected, found: *found });
+                    }
+                }
+                None
+            };
+
+            match terr {
+                // If two types mismatch but have similar names, mention that specifically.
+                TypeError::Sorts(values) if let Some(s) = similarity(values) => {
+                    let diagnose_primitive =
+                        |prim: Ty<'tcx>, shadow: Ty<'tcx>, defid: DefId, diag: &mut Diag<'_>| {
+                            let name = shadow.sort_string(self.tcx);
+                            diag.note(format!(
+                                "{prim} and {name} have similar names, but are actually distinct types"
+                            ));
+                            diag.note(format!("{prim} is a primitive defined by the language"));
+                            let def_span = self.tcx.def_span(defid);
+                            let msg = if defid.is_local() {
+                                format!("{name} is defined in the current crate")
+                            } else {
+                                let crate_name = self.tcx.crate_name(defid.krate);
+                                format!("{name} is defined in crate `{crate_name}`")
+                            };
+                            diag.span_note(def_span, msg);
+                        };
+
+                    let diagnose_adts =
+                        |expected_adt: ty::AdtDef<'tcx>,
+                         found_adt: ty::AdtDef<'tcx>,
+                         diag: &mut Diag<'_>| {
+                            let found_name = values.found.sort_string(self.tcx);
+                            let expected_name = values.expected.sort_string(self.tcx);
+
+                            let found_defid = found_adt.did();
+                            let expected_defid = expected_adt.did();
+
+                            diag.note(format!("{found_name} and {expected_name} have similar names, but are actually distinct types"));
+                            for (defid, name) in
+                                [(found_defid, found_name), (expected_defid, expected_name)]
+                            {
+                                let def_span = self.tcx.def_span(defid);
+
+                                let msg = if found_defid.is_local() && expected_defid.is_local() {
+                                    let module = self
+                                        .tcx
+                                        .parent_module_from_def_id(defid.expect_local())
+                                        .to_def_id();
+                                    let module_name =
+                                        self.tcx.def_path(module).to_string_no_crate_verbose();
+                                    format!(
+                                        "{name} is defined in module `crate{module_name}` of the current crate"
+                                    )
+                                } else if defid.is_local() {
+                                    format!("{name} is defined in the current crate")
+                                } else {
+                                    let crate_name = self.tcx.crate_name(defid.krate);
+                                    format!("{name} is defined in crate `{crate_name}`")
+                                };
+                                diag.span_note(def_span, msg);
+                            }
+                        };
+
+                    match s {
+                        Similar::Adts { expected, found } => diagnose_adts(expected, found, diag),
+                        Similar::PrimitiveFound { expected, found: prim } => {
+                            diagnose_primitive(prim, values.expected, expected.did(), diag)
+                        }
+                        Similar::PrimitiveExpected { expected: prim, found } => {
+                            diagnose_primitive(prim, values.found, found.did(), diag)
+                        }
+                    }
+                }
+                TypeError::Sorts(values) => {
+                    let extra = expected == found;
+                    let sort_string = |ty: Ty<'tcx>| match (extra, ty.kind()) {
+                        (true, ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. })) => {
+                            let sm = self.tcx.sess.source_map();
+                            let pos = sm.lookup_char_pos(self.tcx.def_span(*def_id).lo());
+                            format!(
+                                " (opaque type at <{}:{}:{}>)",
+                                sm.filename_for_diagnostics(&pos.file.name),
+                                pos.line,
+                                pos.col.to_usize() + 1,
+                            )
+                        }
+                        (true, ty::Alias(ty::Projection, proj))
+                            if self.tcx.is_impl_trait_in_trait(proj.def_id) =>
+                        {
+                            let sm = self.tcx.sess.source_map();
+                            let pos = sm.lookup_char_pos(self.tcx.def_span(proj.def_id).lo());
+                            format!(
+                                " (trait associated opaque type at <{}:{}:{}>)",
+                                sm.filename_for_diagnostics(&pos.file.name),
+                                pos.line,
+                                pos.col.to_usize() + 1,
+                            )
+                        }
+                        (true, _) => format!(" ({})", ty.sort_string(self.tcx)),
+                        (false, _) => "".to_string(),
+                    };
+                    if !(values.expected.is_simple_text(self.tcx)
+                        && values.found.is_simple_text(self.tcx))
+                        || (exp_found.is_some_and(|ef| {
+                            // This happens when the type error is a subset of the expectation,
+                            // like when you have two references but one is `usize` and the other
+                            // is `f32`. In those cases we still want to show the `note`. If the
+                            // value from `ef` is `Infer(_)`, then we ignore it.
+                            if !ef.expected.is_ty_or_numeric_infer() {
+                                ef.expected != values.expected
+                            } else if !ef.found.is_ty_or_numeric_infer() {
+                                ef.found != values.found
+                            } else {
+                                false
+                            }
+                        }))
+                    {
+                        if let Some(ExpectedFound { found: found_ty, .. }) = exp_found {
+                            // `Future` is a special opaque type that the compiler
+                            // will try to hide in some case such as `async fn`, so
+                            // to make an error more use friendly we will
+                            // avoid to suggest a mismatch type with a
+                            // type that the user usually are not using
+                            // directly such as `impl Future<Output = u8>`.
+                            if !self.tcx.ty_is_opaque_future(found_ty) {
+                                diag.note_expected_found_extra(
+                                    &expected_label,
+                                    expected,
+                                    &found_label,
+                                    found,
+                                    &sort_string(values.expected),
+                                    &sort_string(values.found),
+                                );
+                                if let Some(path) = path {
+                                    diag.note(format!(
+                                        "the full type name has been written to '{}'",
+                                        path.display(),
+                                    ));
+                                    diag.note("consider using `--verbose` to print the full type name to the console");
+                                }
+                            }
+                        }
+                    }
+                }
+                _ => {
+                    debug!(
+                        "note_type_err: exp_found={:?}, expected={:?} found={:?}",
+                        exp_found, expected, found
+                    );
+                    if !is_simple_error || terr.must_include_note() {
+                        diag.note_expected_found(&expected_label, expected, &found_label, found);
+
+                        if let Some(ty::Closure(_, args)) =
+                            exp_found.map(|expected_type_found| expected_type_found.found.kind())
+                        {
+                            diag.highlighted_note(vec![
+                                StringPart::normal("closure has signature: `"),
+                                StringPart::highlighted(
+                                    self.tcx
+                                        .signature_unclosure(
+                                            args.as_closure().sig(),
+                                            rustc_hir::Safety::Safe,
+                                        )
+                                        .to_string(),
+                                ),
+                                StringPart::normal("`"),
+                            ]);
+                        }
+                    }
+                }
+            }
+        }
+        let exp_found = match exp_found {
+            Mismatch::Variable(exp_found) => Some(exp_found),
+            Mismatch::Fixed(_) => None,
+        };
+        let exp_found = match terr {
+            // `terr` has more accurate type information than `exp_found` in match expressions.
+            ty::error::TypeError::Sorts(terr)
+                if exp_found.is_some_and(|ef| terr.found == ef.found) =>
+            {
+                Some(terr)
+            }
+            _ => exp_found,
+        };
+        debug!("exp_found {:?} terr {:?} cause.code {:?}", exp_found, terr, cause.code());
+        if let Some(exp_found) = exp_found {
+            let should_suggest_fixes =
+                if let ObligationCauseCode::Pattern { root_ty, .. } = cause.code() {
+                    // Skip if the root_ty of the pattern is not the same as the expected_ty.
+                    // If these types aren't equal then we've probably peeled off a layer of arrays.
+                    self.same_type_modulo_infer(*root_ty, exp_found.expected)
+                } else {
+                    true
+                };
+
+            // FIXME(#73154): For now, we do leak check when coercing function
+            // pointers in typeck, instead of only during borrowck. This can lead
+            // to these `RegionsInsufficientlyPolymorphic` errors that aren't helpful.
+            if should_suggest_fixes
+                && !matches!(terr, TypeError::RegionsInsufficientlyPolymorphic(..))
+            {
+                self.suggest_tuple_pattern(cause, &exp_found, diag);
+                self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag);
+                self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
+                self.suggest_function_pointers(cause, span, &exp_found, diag);
+                self.suggest_turning_stmt_into_expr(cause, &exp_found, diag);
+            }
+        }
+
+        self.check_and_note_conflicting_crates(diag, terr);
+
+        self.note_and_explain_type_err(diag, terr, cause, span, cause.body_id.to_def_id());
+        if let Some(exp_found) = exp_found
+            && let exp_found = TypeError::Sorts(exp_found)
+            && exp_found != terr
+        {
+            self.note_and_explain_type_err(diag, exp_found, cause, span, cause.body_id.to_def_id());
+        }
+
+        if let Some(ValuePairs::TraitRefs(exp_found)) = values
+            && let ty::Closure(def_id, _) = exp_found.expected.self_ty().kind()
+            && let Some(def_id) = def_id.as_local()
+            && terr.involves_regions()
+        {
+            let span = self.tcx.def_span(def_id);
+            diag.span_note(span, "this closure does not fulfill the lifetime requirements");
+            self.suggest_for_all_lifetime_closure(
+                span,
+                self.tcx.hir_node_by_def_id(def_id),
+                &exp_found,
+                diag,
+            );
+        }
+
+        // It reads better to have the error origin as the final
+        // thing.
+        self.note_error_origin(diag, cause, exp_found, terr);
+
+        debug!(?diag);
+    }
+
+    pub fn type_error_additional_suggestions(
+        &self,
+        trace: &TypeTrace<'tcx>,
+        terr: TypeError<'tcx>,
+    ) -> Vec<TypeErrorAdditionalDiags> {
+        let mut suggestions = Vec::new();
+        let span = trace.cause.span();
+        let values = self.resolve_vars_if_possible(trace.values);
+        if let Some((expected, found)) = values.ty() {
+            match (expected.kind(), found.kind()) {
+                (ty::Tuple(_), ty::Tuple(_)) => {}
+                // If a tuple of length one was expected and the found expression has
+                // parentheses around it, perhaps the user meant to write `(expr,)` to
+                // build a tuple (issue #86100)
+                (ty::Tuple(fields), _) => {
+                    suggestions.extend(self.suggest_wrap_to_build_a_tuple(span, found, fields))
+                }
+                // If a byte was expected and the found expression is a char literal
+                // containing a single ASCII character, perhaps the user meant to write `b'c'` to
+                // specify a byte literal
+                (ty::Uint(ty::UintTy::U8), ty::Char) => {
+                    if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span)
+                        && let Some(code) =
+                            code.strip_prefix('\'').and_then(|s| s.strip_suffix('\''))
+                        // forbid all Unicode escapes
+                        && !code.starts_with("\\u")
+                        // forbids literal Unicode characters beyond ASCII
+                        && code.chars().next().is_some_and(|c| c.is_ascii())
+                    {
+                        suggestions.push(TypeErrorAdditionalDiags::MeantByteLiteral {
+                            span,
+                            code: escape_literal(code),
+                        })
+                    }
+                }
+                // If a character was expected and the found expression is a string literal
+                // containing a single character, perhaps the user meant to write `'c'` to
+                // specify a character literal (issue #92479)
+                (ty::Char, ty::Ref(_, r, _)) if r.is_str() => {
+                    if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span)
+                        && let Some(code) = code.strip_prefix('"').and_then(|s| s.strip_suffix('"'))
+                        && code.chars().count() == 1
+                    {
+                        suggestions.push(TypeErrorAdditionalDiags::MeantCharLiteral {
+                            span,
+                            code: escape_literal(code),
+                        })
+                    }
+                }
+                // If a string was expected and the found expression is a character literal,
+                // perhaps the user meant to write `"s"` to specify a string literal.
+                (ty::Ref(_, r, _), ty::Char) if r.is_str() => {
+                    if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span)
+                        && code.starts_with("'")
+                        && code.ends_with("'")
+                    {
+                        suggestions.push(TypeErrorAdditionalDiags::MeantStrLiteral {
+                            start: span.with_hi(span.lo() + BytePos(1)),
+                            end: span.with_lo(span.hi() - BytePos(1)),
+                        });
+                    }
+                }
+                // For code `if Some(..) = expr `, the type mismatch may be expected `bool` but found `()`,
+                // we try to suggest to add the missing `let` for `if let Some(..) = expr`
+                (ty::Bool, ty::Tuple(list)) => {
+                    if list.len() == 0 {
+                        suggestions.extend(self.suggest_let_for_letchains(&trace.cause, span));
+                    }
+                }
+                (ty::Array(_, _), ty::Array(_, _)) => {
+                    suggestions.extend(self.suggest_specify_actual_length(terr, trace, span))
+                }
+                _ => {}
+            }
+        }
+        let code = trace.cause.code();
+        if let &(ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
+            source,
+            ..
+        })
+        | ObligationCauseCode::BlockTailExpression(.., source)) = code
+            && let hir::MatchSource::TryDesugar(_) = source
+            && let Some((expected_ty, found_ty, _)) = self.values_str(trace.values)
+        {
+            suggestions.push(TypeErrorAdditionalDiags::TryCannotConvert {
+                found: found_ty.content(),
+                expected: expected_ty.content(),
+            });
+        }
+        suggestions
+    }
+
+    fn suggest_specify_actual_length(
+        &self,
+        terr: TypeError<'_>,
+        trace: &TypeTrace<'_>,
+        span: Span,
+    ) -> Option<TypeErrorAdditionalDiags> {
+        let hir = self.tcx.hir();
+        let TypeError::FixedArraySize(sz) = terr else {
+            return None;
+        };
+        let tykind = match self.tcx.hir_node_by_def_id(trace.cause.body_id) {
+            hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) => {
+                let body = hir.body(*body_id);
+                struct LetVisitor {
+                    span: Span,
+                }
+                impl<'v> Visitor<'v> for LetVisitor {
+                    type Result = ControlFlow<&'v hir::TyKind<'v>>;
+                    fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) -> Self::Result {
+                        // Find a local statement where the initializer has
+                        // the same span as the error and the type is specified.
+                        if let hir::Stmt {
+                            kind:
+                                hir::StmtKind::Let(hir::LetStmt {
+                                    init: Some(hir::Expr { span: init_span, .. }),
+                                    ty: Some(array_ty),
+                                    ..
+                                }),
+                            ..
+                        } = s
+                            && init_span == &self.span
+                        {
+                            ControlFlow::Break(&array_ty.peel_refs().kind)
+                        } else {
+                            ControlFlow::Continue(())
+                        }
+                    }
+                }
+                LetVisitor { span }.visit_body(body).break_value()
+            }
+            hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _, _), .. }) => {
+                Some(&ty.peel_refs().kind)
+            }
+            _ => None,
+        };
+        if let Some(tykind) = tykind
+            && let hir::TyKind::Array(_, length) = tykind
+            && let hir::ArrayLen::Body(ct) = length
+        {
+            let span = ct.span();
+            Some(TypeErrorAdditionalDiags::ConsiderSpecifyingLength { span, length: sz.found })
+        } else {
+            None
+        }
+    }
+
+    pub fn report_and_explain_type_error(
+        &self,
+        trace: TypeTrace<'tcx>,
+        terr: TypeError<'tcx>,
+    ) -> Diag<'a> {
+        debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr);
+
+        let span = trace.cause.span();
+        let failure_code = trace.cause.as_failure_code_diag(
+            terr,
+            span,
+            self.type_error_additional_suggestions(&trace, terr),
+        );
+        let mut diag = self.dcx().create_err(failure_code);
+        self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr, false, false);
+        diag
+    }
+
+    fn suggest_wrap_to_build_a_tuple(
+        &self,
+        span: Span,
+        found: Ty<'tcx>,
+        expected_fields: &List<Ty<'tcx>>,
+    ) -> Option<TypeErrorAdditionalDiags> {
+        let [expected_tup_elem] = expected_fields[..] else { return None };
+
+        if !self.same_type_modulo_infer(expected_tup_elem, found) {
+            return None;
+        }
+
+        let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) else { return None };
+
+        let sugg = if code.starts_with('(') && code.ends_with(')') {
+            let before_close = span.hi() - BytePos::from_u32(1);
+            TypeErrorAdditionalDiags::TupleOnlyComma {
+                span: span.with_hi(before_close).shrink_to_hi(),
+            }
+        } else {
+            TypeErrorAdditionalDiags::TupleAlsoParentheses {
+                span_low: span.shrink_to_lo(),
+                span_high: span.shrink_to_hi(),
+            }
+        };
+        Some(sugg)
+    }
+
+    fn values_str(
+        &self,
+        values: ValuePairs<'tcx>,
+    ) -> Option<(DiagStyledString, DiagStyledString, Option<PathBuf>)> {
+        match values {
+            ValuePairs::Regions(exp_found) => self.expected_found_str(exp_found),
+            ValuePairs::Terms(exp_found) => self.expected_found_str_term(exp_found),
+            ValuePairs::Aliases(exp_found) => self.expected_found_str(exp_found),
+            ValuePairs::ExistentialTraitRef(exp_found) => self.expected_found_str(exp_found),
+            ValuePairs::ExistentialProjection(exp_found) => self.expected_found_str(exp_found),
+            ValuePairs::TraitRefs(exp_found) => {
+                let pretty_exp_found = ty::error::ExpectedFound {
+                    expected: exp_found.expected.print_trait_sugared(),
+                    found: exp_found.found.print_trait_sugared(),
+                };
+                match self.expected_found_str(pretty_exp_found) {
+                    Some((expected, found, _)) if expected == found => {
+                        self.expected_found_str(exp_found)
+                    }
+                    ret => ret,
+                }
+            }
+            ValuePairs::PolySigs(exp_found) => {
+                let exp_found = self.resolve_vars_if_possible(exp_found);
+                if exp_found.references_error() {
+                    return None;
+                }
+                let (exp, fnd) = self.cmp_fn_sig(&exp_found.expected, &exp_found.found);
+                Some((exp, fnd, None))
+            }
+            ValuePairs::Dummy => {
+                bug!("do not expect to report a type error from a ValuePairs::Dummy")
+            }
+        }
+    }
+
+    fn expected_found_str_term(
+        &self,
+        exp_found: ty::error::ExpectedFound<ty::Term<'tcx>>,
+    ) -> Option<(DiagStyledString, DiagStyledString, Option<PathBuf>)> {
+        let exp_found = self.resolve_vars_if_possible(exp_found);
+        if exp_found.references_error() {
+            return None;
+        }
+
+        Some(match (exp_found.expected.unpack(), exp_found.found.unpack()) {
+            (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => {
+                let (mut exp, mut fnd) = self.cmp(expected, found);
+                // Use the terminal width as the basis to determine when to compress the printed
+                // out type, but give ourselves some leeway to avoid ending up creating a file for
+                // a type that is somewhat shorter than the path we'd write to.
+                let len = self.tcx.sess().diagnostic_width() + 40;
+                let exp_s = exp.content();
+                let fnd_s = fnd.content();
+                let mut path = None;
+                if exp_s.len() > len {
+                    let exp_s = self.tcx.short_ty_string(expected, &mut path);
+                    exp = DiagStyledString::highlighted(exp_s);
+                }
+                if fnd_s.len() > len {
+                    let fnd_s = self.tcx.short_ty_string(found, &mut path);
+                    fnd = DiagStyledString::highlighted(fnd_s);
+                }
+                (exp, fnd, path)
+            }
+            _ => (
+                DiagStyledString::highlighted(exp_found.expected.to_string()),
+                DiagStyledString::highlighted(exp_found.found.to_string()),
+                None,
+            ),
+        })
+    }
+
+    /// Returns a string of the form "expected `{}`, found `{}`".
+    fn expected_found_str<T: fmt::Display + TypeFoldable<TyCtxt<'tcx>>>(
+        &self,
+        exp_found: ty::error::ExpectedFound<T>,
+    ) -> Option<(DiagStyledString, DiagStyledString, Option<PathBuf>)> {
+        let exp_found = self.resolve_vars_if_possible(exp_found);
+        if exp_found.references_error() {
+            return None;
+        }
+
+        Some((
+            DiagStyledString::highlighted(exp_found.expected.to_string()),
+            DiagStyledString::highlighted(exp_found.found.to_string()),
+            None,
+        ))
+    }
+
+    /// Determine whether an error associated with the given span and definition
+    /// should be treated as being caused by the implicit `From` conversion
+    /// within `?` desugaring.
+    pub fn is_try_conversion(&self, span: Span, trait_def_id: DefId) -> bool {
+        span.is_desugaring(DesugaringKind::QuestionMark)
+            && self.tcx.is_diagnostic_item(sym::From, trait_def_id)
+    }
+
+    /// Structurally compares two types, modulo any inference variables.
+    ///
+    /// Returns `true` if two types are equal, or if one type is an inference variable compatible
+    /// with the other type. A TyVar inference type is compatible with any type, and an IntVar or
+    /// FloatVar inference type are compatible with themselves or their concrete types (Int and
+    /// Float types, respectively). When comparing two ADTs, these rules apply recursively.
+    pub fn same_type_modulo_infer<T: relate::Relate<TyCtxt<'tcx>>>(&self, a: T, b: T) -> bool {
+        let (a, b) = self.resolve_vars_if_possible((a, b));
+        SameTypeModuloInfer(self).relate(a, b).is_ok()
+    }
+}
+
+struct SameTypeModuloInfer<'a, 'tcx>(&'a InferCtxt<'tcx>);
+
+impl<'tcx> TypeRelation<TyCtxt<'tcx>> for SameTypeModuloInfer<'_, 'tcx> {
+    fn cx(&self) -> TyCtxt<'tcx> {
+        self.0.tcx
+    }
+
+    fn relate_with_variance<T: relate::Relate<TyCtxt<'tcx>>>(
+        &mut self,
+        _variance: ty::Variance,
+        _info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
+        a: T,
+        b: T,
+    ) -> relate::RelateResult<'tcx, T> {
+        self.relate(a, b)
+    }
+
+    fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+        match (a.kind(), b.kind()) {
+            (ty::Int(_) | ty::Uint(_), ty::Infer(ty::InferTy::IntVar(_)))
+            | (
+                ty::Infer(ty::InferTy::IntVar(_)),
+                ty::Int(_) | ty::Uint(_) | ty::Infer(ty::InferTy::IntVar(_)),
+            )
+            | (ty::Float(_), ty::Infer(ty::InferTy::FloatVar(_)))
+            | (
+                ty::Infer(ty::InferTy::FloatVar(_)),
+                ty::Float(_) | ty::Infer(ty::InferTy::FloatVar(_)),
+            )
+            | (ty::Infer(ty::InferTy::TyVar(_)), _)
+            | (_, ty::Infer(ty::InferTy::TyVar(_))) => Ok(a),
+            (ty::Infer(_), _) | (_, ty::Infer(_)) => Err(TypeError::Mismatch),
+            _ => relate::structurally_relate_tys(self, a, b),
+        }
+    }
+
+    fn regions(
+        &mut self,
+        a: ty::Region<'tcx>,
+        b: ty::Region<'tcx>,
+    ) -> RelateResult<'tcx, ty::Region<'tcx>> {
+        if (a.is_var() && b.is_free())
+            || (b.is_var() && a.is_free())
+            || (a.is_var() && b.is_var())
+            || a == b
+        {
+            Ok(a)
+        } else {
+            Err(TypeError::Mismatch)
+        }
+    }
+
+    fn binders<T>(
+        &mut self,
+        a: ty::Binder<'tcx, T>,
+        b: ty::Binder<'tcx, T>,
+    ) -> relate::RelateResult<'tcx, ty::Binder<'tcx, T>>
+    where
+        T: relate::Relate<TyCtxt<'tcx>>,
+    {
+        Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?))
+    }
+
+    fn consts(
+        &mut self,
+        a: ty::Const<'tcx>,
+        _b: ty::Const<'tcx>,
+    ) -> relate::RelateResult<'tcx, ty::Const<'tcx>> {
+        // FIXME(compiler-errors): This could at least do some first-order
+        // relation
+        Ok(a)
+    }
+}
+
+pub enum FailureCode {
+    Error0317,
+    Error0580,
+    Error0308,
+    Error0644,
+}
+
+#[extension(pub trait ObligationCauseExt<'tcx>)]
+impl<'tcx> ObligationCause<'tcx> {
+    fn as_failure_code(&self, terr: TypeError<'tcx>) -> FailureCode {
+        match self.code() {
+            ObligationCauseCode::IfExpressionWithNoElse => FailureCode::Error0317,
+            ObligationCauseCode::MainFunctionType => FailureCode::Error0580,
+            ObligationCauseCode::CompareImplItem { .. }
+            | ObligationCauseCode::MatchExpressionArm(_)
+            | ObligationCauseCode::IfExpression { .. }
+            | ObligationCauseCode::LetElse
+            | ObligationCauseCode::StartFunctionType
+            | ObligationCauseCode::LangFunctionType(_)
+            | ObligationCauseCode::IntrinsicType
+            | ObligationCauseCode::MethodReceiver => FailureCode::Error0308,
+
+            // In the case where we have no more specific thing to
+            // say, also take a look at the error code, maybe we can
+            // tailor to that.
+            _ => match terr {
+                TypeError::CyclicTy(ty)
+                    if ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() =>
+                {
+                    FailureCode::Error0644
+                }
+                TypeError::IntrinsicCast => FailureCode::Error0308,
+                _ => FailureCode::Error0308,
+            },
+        }
+    }
+    fn as_failure_code_diag(
+        &self,
+        terr: TypeError<'tcx>,
+        span: Span,
+        subdiags: Vec<TypeErrorAdditionalDiags>,
+    ) -> ObligationCauseFailureCode {
+        match self.code() {
+            ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Fn, .. } => {
+                ObligationCauseFailureCode::MethodCompat { span, subdiags }
+            }
+            ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Type, .. } => {
+                ObligationCauseFailureCode::TypeCompat { span, subdiags }
+            }
+            ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Const, .. } => {
+                ObligationCauseFailureCode::ConstCompat { span, subdiags }
+            }
+            ObligationCauseCode::BlockTailExpression(.., hir::MatchSource::TryDesugar(_)) => {
+                ObligationCauseFailureCode::TryCompat { span, subdiags }
+            }
+            ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
+                source, ..
+            }) => match source {
+                hir::MatchSource::TryDesugar(_) => {
+                    ObligationCauseFailureCode::TryCompat { span, subdiags }
+                }
+                _ => ObligationCauseFailureCode::MatchCompat { span, subdiags },
+            },
+            ObligationCauseCode::IfExpression { .. } => {
+                ObligationCauseFailureCode::IfElseDifferent { span, subdiags }
+            }
+            ObligationCauseCode::IfExpressionWithNoElse => {
+                ObligationCauseFailureCode::NoElse { span }
+            }
+            ObligationCauseCode::LetElse => {
+                ObligationCauseFailureCode::NoDiverge { span, subdiags }
+            }
+            ObligationCauseCode::MainFunctionType => {
+                ObligationCauseFailureCode::FnMainCorrectType { span }
+            }
+            ObligationCauseCode::StartFunctionType => {
+                ObligationCauseFailureCode::FnStartCorrectType { span, subdiags }
+            }
+            &ObligationCauseCode::LangFunctionType(lang_item_name) => {
+                ObligationCauseFailureCode::FnLangCorrectType { span, subdiags, lang_item_name }
+            }
+            ObligationCauseCode::IntrinsicType => {
+                ObligationCauseFailureCode::IntrinsicCorrectType { span, subdiags }
+            }
+            ObligationCauseCode::MethodReceiver => {
+                ObligationCauseFailureCode::MethodCorrectType { span, subdiags }
+            }
+
+            // In the case where we have no more specific thing to
+            // say, also take a look at the error code, maybe we can
+            // tailor to that.
+            _ => match terr {
+                TypeError::CyclicTy(ty)
+                    if ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() =>
+                {
+                    ObligationCauseFailureCode::ClosureSelfref { span }
+                }
+                TypeError::IntrinsicCast => {
+                    ObligationCauseFailureCode::CantCoerce { span, subdiags }
+                }
+                _ => ObligationCauseFailureCode::Generic { span, subdiags },
+            },
+        }
+    }
+
+    fn as_requirement_str(&self) -> &'static str {
+        match self.code() {
+            ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Fn, .. } => {
+                "method type is compatible with trait"
+            }
+            ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Type, .. } => {
+                "associated type is compatible with trait"
+            }
+            ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Const, .. } => {
+                "const is compatible with trait"
+            }
+            ObligationCauseCode::MainFunctionType => "`main` function has the correct type",
+            ObligationCauseCode::StartFunctionType => "`#[start]` function has the correct type",
+            ObligationCauseCode::LangFunctionType(_) => "lang item function has the correct type",
+            ObligationCauseCode::IntrinsicType => "intrinsic has the correct type",
+            ObligationCauseCode::MethodReceiver => "method receiver has the correct type",
+            _ => "types are compatible",
+        }
+    }
+}
+
+/// Newtype to allow implementing IntoDiagArg
+pub struct ObligationCauseAsDiagArg<'tcx>(pub ObligationCause<'tcx>);
+
+impl IntoDiagArg for ObligationCauseAsDiagArg<'_> {
+    fn into_diag_arg(self) -> rustc_errors::DiagArgValue {
+        let kind = match self.0.code() {
+            ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Fn, .. } => "method_compat",
+            ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Type, .. } => "type_compat",
+            ObligationCauseCode::CompareImplItem { kind: ty::AssocKind::Const, .. } => {
+                "const_compat"
+            }
+            ObligationCauseCode::MainFunctionType => "fn_main_correct_type",
+            ObligationCauseCode::StartFunctionType => "fn_start_correct_type",
+            ObligationCauseCode::LangFunctionType(_) => "fn_lang_correct_type",
+            ObligationCauseCode::IntrinsicType => "intrinsic_correct_type",
+            ObligationCauseCode::MethodReceiver => "method_correct_type",
+            _ => "other",
+        }
+        .into();
+        rustc_errors::DiagArgValue::Str(kind)
+    }
+}
+
+/// This is a bare signal of what kind of type we're dealing with. `ty::TyKind` tracks
+/// extra information about each type, but we only care about the category.
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+pub enum TyCategory {
+    Closure,
+    Opaque,
+    OpaqueFuture,
+    Coroutine(hir::CoroutineKind),
+    Foreign,
+}
+
+impl fmt::Display for TyCategory {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::Closure => "closure".fmt(f),
+            Self::Opaque => "opaque type".fmt(f),
+            Self::OpaqueFuture => "future".fmt(f),
+            Self::Coroutine(gk) => gk.fmt(f),
+            Self::Foreign => "foreign type".fmt(f),
+        }
+    }
+}
+
+impl TyCategory {
+    pub fn from_ty(tcx: TyCtxt<'_>, ty: Ty<'_>) -> Option<(Self, DefId)> {
+        match *ty.kind() {
+            ty::Closure(def_id, _) => Some((Self::Closure, def_id)),
+            ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => {
+                let kind =
+                    if tcx.ty_is_opaque_future(ty) { Self::OpaqueFuture } else { Self::Opaque };
+                Some((kind, def_id))
+            }
+            ty::Coroutine(def_id, ..) => {
+                Some((Self::Coroutine(tcx.coroutine_kind(def_id).unwrap()), def_id))
+            }
+            ty::Foreign(def_id) => Some((Self::Foreign, def_id)),
+            _ => None,
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs
new file mode 100644
index 00000000000..a93b633ce1a
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs
@@ -0,0 +1,1314 @@
+use std::borrow::Cow;
+use std::iter;
+use std::path::PathBuf;
+
+use rustc_errors::codes::*;
+use rustc_errors::{Diag, IntoDiagArg};
+use rustc_hir as hir;
+use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
+use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
+use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, LetStmt, LocalSource};
+use rustc_middle::bug;
+use rustc_middle::hir::nested_filter;
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
+use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer};
+use rustc_middle::ty::{
+    self, GenericArg, GenericArgKind, GenericArgsRef, InferConst, IsSuggestable, Ty, TyCtxt,
+    TypeFoldable, TypeFolder, TypeSuperFoldable, TypeckResults,
+};
+use rustc_span::symbol::{sym, Ident};
+use rustc_span::{BytePos, FileName, Span, DUMMY_SP};
+use tracing::{debug, instrument, warn};
+
+use crate::error_reporting::TypeErrCtxt;
+use crate::errors::{
+    AmbiguousImpl, AmbiguousReturn, AnnotationRequired, InferenceBadError,
+    SourceKindMultiSuggestion, SourceKindSubdiag,
+};
+use crate::infer::InferCtxt;
+
+pub enum TypeAnnotationNeeded {
+    /// ```compile_fail,E0282
+    /// let x;
+    /// ```
+    E0282,
+    /// An implementation cannot be chosen unambiguously because of lack of information.
+    /// ```compile_fail,E0790
+    /// let _ = Default::default();
+    /// ```
+    E0283,
+    /// ```compile_fail,E0284
+    /// let mut d: u64 = 2;
+    /// d = d % 1u32.into();
+    /// ```
+    E0284,
+}
+
+impl Into<ErrCode> for TypeAnnotationNeeded {
+    fn into(self) -> ErrCode {
+        match self {
+            Self::E0282 => E0282,
+            Self::E0283 => E0283,
+            Self::E0284 => E0284,
+        }
+    }
+}
+
+/// Information about a constant or a type containing inference variables.
+pub struct InferenceDiagnosticsData {
+    pub name: String,
+    pub span: Option<Span>,
+    pub kind: UnderspecifiedArgKind,
+    pub parent: Option<InferenceDiagnosticsParentData>,
+}
+
+/// Data on the parent definition where a generic argument was declared.
+pub struct InferenceDiagnosticsParentData {
+    prefix: &'static str,
+    name: String,
+}
+
+#[derive(Clone)]
+pub enum UnderspecifiedArgKind {
+    Type { prefix: Cow<'static, str> },
+    Const { is_parameter: bool },
+}
+
+impl InferenceDiagnosticsData {
+    fn can_add_more_info(&self) -> bool {
+        !(self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. }))
+    }
+
+    fn where_x_is_kind(&self, in_type: Ty<'_>) -> &'static str {
+        if in_type.is_ty_or_numeric_infer() {
+            ""
+        } else if self.name == "_" {
+            // FIXME: Consider specializing this message if there is a single `_`
+            // in the type.
+            "underscore"
+        } else {
+            "has_name"
+        }
+    }
+
+    /// Generate a label for a generic argument which can't be inferred. When not
+    /// much is known about the argument, `use_diag` may be used to describe the
+    /// labeled value.
+    fn make_bad_error(&self, span: Span) -> InferenceBadError<'_> {
+        let has_parent = self.parent.is_some();
+        let bad_kind = if self.can_add_more_info() { "more_info" } else { "other" };
+        let (parent_prefix, parent_name) = self
+            .parent
+            .as_ref()
+            .map(|parent| (parent.prefix, parent.name.clone()))
+            .unwrap_or_default();
+        InferenceBadError {
+            span,
+            bad_kind,
+            prefix_kind: self.kind.clone(),
+            prefix: self.kind.try_get_prefix().unwrap_or_default(),
+            name: self.name.clone(),
+            has_parent,
+            parent_prefix,
+            parent_name,
+        }
+    }
+}
+
+impl InferenceDiagnosticsParentData {
+    fn for_parent_def_id(
+        tcx: TyCtxt<'_>,
+        parent_def_id: DefId,
+    ) -> Option<InferenceDiagnosticsParentData> {
+        let parent_name =
+            tcx.def_key(parent_def_id).disambiguated_data.data.get_opt_name()?.to_string();
+
+        Some(InferenceDiagnosticsParentData {
+            prefix: tcx.def_descr(parent_def_id),
+            name: parent_name,
+        })
+    }
+
+    fn for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<InferenceDiagnosticsParentData> {
+        Self::for_parent_def_id(tcx, tcx.parent(def_id))
+    }
+}
+
+impl IntoDiagArg for UnderspecifiedArgKind {
+    fn into_diag_arg(self) -> rustc_errors::DiagArgValue {
+        let kind = match self {
+            Self::Type { .. } => "type",
+            Self::Const { is_parameter: true } => "const_with_param",
+            Self::Const { is_parameter: false } => "const",
+        };
+        rustc_errors::DiagArgValue::Str(kind.into())
+    }
+}
+
+impl UnderspecifiedArgKind {
+    fn try_get_prefix(&self) -> Option<&str> {
+        match self {
+            Self::Type { prefix } => Some(prefix.as_ref()),
+            Self::Const { .. } => None,
+        }
+    }
+}
+
+struct ClosureEraser<'tcx> {
+    tcx: TyCtxt<'tcx>,
+}
+
+impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ClosureEraser<'tcx> {
+    fn cx(&self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        match ty.kind() {
+            ty::Closure(_, args) => {
+                let closure_sig = args.as_closure().sig();
+                Ty::new_fn_ptr(
+                    self.tcx,
+                    self.tcx.signature_unclosure(closure_sig, hir::Safety::Safe),
+                )
+            }
+            _ => ty.super_fold_with(self),
+        }
+    }
+}
+
+fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'tcx>, ns: Namespace) -> FmtPrinter<'a, 'tcx> {
+    let mut printer = FmtPrinter::new(infcx.tcx, ns);
+    let ty_getter = move |ty_vid| {
+        if infcx.probe_ty_var(ty_vid).is_ok() {
+            warn!("resolved ty var in error message");
+        }
+
+        let var_origin = infcx.type_var_origin(ty_vid);
+        if let Some(def_id) = var_origin.param_def_id
+            // The `Self` param of a trait has the def-id of the trait,
+            // since it's a synthetic parameter.
+            && infcx.tcx.def_kind(def_id) == DefKind::TyParam
+            && let name = infcx.tcx.item_name(def_id)
+            && !var_origin.span.from_expansion()
+        {
+            let generics = infcx.tcx.generics_of(infcx.tcx.parent(def_id));
+            let idx = generics.param_def_id_to_index(infcx.tcx, def_id).unwrap();
+            let generic_param_def = generics.param_at(idx as usize, infcx.tcx);
+            if let ty::GenericParamDefKind::Type { synthetic: true, .. } = generic_param_def.kind {
+                None
+            } else {
+                Some(name)
+            }
+        } else {
+            None
+        }
+    };
+    printer.ty_infer_name_resolver = Some(Box::new(ty_getter));
+    let const_getter =
+        move |ct_vid| Some(infcx.tcx.item_name(infcx.const_var_origin(ct_vid)?.param_def_id?));
+    printer.const_infer_name_resolver = Some(Box::new(const_getter));
+    printer
+}
+
+fn ty_to_string<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    ty: Ty<'tcx>,
+    called_method_def_id: Option<DefId>,
+) -> String {
+    let mut printer = fmt_printer(infcx, Namespace::TypeNS);
+    let ty = infcx.resolve_vars_if_possible(ty);
+    // We use `fn` ptr syntax for closures, but this only works when the closure
+    // does not capture anything.
+    let ty = ty.fold_with(&mut ClosureEraser { tcx: infcx.tcx });
+
+    match (ty.kind(), called_method_def_id) {
+        // We don't want the regular output for `fn`s because it includes its path in
+        // invalid pseudo-syntax, we want the `fn`-pointer output instead.
+        (ty::FnDef(..), _) => {
+            ty.fn_sig(infcx.tcx).print(&mut printer).unwrap();
+            printer.into_buffer()
+        }
+        (_, Some(def_id))
+            if ty.is_ty_or_numeric_infer()
+                && infcx.tcx.get_diagnostic_item(sym::iterator_collect_fn) == Some(def_id) =>
+        {
+            "Vec<_>".to_string()
+        }
+        _ if ty.is_ty_or_numeric_infer() => "/* Type */".to_string(),
+        _ => {
+            ty.print(&mut printer).unwrap();
+            printer.into_buffer()
+        }
+    }
+}
+
+/// We don't want to directly use `ty_to_string` for closures as their type isn't really
+/// something users are familiar with. Directly printing the `fn_sig` of closures also
+/// doesn't work as they actually use the "rust-call" API.
+fn closure_as_fn_str<'tcx>(infcx: &InferCtxt<'tcx>, ty: Ty<'tcx>) -> String {
+    let ty::Closure(_, args) = ty.kind() else {
+        bug!("cannot convert non-closure to fn str in `closure_as_fn_str`")
+    };
+    let fn_sig = args.as_closure().sig();
+    let args = fn_sig
+        .inputs()
+        .skip_binder()
+        .iter()
+        .next()
+        .map(|args| {
+            args.tuple_fields()
+                .iter()
+                .map(|arg| ty_to_string(infcx, arg, None))
+                .collect::<Vec<_>>()
+                .join(", ")
+        })
+        .unwrap_or_default();
+    let ret = if fn_sig.output().skip_binder().is_unit() {
+        String::new()
+    } else {
+        format!(" -> {}", ty_to_string(infcx, fn_sig.output().skip_binder(), None))
+    };
+    format!("fn({args}){ret}")
+}
+
+impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
+    /// Extracts data used by diagnostic for either types or constants
+    /// which were stuck during inference.
+    pub fn extract_inference_diagnostics_data(
+        &self,
+        arg: GenericArg<'tcx>,
+        highlight: Option<ty::print::RegionHighlightMode<'tcx>>,
+    ) -> InferenceDiagnosticsData {
+        match arg.unpack() {
+            GenericArgKind::Type(ty) => {
+                if let ty::Infer(ty::TyVar(ty_vid)) = *ty.kind() {
+                    let var_origin = self.infcx.type_var_origin(ty_vid);
+                    if let Some(def_id) = var_origin.param_def_id
+                        // The `Self` param of a trait has the def-id of the trait,
+                        // since it's a synthetic parameter.
+                        && self.tcx.def_kind(def_id) == DefKind::TyParam
+                        && !var_origin.span.from_expansion()
+                    {
+                        return InferenceDiagnosticsData {
+                            name: self.tcx.item_name(def_id).to_string(),
+                            span: Some(var_origin.span),
+                            kind: UnderspecifiedArgKind::Type { prefix: "type parameter".into() },
+                            parent: InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id),
+                        };
+                    }
+                }
+
+                let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS);
+                if let Some(highlight) = highlight {
+                    printer.region_highlight_mode = highlight;
+                }
+                ty.print(&mut printer).unwrap();
+                InferenceDiagnosticsData {
+                    name: printer.into_buffer(),
+                    span: None,
+                    kind: UnderspecifiedArgKind::Type { prefix: ty.prefix_string(self.tcx) },
+                    parent: None,
+                }
+            }
+            GenericArgKind::Const(ct) => {
+                if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.kind() {
+                    let origin = self.const_var_origin(vid).expect("expected unresolved const var");
+                    if let Some(def_id) = origin.param_def_id {
+                        return InferenceDiagnosticsData {
+                            name: self.tcx.item_name(def_id).to_string(),
+                            span: Some(origin.span),
+                            kind: UnderspecifiedArgKind::Const { is_parameter: true },
+                            parent: InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id),
+                        };
+                    }
+
+                    debug_assert!(!origin.span.is_dummy());
+                    let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::ValueNS);
+                    if let Some(highlight) = highlight {
+                        printer.region_highlight_mode = highlight;
+                    }
+                    ct.print(&mut printer).unwrap();
+                    InferenceDiagnosticsData {
+                        name: printer.into_buffer(),
+                        span: Some(origin.span),
+                        kind: UnderspecifiedArgKind::Const { is_parameter: false },
+                        parent: None,
+                    }
+                } else {
+                    // If we end up here the `FindInferSourceVisitor`
+                    // won't work, as its expected argument isn't an inference variable.
+                    //
+                    // FIXME: Ideally we should look into the generic constant
+                    // to figure out which inference var is actually unresolved so that
+                    // this path is unreachable.
+                    let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::ValueNS);
+                    if let Some(highlight) = highlight {
+                        printer.region_highlight_mode = highlight;
+                    }
+                    ct.print(&mut printer).unwrap();
+                    InferenceDiagnosticsData {
+                        name: printer.into_buffer(),
+                        span: None,
+                        kind: UnderspecifiedArgKind::Const { is_parameter: false },
+                        parent: None,
+                    }
+                }
+            }
+            GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"),
+        }
+    }
+
+    /// Used as a fallback in [TypeErrCtxt::emit_inference_failure_err]
+    /// in case we weren't able to get a better error.
+    fn bad_inference_failure_err(
+        &self,
+        span: Span,
+        arg_data: InferenceDiagnosticsData,
+        error_code: TypeAnnotationNeeded,
+    ) -> Diag<'a> {
+        let source_kind = "other";
+        let source_name = "";
+        let failure_span = None;
+        let infer_subdiags = Vec::new();
+        let multi_suggestions = Vec::new();
+        let bad_label = Some(arg_data.make_bad_error(span));
+        match error_code {
+            TypeAnnotationNeeded::E0282 => self.dcx().create_err(AnnotationRequired {
+                span,
+                source_kind,
+                source_name,
+                failure_span,
+                infer_subdiags,
+                multi_suggestions,
+                bad_label,
+                was_written: false,
+                path: Default::default(),
+                time_version: false,
+            }),
+            TypeAnnotationNeeded::E0283 => self.dcx().create_err(AmbiguousImpl {
+                span,
+                source_kind,
+                source_name,
+                failure_span,
+                infer_subdiags,
+                multi_suggestions,
+                bad_label,
+                was_written: false,
+                path: Default::default(),
+            }),
+            TypeAnnotationNeeded::E0284 => self.dcx().create_err(AmbiguousReturn {
+                span,
+                source_kind,
+                source_name,
+                failure_span,
+                infer_subdiags,
+                multi_suggestions,
+                bad_label,
+                was_written: false,
+                path: Default::default(),
+            }),
+        }
+    }
+
+    #[instrument(level = "debug", skip(self, error_code))]
+    pub fn emit_inference_failure_err(
+        &self,
+        body_def_id: LocalDefId,
+        failure_span: Span,
+        arg: GenericArg<'tcx>,
+        error_code: TypeAnnotationNeeded,
+        should_label_span: bool,
+    ) -> Diag<'a> {
+        let arg = self.resolve_vars_if_possible(arg);
+        let arg_data = self.extract_inference_diagnostics_data(arg, None);
+
+        let Some(typeck_results) = &self.typeck_results else {
+            // If we don't have any typeck results we're outside
+            // of a body, so we won't be able to get better info
+            // here.
+            return self.bad_inference_failure_err(failure_span, arg_data, error_code);
+        };
+
+        let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, arg);
+        if let Some(body) = self.tcx.hir().maybe_body_owned_by(
+            self.tcx.typeck_root_def_id(body_def_id.to_def_id()).expect_local(),
+        ) {
+            let expr = body.value;
+            local_visitor.visit_expr(expr);
+        }
+
+        let Some(InferSource { span, kind }) = local_visitor.infer_source else {
+            return self.bad_inference_failure_err(failure_span, arg_data, error_code);
+        };
+
+        let (source_kind, name, path) = kind.ty_localized_msg(self);
+        let failure_span = if should_label_span && !failure_span.overlaps(span) {
+            Some(failure_span)
+        } else {
+            None
+        };
+
+        let mut infer_subdiags = Vec::new();
+        let mut multi_suggestions = Vec::new();
+        match kind {
+            InferSourceKind::LetBinding { insert_span, pattern_name, ty, def_id } => {
+                infer_subdiags.push(SourceKindSubdiag::LetLike {
+                    span: insert_span,
+                    name: pattern_name.map(|name| name.to_string()).unwrap_or_else(String::new),
+                    x_kind: arg_data.where_x_is_kind(ty),
+                    prefix_kind: arg_data.kind.clone(),
+                    prefix: arg_data.kind.try_get_prefix().unwrap_or_default(),
+                    arg_name: arg_data.name,
+                    kind: if pattern_name.is_some() { "with_pattern" } else { "other" },
+                    type_name: ty_to_string(self, ty, def_id),
+                });
+            }
+            InferSourceKind::ClosureArg { insert_span, ty } => {
+                infer_subdiags.push(SourceKindSubdiag::LetLike {
+                    span: insert_span,
+                    name: String::new(),
+                    x_kind: arg_data.where_x_is_kind(ty),
+                    prefix_kind: arg_data.kind.clone(),
+                    prefix: arg_data.kind.try_get_prefix().unwrap_or_default(),
+                    arg_name: arg_data.name,
+                    kind: "closure",
+                    type_name: ty_to_string(self, ty, None),
+                });
+            }
+            InferSourceKind::GenericArg {
+                insert_span,
+                argument_index,
+                generics_def_id,
+                def_id: _,
+                generic_args,
+                have_turbofish,
+            } => {
+                let generics = self.tcx.generics_of(generics_def_id);
+                let is_type = matches!(arg.unpack(), GenericArgKind::Type(_));
+
+                let (parent_exists, parent_prefix, parent_name) =
+                    InferenceDiagnosticsParentData::for_parent_def_id(self.tcx, generics_def_id)
+                        .map_or((false, String::new(), String::new()), |parent| {
+                            (true, parent.prefix.to_string(), parent.name)
+                        });
+
+                infer_subdiags.push(SourceKindSubdiag::GenericLabel {
+                    span,
+                    is_type,
+                    param_name: generics.own_params[argument_index].name.to_string(),
+                    parent_exists,
+                    parent_prefix,
+                    parent_name,
+                });
+
+                let args = if self.tcx.get_diagnostic_item(sym::iterator_collect_fn)
+                    == Some(generics_def_id)
+                {
+                    "Vec<_>".to_string()
+                } else {
+                    let mut printer = fmt_printer(self, Namespace::TypeNS);
+                    printer
+                        .comma_sep(generic_args.iter().copied().map(|arg| {
+                            if arg.is_suggestable(self.tcx, true) {
+                                return arg;
+                            }
+
+                            match arg.unpack() {
+                                GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"),
+                                GenericArgKind::Type(_) => self.next_ty_var(DUMMY_SP).into(),
+                                GenericArgKind::Const(_) => self.next_const_var(DUMMY_SP).into(),
+                            }
+                        }))
+                        .unwrap();
+                    printer.into_buffer()
+                };
+
+                if !have_turbofish {
+                    infer_subdiags.push(SourceKindSubdiag::GenericSuggestion {
+                        span: insert_span,
+                        arg_count: generic_args.len(),
+                        args,
+                    });
+                }
+            }
+            InferSourceKind::FullyQualifiedMethodCall { receiver, successor, args, def_id } => {
+                let placeholder = Some(self.next_ty_var(DUMMY_SP));
+                if let Some(args) = args.make_suggestable(self.infcx.tcx, true, placeholder) {
+                    let mut printer = fmt_printer(self, Namespace::ValueNS);
+                    printer.print_def_path(def_id, args).unwrap();
+                    let def_path = printer.into_buffer();
+
+                    // We only care about whether we have to add `&` or `&mut ` for now.
+                    // This is the case if the last adjustment is a borrow and the
+                    // first adjustment was not a builtin deref.
+                    let adjustment = match typeck_results.expr_adjustments(receiver) {
+                        [
+                            Adjustment { kind: Adjust::Deref(None), target: _ },
+                            ..,
+                            Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), target: _ },
+                        ] => "",
+                        [
+                            ..,
+                            Adjustment {
+                                kind: Adjust::Borrow(AutoBorrow::Ref(_, mut_)),
+                                target: _,
+                            },
+                        ] => hir::Mutability::from(*mut_).ref_prefix_str(),
+                        _ => "",
+                    };
+
+                    multi_suggestions.push(SourceKindMultiSuggestion::new_fully_qualified(
+                        receiver.span,
+                        def_path,
+                        adjustment,
+                        successor,
+                    ));
+                }
+            }
+            InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => {
+                let placeholder = Some(self.next_ty_var(DUMMY_SP));
+                if let Some(ty) = ty.make_suggestable(self.infcx.tcx, true, placeholder) {
+                    let ty_info = ty_to_string(self, ty, None);
+                    multi_suggestions.push(SourceKindMultiSuggestion::new_closure_return(
+                        ty_info,
+                        data,
+                        should_wrap_expr,
+                    ));
+                }
+            }
+        }
+
+        let time_version =
+            self.detect_old_time_crate_version(failure_span, &kind, &mut infer_subdiags);
+
+        match error_code {
+            TypeAnnotationNeeded::E0282 => self.dcx().create_err(AnnotationRequired {
+                span,
+                source_kind,
+                source_name: &name,
+                failure_span,
+                infer_subdiags,
+                multi_suggestions,
+                bad_label: None,
+                was_written: path.is_some(),
+                path: path.unwrap_or_default(),
+                time_version,
+            }),
+            TypeAnnotationNeeded::E0283 => self.dcx().create_err(AmbiguousImpl {
+                span,
+                source_kind,
+                source_name: &name,
+                failure_span,
+                infer_subdiags,
+                multi_suggestions,
+                bad_label: None,
+                was_written: path.is_some(),
+                path: path.unwrap_or_default(),
+            }),
+            TypeAnnotationNeeded::E0284 => self.dcx().create_err(AmbiguousReturn {
+                span,
+                source_kind,
+                source_name: &name,
+                failure_span,
+                infer_subdiags,
+                multi_suggestions,
+                bad_label: None,
+                was_written: path.is_some(),
+                path: path.unwrap_or_default(),
+            }),
+        }
+    }
+
+    /// Detect the inference regression on crate `time` <= 0.3.35 and emit a more targeted error.
+    /// <https://github.com/rust-lang/rust/issues/127343>
+    // FIXME: we should figure out a more generic version of doing this, ideally in cargo itself.
+    fn detect_old_time_crate_version(
+        &self,
+        span: Option<Span>,
+        kind: &InferSourceKind<'_>,
+        // We will clear the non-actionable suggestion from the error to reduce noise.
+        infer_subdiags: &mut Vec<SourceKindSubdiag<'_>>,
+    ) -> bool {
+        // FIXME(#129461): We are time-boxing this code in the compiler. It'll start failing
+        // compilation once we promote 1.89 to beta, which will happen in 9 months from now.
+        #[cfg(not(version("1.89")))]
+        const fn version_check() {}
+        #[cfg(version("1.89"))]
+        const fn version_check() {
+            panic!("remove this check as presumably the ecosystem has moved from needing it");
+        }
+        const { version_check() };
+        // Only relevant when building the `time` crate.
+        if self.infcx.tcx.crate_name(LOCAL_CRATE) == sym::time
+            && let Some(span) = span
+            && let InferSourceKind::LetBinding { pattern_name, .. } = kind
+            && let Some(name) = pattern_name
+            && name.as_str() == "items"
+            && let FileName::Real(file) = self.infcx.tcx.sess.source_map().span_to_filename(span)
+        {
+            let path = file.local_path_if_available().to_string_lossy();
+            if path.contains("format_description") && path.contains("parse") {
+                infer_subdiags.clear();
+                return true;
+            }
+        }
+        false
+    }
+}
+
+#[derive(Debug)]
+struct InferSource<'tcx> {
+    span: Span,
+    kind: InferSourceKind<'tcx>,
+}
+
+#[derive(Debug)]
+enum InferSourceKind<'tcx> {
+    LetBinding {
+        insert_span: Span,
+        pattern_name: Option<Ident>,
+        ty: Ty<'tcx>,
+        def_id: Option<DefId>,
+    },
+    ClosureArg {
+        insert_span: Span,
+        ty: Ty<'tcx>,
+    },
+    GenericArg {
+        insert_span: Span,
+        argument_index: usize,
+        generics_def_id: DefId,
+        def_id: DefId,
+        generic_args: &'tcx [GenericArg<'tcx>],
+        have_turbofish: bool,
+    },
+    FullyQualifiedMethodCall {
+        receiver: &'tcx Expr<'tcx>,
+        /// If the method has other arguments, this is ", " and the start of the first argument,
+        /// while for methods without arguments this is ")" and the end of the method call.
+        successor: (&'static str, BytePos),
+        args: GenericArgsRef<'tcx>,
+        def_id: DefId,
+    },
+    ClosureReturn {
+        ty: Ty<'tcx>,
+        data: &'tcx FnRetTy<'tcx>,
+        should_wrap_expr: Option<Span>,
+    },
+}
+
+impl<'tcx> InferSource<'tcx> {
+    fn from_expansion(&self) -> bool {
+        let source_from_expansion = match self.kind {
+            InferSourceKind::LetBinding { insert_span, .. }
+            | InferSourceKind::ClosureArg { insert_span, .. }
+            | InferSourceKind::GenericArg { insert_span, .. } => insert_span.from_expansion(),
+            InferSourceKind::FullyQualifiedMethodCall { receiver, .. } => {
+                receiver.span.from_expansion()
+            }
+            InferSourceKind::ClosureReturn { data, should_wrap_expr, .. } => {
+                data.span().from_expansion() || should_wrap_expr.is_some_and(Span::from_expansion)
+            }
+        };
+        source_from_expansion || self.span.from_expansion()
+    }
+}
+
+impl<'tcx> InferSourceKind<'tcx> {
+    fn ty_localized_msg(&self, infcx: &InferCtxt<'tcx>) -> (&'static str, String, Option<PathBuf>) {
+        let mut path = None;
+        match *self {
+            InferSourceKind::LetBinding { ty, .. }
+            | InferSourceKind::ClosureArg { ty, .. }
+            | InferSourceKind::ClosureReturn { ty, .. } => {
+                if ty.is_closure() {
+                    ("closure", closure_as_fn_str(infcx, ty), path)
+                } else if !ty.is_ty_or_numeric_infer() {
+                    ("normal", infcx.tcx.short_ty_string(ty, &mut path), path)
+                } else {
+                    ("other", String::new(), path)
+                }
+            }
+            // FIXME: We should be able to add some additional info here.
+            InferSourceKind::GenericArg { .. }
+            | InferSourceKind::FullyQualifiedMethodCall { .. } => ("other", String::new(), path),
+        }
+    }
+}
+
+#[derive(Debug)]
+struct InsertableGenericArgs<'tcx> {
+    insert_span: Span,
+    args: GenericArgsRef<'tcx>,
+    generics_def_id: DefId,
+    def_id: DefId,
+    have_turbofish: bool,
+}
+
+/// A visitor which searches for the "best" spot to use in the inference error.
+///
+/// For this it walks over the hir body and tries to check all places where
+/// inference variables could be bound.
+///
+/// While doing so, the currently best spot is stored in `infer_source`.
+/// For details on how we rank spots, see [Self::source_cost]
+struct FindInferSourceVisitor<'a, 'tcx> {
+    tecx: &'a TypeErrCtxt<'a, 'tcx>,
+    typeck_results: &'a TypeckResults<'tcx>,
+
+    target: GenericArg<'tcx>,
+
+    attempt: usize,
+    infer_source_cost: usize,
+    infer_source: Option<InferSource<'tcx>>,
+}
+
+impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
+    fn new(
+        tecx: &'a TypeErrCtxt<'a, 'tcx>,
+        typeck_results: &'a TypeckResults<'tcx>,
+        target: GenericArg<'tcx>,
+    ) -> Self {
+        FindInferSourceVisitor {
+            tecx,
+            typeck_results,
+
+            target,
+
+            attempt: 0,
+            infer_source_cost: usize::MAX,
+            infer_source: None,
+        }
+    }
+
+    /// Computes cost for the given source.
+    ///
+    /// Sources with a small cost are prefer and should result
+    /// in a clearer and idiomatic suggestion.
+    fn source_cost(&self, source: &InferSource<'tcx>) -> usize {
+        #[derive(Clone, Copy)]
+        struct CostCtxt<'tcx> {
+            tcx: TyCtxt<'tcx>,
+        }
+        impl<'tcx> CostCtxt<'tcx> {
+            fn arg_cost(self, arg: GenericArg<'tcx>) -> usize {
+                match arg.unpack() {
+                    GenericArgKind::Lifetime(_) => 0, // erased
+                    GenericArgKind::Type(ty) => self.ty_cost(ty),
+                    GenericArgKind::Const(_) => 3, // some non-zero value
+                }
+            }
+            fn ty_cost(self, ty: Ty<'tcx>) -> usize {
+                match *ty.kind() {
+                    ty::Closure(..) => 1000,
+                    ty::FnDef(..) => 150,
+                    ty::FnPtr(..) => 30,
+                    ty::Adt(def, args) => {
+                        5 + self
+                            .tcx
+                            .generics_of(def.did())
+                            .own_args_no_defaults(self.tcx, args)
+                            .iter()
+                            .map(|&arg| self.arg_cost(arg))
+                            .sum::<usize>()
+                    }
+                    ty::Tuple(args) => 5 + args.iter().map(|arg| self.ty_cost(arg)).sum::<usize>(),
+                    ty::Ref(_, ty, _) => 2 + self.ty_cost(ty),
+                    ty::Infer(..) => 0,
+                    _ => 1,
+                }
+            }
+        }
+
+        // The sources are listed in order of preference here.
+        let tcx = self.tecx.tcx;
+        let ctx = CostCtxt { tcx };
+        match source.kind {
+            InferSourceKind::LetBinding { ty, .. } => ctx.ty_cost(ty),
+            InferSourceKind::ClosureArg { ty, .. } => ctx.ty_cost(ty),
+            InferSourceKind::GenericArg { def_id, generic_args, .. } => {
+                let variant_cost = match tcx.def_kind(def_id) {
+                    // `None::<u32>` and friends are ugly.
+                    DefKind::Variant | DefKind::Ctor(CtorOf::Variant, _) => 15,
+                    _ => 10,
+                };
+                variant_cost + generic_args.iter().map(|&arg| ctx.arg_cost(arg)).sum::<usize>()
+            }
+            InferSourceKind::FullyQualifiedMethodCall { args, .. } => {
+                20 + args.iter().map(|arg| ctx.arg_cost(arg)).sum::<usize>()
+            }
+            InferSourceKind::ClosureReturn { ty, should_wrap_expr, .. } => {
+                30 + ctx.ty_cost(ty) + if should_wrap_expr.is_some() { 10 } else { 0 }
+            }
+        }
+    }
+
+    /// Uses `fn source_cost` to determine whether this inference source is preferable to
+    /// previous sources. We generally prefer earlier sources.
+    #[instrument(level = "debug", skip(self))]
+    fn update_infer_source(&mut self, mut new_source: InferSource<'tcx>) {
+        if new_source.from_expansion() {
+            return;
+        }
+
+        let cost = self.source_cost(&new_source) + self.attempt;
+        debug!(?cost);
+        self.attempt += 1;
+        if let Some(InferSource { kind: InferSourceKind::GenericArg { def_id: did, .. }, .. }) =
+            self.infer_source
+            && let InferSourceKind::LetBinding { ref ty, ref mut def_id, .. } = new_source.kind
+            && ty.is_ty_or_numeric_infer()
+        {
+            // Customize the output so we talk about `let x: Vec<_> = iter.collect();` instead of
+            // `let x: _ = iter.collect();`, as this is a very common case.
+            *def_id = Some(did);
+        }
+
+        if cost < self.infer_source_cost {
+            self.infer_source_cost = cost;
+            self.infer_source = Some(new_source);
+        }
+    }
+
+    fn node_args_opt(&self, hir_id: HirId) -> Option<GenericArgsRef<'tcx>> {
+        let args = self.typeck_results.node_args_opt(hir_id);
+        self.tecx.resolve_vars_if_possible(args)
+    }
+
+    fn opt_node_type(&self, hir_id: HirId) -> Option<Ty<'tcx>> {
+        let ty = self.typeck_results.node_type_opt(hir_id);
+        self.tecx.resolve_vars_if_possible(ty)
+    }
+
+    // Check whether this generic argument is the inference variable we
+    // are looking for.
+    fn generic_arg_is_target(&self, arg: GenericArg<'tcx>) -> bool {
+        if arg == self.target {
+            return true;
+        }
+
+        match (arg.unpack(), self.target.unpack()) {
+            (GenericArgKind::Type(inner_ty), GenericArgKind::Type(target_ty)) => {
+                use ty::{Infer, TyVar};
+                match (inner_ty.kind(), target_ty.kind()) {
+                    (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => {
+                        self.tecx.sub_relations.borrow_mut().unified(self.tecx, a_vid, b_vid)
+                    }
+                    _ => false,
+                }
+            }
+            (GenericArgKind::Const(inner_ct), GenericArgKind::Const(target_ct)) => {
+                use ty::InferConst::*;
+                match (inner_ct.kind(), target_ct.kind()) {
+                    (ty::ConstKind::Infer(Var(a_vid)), ty::ConstKind::Infer(Var(b_vid))) => {
+                        self.tecx.root_const_var(a_vid) == self.tecx.root_const_var(b_vid)
+                    }
+                    _ => false,
+                }
+            }
+            _ => false,
+        }
+    }
+
+    /// Does this generic argument contain our target inference variable
+    /// in a way which can be written by the user.
+    fn generic_arg_contains_target(&self, arg: GenericArg<'tcx>) -> bool {
+        let mut walker = arg.walk();
+        while let Some(inner) = walker.next() {
+            if self.generic_arg_is_target(inner) {
+                return true;
+            }
+            match inner.unpack() {
+                GenericArgKind::Lifetime(_) => {}
+                GenericArgKind::Type(ty) => {
+                    if matches!(
+                        ty.kind(),
+                        ty::Alias(ty::Opaque, ..)
+                            | ty::Closure(..)
+                            | ty::CoroutineClosure(..)
+                            | ty::Coroutine(..)
+                    ) {
+                        // Opaque types can't be named by the user right now.
+                        //
+                        // Both the generic arguments of closures and coroutines can
+                        // also not be named. We may want to only look into the closure
+                        // signature in case it has no captures, as that can be represented
+                        // using `fn(T) -> R`.
+
+                        // FIXME(type_alias_impl_trait): These opaque types
+                        // can actually be named, so it would make sense to
+                        // adjust this case and add a test for it.
+                        walker.skip_current_subtree();
+                    }
+                }
+                GenericArgKind::Const(ct) => {
+                    if matches!(ct.kind(), ty::ConstKind::Unevaluated(..)) {
+                        // You can't write the generic arguments for
+                        // unevaluated constants.
+                        walker.skip_current_subtree();
+                    }
+                }
+            }
+        }
+        false
+    }
+
+    fn expr_inferred_arg_iter(
+        &self,
+        expr: &'tcx hir::Expr<'tcx>,
+    ) -> Box<dyn Iterator<Item = InsertableGenericArgs<'tcx>> + 'a> {
+        let tcx = self.tecx.tcx;
+        match expr.kind {
+            hir::ExprKind::Path(ref path) => {
+                if let Some(args) = self.node_args_opt(expr.hir_id) {
+                    return self.path_inferred_arg_iter(expr.hir_id, args, path);
+                }
+            }
+            // FIXME(#98711): Ideally we would also deal with type relative
+            // paths here, even if that is quite rare.
+            //
+            // See the `need_type_info/expr-struct-type-relative-gat.rs` test
+            // for an example where that would be needed.
+            //
+            // However, the `type_dependent_def_id` for `Self::Output` in an
+            // impl is currently the `DefId` of `Output` in the trait definition
+            // which makes this somewhat difficult and prevents us from just
+            // using `self.path_inferred_arg_iter` here.
+            hir::ExprKind::Struct(&hir::QPath::Resolved(_self_ty, path), _, _)
+            // FIXME(TaKO8Ki): Ideally we should support other kinds,
+            // such as `TyAlias` or `AssocTy`. For that we have to map
+            // back from the self type to the type alias though. That's difficult.
+            //
+            // See the `need_type_info/issue-103053.rs` test for
+            // a example.
+            if matches!(path.res, Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)) => {
+                if let Some(ty) = self.opt_node_type(expr.hir_id)
+                    && let ty::Adt(_, args) = ty.kind()
+                {
+                    return Box::new(self.resolved_path_inferred_arg_iter(path, args));
+                }
+            }
+            hir::ExprKind::MethodCall(segment, ..) => {
+                if let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id) {
+                    let generics = tcx.generics_of(def_id);
+                    let insertable: Option<_> = try {
+                        if generics.has_impl_trait() {
+                            None?
+                        }
+                        let args = self.node_args_opt(expr.hir_id)?;
+                        let span = tcx.hir().span(segment.hir_id);
+                        let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
+                        InsertableGenericArgs {
+                            insert_span,
+                            args,
+                            generics_def_id: def_id,
+                            def_id,
+                            have_turbofish: false,
+                        }
+                    };
+                    return Box::new(insertable.into_iter());
+                }
+            }
+            _ => {}
+        }
+
+        Box::new(iter::empty())
+    }
+
+    fn resolved_path_inferred_arg_iter(
+        &self,
+        path: &'tcx hir::Path<'tcx>,
+        args: GenericArgsRef<'tcx>,
+    ) -> impl Iterator<Item = InsertableGenericArgs<'tcx>> + 'a {
+        let tcx = self.tecx.tcx;
+        let have_turbofish = path.segments.iter().any(|segment| {
+            segment.args.is_some_and(|args| args.args.iter().any(|arg| arg.is_ty_or_const()))
+        });
+        // The last segment of a path often has `Res::Err` and the
+        // correct `Res` is the one of the whole path.
+        //
+        // FIXME: We deal with that one separately for now,
+        // would be good to remove this special case.
+        let last_segment_using_path_data: Option<_> = try {
+            let generics_def_id = tcx.res_generics_def_id(path.res)?;
+            let generics = tcx.generics_of(generics_def_id);
+            if generics.has_impl_trait() {
+                do yeet ();
+            }
+            let insert_span =
+                path.segments.last().unwrap().ident.span.shrink_to_hi().with_hi(path.span.hi());
+            InsertableGenericArgs {
+                insert_span,
+                args,
+                generics_def_id,
+                def_id: path.res.def_id(),
+                have_turbofish,
+            }
+        };
+
+        path.segments
+            .iter()
+            .filter_map(move |segment| {
+                let res = segment.res;
+                let generics_def_id = tcx.res_generics_def_id(res)?;
+                let generics = tcx.generics_of(generics_def_id);
+                if generics.has_impl_trait() {
+                    return None;
+                }
+                let span = tcx.hir().span(segment.hir_id);
+                let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
+                Some(InsertableGenericArgs {
+                    insert_span,
+                    args,
+                    generics_def_id,
+                    def_id: res.def_id(),
+                    have_turbofish,
+                })
+            })
+            .chain(last_segment_using_path_data)
+    }
+
+    fn path_inferred_arg_iter(
+        &self,
+        hir_id: HirId,
+        args: GenericArgsRef<'tcx>,
+        qpath: &'tcx hir::QPath<'tcx>,
+    ) -> Box<dyn Iterator<Item = InsertableGenericArgs<'tcx>> + 'a> {
+        let tcx = self.tecx.tcx;
+        match qpath {
+            hir::QPath::Resolved(_self_ty, path) => {
+                Box::new(self.resolved_path_inferred_arg_iter(path, args))
+            }
+            hir::QPath::TypeRelative(ty, segment) => {
+                let Some(def_id) = self.typeck_results.type_dependent_def_id(hir_id) else {
+                    return Box::new(iter::empty());
+                };
+
+                let generics = tcx.generics_of(def_id);
+                let segment: Option<_> = try {
+                    if !segment.infer_args || generics.has_impl_trait() {
+                        do yeet ();
+                    }
+                    let span = tcx.hir().span(segment.hir_id);
+                    let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
+                    InsertableGenericArgs {
+                        insert_span,
+                        args,
+                        generics_def_id: def_id,
+                        def_id,
+                        have_turbofish: false,
+                    }
+                };
+
+                let parent_def_id = generics.parent.unwrap();
+                if let DefKind::Impl { .. } = tcx.def_kind(parent_def_id) {
+                    let parent_ty = tcx.type_of(parent_def_id).instantiate(tcx, args);
+                    match (parent_ty.kind(), &ty.kind) {
+                        (
+                            ty::Adt(def, args),
+                            hir::TyKind::Path(hir::QPath::Resolved(_self_ty, path)),
+                        ) => {
+                            if tcx.res_generics_def_id(path.res) != Some(def.did()) {
+                                match path.res {
+                                    Res::Def(DefKind::TyAlias, _) => {
+                                        // FIXME: Ideally we should support this. For that
+                                        // we have to map back from the self type to the
+                                        // type alias though. That's difficult.
+                                        //
+                                        // See the `need_type_info/type-alias.rs` test for
+                                        // some examples.
+                                    }
+                                    // There cannot be inference variables in the self type,
+                                    // so there's nothing for us to do here.
+                                    Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } => {}
+                                    _ => warn!(
+                                        "unexpected path: def={:?} args={:?} path={:?}",
+                                        def, args, path,
+                                    ),
+                                }
+                            } else {
+                                return Box::new(
+                                    self.resolved_path_inferred_arg_iter(path, args).chain(segment),
+                                );
+                            }
+                        }
+                        _ => (),
+                    }
+                }
+
+                Box::new(segment.into_iter())
+            }
+            hir::QPath::LangItem(_, _) => Box::new(iter::empty()),
+        }
+    }
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
+    type NestedFilter = nested_filter::OnlyBodies;
+
+    fn nested_visit_map(&mut self) -> Self::Map {
+        self.tecx.tcx.hir()
+    }
+
+    fn visit_local(&mut self, local: &'tcx LetStmt<'tcx>) {
+        intravisit::walk_local(self, local);
+
+        if let Some(ty) = self.opt_node_type(local.hir_id) {
+            if self.generic_arg_contains_target(ty.into()) {
+                match local.source {
+                    LocalSource::Normal if local.ty.is_none() => {
+                        self.update_infer_source(InferSource {
+                            span: local.pat.span,
+                            kind: InferSourceKind::LetBinding {
+                                insert_span: local.pat.span.shrink_to_hi(),
+                                pattern_name: local.pat.simple_ident(),
+                                ty,
+                                def_id: None,
+                            },
+                        })
+                    }
+                    _ => {}
+                }
+            }
+        }
+    }
+
+    /// For closures, we first visit the parameters and then the content,
+    /// as we prefer those.
+    fn visit_body(&mut self, body: &Body<'tcx>) {
+        for param in body.params {
+            debug!(
+                "param: span {:?}, ty_span {:?}, pat.span {:?}",
+                param.span, param.ty_span, param.pat.span
+            );
+            if param.ty_span != param.pat.span {
+                debug!("skipping param: has explicit type");
+                continue;
+            }
+
+            let Some(param_ty) = self.opt_node_type(param.hir_id) else { continue };
+
+            if self.generic_arg_contains_target(param_ty.into()) {
+                self.update_infer_source(InferSource {
+                    span: param.pat.span,
+                    kind: InferSourceKind::ClosureArg {
+                        insert_span: param.pat.span.shrink_to_hi(),
+                        ty: param_ty,
+                    },
+                })
+            }
+        }
+        intravisit::walk_body(self, body);
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+        let tcx = self.tecx.tcx;
+        match expr.kind {
+            // When encountering `func(arg)` first look into `arg` and then `func`,
+            // as `arg` is "more specific".
+            ExprKind::Call(func, args) => {
+                for arg in args {
+                    self.visit_expr(arg);
+                }
+                self.visit_expr(func);
+            }
+            _ => intravisit::walk_expr(self, expr),
+        }
+
+        for args in self.expr_inferred_arg_iter(expr) {
+            debug!(?args);
+            let InsertableGenericArgs {
+                insert_span,
+                args,
+                generics_def_id,
+                def_id,
+                have_turbofish,
+            } = args;
+            let generics = tcx.generics_of(generics_def_id);
+            if let Some(mut argument_index) = generics
+                .own_args(args)
+                .iter()
+                .position(|&arg| self.generic_arg_contains_target(arg))
+            {
+                if generics.parent.is_none() && generics.has_self {
+                    argument_index += 1;
+                }
+                let args = self.tecx.resolve_vars_if_possible(args);
+                let generic_args =
+                    &generics.own_args_no_defaults(tcx, args)[generics.own_counts().lifetimes..];
+                let span = match expr.kind {
+                    ExprKind::MethodCall(path, ..) => path.ident.span,
+                    _ => expr.span,
+                };
+
+                self.update_infer_source(InferSource {
+                    span,
+                    kind: InferSourceKind::GenericArg {
+                        insert_span,
+                        argument_index,
+                        generics_def_id,
+                        def_id,
+                        generic_args,
+                        have_turbofish,
+                    },
+                });
+            }
+        }
+
+        if let Some(node_ty) = self.opt_node_type(expr.hir_id) {
+            if let (
+                &ExprKind::Closure(&Closure { fn_decl, body, fn_decl_span, .. }),
+                ty::Closure(_, args),
+            ) = (&expr.kind, node_ty.kind())
+            {
+                let output = args.as_closure().sig().output().skip_binder();
+                if self.generic_arg_contains_target(output.into()) {
+                    let body = self.tecx.tcx.hir().body(body);
+                    let should_wrap_expr = if matches!(body.value.kind, ExprKind::Block(..)) {
+                        None
+                    } else {
+                        Some(body.value.span.shrink_to_hi())
+                    };
+                    self.update_infer_source(InferSource {
+                        span: fn_decl_span,
+                        kind: InferSourceKind::ClosureReturn {
+                            ty: output,
+                            data: &fn_decl.output,
+                            should_wrap_expr,
+                        },
+                    })
+                }
+            }
+        }
+
+        let has_impl_trait = |def_id| {
+            iter::successors(Some(tcx.generics_of(def_id)), |generics| {
+                generics.parent.map(|def_id| tcx.generics_of(def_id))
+            })
+            .any(|generics| generics.has_impl_trait())
+        };
+        if let ExprKind::MethodCall(path, receiver, method_args, span) = expr.kind
+            && let Some(args) = self.node_args_opt(expr.hir_id)
+            && args.iter().any(|arg| self.generic_arg_contains_target(arg))
+            && let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id)
+            && self.tecx.tcx.trait_of_item(def_id).is_some()
+            && !has_impl_trait(def_id)
+        {
+            let successor =
+                method_args.get(0).map_or_else(|| (")", span.hi()), |arg| (", ", arg.span.lo()));
+            let args = self.tecx.resolve_vars_if_possible(args);
+            self.update_infer_source(InferSource {
+                span: path.ident.span,
+                kind: InferSourceKind::FullyQualifiedMethodCall {
+                    receiver,
+                    successor,
+                    args,
+                    def_id,
+                },
+            })
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/different_lifetimes.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/different_lifetimes.rs
new file mode 100644
index 00000000000..47e9a3755e8
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/different_lifetimes.rs
@@ -0,0 +1,160 @@
+//! Error Reporting for Anonymous Region Lifetime Errors
+//! where both the regions are anonymous.
+
+use rustc_errors::{Diag, ErrorGuaranteed, Subdiagnostic};
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::Ty;
+use rustc_middle::ty::{Region, TyCtxt};
+use tracing::debug;
+
+use crate::error_reporting::infer::nice_region_error::find_anon_type::find_anon_type;
+use crate::error_reporting::infer::nice_region_error::util::AnonymousParamInfo;
+use crate::error_reporting::infer::nice_region_error::NiceRegionError;
+use crate::errors::{AddLifetimeParamsSuggestion, LifetimeMismatch, LifetimeMismatchLabels};
+use crate::infer::{RegionResolutionError, SubregionOrigin};
+
+impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
+    /// Print the error message for lifetime errors when both the concerned regions are anonymous.
+    ///
+    /// Consider a case where we have
+    ///
+    /// ```compile_fail
+    /// fn foo(x: &mut Vec<&u8>, y: &u8) {
+    ///     x.push(y);
+    /// }
+    /// ```
+    ///
+    /// The example gives
+    ///
+    /// ```text
+    /// fn foo(x: &mut Vec<&u8>, y: &u8) {
+    ///                    ---      --- these references are declared with different lifetimes...
+    ///     x.push(y);
+    ///     ^ ...but data from `y` flows into `x` here
+    /// ```
+    ///
+    /// It has been extended for the case of structs too.
+    ///
+    /// Consider the example
+    ///
+    /// ```no_run
+    /// struct Ref<'a> { x: &'a u32 }
+    /// ```
+    ///
+    /// ```text
+    /// fn foo(mut x: Vec<Ref>, y: Ref) {
+    ///                   ---      --- these structs are declared with different lifetimes...
+    ///     x.push(y);
+    ///     ^ ...but data from `y` flows into `x` here
+    /// }
+    /// ```
+    ///
+    /// It will later be extended to trait objects.
+    pub(super) fn try_report_anon_anon_conflict(&self) -> Option<ErrorGuaranteed> {
+        let (span, sub, sup) = self.regions()?;
+
+        if let Some(RegionResolutionError::ConcreteFailure(
+            SubregionOrigin::ReferenceOutlivesReferent(..),
+            ..,
+        )) = self.error
+        {
+            // This error doesn't make much sense in this case.
+            return None;
+        }
+
+        // Determine whether the sub and sup consist of both anonymous (elided) regions.
+        let anon_reg_sup = self.tcx().is_suitable_region(self.generic_param_scope, sup)?;
+
+        let anon_reg_sub = self.tcx().is_suitable_region(self.generic_param_scope, sub)?;
+        let scope_def_id_sup = anon_reg_sup.def_id;
+        let bregion_sup = anon_reg_sup.bound_region;
+        let scope_def_id_sub = anon_reg_sub.def_id;
+        let bregion_sub = anon_reg_sub.bound_region;
+
+        let ty_sup = find_anon_type(self.tcx(), self.generic_param_scope, sup, &bregion_sup)?;
+
+        let ty_sub = find_anon_type(self.tcx(), self.generic_param_scope, sub, &bregion_sub)?;
+
+        debug!(
+            "try_report_anon_anon_conflict: found_param1={:?} sup={:?} br1={:?}",
+            ty_sub, sup, bregion_sup
+        );
+        debug!(
+            "try_report_anon_anon_conflict: found_param2={:?} sub={:?} br2={:?}",
+            ty_sup, sub, bregion_sub
+        );
+
+        let (ty_sup, ty_fndecl_sup) = ty_sup;
+        let (ty_sub, ty_fndecl_sub) = ty_sub;
+
+        let AnonymousParamInfo { param: anon_param_sup, .. } =
+            self.find_param_with_region(sup, sup)?;
+        let AnonymousParamInfo { param: anon_param_sub, .. } =
+            self.find_param_with_region(sub, sub)?;
+
+        let sup_is_ret_type =
+            self.is_return_type_anon(scope_def_id_sup, bregion_sup, ty_fndecl_sup);
+        let sub_is_ret_type =
+            self.is_return_type_anon(scope_def_id_sub, bregion_sub, ty_fndecl_sub);
+
+        debug!(
+            "try_report_anon_anon_conflict: sub_is_ret_type={:?} sup_is_ret_type={:?}",
+            sub_is_ret_type, sup_is_ret_type
+        );
+
+        let labels = match (sup_is_ret_type, sub_is_ret_type) {
+            (ret_capture @ Some(ret_span), _) | (_, ret_capture @ Some(ret_span)) => {
+                let param_span =
+                    if sup_is_ret_type == ret_capture { ty_sub.span } else { ty_sup.span };
+                LifetimeMismatchLabels::InRet {
+                    param_span,
+                    ret_span,
+                    span,
+                    label_var1: anon_param_sup.pat.simple_ident(),
+                }
+            }
+
+            (None, None) => LifetimeMismatchLabels::Normal {
+                hir_equal: ty_sup.hir_id == ty_sub.hir_id,
+                ty_sup: ty_sup.span,
+                ty_sub: ty_sub.span,
+                span,
+                sup: anon_param_sup.pat.simple_ident(),
+                sub: anon_param_sub.pat.simple_ident(),
+            },
+        };
+
+        let suggestion = AddLifetimeParamsSuggestion {
+            tcx: self.tcx(),
+            sub,
+            ty_sup,
+            ty_sub,
+            add_note: true,
+            generic_param_scope: self.generic_param_scope,
+        };
+        let err = LifetimeMismatch { span, labels, suggestion };
+        let reported = self.tcx().dcx().emit_err(err);
+        Some(reported)
+    }
+}
+
+/// Currently only used in rustc_borrowck, probably should be
+/// removed in favour of public_errors::AddLifetimeParamsSuggestion
+pub fn suggest_adding_lifetime_params<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    err: &mut Diag<'_>,
+    generic_param_scope: LocalDefId,
+    sub: Region<'tcx>,
+    ty_sup: &'tcx Ty<'_>,
+    ty_sub: &'tcx Ty<'_>,
+) {
+    let suggestion = AddLifetimeParamsSuggestion {
+        tcx,
+        sub,
+        ty_sup,
+        ty_sub,
+        add_note: false,
+        generic_param_scope,
+    };
+    suggestion.add_to_diag(err);
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs
new file mode 100644
index 00000000000..7a44c2ad661
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs
@@ -0,0 +1,238 @@
+use core::ops::ControlFlow;
+
+use rustc_hir as hir;
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::intravisit::{self, Visitor};
+use rustc_middle::hir::map::Map;
+use rustc_middle::hir::nested_filter;
+use rustc_middle::middle::resolve_bound_vars as rbv;
+use rustc_middle::ty::{self, Region, TyCtxt};
+use tracing::debug;
+
+/// This function calls the `visit_ty` method for the parameters
+/// corresponding to the anonymous regions. The `nested_visitor.found_type`
+/// contains the anonymous type.
+///
+/// # Arguments
+/// region - the anonymous region corresponding to the anon_anon conflict
+/// br - the bound region corresponding to the above region which is of type `BrAnon(_)`
+///
+/// # Example
+/// ```compile_fail
+/// fn foo(x: &mut Vec<&u8>, y: &u8)
+///    { x.push(y); }
+/// ```
+/// The function returns the nested type corresponding to the anonymous region
+/// for e.g., `&u8` and `Vec<&u8>`.
+pub fn find_anon_type<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    generic_param_scope: LocalDefId,
+    region: Region<'tcx>,
+    br: &ty::BoundRegionKind,
+) -> Option<(&'tcx hir::Ty<'tcx>, &'tcx hir::FnSig<'tcx>)> {
+    let anon_reg = tcx.is_suitable_region(generic_param_scope, region)?;
+    let fn_sig = tcx.hir_node_by_def_id(anon_reg.def_id).fn_sig()?;
+
+    fn_sig
+        .decl
+        .inputs
+        .iter()
+        .find_map(|arg| find_component_for_bound_region(tcx, arg, br))
+        .map(|ty| (ty, fn_sig))
+}
+
+// This method creates a FindNestedTypeVisitor which returns the type corresponding
+// to the anonymous region.
+fn find_component_for_bound_region<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    arg: &'tcx hir::Ty<'tcx>,
+    br: &ty::BoundRegionKind,
+) -> Option<&'tcx hir::Ty<'tcx>> {
+    FindNestedTypeVisitor { tcx, bound_region: *br, current_index: ty::INNERMOST }
+        .visit_ty(arg)
+        .break_value()
+}
+
+// The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the
+// anonymous region. The example above would lead to a conflict between
+// the two anonymous lifetimes for &u8 in x and y respectively. This visitor
+// would be invoked twice, once for each lifetime, and would
+// walk the types like &mut Vec<&u8> and &u8 looking for the HIR
+// where that lifetime appears. This allows us to highlight the
+// specific part of the type in the error message.
+struct FindNestedTypeVisitor<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    // The bound_region corresponding to the Refree(freeregion)
+    // associated with the anonymous region we are looking for.
+    bound_region: ty::BoundRegionKind,
+    current_index: ty::DebruijnIndex,
+}
+
+impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
+    type Result = ControlFlow<&'tcx hir::Ty<'tcx>>;
+    type NestedFilter = nested_filter::OnlyBodies;
+
+    fn nested_visit_map(&mut self) -> Self::Map {
+        self.tcx.hir()
+    }
+
+    fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result {
+        match arg.kind {
+            hir::TyKind::BareFn(_) => {
+                self.current_index.shift_in(1);
+                intravisit::walk_ty(self, arg);
+                self.current_index.shift_out(1);
+                return ControlFlow::Continue(());
+            }
+
+            hir::TyKind::TraitObject(bounds, ..) => {
+                for (bound, _) in bounds {
+                    self.current_index.shift_in(1);
+                    self.visit_poly_trait_ref(bound);
+                    self.current_index.shift_out(1);
+                }
+            }
+
+            hir::TyKind::Ref(lifetime, _) => {
+                // the lifetime of the Ref
+                let hir_id = lifetime.hir_id;
+                match (self.tcx.named_bound_var(hir_id), self.bound_region) {
+                    // Find the index of the named region that was part of the
+                    // error. We will then search the function parameters for a bound
+                    // region at the right depth with the same index
+                    (Some(rbv::ResolvedArg::EarlyBound(id)), ty::BrNamed(def_id, _)) => {
+                        debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
+                        if id.to_def_id() == def_id {
+                            return ControlFlow::Break(arg);
+                        }
+                    }
+
+                    // Find the index of the named region that was part of the
+                    // error. We will then search the function parameters for a bound
+                    // region at the right depth with the same index
+                    (
+                        Some(rbv::ResolvedArg::LateBound(debruijn_index, _, id)),
+                        ty::BrNamed(def_id, _),
+                    ) => {
+                        debug!(
+                            "FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}",
+                            debruijn_index
+                        );
+                        debug!("LateBound id={:?} def_id={:?}", id, def_id);
+                        if debruijn_index == self.current_index && id.to_def_id() == def_id {
+                            return ControlFlow::Break(arg);
+                        }
+                    }
+
+                    (
+                        Some(
+                            rbv::ResolvedArg::StaticLifetime
+                            | rbv::ResolvedArg::Free(_, _)
+                            | rbv::ResolvedArg::EarlyBound(_)
+                            | rbv::ResolvedArg::LateBound(_, _, _)
+                            | rbv::ResolvedArg::Error(_),
+                        )
+                        | None,
+                        _,
+                    ) => {
+                        debug!("no arg found");
+                    }
+                }
+            }
+            // Checks if it is of type `hir::TyKind::Path` which corresponds to a struct.
+            hir::TyKind::Path(_) => {
+                // Prefer using the lifetime in type arguments rather than lifetime arguments.
+                intravisit::walk_ty(self, arg)?;
+
+                // Call `walk_ty` as `visit_ty` is empty.
+                return if intravisit::walk_ty(
+                    &mut TyPathVisitor {
+                        tcx: self.tcx,
+                        bound_region: self.bound_region,
+                        current_index: self.current_index,
+                    },
+                    arg,
+                )
+                .is_break()
+                {
+                    ControlFlow::Break(arg)
+                } else {
+                    ControlFlow::Continue(())
+                };
+            }
+            _ => {}
+        }
+        // walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`,
+        // go on to visit `&Foo`
+        intravisit::walk_ty(self, arg)
+    }
+}
+
+// The visitor captures the corresponding `hir::Ty` of the anonymous region
+// in the case of structs ie. `hir::TyKind::Path`.
+// This visitor would be invoked for each lifetime corresponding to a struct,
+// and would walk the types like Vec<Ref> in the above example and Ref looking for the HIR
+// where that lifetime appears. This allows us to highlight the
+// specific part of the type in the error message.
+struct TyPathVisitor<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    bound_region: ty::BoundRegionKind,
+    current_index: ty::DebruijnIndex,
+}
+
+impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> {
+    type Result = ControlFlow<()>;
+    type NestedFilter = nested_filter::OnlyBodies;
+
+    fn nested_visit_map(&mut self) -> Map<'tcx> {
+        self.tcx.hir()
+    }
+
+    fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) -> Self::Result {
+        match (self.tcx.named_bound_var(lifetime.hir_id), self.bound_region) {
+            // the lifetime of the TyPath!
+            (Some(rbv::ResolvedArg::EarlyBound(id)), ty::BrNamed(def_id, _)) => {
+                debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
+                if id.to_def_id() == def_id {
+                    return ControlFlow::Break(());
+                }
+            }
+
+            (Some(rbv::ResolvedArg::LateBound(debruijn_index, _, id)), ty::BrNamed(def_id, _)) => {
+                debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", debruijn_index,);
+                debug!("id={:?}", id);
+                debug!("def_id={:?}", def_id);
+                if debruijn_index == self.current_index && id.to_def_id() == def_id {
+                    return ControlFlow::Break(());
+                }
+            }
+
+            (
+                Some(
+                    rbv::ResolvedArg::StaticLifetime
+                    | rbv::ResolvedArg::EarlyBound(_)
+                    | rbv::ResolvedArg::LateBound(_, _, _)
+                    | rbv::ResolvedArg::Free(_, _)
+                    | rbv::ResolvedArg::Error(_),
+                )
+                | None,
+                _,
+            ) => {
+                debug!("no arg found");
+            }
+        }
+        ControlFlow::Continue(())
+    }
+
+    fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result {
+        // ignore nested types
+        //
+        // If you have a type like `Foo<'a, &Ty>` we
+        // are only interested in the immediate lifetimes ('a).
+        //
+        // Making `visit_ty` empty will ignore the `&Ty` embedded
+        // inside, it will get reached by the outer visitor.
+        debug!("`Ty` corresponding to a struct is {:?}", arg);
+        ControlFlow::Continue(())
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs
new file mode 100644
index 00000000000..4fe26fa81af
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs
@@ -0,0 +1,128 @@
+//! Error Reporting for when the lifetime for a type doesn't match the `impl` selected for a predicate
+//! to hold.
+
+use rustc_data_structures::fx::FxIndexSet;
+use rustc_errors::{ErrorGuaranteed, MultiSpan};
+use rustc_hir as hir;
+use rustc_hir::intravisit::Visitor;
+use rustc_middle::bug;
+use rustc_middle::ty::TypeVisitor;
+use tracing::debug;
+
+use crate::error_reporting::infer::nice_region_error::NiceRegionError;
+use crate::errors::{
+    note_and_explain, DoesNotOutliveStaticFromImpl, ImplicitStaticLifetimeSubdiag,
+    IntroducesStaticBecauseUnmetLifetimeReq, MismatchedStaticLifetime,
+};
+use crate::infer::{RegionResolutionError, SubregionOrigin, TypeTrace};
+use crate::traits::ObligationCauseCode;
+
+impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
+    pub(super) fn try_report_mismatched_static_lifetime(&self) -> Option<ErrorGuaranteed> {
+        let error = self.error.as_ref()?;
+        debug!("try_report_mismatched_static_lifetime {:?}", error);
+
+        let RegionResolutionError::ConcreteFailure(origin, sub, sup) = error.clone() else {
+            return None;
+        };
+        if !sub.is_static() {
+            return None;
+        }
+        let SubregionOrigin::Subtype(box TypeTrace { ref cause, .. }) = origin else {
+            return None;
+        };
+        // If we added a "points at argument expression" obligation, we remove it here, we care
+        // about the original obligation only.
+        let code = match cause.code() {
+            ObligationCauseCode::FunctionArg { parent_code, .. } => &*parent_code,
+            code => code,
+        };
+        let ObligationCauseCode::MatchImpl(parent, impl_def_id) = code else {
+            return None;
+        };
+        let (ObligationCauseCode::WhereClause(_, binding_span)
+        | ObligationCauseCode::WhereClauseInExpr(_, binding_span, ..)) = *parent.code()
+        else {
+            return None;
+        };
+        if binding_span.is_dummy() {
+            return None;
+        }
+
+        // FIXME: we should point at the lifetime
+        let multi_span: MultiSpan = vec![binding_span].into();
+        let multispan_subdiag = IntroducesStaticBecauseUnmetLifetimeReq {
+            unmet_requirements: multi_span,
+            binding_span,
+        };
+
+        let expl = note_and_explain::RegionExplanation::new(
+            self.tcx(),
+            self.generic_param_scope,
+            sup,
+            Some(binding_span),
+            note_and_explain::PrefixKind::Empty,
+            note_and_explain::SuffixKind::Continues,
+        );
+        let mut impl_span = None;
+        let mut implicit_static_lifetimes = Vec::new();
+        if let Some(impl_node) = self.tcx().hir().get_if_local(*impl_def_id) {
+            // If an impl is local, then maybe this isn't what they want. Try to
+            // be as helpful as possible with implicit lifetimes.
+
+            // First, let's get the hir self type of the impl
+            let hir::Node::Item(hir::Item {
+                kind: hir::ItemKind::Impl(hir::Impl { self_ty: impl_self_ty, .. }),
+                ..
+            }) = impl_node
+            else {
+                bug!("Node not an impl.");
+            };
+
+            // Next, let's figure out the set of trait objects with implicit static bounds
+            let ty = self.tcx().type_of(*impl_def_id).instantiate_identity();
+            let mut v = super::static_impl_trait::TraitObjectVisitor(FxIndexSet::default());
+            v.visit_ty(ty);
+            let mut traits = vec![];
+            for matching_def_id in v.0 {
+                let mut hir_v =
+                    super::static_impl_trait::HirTraitObjectVisitor(&mut traits, matching_def_id);
+                hir_v.visit_ty(impl_self_ty);
+            }
+
+            if traits.is_empty() {
+                // If there are no trait object traits to point at, either because
+                // there aren't trait objects or because none are implicit, then just
+                // write a single note on the impl itself.
+
+                impl_span = Some(self.tcx().def_span(*impl_def_id));
+            } else {
+                // Otherwise, point at all implicit static lifetimes
+
+                for span in &traits {
+                    implicit_static_lifetimes
+                        .push(ImplicitStaticLifetimeSubdiag::Note { span: *span });
+                    // It would be nice to put this immediately under the above note, but they get
+                    // pushed to the end.
+                    implicit_static_lifetimes
+                        .push(ImplicitStaticLifetimeSubdiag::Sugg { span: span.shrink_to_hi() });
+                }
+            }
+        } else {
+            // Otherwise just point out the impl.
+
+            impl_span = Some(self.tcx().def_span(*impl_def_id));
+        }
+        let err = MismatchedStaticLifetime {
+            cause_span: cause.span,
+            unmet_lifetime_reqs: multispan_subdiag,
+            expl,
+            does_not_outlive_static_from_impl: impl_span
+                .map(|span| DoesNotOutliveStaticFromImpl::Spanned { span })
+                .unwrap_or(DoesNotOutliveStaticFromImpl::Unspanned),
+            implicit_static_lifetimes,
+        };
+        let reported = self.tcx().dcx().emit_err(err);
+        Some(reported)
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mod.rs
new file mode 100644
index 00000000000..79a770ac9b3
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mod.rs
@@ -0,0 +1,94 @@
+use rustc_errors::{Diag, ErrorGuaranteed};
+use rustc_hir::def_id::LocalDefId;
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_span::Span;
+
+use crate::error_reporting::TypeErrCtxt;
+use crate::infer::RegionResolutionError;
+use crate::infer::RegionResolutionError::*;
+
+mod different_lifetimes;
+pub mod find_anon_type;
+mod mismatched_static_lifetime;
+mod named_anon_conflict;
+pub(crate) mod placeholder_error;
+mod placeholder_relation;
+mod static_impl_trait;
+mod trait_impl_difference;
+mod util;
+
+pub use different_lifetimes::suggest_adding_lifetime_params;
+pub use find_anon_type::find_anon_type;
+pub use static_impl_trait::{suggest_new_region_bound, HirTraitObjectVisitor, TraitObjectVisitor};
+pub use util::find_param_with_region;
+
+impl<'cx, 'tcx> TypeErrCtxt<'cx, 'tcx> {
+    pub fn try_report_nice_region_error(
+        &'cx self,
+        generic_param_scope: LocalDefId,
+        error: &RegionResolutionError<'tcx>,
+    ) -> Option<ErrorGuaranteed> {
+        NiceRegionError::new(self, generic_param_scope, error.clone()).try_report()
+    }
+}
+
+pub struct NiceRegionError<'cx, 'tcx> {
+    cx: &'cx TypeErrCtxt<'cx, 'tcx>,
+    /// The innermost definition that introduces generic parameters that may be involved in
+    /// the region errors we are dealing with.
+    generic_param_scope: LocalDefId,
+    error: Option<RegionResolutionError<'tcx>>,
+    regions: Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)>,
+}
+
+impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> {
+    pub fn new(
+        cx: &'cx TypeErrCtxt<'cx, 'tcx>,
+        generic_param_scope: LocalDefId,
+        error: RegionResolutionError<'tcx>,
+    ) -> Self {
+        Self { cx, error: Some(error), regions: None, generic_param_scope }
+    }
+
+    pub fn new_from_span(
+        cx: &'cx TypeErrCtxt<'cx, 'tcx>,
+        generic_param_scope: LocalDefId,
+        span: Span,
+        sub: ty::Region<'tcx>,
+        sup: ty::Region<'tcx>,
+    ) -> Self {
+        Self { cx, error: None, regions: Some((span, sub, sup)), generic_param_scope }
+    }
+
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.cx.tcx
+    }
+
+    pub fn try_report_from_nll(&self) -> Option<Diag<'tcx>> {
+        // Due to the improved diagnostics returned by the MIR borrow checker, only a subset of
+        // the nice region errors are required when running under the MIR borrow checker.
+        self.try_report_named_anon_conflict()
+            .or_else(|| self.try_report_placeholder_conflict())
+            .or_else(|| self.try_report_placeholder_relation())
+    }
+
+    pub fn try_report(&self) -> Option<ErrorGuaranteed> {
+        self.try_report_from_nll()
+            .map(|diag| diag.emit())
+            .or_else(|| self.try_report_impl_not_conforming_to_trait())
+            .or_else(|| self.try_report_anon_anon_conflict())
+            .or_else(|| self.try_report_static_impl_trait())
+            .or_else(|| self.try_report_mismatched_static_lifetime())
+    }
+
+    pub(super) fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> {
+        match (&self.error, self.regions) {
+            (Some(ConcreteFailure(origin, sub, sup)), None) => Some((origin.span(), *sub, *sup)),
+            (Some(SubSupConflict(_, _, origin, sub, _, sup, _)), None) => {
+                Some((origin.span(), *sub, *sup))
+            }
+            (None, Some((span, sub, sup))) => Some((span, sub, sup)),
+            _ => None,
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs
new file mode 100644
index 00000000000..a7a1fa1c2b4
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs
@@ -0,0 +1,94 @@
+//! Error Reporting for Anonymous Region Lifetime Errors
+//! where one region is named and the other is anonymous.
+
+use rustc_errors::Diag;
+use rustc_middle::ty;
+use rustc_span::symbol::kw;
+use tracing::debug;
+
+use crate::error_reporting::infer::nice_region_error::find_anon_type::find_anon_type;
+use crate::error_reporting::infer::nice_region_error::NiceRegionError;
+use crate::errors::ExplicitLifetimeRequired;
+
+impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
+    /// When given a `ConcreteFailure` for a function with parameters containing a named region and
+    /// an anonymous region, emit an descriptive diagnostic error.
+    pub(super) fn try_report_named_anon_conflict(&self) -> Option<Diag<'tcx>> {
+        let (span, sub, sup) = self.regions()?;
+
+        debug!(
+            "try_report_named_anon_conflict(sub={:?}, sup={:?}, error={:?})",
+            sub, sup, self.error,
+        );
+
+        // Determine whether the sub and sup consist of one named region ('a)
+        // and one anonymous (elided) region. If so, find the parameter arg
+        // where the anonymous region appears (there must always be one; we
+        // only introduced anonymous regions in parameters) as well as a
+        // version new_ty of its type where the anonymous region is replaced
+        // with the named one.
+        let (named, anon, anon_param_info, region_info) = if sub.has_name()
+            && let Some(region_info) = self.tcx().is_suitable_region(self.generic_param_scope, sup)
+            && let Some(anon_param_info) = self.find_param_with_region(sup, sub)
+        {
+            (sub, sup, anon_param_info, region_info)
+        } else if sup.has_name()
+            && let Some(region_info) = self.tcx().is_suitable_region(self.generic_param_scope, sub)
+            && let Some(anon_param_info) = self.find_param_with_region(sub, sup)
+        {
+            (sup, sub, anon_param_info, region_info)
+        } else {
+            return None; // inapplicable
+        };
+
+        // Suggesting to add a `'static` lifetime to a parameter is nearly always incorrect,
+        // and can steer users down the wrong path.
+        if named.is_static() {
+            return None;
+        }
+
+        debug!("try_report_named_anon_conflict: named = {:?}", named);
+        debug!("try_report_named_anon_conflict: anon_param_info = {:?}", anon_param_info);
+        debug!("try_report_named_anon_conflict: region_info = {:?}", region_info);
+
+        let param = anon_param_info.param;
+        let new_ty = anon_param_info.param_ty;
+        let new_ty_span = anon_param_info.param_ty_span;
+        let br = anon_param_info.bound_region;
+        let is_first = anon_param_info.is_first;
+        let scope_def_id = region_info.def_id;
+        let is_impl_item = region_info.is_impl_item;
+
+        match br {
+            ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon => {}
+            _ => {
+                /* not an anonymous region */
+                debug!("try_report_named_anon_conflict: not an anonymous region");
+                return None;
+            }
+        }
+
+        if is_impl_item {
+            debug!("try_report_named_anon_conflict: impl item, bail out");
+            return None;
+        }
+
+        if find_anon_type(self.tcx(), self.generic_param_scope, anon, &br).is_some()
+            && self.is_self_anon(is_first, scope_def_id)
+        {
+            return None;
+        }
+        let named = named.to_string();
+        let err = match param.pat.simple_ident() {
+            Some(simple_ident) => ExplicitLifetimeRequired::WithIdent {
+                span,
+                simple_ident,
+                named,
+                new_ty_span,
+                new_ty,
+            },
+            None => ExplicitLifetimeRequired::WithParamType { span, named, new_ty_span, new_ty },
+        };
+        Some(self.tcx().sess.dcx().create_err(err))
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs
new file mode 100644
index 00000000000..2b7927367d8
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs
@@ -0,0 +1,495 @@
+use std::fmt;
+
+use rustc_data_structures::intern::Interned;
+use rustc_errors::{Diag, IntoDiagArg};
+use rustc_hir::def::Namespace;
+use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
+use rustc_middle::bug;
+use rustc_middle::ty::error::ExpectedFound;
+use rustc_middle::ty::print::{FmtPrinter, Print, PrintTraitRefExt as _, RegionHighlightMode};
+use rustc_middle::ty::{self, GenericArgsRef, RePlaceholder, Region, TyCtxt};
+use tracing::{debug, instrument};
+
+use crate::error_reporting::infer::nice_region_error::NiceRegionError;
+use crate::errors::{
+    ActualImplExpectedKind, ActualImplExpectedLifetimeKind, ActualImplExplNotes,
+    TraitPlaceholderMismatch, TyOrSig,
+};
+use crate::infer::{RegionResolutionError, SubregionOrigin, TypeTrace, ValuePairs};
+use crate::traits::{ObligationCause, ObligationCauseCode};
+
+// HACK(eddyb) maybe move this in a more central location.
+#[derive(Copy, Clone)]
+pub struct Highlighted<'tcx, T> {
+    tcx: TyCtxt<'tcx>,
+    highlight: RegionHighlightMode<'tcx>,
+    value: T,
+}
+
+impl<'tcx, T> IntoDiagArg for Highlighted<'tcx, T>
+where
+    T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>>,
+{
+    fn into_diag_arg(self) -> rustc_errors::DiagArgValue {
+        rustc_errors::DiagArgValue::Str(self.to_string().into())
+    }
+}
+
+impl<'tcx, T> Highlighted<'tcx, T> {
+    fn map<U>(self, f: impl FnOnce(T) -> U) -> Highlighted<'tcx, U> {
+        Highlighted { tcx: self.tcx, highlight: self.highlight, value: f(self.value) }
+    }
+}
+
+impl<'tcx, T> fmt::Display for Highlighted<'tcx, T>
+where
+    T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>>,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS);
+        printer.region_highlight_mode = self.highlight;
+
+        self.value.print(&mut printer)?;
+        f.write_str(&printer.into_buffer())
+    }
+}
+
+impl<'tcx> NiceRegionError<'_, 'tcx> {
+    /// When given a `ConcreteFailure` for a function with arguments containing a named region and
+    /// an anonymous region, emit a descriptive diagnostic error.
+    pub(super) fn try_report_placeholder_conflict(&self) -> Option<Diag<'tcx>> {
+        match &self.error {
+            ///////////////////////////////////////////////////////////////////////////
+            // NB. The ordering of cases in this match is very
+            // sensitive, because we are often matching against
+            // specific cases and then using an `_` to match all
+            // others.
+
+            ///////////////////////////////////////////////////////////////////////////
+            // Check for errors from comparing trait failures -- first
+            // with two placeholders, then with one.
+            Some(RegionResolutionError::SubSupConflict(
+                vid,
+                _,
+                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+                sub_placeholder @ Region(Interned(RePlaceholder(_), _)),
+                _,
+                sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
+                _,
+            )) => self.try_report_trait_placeholder_mismatch(
+                Some(ty::Region::new_var(self.tcx(), *vid)),
+                cause,
+                Some(*sub_placeholder),
+                Some(*sup_placeholder),
+                values,
+            ),
+
+            Some(RegionResolutionError::SubSupConflict(
+                vid,
+                _,
+                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+                sub_placeholder @ Region(Interned(RePlaceholder(_), _)),
+                _,
+                _,
+                _,
+            )) => self.try_report_trait_placeholder_mismatch(
+                Some(ty::Region::new_var(self.tcx(), *vid)),
+                cause,
+                Some(*sub_placeholder),
+                None,
+                values,
+            ),
+
+            Some(RegionResolutionError::SubSupConflict(
+                vid,
+                _,
+                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+                _,
+                _,
+                sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
+                _,
+            )) => self.try_report_trait_placeholder_mismatch(
+                Some(ty::Region::new_var(self.tcx(), *vid)),
+                cause,
+                None,
+                Some(*sup_placeholder),
+                values,
+            ),
+
+            Some(RegionResolutionError::SubSupConflict(
+                vid,
+                _,
+                _,
+                _,
+                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+                sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
+                _,
+            )) => self.try_report_trait_placeholder_mismatch(
+                Some(ty::Region::new_var(self.tcx(), *vid)),
+                cause,
+                None,
+                Some(*sup_placeholder),
+                values,
+            ),
+
+            Some(RegionResolutionError::UpperBoundUniverseConflict(
+                vid,
+                _,
+                _,
+                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+                sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
+            )) => self.try_report_trait_placeholder_mismatch(
+                Some(ty::Region::new_var(self.tcx(), *vid)),
+                cause,
+                None,
+                Some(*sup_placeholder),
+                values,
+            ),
+
+            Some(RegionResolutionError::ConcreteFailure(
+                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+                sub_region @ Region(Interned(RePlaceholder(_), _)),
+                sup_region @ Region(Interned(RePlaceholder(_), _)),
+            )) => self.try_report_trait_placeholder_mismatch(
+                None,
+                cause,
+                Some(*sub_region),
+                Some(*sup_region),
+                values,
+            ),
+
+            Some(RegionResolutionError::ConcreteFailure(
+                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+                sub_region @ Region(Interned(RePlaceholder(_), _)),
+                sup_region,
+            )) => self.try_report_trait_placeholder_mismatch(
+                (!sup_region.has_name()).then_some(*sup_region),
+                cause,
+                Some(*sub_region),
+                None,
+                values,
+            ),
+
+            Some(RegionResolutionError::ConcreteFailure(
+                SubregionOrigin::Subtype(box TypeTrace { cause, values }),
+                sub_region,
+                sup_region @ Region(Interned(RePlaceholder(_), _)),
+            )) => self.try_report_trait_placeholder_mismatch(
+                (!sub_region.has_name()).then_some(*sub_region),
+                cause,
+                None,
+                Some(*sup_region),
+                values,
+            ),
+
+            _ => None,
+        }
+    }
+
+    fn try_report_trait_placeholder_mismatch(
+        &self,
+        vid: Option<Region<'tcx>>,
+        cause: &ObligationCause<'tcx>,
+        sub_placeholder: Option<Region<'tcx>>,
+        sup_placeholder: Option<Region<'tcx>>,
+        value_pairs: &ValuePairs<'tcx>,
+    ) -> Option<Diag<'tcx>> {
+        let (expected_args, found_args, trait_def_id) = match value_pairs {
+            ValuePairs::TraitRefs(ExpectedFound { expected, found })
+                if expected.def_id == found.def_id =>
+            {
+                // It's possible that the placeholders come from a binder
+                // outside of this value pair. Use `no_bound_vars` as a
+                // simple heuristic for that.
+                (expected.args, found.args, expected.def_id)
+            }
+            _ => return None,
+        };
+
+        Some(self.report_trait_placeholder_mismatch(
+            vid,
+            cause,
+            sub_placeholder,
+            sup_placeholder,
+            trait_def_id,
+            expected_args,
+            found_args,
+        ))
+    }
+
+    // error[E0308]: implementation of `Foo` does not apply to enough lifetimes
+    //   --> /home/nmatsakis/tmp/foo.rs:12:5
+    //    |
+    // 12 |     all::<&'static u32>();
+    //    |     ^^^^^^^^^^^^^^^^^^^ lifetime mismatch
+    //    |
+    //    = note: Due to a where-clause on the function `all`,
+    //    = note: `T` must implement `...` for any two lifetimes `'1` and `'2`.
+    //    = note: However, the type `T` only implements `...` for some specific lifetime `'2`.
+    #[instrument(level = "debug", skip(self))]
+    fn report_trait_placeholder_mismatch(
+        &self,
+        vid: Option<Region<'tcx>>,
+        cause: &ObligationCause<'tcx>,
+        sub_placeholder: Option<Region<'tcx>>,
+        sup_placeholder: Option<Region<'tcx>>,
+        trait_def_id: DefId,
+        expected_args: GenericArgsRef<'tcx>,
+        actual_args: GenericArgsRef<'tcx>,
+    ) -> Diag<'tcx> {
+        let span = cause.span();
+
+        let (leading_ellipsis, satisfy_span, where_span, dup_span, def_id) =
+            if let ObligationCauseCode::WhereClause(def_id, span)
+            | ObligationCauseCode::WhereClauseInExpr(def_id, span, ..) = *cause.code()
+                && def_id != CRATE_DEF_ID.to_def_id()
+            {
+                (
+                    true,
+                    Some(span),
+                    Some(self.tcx().def_span(def_id)),
+                    None,
+                    self.tcx().def_path_str(def_id),
+                )
+            } else {
+                (false, None, None, Some(span), String::new())
+            };
+
+        let expected_trait_ref = self.cx.resolve_vars_if_possible(ty::TraitRef::new_from_args(
+            self.cx.tcx,
+            trait_def_id,
+            expected_args,
+        ));
+        let actual_trait_ref = self.cx.resolve_vars_if_possible(ty::TraitRef::new_from_args(
+            self.cx.tcx,
+            trait_def_id,
+            actual_args,
+        ));
+
+        // Search the expected and actual trait references to see (a)
+        // whether the sub/sup placeholders appear in them (sometimes
+        // you have a trait ref like `T: Foo<fn(&u8)>`, where the
+        // placeholder was created as part of an inner type) and (b)
+        // whether the inference variable appears. In each case,
+        // assign a counter value in each case if so.
+        let mut counter = 0;
+        let mut has_sub = None;
+        let mut has_sup = None;
+
+        let mut actual_has_vid = None;
+        let mut expected_has_vid = None;
+
+        self.tcx().for_each_free_region(&expected_trait_ref, |r| {
+            if Some(r) == sub_placeholder && has_sub.is_none() {
+                has_sub = Some(counter);
+                counter += 1;
+            } else if Some(r) == sup_placeholder && has_sup.is_none() {
+                has_sup = Some(counter);
+                counter += 1;
+            }
+
+            if Some(r) == vid && expected_has_vid.is_none() {
+                expected_has_vid = Some(counter);
+                counter += 1;
+            }
+        });
+
+        self.tcx().for_each_free_region(&actual_trait_ref, |r| {
+            if Some(r) == vid && actual_has_vid.is_none() {
+                actual_has_vid = Some(counter);
+                counter += 1;
+            }
+        });
+
+        let actual_self_ty_has_vid =
+            self.tcx().any_free_region_meets(&actual_trait_ref.self_ty(), |r| Some(r) == vid);
+
+        let expected_self_ty_has_vid =
+            self.tcx().any_free_region_meets(&expected_trait_ref.self_ty(), |r| Some(r) == vid);
+
+        let any_self_ty_has_vid = actual_self_ty_has_vid || expected_self_ty_has_vid;
+
+        debug!(
+            ?actual_has_vid,
+            ?expected_has_vid,
+            ?has_sub,
+            ?has_sup,
+            ?actual_self_ty_has_vid,
+            ?expected_self_ty_has_vid,
+        );
+
+        let actual_impl_expl_notes = self.explain_actual_impl_that_was_found(
+            sub_placeholder,
+            sup_placeholder,
+            has_sub,
+            has_sup,
+            expected_trait_ref,
+            actual_trait_ref,
+            vid,
+            expected_has_vid,
+            actual_has_vid,
+            any_self_ty_has_vid,
+            leading_ellipsis,
+        );
+
+        self.tcx().dcx().create_err(TraitPlaceholderMismatch {
+            span,
+            satisfy_span,
+            where_span,
+            dup_span,
+            def_id,
+            trait_def_id: self.tcx().def_path_str(trait_def_id),
+            actual_impl_expl_notes,
+        })
+    }
+
+    /// Add notes with details about the expected and actual trait refs, with attention to cases
+    /// when placeholder regions are involved: either the trait or the self type containing
+    /// them needs to be mentioned the closest to the placeholders.
+    /// This makes the error messages read better, however at the cost of some complexity
+    /// due to the number of combinations we have to deal with.
+    fn explain_actual_impl_that_was_found(
+        &self,
+        sub_placeholder: Option<Region<'tcx>>,
+        sup_placeholder: Option<Region<'tcx>>,
+        has_sub: Option<usize>,
+        has_sup: Option<usize>,
+        expected_trait_ref: ty::TraitRef<'tcx>,
+        actual_trait_ref: ty::TraitRef<'tcx>,
+        vid: Option<Region<'tcx>>,
+        expected_has_vid: Option<usize>,
+        actual_has_vid: Option<usize>,
+        any_self_ty_has_vid: bool,
+        leading_ellipsis: bool,
+    ) -> Vec<ActualImplExplNotes<'tcx>> {
+        // The weird thing here with the `maybe_highlighting_region` calls and the
+        // the match inside is meant to be like this:
+        //
+        // - The match checks whether the given things (placeholders, etc) appear
+        //   in the types are about to print
+        // - Meanwhile, the `maybe_highlighting_region` calls set up
+        //   highlights so that, if they do appear, we will replace
+        //   them `'0` and whatever. (This replacement takes place
+        //   inside the closure given to `maybe_highlighting_region`.)
+        //
+        // There is some duplication between the calls -- i.e., the
+        // `maybe_highlighting_region` checks if (e.g.) `has_sub` is
+        // None, an then we check again inside the closure, but this
+        // setup sort of minimized the number of calls and so form.
+
+        let highlight_trait_ref = |trait_ref| Highlighted {
+            tcx: self.tcx(),
+            highlight: RegionHighlightMode::default(),
+            value: trait_ref,
+        };
+
+        let same_self_type = actual_trait_ref.self_ty() == expected_trait_ref.self_ty();
+
+        let mut expected_trait_ref = highlight_trait_ref(expected_trait_ref);
+        expected_trait_ref.highlight.maybe_highlighting_region(sub_placeholder, has_sub);
+        expected_trait_ref.highlight.maybe_highlighting_region(sup_placeholder, has_sup);
+
+        let passive_voice = match (has_sub, has_sup) {
+            (Some(_), _) | (_, Some(_)) => any_self_ty_has_vid,
+            (None, None) => {
+                expected_trait_ref.highlight.maybe_highlighting_region(vid, expected_has_vid);
+                match expected_has_vid {
+                    Some(_) => true,
+                    None => any_self_ty_has_vid,
+                }
+            }
+        };
+
+        let (kind, ty_or_sig, trait_path) = if same_self_type {
+            let mut self_ty = expected_trait_ref.map(|tr| tr.self_ty());
+            self_ty.highlight.maybe_highlighting_region(vid, actual_has_vid);
+
+            if self_ty.value.is_closure() && self.tcx().is_fn_trait(expected_trait_ref.value.def_id)
+            {
+                let closure_sig = self_ty.map(|closure| {
+                    if let ty::Closure(_, args) = closure.kind() {
+                        self.tcx()
+                            .signature_unclosure(args.as_closure().sig(), rustc_hir::Safety::Safe)
+                    } else {
+                        bug!("type is not longer closure");
+                    }
+                });
+                (
+                    ActualImplExpectedKind::Signature,
+                    TyOrSig::ClosureSig(closure_sig),
+                    expected_trait_ref.map(|tr| tr.print_only_trait_path()),
+                )
+            } else {
+                (
+                    ActualImplExpectedKind::Other,
+                    TyOrSig::Ty(self_ty),
+                    expected_trait_ref.map(|tr| tr.print_only_trait_path()),
+                )
+            }
+        } else if passive_voice {
+            (
+                ActualImplExpectedKind::Passive,
+                TyOrSig::Ty(expected_trait_ref.map(|tr| tr.self_ty())),
+                expected_trait_ref.map(|tr| tr.print_only_trait_path()),
+            )
+        } else {
+            (
+                ActualImplExpectedKind::Other,
+                TyOrSig::Ty(expected_trait_ref.map(|tr| tr.self_ty())),
+                expected_trait_ref.map(|tr| tr.print_only_trait_path()),
+            )
+        };
+
+        let (lt_kind, lifetime_1, lifetime_2) = match (has_sub, has_sup) {
+            (Some(n1), Some(n2)) => {
+                (ActualImplExpectedLifetimeKind::Two, std::cmp::min(n1, n2), std::cmp::max(n1, n2))
+            }
+            (Some(n), _) | (_, Some(n)) => (ActualImplExpectedLifetimeKind::Any, n, 0),
+            (None, None) => {
+                if let Some(n) = expected_has_vid {
+                    (ActualImplExpectedLifetimeKind::Some, n, 0)
+                } else {
+                    (ActualImplExpectedLifetimeKind::Nothing, 0, 0)
+                }
+            }
+        };
+
+        let note_1 = ActualImplExplNotes::new_expected(
+            kind,
+            lt_kind,
+            leading_ellipsis,
+            ty_or_sig,
+            trait_path,
+            lifetime_1,
+            lifetime_2,
+        );
+
+        let mut actual_trait_ref = highlight_trait_ref(actual_trait_ref);
+        actual_trait_ref.highlight.maybe_highlighting_region(vid, actual_has_vid);
+
+        let passive_voice = match actual_has_vid {
+            Some(_) => any_self_ty_has_vid,
+            None => true,
+        };
+
+        let trait_path = actual_trait_ref.map(|tr| tr.print_only_trait_path());
+        let ty = actual_trait_ref.map(|tr| tr.self_ty()).to_string();
+        let has_lifetime = actual_has_vid.is_some();
+        let lifetime = actual_has_vid.unwrap_or_default();
+
+        let note_2 = if same_self_type {
+            ActualImplExplNotes::ButActuallyImplementsTrait { trait_path, has_lifetime, lifetime }
+        } else if passive_voice {
+            ActualImplExplNotes::ButActuallyImplementedForTy {
+                trait_path,
+                ty,
+                has_lifetime,
+                lifetime,
+            }
+        } else {
+            ActualImplExplNotes::ButActuallyTyImplements { trait_path, ty, has_lifetime, lifetime }
+        };
+
+        vec![note_1, note_2]
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs
new file mode 100644
index 00000000000..f2a7da707b8
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs
@@ -0,0 +1,87 @@
+use rustc_data_structures::intern::Interned;
+use rustc_errors::Diag;
+use rustc_middle::ty::{self, RePlaceholder, Region};
+
+use crate::error_reporting::infer::nice_region_error::NiceRegionError;
+use crate::errors::PlaceholderRelationLfNotSatisfied;
+use crate::infer::{RegionResolutionError, SubregionOrigin};
+
+impl<'tcx> NiceRegionError<'_, 'tcx> {
+    /// Emitted wwhen given a `ConcreteFailure` when relating two placeholders.
+    pub(super) fn try_report_placeholder_relation(&self) -> Option<Diag<'tcx>> {
+        match &self.error {
+            Some(RegionResolutionError::ConcreteFailure(
+                SubregionOrigin::RelateRegionParamBound(span, _),
+                Region(Interned(
+                    RePlaceholder(ty::Placeholder {
+                        bound: ty::BoundRegion { kind: sub_name, .. },
+                        ..
+                    }),
+                    _,
+                )),
+                Region(Interned(
+                    RePlaceholder(ty::Placeholder {
+                        bound: ty::BoundRegion { kind: sup_name, .. },
+                        ..
+                    }),
+                    _,
+                )),
+            )) => {
+                let span = *span;
+                let (sub_span, sub_symbol) = match sub_name {
+                    ty::BrNamed(def_id, symbol) => {
+                        (Some(self.tcx().def_span(def_id)), Some(symbol))
+                    }
+                    ty::BrAnon | ty::BrEnv => (None, None),
+                };
+                let (sup_span, sup_symbol) = match sup_name {
+                    ty::BrNamed(def_id, symbol) => {
+                        (Some(self.tcx().def_span(def_id)), Some(symbol))
+                    }
+                    ty::BrAnon | ty::BrEnv => (None, None),
+                };
+                let diag = match (sub_span, sup_span, sub_symbol, sup_symbol) {
+                    (Some(sub_span), Some(sup_span), Some(&sub_symbol), Some(&sup_symbol)) => {
+                        PlaceholderRelationLfNotSatisfied::HasBoth {
+                            span,
+                            sub_span,
+                            sup_span,
+                            sub_symbol,
+                            sup_symbol,
+                            note: (),
+                        }
+                    }
+                    (Some(sub_span), Some(sup_span), _, Some(&sup_symbol)) => {
+                        PlaceholderRelationLfNotSatisfied::HasSup {
+                            span,
+                            sub_span,
+                            sup_span,
+                            sup_symbol,
+                            note: (),
+                        }
+                    }
+                    (Some(sub_span), Some(sup_span), Some(&sub_symbol), _) => {
+                        PlaceholderRelationLfNotSatisfied::HasSub {
+                            span,
+                            sub_span,
+                            sup_span,
+                            sub_symbol,
+                            note: (),
+                        }
+                    }
+                    (Some(sub_span), Some(sup_span), _, _) => {
+                        PlaceholderRelationLfNotSatisfied::HasNone {
+                            span,
+                            sub_span,
+                            sup_span,
+                            note: (),
+                        }
+                    }
+                    _ => PlaceholderRelationLfNotSatisfied::OnlyPrimarySpan { span, note: () },
+                };
+                Some(self.tcx().dcx().create_err(diag))
+            }
+            _ => None,
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
new file mode 100644
index 00000000000..95ebeab13ef
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs
@@ -0,0 +1,618 @@
+//! Error Reporting for static impl Traits.
+
+use rustc_data_structures::fx::FxIndexSet;
+use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, Subdiagnostic};
+use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{walk_ty, Visitor};
+use rustc_hir::{
+    self as hir, GenericBound, GenericParam, GenericParamKind, Item, ItemKind, Lifetime,
+    LifetimeName, LifetimeParamKind, MissingLifetimeKind, Node, TyKind,
+};
+use rustc_middle::ty::{
+    self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
+};
+use rustc_span::def_id::LocalDefId;
+use rustc_span::symbol::Ident;
+use rustc_span::Span;
+use tracing::debug;
+
+use crate::error_reporting::infer::nice_region_error::NiceRegionError;
+use crate::errors::{
+    ButCallingIntroduces, ButNeedsToSatisfy, DynTraitConstraintSuggestion, MoreTargeted,
+    ReqIntroducedLocations,
+};
+use crate::infer::{RegionResolutionError, SubregionOrigin, TypeTrace};
+use crate::traits::{ObligationCauseCode, UnifyReceiverContext};
+
+impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
+    /// Print the error message for lifetime errors when the return type is a static `impl Trait`,
+    /// `dyn Trait` or if a method call on a trait object introduces a static requirement.
+    pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorGuaranteed> {
+        debug!("try_report_static_impl_trait(error={:?})", self.error);
+        let tcx = self.tcx();
+        let (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans) = match self.error.as_ref()? {
+            RegionResolutionError::SubSupConflict(
+                _,
+                var_origin,
+                sub_origin,
+                sub_r,
+                sup_origin,
+                sup_r,
+                spans,
+            ) if sub_r.is_static() => (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans),
+            RegionResolutionError::ConcreteFailure(
+                SubregionOrigin::Subtype(box TypeTrace { cause, .. }),
+                sub_r,
+                sup_r,
+            ) if sub_r.is_static() => {
+                // This is for an implicit `'static` requirement coming from `impl dyn Trait {}`.
+                if let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code() {
+                    // This may have a closure and it would cause ICE
+                    // through `find_param_with_region` (#78262).
+                    let anon_reg_sup = tcx.is_suitable_region(self.generic_param_scope, *sup_r)?;
+                    let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
+                    if fn_returns.is_empty() {
+                        return None;
+                    }
+
+                    let param = self.find_param_with_region(*sup_r, *sub_r)?;
+                    let simple_ident = param.param.pat.simple_ident();
+
+                    let (has_impl_path, impl_path) = match ctxt.assoc_item.container {
+                        AssocItemContainer::TraitContainer => {
+                            let id = ctxt.assoc_item.container_id(tcx);
+                            (true, tcx.def_path_str(id))
+                        }
+                        AssocItemContainer::ImplContainer => (false, String::new()),
+                    };
+
+                    let mut err = self.tcx().dcx().create_err(ButCallingIntroduces {
+                        param_ty_span: param.param_ty_span,
+                        cause_span: cause.span,
+                        has_param_name: simple_ident.is_some(),
+                        param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(),
+                        has_lifetime: sup_r.has_name(),
+                        lifetime: sup_r.to_string(),
+                        assoc_item: ctxt.assoc_item.name,
+                        has_impl_path,
+                        impl_path,
+                    });
+                    if self.find_impl_on_dyn_trait(&mut err, param.param_ty, ctxt) {
+                        let reported = err.emit();
+                        return Some(reported);
+                    } else {
+                        err.cancel()
+                    }
+                }
+                return None;
+            }
+            _ => return None,
+        };
+        debug!(
+            "try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})",
+            var_origin, sub_origin, sub_r, sup_origin, sup_r
+        );
+        let anon_reg_sup = tcx.is_suitable_region(self.generic_param_scope, *sup_r)?;
+        debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup);
+        let sp = var_origin.span();
+        let return_sp = sub_origin.span();
+        let param = self.find_param_with_region(*sup_r, *sub_r)?;
+        let simple_ident = param.param.pat.simple_ident();
+        let lifetime_name = if sup_r.has_name() { sup_r.to_string() } else { "'_".to_owned() };
+
+        let (mention_influencer, influencer_point) =
+            if sup_origin.span().overlaps(param.param_ty_span) {
+                // Account for `async fn` like in `async-await/issues/issue-62097.rs`.
+                // The desugaring of `async fn`s causes `sup_origin` and `param` to point at the same
+                // place (but with different `ctxt`, hence `overlaps` instead of `==` above).
+                //
+                // This avoids the following:
+                //
+                // LL |     pub async fn run_dummy_fn(&self) {
+                //    |                               ^^^^^
+                //    |                               |
+                //    |                               this data with an anonymous lifetime `'_`...
+                //    |                               ...is captured here...
+                (false, sup_origin.span())
+            } else {
+                (!sup_origin.span().overlaps(return_sp), param.param_ty_span)
+            };
+
+        debug!("try_report_static_impl_trait: param_info={:?}", param);
+
+        let mut spans = spans.clone();
+
+        if mention_influencer {
+            spans.push(sup_origin.span());
+        }
+        // We dedup the spans *ignoring* expansion context.
+        spans.sort();
+        spans.dedup_by_key(|span| (span.lo(), span.hi()));
+
+        // We try to make the output have fewer overlapping spans if possible.
+        let require_span =
+            if sup_origin.span().overlaps(return_sp) { sup_origin.span() } else { return_sp };
+
+        let spans_empty = spans.is_empty();
+        let require_as_note = spans.iter().any(|sp| sp.overlaps(return_sp) || *sp > return_sp);
+        let bound = if let SubregionOrigin::RelateParamBound(_, _, Some(bound)) = sub_origin {
+            Some(*bound)
+        } else {
+            None
+        };
+
+        let mut subdiag = None;
+
+        if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = sub_origin {
+            if let ObligationCauseCode::ReturnValue(hir_id)
+            | ObligationCauseCode::BlockTailExpression(hir_id, ..) = cause.code()
+            {
+                let parent_id = tcx.hir().get_parent_item(*hir_id);
+                if let Some(fn_decl) = tcx.hir().fn_decl_by_hir_id(parent_id.into()) {
+                    let mut span: MultiSpan = fn_decl.output.span().into();
+                    let mut spans = Vec::new();
+                    let mut add_label = true;
+                    if let hir::FnRetTy::Return(ty) = fn_decl.output {
+                        let mut v = StaticLifetimeVisitor(vec![], tcx.hir());
+                        v.visit_ty(ty);
+                        if !v.0.is_empty() {
+                            span = v.0.clone().into();
+                            spans = v.0;
+                            add_label = false;
+                        }
+                    }
+                    let fn_decl_span = fn_decl.output.span();
+
+                    subdiag = Some(ReqIntroducedLocations {
+                        span,
+                        spans,
+                        fn_decl_span,
+                        cause_span: cause.span,
+                        add_label,
+                    });
+                }
+            }
+        }
+
+        let diag = ButNeedsToSatisfy {
+            sp,
+            influencer_point,
+            spans: spans.clone(),
+            // If any of the "captured here" labels appears on the same line or after
+            // `require_span`, we put it on a note to ensure the text flows by appearing
+            // always at the end.
+            require_span_as_note: require_as_note.then_some(require_span),
+            // We don't need a note, it's already at the end, it can be shown as a `span_label`.
+            require_span_as_label: (!require_as_note).then_some(require_span),
+            req_introduces_loc: subdiag,
+
+            has_lifetime: sup_r.has_name(),
+            lifetime: lifetime_name.clone(),
+            has_param_name: simple_ident.is_some(),
+            param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(),
+            spans_empty,
+            bound,
+        };
+
+        let mut err = self.tcx().dcx().create_err(diag);
+
+        let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
+
+        let mut override_error_code = None;
+        if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sup_origin
+            && let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code()
+            // Handle case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a
+            // `'static` lifetime when called as a method on a binding: `bar.qux()`.
+            && self.find_impl_on_dyn_trait(&mut err, param.param_ty, ctxt)
+        {
+            override_error_code = Some(ctxt.assoc_item.name);
+        }
+
+        if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sub_origin
+            && let code = match cause.code() {
+                ObligationCauseCode::MatchImpl(parent, ..) => parent.code(),
+                _ => cause.code(),
+            }
+            && let (
+                &ObligationCauseCode::WhereClause(item_def_id, _)
+                | &ObligationCauseCode::WhereClauseInExpr(item_def_id, ..),
+                None,
+            ) = (code, override_error_code)
+        {
+            // Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static`
+            // lifetime as above, but called using a fully-qualified path to the method:
+            // `Foo::qux(bar)`.
+            let mut v = TraitObjectVisitor(FxIndexSet::default());
+            v.visit_ty(param.param_ty);
+            if let Some((ident, self_ty)) =
+                NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, item_def_id, &v.0)
+                && self.suggest_constrain_dyn_trait_in_impl(&mut err, &v.0, ident, self_ty)
+            {
+                override_error_code = Some(ident.name);
+            }
+        }
+        if let (Some(ident), true) = (override_error_code, fn_returns.is_empty()) {
+            // Provide a more targeted error code and description.
+            let retarget_subdiag = MoreTargeted { ident };
+            retarget_subdiag.add_to_diag(&mut err);
+        }
+
+        let arg = match param.param.pat.simple_ident() {
+            Some(simple_ident) => format!("argument `{simple_ident}`"),
+            None => "the argument".to_string(),
+        };
+        let captures = format!("captures data from {arg}");
+        suggest_new_region_bound(
+            tcx,
+            &mut err,
+            fn_returns,
+            lifetime_name,
+            Some(arg),
+            captures,
+            Some((param.param_ty_span, param.param_ty.to_string())),
+            Some(anon_reg_sup.def_id),
+        );
+
+        let reported = err.emit();
+        Some(reported)
+    }
+}
+
+pub fn suggest_new_region_bound(
+    tcx: TyCtxt<'_>,
+    err: &mut Diag<'_>,
+    fn_returns: Vec<&rustc_hir::Ty<'_>>,
+    lifetime_name: String,
+    arg: Option<String>,
+    captures: String,
+    param: Option<(Span, String)>,
+    scope_def_id: Option<LocalDefId>,
+) {
+    debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns);
+    // FIXME: account for the need of parens in `&(dyn Trait + '_)`
+    let consider = "consider changing";
+    let declare = "to declare that";
+    let explicit = format!("you can add an explicit `{lifetime_name}` lifetime bound");
+    let explicit_static =
+        arg.map(|arg| format!("explicit `'static` bound to the lifetime of {arg}"));
+    let add_static_bound = "alternatively, add an explicit `'static` bound to this reference";
+    let plus_lt = format!(" + {lifetime_name}");
+    for fn_return in fn_returns {
+        if fn_return.span.desugaring_kind().is_some() {
+            // Skip `async` desugaring `impl Future`.
+            continue;
+        }
+        match fn_return.kind {
+            // FIXME(precise_captures): Suggest adding to `use<...>` list instead.
+            TyKind::OpaqueDef(item_id, _, _) => {
+                let item = tcx.hir().item(item_id);
+                let ItemKind::OpaqueTy(opaque) = &item.kind else {
+                    return;
+                };
+
+                // Get the identity type for this RPIT
+                let did = item_id.owner_id.to_def_id();
+                let ty = Ty::new_opaque(tcx, did, ty::GenericArgs::identity_for_item(tcx, did));
+
+                if let Some(span) = opaque.bounds.iter().find_map(|arg| match arg {
+                    GenericBound::Outlives(Lifetime {
+                        res: LifetimeName::Static, ident, ..
+                    }) => Some(ident.span),
+                    _ => None,
+                }) {
+                    if let Some(explicit_static) = &explicit_static {
+                        err.span_suggestion_verbose(
+                            span,
+                            format!("{consider} `{ty}`'s {explicit_static}"),
+                            &lifetime_name,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                    if let Some((param_span, ref param_ty)) = param {
+                        err.span_suggestion_verbose(
+                            param_span,
+                            add_static_bound,
+                            param_ty,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                } else if opaque.bounds.iter().any(|arg| {
+                    matches!(arg,
+                        GenericBound::Outlives(Lifetime { ident, .. })
+                        if ident.name.to_string() == lifetime_name )
+                }) {
+                } else {
+                    // get a lifetime name of existing named lifetimes if any
+                    let existing_lt_name = if let Some(id) = scope_def_id
+                        && let Some(generics) = tcx.hir().get_generics(id)
+                        && let named_lifetimes = generics
+                            .params
+                            .iter()
+                            .filter(|p| {
+                                matches!(
+                                    p.kind,
+                                    GenericParamKind::Lifetime {
+                                        kind: hir::LifetimeParamKind::Explicit
+                                    }
+                                )
+                            })
+                            .map(|p| {
+                                if let hir::ParamName::Plain(name) = p.name {
+                                    Some(name.to_string())
+                                } else {
+                                    None
+                                }
+                            })
+                            .filter(|n| !matches!(n, None))
+                            .collect::<Vec<_>>()
+                        && named_lifetimes.len() > 0
+                    {
+                        named_lifetimes[0].clone()
+                    } else {
+                        None
+                    };
+                    let name = if let Some(name) = &existing_lt_name { name } else { "'a" };
+                    // if there are more than one elided lifetimes in inputs, the explicit `'_` lifetime cannot be used.
+                    // introducing a new lifetime `'a` or making use of one from existing named lifetimes if any
+                    if let Some(id) = scope_def_id
+                        && let Some(generics) = tcx.hir().get_generics(id)
+                        && let mut spans_suggs =
+                            make_elided_region_spans_suggs(name, generics.params.iter())
+                        && spans_suggs.len() > 1
+                    {
+                        let use_lt = if existing_lt_name == None {
+                            spans_suggs.push((generics.span.shrink_to_hi(), format!("<{name}>")));
+                            format!("you can introduce a named lifetime parameter `{name}`")
+                        } else {
+                            // make use the existing named lifetime
+                            format!("you can use the named lifetime parameter `{name}`")
+                        };
+                        spans_suggs.push((fn_return.span.shrink_to_hi(), format!(" + {name} ")));
+                        err.multipart_suggestion_verbose(
+                            format!("{declare} `{ty}` {captures}, {use_lt}",),
+                            spans_suggs,
+                            Applicability::MaybeIncorrect,
+                        );
+                    } else {
+                        err.span_suggestion_verbose(
+                            fn_return.span.shrink_to_hi(),
+                            format!("{declare} `{ty}` {captures}, {explicit}",),
+                            &plus_lt,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+            }
+            TyKind::TraitObject(_, lt, _) => {
+                if let LifetimeName::ImplicitObjectLifetimeDefault = lt.res {
+                    err.span_suggestion_verbose(
+                        fn_return.span.shrink_to_hi(),
+                        format!("{declare} the trait object {captures}, {explicit}",),
+                        &plus_lt,
+                        Applicability::MaybeIncorrect,
+                    );
+                } else if lt.ident.name.to_string() != lifetime_name {
+                    // With this check we avoid suggesting redundant bounds. This
+                    // would happen if there are nested impl/dyn traits and only
+                    // one of them has the bound we'd suggest already there, like
+                    // in `impl Foo<X = dyn Bar> + '_`.
+                    if let Some(explicit_static) = &explicit_static {
+                        err.span_suggestion_verbose(
+                            lt.ident.span,
+                            format!("{consider} the trait object's {explicit_static}"),
+                            &lifetime_name,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                    if let Some((param_span, param_ty)) = param.clone() {
+                        err.span_suggestion_verbose(
+                            param_span,
+                            add_static_bound,
+                            param_ty,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+            }
+            _ => {}
+        }
+    }
+}
+
+fn make_elided_region_spans_suggs<'a>(
+    name: &str,
+    generic_params: impl Iterator<Item = &'a GenericParam<'a>>,
+) -> Vec<(Span, String)> {
+    let mut spans_suggs = Vec::new();
+    let mut bracket_span = None;
+    let mut consecutive_brackets = 0;
+
+    let mut process_consecutive_brackets =
+        |span: Option<Span>, spans_suggs: &mut Vec<(Span, String)>| {
+            if span
+                .is_some_and(|span| bracket_span.map_or(true, |bracket_span| span == bracket_span))
+            {
+                consecutive_brackets += 1;
+            } else if let Some(bracket_span) = bracket_span.take() {
+                let sugg = std::iter::once("<")
+                    .chain(std::iter::repeat(name).take(consecutive_brackets).intersperse(", "))
+                    .chain([">"])
+                    .collect();
+                spans_suggs.push((bracket_span.shrink_to_hi(), sugg));
+                consecutive_brackets = 0;
+            }
+            bracket_span = span;
+        };
+
+    for p in generic_params {
+        if let GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided(kind) } = p.kind {
+            match kind {
+                MissingLifetimeKind::Underscore => {
+                    process_consecutive_brackets(None, &mut spans_suggs);
+                    spans_suggs.push((p.span, name.to_string()))
+                }
+                MissingLifetimeKind::Ampersand => {
+                    process_consecutive_brackets(None, &mut spans_suggs);
+                    spans_suggs.push((p.span.shrink_to_hi(), format!("{name} ")));
+                }
+                MissingLifetimeKind::Comma => {
+                    process_consecutive_brackets(None, &mut spans_suggs);
+                    spans_suggs.push((p.span.shrink_to_hi(), format!("{name}, ")));
+                }
+                MissingLifetimeKind::Brackets => {
+                    process_consecutive_brackets(Some(p.span), &mut spans_suggs);
+                }
+            }
+        }
+    }
+    process_consecutive_brackets(None, &mut spans_suggs);
+
+    spans_suggs
+}
+
+impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
+    pub fn get_impl_ident_and_self_ty_from_trait(
+        tcx: TyCtxt<'tcx>,
+        def_id: DefId,
+        trait_objects: &FxIndexSet<DefId>,
+    ) -> Option<(Ident, &'tcx hir::Ty<'tcx>)> {
+        match tcx.hir().get_if_local(def_id)? {
+            Node::ImplItem(impl_item) => {
+                let impl_did = tcx.hir().get_parent_item(impl_item.hir_id());
+                if let hir::OwnerNode::Item(Item {
+                    kind: ItemKind::Impl(hir::Impl { self_ty, .. }),
+                    ..
+                }) = tcx.hir_owner_node(impl_did)
+                {
+                    Some((impl_item.ident, self_ty))
+                } else {
+                    None
+                }
+            }
+            Node::TraitItem(trait_item) => {
+                let trait_id = tcx.hir().get_parent_item(trait_item.hir_id());
+                debug_assert_eq!(tcx.def_kind(trait_id.def_id), hir::def::DefKind::Trait);
+                // The method being called is defined in the `trait`, but the `'static`
+                // obligation comes from the `impl`. Find that `impl` so that we can point
+                // at it in the suggestion.
+                let trait_did = trait_id.to_def_id();
+                tcx.hir().trait_impls(trait_did).iter().find_map(|&impl_did| {
+                    if let Node::Item(Item {
+                        kind: ItemKind::Impl(hir::Impl { self_ty, .. }), ..
+                    }) = tcx.hir_node_by_def_id(impl_did)
+                        && trait_objects.iter().all(|did| {
+                            // FIXME: we should check `self_ty` against the receiver
+                            // type in the `UnifyReceiver` context, but for now, use
+                            // this imperfect proxy. This will fail if there are
+                            // multiple `impl`s for the same trait like
+                            // `impl Foo for Box<dyn Bar>` and `impl Foo for dyn Bar`.
+                            // In that case, only the first one will get suggestions.
+                            let mut traits = vec![];
+                            let mut hir_v = HirTraitObjectVisitor(&mut traits, *did);
+                            hir_v.visit_ty(self_ty);
+                            !traits.is_empty()
+                        })
+                    {
+                        Some((trait_item.ident, *self_ty))
+                    } else {
+                        None
+                    }
+                })
+            }
+            _ => None,
+        }
+    }
+
+    /// When we call a method coming from an `impl Foo for dyn Bar`, `dyn Bar` introduces a default
+    /// `'static` obligation. Suggest relaxing that implicit bound.
+    fn find_impl_on_dyn_trait(
+        &self,
+        err: &mut Diag<'_>,
+        ty: Ty<'_>,
+        ctxt: &UnifyReceiverContext<'tcx>,
+    ) -> bool {
+        let tcx = self.tcx();
+
+        // Find the method being called.
+        let Ok(Some(instance)) = ty::Instance::try_resolve(
+            tcx,
+            ctxt.param_env,
+            ctxt.assoc_item.def_id,
+            self.cx.resolve_vars_if_possible(ctxt.args),
+        ) else {
+            return false;
+        };
+
+        let mut v = TraitObjectVisitor(FxIndexSet::default());
+        v.visit_ty(ty);
+
+        // Get the `Ident` of the method being called and the corresponding `impl` (to point at
+        // `Bar` in `impl Foo for dyn Bar {}` and the definition of the method being called).
+        let Some((ident, self_ty)) =
+            NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, instance.def_id(), &v.0)
+        else {
+            return false;
+        };
+
+        // Find the trait object types in the argument, so we point at *only* the trait object.
+        self.suggest_constrain_dyn_trait_in_impl(err, &v.0, ident, self_ty)
+    }
+
+    fn suggest_constrain_dyn_trait_in_impl(
+        &self,
+        err: &mut Diag<'_>,
+        found_dids: &FxIndexSet<DefId>,
+        ident: Ident,
+        self_ty: &hir::Ty<'_>,
+    ) -> bool {
+        let mut suggested = false;
+        for found_did in found_dids {
+            let mut traits = vec![];
+            let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did);
+            hir_v.visit_ty(self_ty);
+            for &span in &traits {
+                let subdiag = DynTraitConstraintSuggestion { span, ident };
+                subdiag.add_to_diag(err);
+                suggested = true;
+            }
+        }
+        suggested
+    }
+}
+
+/// Collect all the trait objects in a type that could have received an implicit `'static` lifetime.
+pub struct TraitObjectVisitor(pub FxIndexSet<DefId>);
+
+impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for TraitObjectVisitor {
+    fn visit_ty(&mut self, t: Ty<'tcx>) {
+        match t.kind() {
+            ty::Dynamic(preds, re, _) if re.is_static() => {
+                if let Some(def_id) = preds.principal_def_id() {
+                    self.0.insert(def_id);
+                }
+            }
+            _ => t.super_visit_with(self),
+        }
+    }
+}
+
+/// Collect all `hir::Ty<'_>` `Span`s for trait objects with an implicit lifetime.
+pub struct HirTraitObjectVisitor<'a>(pub &'a mut Vec<Span>, pub DefId);
+
+impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> {
+    fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
+        if let TyKind::TraitObject(
+            poly_trait_refs,
+            Lifetime { res: LifetimeName::ImplicitObjectLifetimeDefault, .. },
+            _,
+        ) = t.kind
+        {
+            for (ptr, _) in poly_trait_refs {
+                if Some(self.1) == ptr.trait_ref.trait_def_id() {
+                    self.0.push(ptr.span);
+                }
+            }
+        }
+        walk_ty(self, t);
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs
new file mode 100644
index 00000000000..592ade8ede2
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs
@@ -0,0 +1,163 @@
+//! Error Reporting for `impl` items that do not match the obligations from their `trait`.
+
+use rustc_errors::ErrorGuaranteed;
+use rustc_hir as hir;
+use rustc_hir::def::Res;
+use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::Visitor;
+use rustc_middle::hir::nested_filter;
+use rustc_middle::traits::ObligationCauseCode;
+use rustc_middle::ty::error::ExpectedFound;
+use rustc_middle::ty::print::RegionHighlightMode;
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor};
+use rustc_span::Span;
+use tracing::debug;
+
+use crate::error_reporting::infer::nice_region_error::NiceRegionError;
+use crate::errors::{ConsiderBorrowingParamHelp, RelationshipHelp, TraitImplDiff};
+use crate::infer::{RegionResolutionError, Subtype, ValuePairs};
+
+impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
+    /// Print the error message for lifetime errors when the `impl` doesn't conform to the `trait`.
+    pub(super) fn try_report_impl_not_conforming_to_trait(&self) -> Option<ErrorGuaranteed> {
+        let error = self.error.as_ref()?;
+        debug!("try_report_impl_not_conforming_to_trait {:?}", error);
+        if let RegionResolutionError::SubSupConflict(
+            _,
+            var_origin,
+            sub_origin,
+            _sub,
+            sup_origin,
+            _sup,
+            _,
+        ) = error.clone()
+            && let (Subtype(sup_trace), Subtype(sub_trace)) = (&sup_origin, &sub_origin)
+            && let ObligationCauseCode::CompareImplItem { trait_item_def_id, .. } =
+                sub_trace.cause.code()
+            && sub_trace.values == sup_trace.values
+            && let ValuePairs::PolySigs(ExpectedFound { expected, found }) = sub_trace.values
+        {
+            // FIXME(compiler-errors): Don't like that this needs `Ty`s, but
+            // all of the region highlighting machinery only deals with those.
+            let guar = self.emit_err(
+                var_origin.span(),
+                Ty::new_fn_ptr(self.cx.tcx, expected),
+                Ty::new_fn_ptr(self.cx.tcx, found),
+                *trait_item_def_id,
+            );
+            return Some(guar);
+        }
+        None
+    }
+
+    fn emit_err(
+        &self,
+        sp: Span,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+        trait_def_id: DefId,
+    ) -> ErrorGuaranteed {
+        let trait_sp = self.tcx().def_span(trait_def_id);
+
+        // Mark all unnamed regions in the type with a number.
+        // This diagnostic is called in response to lifetime errors, so be informative.
+        struct HighlightBuilder<'tcx> {
+            highlight: RegionHighlightMode<'tcx>,
+            counter: usize,
+        }
+
+        impl<'tcx> HighlightBuilder<'tcx> {
+            fn build(ty: Ty<'tcx>) -> RegionHighlightMode<'tcx> {
+                let mut builder =
+                    HighlightBuilder { highlight: RegionHighlightMode::default(), counter: 1 };
+                builder.visit_ty(ty);
+                builder.highlight
+            }
+        }
+
+        impl<'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for HighlightBuilder<'tcx> {
+            fn visit_region(&mut self, r: ty::Region<'tcx>) {
+                if !r.has_name() && self.counter <= 3 {
+                    self.highlight.highlighting_region(r, self.counter);
+                    self.counter += 1;
+                }
+            }
+        }
+
+        let expected_highlight = HighlightBuilder::build(expected);
+        let expected = self
+            .cx
+            .extract_inference_diagnostics_data(expected.into(), Some(expected_highlight))
+            .name;
+        let found_highlight = HighlightBuilder::build(found);
+        let found =
+            self.cx.extract_inference_diagnostics_data(found.into(), Some(found_highlight)).name;
+
+        // Get the span of all the used type parameters in the method.
+        let assoc_item = self.tcx().associated_item(trait_def_id);
+        let mut visitor = TypeParamSpanVisitor { tcx: self.tcx(), types: vec![] };
+        match assoc_item.kind {
+            ty::AssocKind::Fn => {
+                let hir = self.tcx().hir();
+                if let Some(hir_id) =
+                    assoc_item.def_id.as_local().map(|id| self.tcx().local_def_id_to_hir_id(id))
+                {
+                    if let Some(decl) = hir.fn_decl_by_hir_id(hir_id) {
+                        visitor.visit_fn_decl(decl);
+                    }
+                }
+            }
+            _ => {}
+        }
+
+        let diag = TraitImplDiff {
+            sp,
+            trait_sp,
+            note: (),
+            param_help: ConsiderBorrowingParamHelp { spans: visitor.types.to_vec() },
+            rel_help: visitor.types.is_empty().then_some(RelationshipHelp),
+            expected,
+            found,
+        };
+
+        self.tcx().dcx().emit_err(diag)
+    }
+}
+
+struct TypeParamSpanVisitor<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    types: Vec<Span>,
+}
+
+impl<'tcx> Visitor<'tcx> for TypeParamSpanVisitor<'tcx> {
+    type NestedFilter = nested_filter::OnlyBodies;
+
+    fn nested_visit_map(&mut self) -> Self::Map {
+        self.tcx.hir()
+    }
+
+    fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
+        match arg.kind {
+            hir::TyKind::Ref(_, ref mut_ty) => {
+                // We don't want to suggest looking into borrowing `&T` or `&Self`.
+                hir::intravisit::walk_ty(self, mut_ty.ty);
+                return;
+            }
+            hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments {
+                [segment]
+                    if matches!(
+                        segment.res,
+                        Res::SelfTyParam { .. }
+                            | Res::SelfTyAlias { .. }
+                            | Res::Def(hir::def::DefKind::TyParam, _)
+                    ) =>
+                {
+                    self.types.push(path.span);
+                }
+                _ => {}
+            },
+            _ => {}
+        }
+        hir::intravisit::walk_ty(self, arg);
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs
new file mode 100644
index 00000000000..90b354305ff
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs
@@ -0,0 +1,167 @@
+//! Helper functions corresponding to lifetime errors due to
+//! anonymous regions.
+
+use rustc_hir as hir;
+use rustc_hir::def_id::LocalDefId;
+use rustc_middle::ty::{self, Binder, Region, Ty, TyCtxt, TypeFoldable};
+use rustc_span::Span;
+use tracing::instrument;
+
+use crate::error_reporting::infer::nice_region_error::NiceRegionError;
+
+/// Information about the anonymous region we are searching for.
+#[derive(Debug)]
+pub struct AnonymousParamInfo<'tcx> {
+    /// The parameter corresponding to the anonymous region.
+    pub param: &'tcx hir::Param<'tcx>,
+    /// The type corresponding to the anonymous region parameter.
+    pub param_ty: Ty<'tcx>,
+    /// The ty::BoundRegionKind corresponding to the anonymous region.
+    pub bound_region: ty::BoundRegionKind,
+    /// The `Span` of the parameter type.
+    pub param_ty_span: Span,
+    /// Signals that the argument is the first parameter in the declaration.
+    pub is_first: bool,
+}
+
+// This method walks the Type of the function body parameters using
+// `fold_regions()` function and returns the
+// &hir::Param of the function parameter corresponding to the anonymous
+// region and the Ty corresponding to the named region.
+// Currently only the case where the function declaration consists of
+// one named region and one anonymous region is handled.
+// Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32`
+// Here, we would return the hir::Param for y, we return the type &'a
+// i32, which is the type of y but with the anonymous region replaced
+// with 'a, the corresponding bound region and is_first which is true if
+// the hir::Param is the first parameter in the function declaration.
+#[instrument(skip(tcx), level = "debug")]
+pub fn find_param_with_region<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    generic_param_scope: LocalDefId,
+    anon_region: Region<'tcx>,
+    replace_region: Region<'tcx>,
+) -> Option<AnonymousParamInfo<'tcx>> {
+    let (id, bound_region) = match *anon_region {
+        ty::ReLateParam(late_param) => (late_param.scope, late_param.bound_region),
+        ty::ReEarlyParam(ebr) => {
+            let region_def = tcx.generics_of(generic_param_scope).region_param(ebr, tcx).def_id;
+            (tcx.parent(region_def), ty::BoundRegionKind::BrNamed(region_def, ebr.name))
+        }
+        _ => return None, // not a free region
+    };
+
+    let hir = &tcx.hir();
+    let def_id = id.as_local()?;
+
+    // FIXME: use def_kind
+    // Don't perform this on closures
+    match tcx.hir_node_by_def_id(generic_param_scope) {
+        hir::Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => {
+            return None;
+        }
+        _ => {}
+    }
+
+    let body = hir.maybe_body_owned_by(def_id)?;
+
+    let owner_id = hir.body_owner(body.id());
+    let fn_decl = hir.fn_decl_by_hir_id(owner_id)?;
+    let poly_fn_sig = tcx.fn_sig(id).instantiate_identity();
+
+    let fn_sig = tcx.liberate_late_bound_regions(id, poly_fn_sig);
+    body.params
+        .iter()
+        .take(if fn_sig.c_variadic {
+            fn_sig.inputs().len()
+        } else {
+            assert_eq!(fn_sig.inputs().len(), body.params.len());
+            body.params.len()
+        })
+        .enumerate()
+        .find_map(|(index, param)| {
+            // May return None; sometimes the tables are not yet populated.
+            let ty = fn_sig.inputs()[index];
+            let mut found_anon_region = false;
+            let new_param_ty = tcx.fold_regions(ty, |r, _| {
+                if r == anon_region {
+                    found_anon_region = true;
+                    replace_region
+                } else {
+                    r
+                }
+            });
+            found_anon_region.then(|| {
+                let ty_hir_id = fn_decl.inputs[index].hir_id;
+                let param_ty_span = hir.span(ty_hir_id);
+                let is_first = index == 0;
+                AnonymousParamInfo {
+                    param,
+                    param_ty: new_param_ty,
+                    param_ty_span,
+                    bound_region,
+                    is_first,
+                }
+            })
+        })
+}
+
+impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
+    pub(super) fn find_param_with_region(
+        &self,
+        anon_region: Region<'tcx>,
+        replace_region: Region<'tcx>,
+    ) -> Option<AnonymousParamInfo<'tcx>> {
+        find_param_with_region(self.tcx(), self.generic_param_scope, anon_region, replace_region)
+    }
+
+    // Here, we check for the case where the anonymous region
+    // is in the return type as written by the user.
+    // FIXME(#42703) - Need to handle certain cases here.
+    pub(super) fn is_return_type_anon(
+        &self,
+        scope_def_id: LocalDefId,
+        br: ty::BoundRegionKind,
+        hir_sig: &hir::FnSig<'_>,
+    ) -> Option<Span> {
+        let fn_ty = self.tcx().type_of(scope_def_id).instantiate_identity();
+        if let ty::FnDef(_, _) = fn_ty.kind() {
+            let ret_ty = fn_ty.fn_sig(self.tcx()).output();
+            let span = hir_sig.decl.output.span();
+            let future_output = if hir_sig.header.is_async() {
+                ret_ty.map_bound(|ty| self.cx.get_impl_future_output_ty(ty)).transpose()
+            } else {
+                None
+            };
+            return match future_output {
+                Some(output) if self.includes_region(output, br) => Some(span),
+                None if self.includes_region(ret_ty, br) => Some(span),
+                _ => None,
+            };
+        }
+        None
+    }
+
+    fn includes_region(
+        &self,
+        ty: Binder<'tcx, impl TypeFoldable<TyCtxt<'tcx>>>,
+        region: ty::BoundRegionKind,
+    ) -> bool {
+        let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(ty);
+        // We are only checking is any region meets the condition so order doesn't matter
+        #[allow(rustc::potential_query_instability)]
+        late_bound_regions.iter().any(|r| *r == region)
+    }
+
+    // Here we check for the case where anonymous region
+    // corresponds to self and if yes, we display E0312.
+    // FIXME(#42700) - Need to format self properly to
+    // enable E0621 for it.
+    pub(super) fn is_self_anon(&self, is_first: bool, scope_def_id: LocalDefId) -> bool {
+        is_first
+            && self
+                .tcx()
+                .opt_associated_item(scope_def_id.to_def_id())
+                .is_some_and(|i| i.fn_has_self_parameter)
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs
new file mode 100644
index 00000000000..e38b8e2f3d6
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs
@@ -0,0 +1,422 @@
+use rustc_errors::{Diag, Subdiagnostic};
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_middle::traits::ObligationCauseCode;
+use rustc_middle::ty::error::TypeError;
+use rustc_middle::ty::{self, IsSuggestable, Region, Ty};
+use rustc_span::symbol::kw;
+use tracing::debug;
+
+use super::ObligationCauseAsDiagArg;
+use crate::error_reporting::infer::{note_and_explain_region, TypeErrCtxt};
+use crate::errors::{
+    note_and_explain, FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent,
+    RefLongerThanData, RegionOriginNote, WhereClauseSuggestions,
+};
+use crate::fluent_generated as fluent;
+use crate::infer::{self, SubregionOrigin};
+
+impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
+    pub(super) fn note_region_origin(&self, err: &mut Diag<'_>, origin: &SubregionOrigin<'tcx>) {
+        match *origin {
+            infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
+                span: trace.cause.span,
+                requirement: ObligationCauseAsDiagArg(trace.cause.clone()),
+                expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)),
+            }
+            .add_to_diag(err),
+            infer::Reborrow(span) => {
+                RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diag(err)
+            }
+            infer::RelateObjectBound(span) => {
+                RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound }
+                    .add_to_diag(err);
+            }
+            infer::ReferenceOutlivesReferent(ty, span) => {
+                RegionOriginNote::WithName {
+                    span,
+                    msg: fluent::infer_reference_outlives_referent,
+                    name: &self.ty_to_string(ty),
+                    continues: false,
+                }
+                .add_to_diag(err);
+            }
+            infer::RelateParamBound(span, ty, opt_span) => {
+                RegionOriginNote::WithName {
+                    span,
+                    msg: fluent::infer_relate_param_bound,
+                    name: &self.ty_to_string(ty),
+                    continues: opt_span.is_some(),
+                }
+                .add_to_diag(err);
+                if let Some(span) = opt_span {
+                    RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 }
+                        .add_to_diag(err);
+                }
+            }
+            infer::RelateRegionParamBound(span, _) => {
+                RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound }
+                    .add_to_diag(err);
+            }
+            infer::CompareImplItemObligation { span, .. } => {
+                RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation }
+                    .add_to_diag(err);
+            }
+            infer::CheckAssociatedTypeBounds { ref parent, .. } => {
+                self.note_region_origin(err, parent);
+            }
+            infer::AscribeUserTypeProvePredicate(span) => {
+                RegionOriginNote::Plain {
+                    span,
+                    msg: fluent::infer_ascribe_user_type_prove_predicate,
+                }
+                .add_to_diag(err);
+            }
+        }
+    }
+
+    pub(super) fn report_concrete_failure(
+        &self,
+        generic_param_scope: LocalDefId,
+        origin: SubregionOrigin<'tcx>,
+        sub: Region<'tcx>,
+        sup: Region<'tcx>,
+    ) -> Diag<'a> {
+        let mut err = match origin {
+            infer::Subtype(box trace) => {
+                let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
+                let mut err = self.report_and_explain_type_error(trace, terr);
+                match (*sub, *sup) {
+                    (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {}
+                    (ty::RePlaceholder(_), _) => {
+                        note_and_explain_region(
+                            self.tcx,
+                            &mut err,
+                            generic_param_scope,
+                            "",
+                            sup,
+                            " doesn't meet the lifetime requirements",
+                            None,
+                        );
+                    }
+                    (_, ty::RePlaceholder(_)) => {
+                        note_and_explain_region(
+                            self.tcx,
+                            &mut err,
+                            generic_param_scope,
+                            "the required lifetime does not necessarily outlive ",
+                            sub,
+                            "",
+                            None,
+                        );
+                    }
+                    _ => {
+                        note_and_explain_region(
+                            self.tcx,
+                            &mut err,
+                            generic_param_scope,
+                            "",
+                            sup,
+                            "...",
+                            None,
+                        );
+                        note_and_explain_region(
+                            self.tcx,
+                            &mut err,
+                            generic_param_scope,
+                            "...does not necessarily outlive ",
+                            sub,
+                            "",
+                            None,
+                        );
+                    }
+                }
+                err
+            }
+            infer::Reborrow(span) => {
+                let reference_valid = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sub,
+                    None,
+                    note_and_explain::PrefixKind::RefValidFor,
+                    note_and_explain::SuffixKind::Continues,
+                );
+                let content_valid = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sup,
+                    None,
+                    note_and_explain::PrefixKind::ContentValidFor,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                self.dcx().create_err(OutlivesContent {
+                    span,
+                    notes: reference_valid.into_iter().chain(content_valid).collect(),
+                })
+            }
+            infer::RelateObjectBound(span) => {
+                let object_valid = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sub,
+                    None,
+                    note_and_explain::PrefixKind::TypeObjValidFor,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                let pointer_valid = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sup,
+                    None,
+                    note_and_explain::PrefixKind::SourcePointerValidFor,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                self.dcx().create_err(OutlivesBound {
+                    span,
+                    notes: object_valid.into_iter().chain(pointer_valid).collect(),
+                })
+            }
+            infer::RelateParamBound(span, ty, opt_span) => {
+                let prefix = match *sub {
+                    ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy,
+                    _ => note_and_explain::PrefixKind::TypeOutlive,
+                };
+                let suffix = if opt_span.is_some() {
+                    note_and_explain::SuffixKind::ReqByBinding
+                } else {
+                    note_and_explain::SuffixKind::Empty
+                };
+                let note = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sub,
+                    opt_span,
+                    prefix,
+                    suffix,
+                );
+                self.dcx().create_err(FulfillReqLifetime {
+                    span,
+                    ty: self.resolve_vars_if_possible(ty),
+                    note,
+                })
+            }
+            infer::RelateRegionParamBound(span, _) => {
+                let param_instantiated = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sup,
+                    None,
+                    note_and_explain::PrefixKind::LfParamInstantiatedWith,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                let param_must_outlive = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sub,
+                    None,
+                    note_and_explain::PrefixKind::LfParamMustOutlive,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                self.dcx().create_err(LfBoundNotSatisfied {
+                    span,
+                    notes: param_instantiated.into_iter().chain(param_must_outlive).collect(),
+                })
+            }
+            infer::ReferenceOutlivesReferent(ty, span) => {
+                let pointer_valid = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sub,
+                    None,
+                    note_and_explain::PrefixKind::PointerValidFor,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                let data_valid = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sup,
+                    None,
+                    note_and_explain::PrefixKind::DataValidFor,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                self.dcx().create_err(RefLongerThanData {
+                    span,
+                    ty: self.resolve_vars_if_possible(ty),
+                    notes: pointer_valid.into_iter().chain(data_valid).collect(),
+                })
+            }
+            infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => {
+                let mut err = self.infcx.report_extra_impl_obligation(
+                    span,
+                    impl_item_def_id,
+                    trait_item_def_id,
+                    &format!("`{sup}: {sub}`"),
+                );
+                // We should only suggest rewriting the `where` clause if the predicate is within that `where` clause
+                if let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id)
+                    && generics.where_clause_span.contains(span)
+                {
+                    self.suggest_copy_trait_method_bounds(
+                        trait_item_def_id,
+                        impl_item_def_id,
+                        &mut err,
+                    );
+                }
+                err
+            }
+            infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => {
+                let mut err = self.report_concrete_failure(generic_param_scope, *parent, sub, sup);
+
+                // Don't mention the item name if it's an RPITIT, since that'll just confuse
+                // folks.
+                if !self.tcx.is_impl_trait_in_trait(impl_item_def_id.to_def_id()) {
+                    let trait_item_span = self.tcx.def_span(trait_item_def_id);
+                    let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
+                    err.span_label(
+                        trait_item_span,
+                        format!("definition of `{item_name}` from trait"),
+                    );
+                }
+
+                self.suggest_copy_trait_method_bounds(
+                    trait_item_def_id,
+                    impl_item_def_id,
+                    &mut err,
+                );
+                err
+            }
+            infer::AscribeUserTypeProvePredicate(span) => {
+                let instantiated = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sup,
+                    None,
+                    note_and_explain::PrefixKind::LfInstantiatedWith,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                let must_outlive = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sub,
+                    None,
+                    note_and_explain::PrefixKind::LfMustOutlive,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                self.dcx().create_err(LfBoundNotSatisfied {
+                    span,
+                    notes: instantiated.into_iter().chain(must_outlive).collect(),
+                })
+            }
+        };
+        if sub.is_error() || sup.is_error() {
+            err.downgrade_to_delayed_bug();
+        }
+        err
+    }
+
+    pub fn suggest_copy_trait_method_bounds(
+        &self,
+        trait_item_def_id: DefId,
+        impl_item_def_id: LocalDefId,
+        err: &mut Diag<'_>,
+    ) {
+        // FIXME(compiler-errors): Right now this is only being used for region
+        // predicate mismatches. Ideally, we'd use it for *all* predicate mismatches,
+        // but right now it's not really very smart when it comes to implicit `Sized`
+        // predicates and bounds on the trait itself.
+
+        let Some(impl_def_id) = self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx)
+        else {
+            return;
+        };
+        let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else {
+            return;
+        };
+        let trait_args = trait_ref
+            .instantiate_identity()
+            // Replace the explicit self type with `Self` for better suggestion rendering
+            .with_self_ty(self.tcx, Ty::new_param(self.tcx, 0, kw::SelfUpper))
+            .args;
+        let trait_item_args = ty::GenericArgs::identity_for_item(self.tcx, impl_item_def_id)
+            .rebase_onto(self.tcx, impl_def_id, trait_args);
+
+        let Ok(trait_predicates) =
+            self.tcx
+                .explicit_predicates_of(trait_item_def_id)
+                .instantiate_own(self.tcx, trait_item_args)
+                .map(|(pred, _)| {
+                    if pred.is_suggestable(self.tcx, false) {
+                        Ok(pred.to_string())
+                    } else {
+                        Err(())
+                    }
+                })
+                .collect::<Result<Vec<_>, ()>>()
+        else {
+            return;
+        };
+
+        let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else {
+            return;
+        };
+
+        let suggestion = if trait_predicates.is_empty() {
+            WhereClauseSuggestions::Remove { span: generics.where_clause_span }
+        } else {
+            let space = if generics.where_clause_span.is_empty() { " " } else { "" };
+            WhereClauseSuggestions::CopyPredicates {
+                span: generics.where_clause_span,
+                space,
+                trait_predicates: trait_predicates.join(", "),
+            }
+        };
+        err.subdiagnostic(suggestion);
+    }
+
+    pub(super) fn report_placeholder_failure(
+        &self,
+        generic_param_scope: LocalDefId,
+        placeholder_origin: SubregionOrigin<'tcx>,
+        sub: Region<'tcx>,
+        sup: Region<'tcx>,
+    ) -> Diag<'a> {
+        // I can't think how to do better than this right now. -nikomatsakis
+        debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure");
+        match placeholder_origin {
+            infer::Subtype(box ref trace)
+                if matches!(
+                    &trace.cause.code().peel_derives(),
+                    ObligationCauseCode::WhereClause(..)
+                        | ObligationCauseCode::WhereClauseInExpr(..)
+                ) =>
+            {
+                // Hack to get around the borrow checker because trace.cause has an `Rc`.
+                if let ObligationCauseCode::WhereClause(_, span)
+                | ObligationCauseCode::WhereClauseInExpr(_, span, ..) =
+                    &trace.cause.code().peel_derives()
+                    && !span.is_dummy()
+                {
+                    let span = *span;
+                    self.report_concrete_failure(generic_param_scope, placeholder_origin, sub, sup)
+                        .with_span_note(span, "the lifetime requirement is introduced here")
+                } else {
+                    unreachable!(
+                        "control flow ensures we have a `BindingObligation` or `WhereClauseInExpr` here..."
+                    )
+                }
+            }
+            infer::Subtype(box trace) => {
+                let terr = TypeError::RegionsPlaceholderMismatch;
+                return self.report_and_explain_type_error(trace, terr);
+            }
+            _ => {
+                return self.report_concrete_failure(
+                    generic_param_scope,
+                    placeholder_origin,
+                    sub,
+                    sup,
+                );
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs
new file mode 100644
index 00000000000..db71331d07f
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs
@@ -0,0 +1,954 @@
+use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
+use rustc_errors::{pluralize, Diag, MultiSpan};
+use rustc_hir as hir;
+use rustc_hir::def::DefKind;
+use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
+use rustc_middle::ty::error::{ExpectedFound, TypeError};
+use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
+use rustc_middle::ty::print::{FmtPrinter, Printer};
+use rustc_middle::ty::{self, suggest_constraining_type_param, Ty};
+use rustc_span::def_id::DefId;
+use rustc_span::{sym, BytePos, Span, Symbol};
+use tracing::debug;
+
+use crate::error_reporting::TypeErrCtxt;
+use crate::infer::InferCtxtExt;
+
+impl<'tcx> TypeErrCtxt<'_, 'tcx> {
+    pub fn note_and_explain_type_err(
+        &self,
+        diag: &mut Diag<'_>,
+        err: TypeError<'tcx>,
+        cause: &ObligationCause<'tcx>,
+        sp: Span,
+        body_owner_def_id: DefId,
+    ) {
+        debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause);
+
+        let tcx = self.tcx;
+
+        match err {
+            TypeError::ArgumentSorts(values, _) | TypeError::Sorts(values) => {
+                match (*values.expected.kind(), *values.found.kind()) {
+                    (ty::Closure(..), ty::Closure(..)) => {
+                        diag.note("no two closures, even if identical, have the same type");
+                        diag.help("consider boxing your closure and/or using it as a trait object");
+                    }
+                    (ty::Coroutine(def_id1, ..), ty::Coroutine(def_id2, ..))
+                        if self.tcx.coroutine_is_async(def_id1)
+                            && self.tcx.coroutine_is_async(def_id2) =>
+                    {
+                        diag.note("no two async blocks, even if identical, have the same type");
+                        diag.help(
+                            "consider pinning your async block and casting it to a trait object",
+                        );
+                    }
+                    (ty::Alias(ty::Opaque, ..), ty::Alias(ty::Opaque, ..)) => {
+                        // Issue #63167
+                        diag.note("distinct uses of `impl Trait` result in different opaque types");
+                    }
+                    (ty::Float(_), ty::Infer(ty::IntVar(_)))
+                        if let Ok(
+                            // Issue #53280
+                            snippet,
+                        ) = tcx.sess.source_map().span_to_snippet(sp) =>
+                    {
+                        if snippet.chars().all(|c| c.is_digit(10) || c == '-' || c == '_') {
+                            diag.span_suggestion_verbose(
+                                sp.shrink_to_hi(),
+                                "use a float literal",
+                                ".0",
+                                MachineApplicable,
+                            );
+                        }
+                    }
+                    (ty::Param(expected), ty::Param(found)) => {
+                        let generics = tcx.generics_of(body_owner_def_id);
+                        let e_span = tcx.def_span(generics.type_param(expected, tcx).def_id);
+                        if !sp.contains(e_span) {
+                            diag.span_label(e_span, "expected type parameter");
+                        }
+                        let f_span = tcx.def_span(generics.type_param(found, tcx).def_id);
+                        if !sp.contains(f_span) {
+                            diag.span_label(f_span, "found type parameter");
+                        }
+                        diag.note(
+                            "a type parameter was expected, but a different one was found; \
+                             you might be missing a type parameter or trait bound",
+                        );
+                        diag.note(
+                            "for more information, visit \
+                             https://doc.rust-lang.org/book/ch10-02-traits.html\
+                             #traits-as-parameters",
+                        );
+                    }
+                    (
+                        ty::Alias(ty::Projection | ty::Inherent, _),
+                        ty::Alias(ty::Projection | ty::Inherent, _),
+                    ) => {
+                        diag.note("an associated type was expected, but a different one was found");
+                    }
+                    // FIXME(inherent_associated_types): Extend this to support `ty::Inherent`, too.
+                    (ty::Param(p), ty::Alias(ty::Projection, proj))
+                    | (ty::Alias(ty::Projection, proj), ty::Param(p))
+                        if !tcx.is_impl_trait_in_trait(proj.def_id) =>
+                    {
+                        let param = tcx.generics_of(body_owner_def_id).type_param(p, tcx);
+                        let p_def_id = param.def_id;
+                        let p_span = tcx.def_span(p_def_id);
+                        let expected = match (values.expected.kind(), values.found.kind()) {
+                            (ty::Param(_), _) => "expected ",
+                            (_, ty::Param(_)) => "found ",
+                            _ => "",
+                        };
+                        if !sp.contains(p_span) {
+                            diag.span_label(p_span, format!("{expected}this type parameter"));
+                        }
+                        let parent = p_def_id.as_local().and_then(|id| {
+                            let local_id = tcx.local_def_id_to_hir_id(id);
+                            let generics = tcx.parent_hir_node(local_id).generics()?;
+                            Some((id, generics))
+                        });
+                        let mut note = true;
+                        if let Some((local_id, generics)) = parent {
+                            // Synthesize the associated type restriction `Add<Output = Expected>`.
+                            // FIXME: extract this logic for use in other diagnostics.
+                            let (trait_ref, assoc_args) = proj.trait_ref_and_own_args(tcx);
+                            let item_name = tcx.item_name(proj.def_id);
+                            let item_args = self.format_generic_args(assoc_args);
+
+                            // Here, we try to see if there's an existing
+                            // trait implementation that matches the one that
+                            // we're suggesting to restrict. If so, find the
+                            // "end", whether it be at the end of the trait
+                            // or the end of the generic arguments.
+                            let mut matching_span = None;
+                            let mut matched_end_of_args = false;
+                            for bound in generics.bounds_for_param(local_id) {
+                                let potential_spans = bound.bounds.iter().find_map(|bound| {
+                                    let bound_trait_path = bound.trait_ref()?.path;
+                                    let def_id = bound_trait_path.res.opt_def_id()?;
+                                    let generic_args = bound_trait_path
+                                        .segments
+                                        .iter()
+                                        .last()
+                                        .map(|path| path.args());
+                                    (def_id == trait_ref.def_id)
+                                        .then_some((bound_trait_path.span, generic_args))
+                                });
+
+                                if let Some((end_of_trait, end_of_args)) = potential_spans {
+                                    let args_span = end_of_args.and_then(|args| args.span());
+                                    matched_end_of_args = args_span.is_some();
+                                    matching_span = args_span
+                                        .or_else(|| Some(end_of_trait))
+                                        .map(|span| span.shrink_to_hi());
+                                    break;
+                                }
+                            }
+
+                            if matched_end_of_args {
+                                // Append suggestion to the end of our args
+                                let path = format!(", {item_name}{item_args} = {p}");
+                                note = !suggest_constraining_type_param(
+                                    tcx,
+                                    generics,
+                                    diag,
+                                    &proj.self_ty().to_string(),
+                                    &path,
+                                    None,
+                                    matching_span,
+                                );
+                            } else {
+                                // Suggest adding a bound to an existing trait
+                                // or if the trait doesn't exist, add the trait
+                                // and the suggested bounds.
+                                let path = format!("<{item_name}{item_args} = {p}>");
+                                note = !suggest_constraining_type_param(
+                                    tcx,
+                                    generics,
+                                    diag,
+                                    &proj.self_ty().to_string(),
+                                    &path,
+                                    None,
+                                    matching_span,
+                                );
+                            }
+                        }
+                        if note {
+                            diag.note("you might be missing a type parameter or trait bound");
+                        }
+                    }
+                    (ty::Param(p), ty::Dynamic(..) | ty::Alias(ty::Opaque, ..))
+                    | (ty::Dynamic(..) | ty::Alias(ty::Opaque, ..), ty::Param(p)) => {
+                        let generics = tcx.generics_of(body_owner_def_id);
+                        let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
+                        let expected = match (values.expected.kind(), values.found.kind()) {
+                            (ty::Param(_), _) => "expected ",
+                            (_, ty::Param(_)) => "found ",
+                            _ => "",
+                        };
+                        if !sp.contains(p_span) {
+                            diag.span_label(p_span, format!("{expected}this type parameter"));
+                        }
+                        diag.help("type parameters must be constrained to match other types");
+                        if diag.code.is_some_and(|code| tcx.sess.teach(code)) {
+                            diag.help(
+                                "given a type parameter `T` and a method `foo`:
+```
+trait Trait<T> { fn foo(&self) -> T; }
+```
+the only ways to implement method `foo` are:
+- constrain `T` with an explicit type:
+```
+impl Trait<String> for X {
+    fn foo(&self) -> String { String::new() }
+}
+```
+- add a trait bound to `T` and call a method on that trait that returns `Self`:
+```
+impl<T: std::default::Default> Trait<T> for X {
+    fn foo(&self) -> T { <T as std::default::Default>::default() }
+}
+```
+- change `foo` to return an argument of type `T`:
+```
+impl<T> Trait<T> for X {
+    fn foo(&self, x: T) -> T { x }
+}
+```",
+                            );
+                        }
+                        diag.note(
+                            "for more information, visit \
+                             https://doc.rust-lang.org/book/ch10-02-traits.html\
+                             #traits-as-parameters",
+                        );
+                    }
+                    (
+                        ty::Param(p),
+                        ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..),
+                    ) => {
+                        let generics = tcx.generics_of(body_owner_def_id);
+                        let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
+                        if !sp.contains(p_span) {
+                            diag.span_label(p_span, "expected this type parameter");
+                        }
+                        diag.help(format!(
+                            "every closure has a distinct type and so could not always match the \
+                             caller-chosen type of parameter `{p}`"
+                        ));
+                    }
+                    (ty::Param(p), _) | (_, ty::Param(p)) => {
+                        let generics = tcx.generics_of(body_owner_def_id);
+                        let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
+                        let expected = match (values.expected.kind(), values.found.kind()) {
+                            (ty::Param(_), _) => "expected ",
+                            (_, ty::Param(_)) => "found ",
+                            _ => "",
+                        };
+                        if !sp.contains(p_span) {
+                            diag.span_label(p_span, format!("{expected}this type parameter"));
+                        }
+                    }
+                    (ty::Alias(ty::Projection | ty::Inherent, proj_ty), _)
+                        if !tcx.is_impl_trait_in_trait(proj_ty.def_id) =>
+                    {
+                        self.expected_projection(
+                            diag,
+                            proj_ty,
+                            values,
+                            body_owner_def_id,
+                            cause.code(),
+                        );
+                    }
+                    (_, ty::Alias(ty::Projection | ty::Inherent, proj_ty))
+                        if !tcx.is_impl_trait_in_trait(proj_ty.def_id) =>
+                    {
+                        let msg = || {
+                            format!(
+                                "consider constraining the associated type `{}` to `{}`",
+                                values.found, values.expected,
+                            )
+                        };
+                        if !(self.suggest_constraining_opaque_associated_type(
+                            diag,
+                            msg,
+                            proj_ty,
+                            values.expected,
+                        ) || self.suggest_constraint(
+                            diag,
+                            &msg,
+                            body_owner_def_id,
+                            proj_ty,
+                            values.expected,
+                        )) {
+                            diag.help(msg());
+                            diag.note(
+                                "for more information, visit \
+                                https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
+                            );
+                        }
+                    }
+                    (ty::Dynamic(t, _, ty::DynKind::Dyn), ty::Alias(ty::Opaque, alias))
+                        if let Some(def_id) = t.principal_def_id()
+                            && tcx
+                                .explicit_item_super_predicates(alias.def_id)
+                                .skip_binder()
+                                .iter()
+                                .any(|(pred, _span)| match pred.kind().skip_binder() {
+                                    ty::ClauseKind::Trait(trait_predicate)
+                                        if trait_predicate.polarity
+                                            == ty::PredicatePolarity::Positive =>
+                                    {
+                                        trait_predicate.def_id() == def_id
+                                    }
+                                    _ => false,
+                                }) =>
+                    {
+                        diag.help(format!(
+                            "you can box the `{}` to coerce it to `Box<{}>`, but you'll have to \
+                             change the expected type as well",
+                            values.found, values.expected,
+                        ));
+                    }
+                    (ty::Dynamic(t, _, ty::DynKind::Dyn), _)
+                        if let Some(def_id) = t.principal_def_id() =>
+                    {
+                        let mut has_matching_impl = false;
+                        tcx.for_each_relevant_impl(def_id, values.found, |did| {
+                            if DeepRejectCtxt::new(tcx, TreatParams::ForLookup)
+                                .types_may_unify(values.found, tcx.type_of(did).skip_binder())
+                            {
+                                has_matching_impl = true;
+                            }
+                        });
+                        if has_matching_impl {
+                            let trait_name = tcx.item_name(def_id);
+                            diag.help(format!(
+                                "`{}` implements `{trait_name}` so you could box the found value \
+                                 and coerce it to the trait object `Box<dyn {trait_name}>`, you \
+                                 will have to change the expected type as well",
+                                values.found,
+                            ));
+                        }
+                    }
+                    (_, ty::Dynamic(t, _, ty::DynKind::Dyn))
+                        if let Some(def_id) = t.principal_def_id() =>
+                    {
+                        let mut has_matching_impl = false;
+                        tcx.for_each_relevant_impl(def_id, values.expected, |did| {
+                            if DeepRejectCtxt::new(tcx, TreatParams::ForLookup)
+                                .types_may_unify(values.expected, tcx.type_of(did).skip_binder())
+                            {
+                                has_matching_impl = true;
+                            }
+                        });
+                        if has_matching_impl {
+                            let trait_name = tcx.item_name(def_id);
+                            diag.help(format!(
+                                "`{}` implements `{trait_name}` so you could change the expected \
+                                 type to `Box<dyn {trait_name}>`",
+                                values.expected,
+                            ));
+                        }
+                    }
+                    (ty::Dynamic(t, _, ty::DynKind::DynStar), _)
+                        if let Some(def_id) = t.principal_def_id() =>
+                    {
+                        let mut has_matching_impl = false;
+                        tcx.for_each_relevant_impl(def_id, values.found, |did| {
+                            if DeepRejectCtxt::new(tcx, TreatParams::ForLookup)
+                                .types_may_unify(values.found, tcx.type_of(did).skip_binder())
+                            {
+                                has_matching_impl = true;
+                            }
+                        });
+                        if has_matching_impl {
+                            let trait_name = tcx.item_name(def_id);
+                            diag.help(format!(
+                                "`{}` implements `{trait_name}`, `#[feature(dyn_star)]` is likely \
+                                 not enabled; that feature it is currently incomplete",
+                                values.found,
+                            ));
+                        }
+                    }
+                    (_, ty::Alias(ty::Opaque, opaque_ty))
+                    | (ty::Alias(ty::Opaque, opaque_ty), _) => {
+                        if opaque_ty.def_id.is_local()
+                            && matches!(
+                                tcx.def_kind(body_owner_def_id),
+                                DefKind::Fn
+                                    | DefKind::Static { .. }
+                                    | DefKind::Const
+                                    | DefKind::AssocFn
+                                    | DefKind::AssocConst
+                            )
+                            && tcx.is_type_alias_impl_trait(opaque_ty.def_id)
+                            && !tcx
+                                .opaque_types_defined_by(body_owner_def_id.expect_local())
+                                .contains(&opaque_ty.def_id.expect_local())
+                        {
+                            let sp = tcx
+                                .def_ident_span(body_owner_def_id)
+                                .unwrap_or_else(|| tcx.def_span(body_owner_def_id));
+                            diag.span_note(
+                                sp,
+                                "this item must have the opaque type in its signature in order to \
+                                 be able to register hidden types",
+                            );
+                        }
+                        // If two if arms can be coerced to a trait object, provide a structured
+                        // suggestion.
+                        let ObligationCauseCode::IfExpression(cause) = cause.code() else {
+                            return;
+                        };
+                        let hir::Node::Block(blk) = self.tcx.hir_node(cause.then_id) else {
+                            return;
+                        };
+                        let Some(then) = blk.expr else {
+                            return;
+                        };
+                        let hir::Node::Block(blk) = self.tcx.hir_node(cause.else_id) else {
+                            return;
+                        };
+                        let Some(else_) = blk.expr else {
+                            return;
+                        };
+                        let expected = match values.found.kind() {
+                            ty::Alias(..) => values.expected,
+                            _ => values.found,
+                        };
+                        let preds = tcx.explicit_item_super_predicates(opaque_ty.def_id);
+                        for (pred, _span) in preds.skip_binder() {
+                            let ty::ClauseKind::Trait(trait_predicate) = pred.kind().skip_binder()
+                            else {
+                                continue;
+                            };
+                            if trait_predicate.polarity != ty::PredicatePolarity::Positive {
+                                continue;
+                            }
+                            let def_id = trait_predicate.def_id();
+                            let mut impl_def_ids = vec![];
+                            tcx.for_each_relevant_impl(def_id, expected, |did| {
+                                impl_def_ids.push(did)
+                            });
+                            if let [_] = &impl_def_ids[..] {
+                                let trait_name = tcx.item_name(def_id);
+                                diag.multipart_suggestion(
+                                    format!(
+                                        "`{expected}` implements `{trait_name}` so you can box \
+                                         both arms and coerce to the trait object \
+                                         `Box<dyn {trait_name}>`",
+                                    ),
+                                    vec![
+                                        (then.span.shrink_to_lo(), "Box::new(".to_string()),
+                                        (
+                                            then.span.shrink_to_hi(),
+                                            format!(") as Box<dyn {}>", tcx.def_path_str(def_id)),
+                                        ),
+                                        (else_.span.shrink_to_lo(), "Box::new(".to_string()),
+                                        (else_.span.shrink_to_hi(), ")".to_string()),
+                                    ],
+                                    MachineApplicable,
+                                );
+                            }
+                        }
+                    }
+                    (ty::FnPtr(_, hdr), ty::FnDef(def_id, _))
+                    | (ty::FnDef(def_id, _), ty::FnPtr(_, hdr)) => {
+                        if tcx.fn_sig(def_id).skip_binder().safety() < hdr.safety {
+                            diag.note(
+                                "unsafe functions cannot be coerced into safe function pointers",
+                            );
+                        }
+                    }
+                    (ty::Adt(_, _), ty::Adt(def, args))
+                        if let ObligationCauseCode::IfExpression(cause) = cause.code()
+                            && let hir::Node::Block(blk) = self.tcx.hir_node(cause.then_id)
+                            && let Some(then) = blk.expr
+                            && def.is_box()
+                            && let boxed_ty = args.type_at(0)
+                            && let ty::Dynamic(t, _, _) = boxed_ty.kind()
+                            && let Some(def_id) = t.principal_def_id()
+                            && let mut impl_def_ids = vec![]
+                            && let _ =
+                                tcx.for_each_relevant_impl(def_id, values.expected, |did| {
+                                    impl_def_ids.push(did)
+                                })
+                            && let [_] = &impl_def_ids[..] =>
+                    {
+                        // We have divergent if/else arms where the expected value is a type that
+                        // implements the trait of the found boxed trait object.
+                        diag.multipart_suggestion(
+                            format!(
+                                "`{}` implements `{}` so you can box it to coerce to the trait \
+                                 object `{}`",
+                                values.expected,
+                                tcx.item_name(def_id),
+                                values.found,
+                            ),
+                            vec![
+                                (then.span.shrink_to_lo(), "Box::new(".to_string()),
+                                (then.span.shrink_to_hi(), ")".to_string()),
+                            ],
+                            MachineApplicable,
+                        );
+                    }
+                    _ => {}
+                }
+                debug!(
+                    "note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})",
+                    values.expected,
+                    values.expected.kind(),
+                    values.found,
+                    values.found.kind(),
+                );
+            }
+            TypeError::CyclicTy(ty) => {
+                // Watch out for various cases of cyclic types and try to explain.
+                if ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() {
+                    diag.note(
+                        "closures cannot capture themselves or take themselves as argument;\n\
+                         this error may be the result of a recent compiler bug-fix,\n\
+                         see issue #46062 <https://github.com/rust-lang/rust/issues/46062>\n\
+                         for more information",
+                    );
+                }
+            }
+            TypeError::TargetFeatureCast(def_id) => {
+                let target_spans = tcx.get_attrs(def_id, sym::target_feature).map(|attr| attr.span);
+                diag.note(
+                    "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers"
+                );
+                diag.span_labels(target_spans, "`#[target_feature]` added here");
+            }
+            _ => {}
+        }
+    }
+
+    fn suggest_constraint(
+        &self,
+        diag: &mut Diag<'_>,
+        msg: impl Fn() -> String,
+        body_owner_def_id: DefId,
+        proj_ty: ty::AliasTy<'tcx>,
+        ty: Ty<'tcx>,
+    ) -> bool {
+        let tcx = self.tcx;
+        let assoc = tcx.associated_item(proj_ty.def_id);
+        let (trait_ref, assoc_args) = proj_ty.trait_ref_and_own_args(tcx);
+        let Some(item) = tcx.hir().get_if_local(body_owner_def_id) else {
+            return false;
+        };
+        let Some(hir_generics) = item.generics() else {
+            return false;
+        };
+        // Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`.
+        // This will also work for `impl Trait`.
+        let ty::Param(param_ty) = *proj_ty.self_ty().kind() else {
+            return false;
+        };
+        let generics = tcx.generics_of(body_owner_def_id);
+        let def_id = generics.type_param(param_ty, tcx).def_id;
+        let Some(def_id) = def_id.as_local() else {
+            return false;
+        };
+
+        // First look in the `where` clause, as this might be
+        // `fn foo<T>(x: T) where T: Trait`.
+        for pred in hir_generics.bounds_for_param(def_id) {
+            if self.constrain_generic_bound_associated_type_structured_suggestion(
+                diag,
+                trait_ref,
+                pred.bounds,
+                assoc,
+                assoc_args,
+                ty,
+                &msg,
+                false,
+            ) {
+                return true;
+            }
+        }
+        if (param_ty.index as usize) >= generics.parent_count {
+            // The param comes from the current item, do not look at the parent. (#117209)
+            return false;
+        }
+        // If associated item, look to constrain the params of the trait/impl.
+        let hir_id = match item {
+            hir::Node::ImplItem(item) => item.hir_id(),
+            hir::Node::TraitItem(item) => item.hir_id(),
+            _ => return false,
+        };
+        let parent = tcx.hir().get_parent_item(hir_id).def_id;
+        self.suggest_constraint(diag, msg, parent.into(), proj_ty, ty)
+    }
+
+    /// An associated type was expected and a different type was found.
+    ///
+    /// We perform a few different checks to see what we can suggest:
+    ///
+    ///  - In the current item, look for associated functions that return the expected type and
+    ///    suggest calling them. (Not a structured suggestion.)
+    ///  - If any of the item's generic bounds can be constrained, we suggest constraining the
+    ///    associated type to the found type.
+    ///  - If the associated type has a default type and was expected inside of a `trait`, we
+    ///    mention that this is disallowed.
+    ///  - If all other things fail, and the error is not because of a mismatch between the `trait`
+    ///    and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc
+    ///    fn that returns the type.
+    fn expected_projection(
+        &self,
+        diag: &mut Diag<'_>,
+        proj_ty: ty::AliasTy<'tcx>,
+        values: ExpectedFound<Ty<'tcx>>,
+        body_owner_def_id: DefId,
+        cause_code: &ObligationCauseCode<'_>,
+    ) {
+        let tcx = self.tcx;
+
+        // Don't suggest constraining a projection to something containing itself
+        if self.tcx.erase_regions(values.found).contains(self.tcx.erase_regions(values.expected)) {
+            return;
+        }
+
+        let msg = || {
+            format!(
+                "consider constraining the associated type `{}` to `{}`",
+                values.expected, values.found
+            )
+        };
+
+        let body_owner = tcx.hir().get_if_local(body_owner_def_id);
+        let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name);
+
+        // We don't want to suggest calling an assoc fn in a scope where that isn't feasible.
+        let callable_scope = matches!(
+            body_owner,
+            Some(
+                hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })
+                    | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. })
+                    | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }),
+            )
+        );
+        let impl_comparison = matches!(cause_code, ObligationCauseCode::CompareImplItem { .. });
+        let assoc = tcx.associated_item(proj_ty.def_id);
+        if impl_comparison {
+            // We do not want to suggest calling functions when the reason of the
+            // type error is a comparison of an `impl` with its `trait`.
+        } else {
+            let point_at_assoc_fn = if callable_scope
+                && self.point_at_methods_that_satisfy_associated_type(
+                    diag,
+                    assoc.container_id(tcx),
+                    current_method_ident,
+                    proj_ty.def_id,
+                    values.expected,
+                ) {
+                // If we find a suitable associated function that returns the expected type, we
+                // don't want the more general suggestion later in this method about "consider
+                // constraining the associated type or calling a method that returns the associated
+                // type".
+                true
+            } else {
+                false
+            };
+            // Possibly suggest constraining the associated type to conform to the
+            // found type.
+            if self.suggest_constraint(diag, &msg, body_owner_def_id, proj_ty, values.found)
+                || point_at_assoc_fn
+            {
+                return;
+            }
+        }
+
+        self.suggest_constraining_opaque_associated_type(diag, &msg, proj_ty, values.found);
+
+        if self.point_at_associated_type(diag, body_owner_def_id, values.found) {
+            return;
+        }
+
+        if !impl_comparison {
+            // Generic suggestion when we can't be more specific.
+            if callable_scope {
+                diag.help(format!(
+                    "{} or calling a method that returns `{}`",
+                    msg(),
+                    values.expected
+                ));
+            } else {
+                diag.help(msg());
+            }
+            diag.note(
+                "for more information, visit \
+                 https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
+            );
+        }
+        if diag.code.is_some_and(|code| tcx.sess.teach(code)) {
+            diag.help(
+                "given an associated type `T` and a method `foo`:
+```
+trait Trait {
+type T;
+fn foo(&self) -> Self::T;
+}
+```
+the only way of implementing method `foo` is to constrain `T` with an explicit associated type:
+```
+impl Trait for X {
+type T = String;
+fn foo(&self) -> Self::T { String::new() }
+}
+```",
+            );
+        }
+    }
+
+    /// When the expected `impl Trait` is not defined in the current item, it will come from
+    /// a return type. This can occur when dealing with `TryStream` (#71035).
+    fn suggest_constraining_opaque_associated_type(
+        &self,
+        diag: &mut Diag<'_>,
+        msg: impl Fn() -> String,
+        proj_ty: ty::AliasTy<'tcx>,
+        ty: Ty<'tcx>,
+    ) -> bool {
+        let tcx = self.tcx;
+
+        let assoc = tcx.associated_item(proj_ty.def_id);
+        if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *proj_ty.self_ty().kind() {
+            let opaque_local_def_id = def_id.as_local();
+            let opaque_hir_ty = if let Some(opaque_local_def_id) = opaque_local_def_id {
+                tcx.hir().expect_item(opaque_local_def_id).expect_opaque_ty()
+            } else {
+                return false;
+            };
+
+            let (trait_ref, assoc_args) = proj_ty.trait_ref_and_own_args(tcx);
+
+            self.constrain_generic_bound_associated_type_structured_suggestion(
+                diag,
+                trait_ref,
+                opaque_hir_ty.bounds,
+                assoc,
+                assoc_args,
+                ty,
+                msg,
+                true,
+            )
+        } else {
+            false
+        }
+    }
+
+    fn point_at_methods_that_satisfy_associated_type(
+        &self,
+        diag: &mut Diag<'_>,
+        assoc_container_id: DefId,
+        current_method_ident: Option<Symbol>,
+        proj_ty_item_def_id: DefId,
+        expected: Ty<'tcx>,
+    ) -> bool {
+        let tcx = self.tcx;
+
+        let items = tcx.associated_items(assoc_container_id);
+        // Find all the methods in the trait that could be called to construct the
+        // expected associated type.
+        // FIXME: consider suggesting the use of associated `const`s.
+        let methods: Vec<(Span, String)> = items
+            .in_definition_order()
+            .filter(|item| {
+                ty::AssocKind::Fn == item.kind
+                    && Some(item.name) != current_method_ident
+                    && !tcx.is_doc_hidden(item.def_id)
+            })
+            .filter_map(|item| {
+                let method = tcx.fn_sig(item.def_id).instantiate_identity();
+                match *method.output().skip_binder().kind() {
+                    ty::Alias(ty::Projection, ty::AliasTy { def_id: item_def_id, .. })
+                        if item_def_id == proj_ty_item_def_id =>
+                    {
+                        Some((
+                            tcx.def_span(item.def_id),
+                            format!("consider calling `{}`", tcx.def_path_str(item.def_id)),
+                        ))
+                    }
+                    _ => None,
+                }
+            })
+            .collect();
+        if !methods.is_empty() {
+            // Use a single `help:` to show all the methods in the trait that can
+            // be used to construct the expected associated type.
+            let mut span: MultiSpan =
+                methods.iter().map(|(sp, _)| *sp).collect::<Vec<Span>>().into();
+            let msg = format!(
+                "{some} method{s} {are} available that return{r} `{ty}`",
+                some = if methods.len() == 1 { "a" } else { "some" },
+                s = pluralize!(methods.len()),
+                are = pluralize!("is", methods.len()),
+                r = if methods.len() == 1 { "s" } else { "" },
+                ty = expected
+            );
+            for (sp, label) in methods.into_iter() {
+                span.push_span_label(sp, label);
+            }
+            diag.span_help(span, msg);
+            return true;
+        }
+        false
+    }
+
+    fn point_at_associated_type(
+        &self,
+        diag: &mut Diag<'_>,
+        body_owner_def_id: DefId,
+        found: Ty<'tcx>,
+    ) -> bool {
+        let tcx = self.tcx;
+
+        let Some(def_id) = body_owner_def_id.as_local() else {
+            return false;
+        };
+
+        // When `body_owner` is an `impl` or `trait` item, look in its associated types for
+        // `expected` and point at it.
+        let hir_id = tcx.local_def_id_to_hir_id(def_id);
+        let parent_id = tcx.hir().get_parent_item(hir_id);
+        let item = tcx.hir_node_by_def_id(parent_id.def_id);
+
+        debug!("expected_projection parent item {:?}", item);
+
+        let param_env = tcx.param_env(body_owner_def_id);
+
+        match item {
+            hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., items), .. }) => {
+                // FIXME: account for `#![feature(specialization)]`
+                for item in &items[..] {
+                    match item.kind {
+                        hir::AssocItemKind::Type => {
+                            // FIXME: account for returning some type in a trait fn impl that has
+                            // an assoc type as a return type (#72076).
+                            if let hir::Defaultness::Default { has_value: true } =
+                                tcx.defaultness(item.id.owner_id)
+                            {
+                                let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity();
+                                if self.infcx.can_eq(param_env, assoc_ty, found) {
+                                    diag.span_label(
+                                        item.span,
+                                        "associated type defaults can't be assumed inside the \
+                                            trait defining them",
+                                    );
+                                    return true;
+                                }
+                            }
+                        }
+                        _ => {}
+                    }
+                }
+            }
+            hir::Node::Item(hir::Item {
+                kind: hir::ItemKind::Impl(hir::Impl { items, .. }),
+                ..
+            }) => {
+                for item in &items[..] {
+                    if let hir::AssocItemKind::Type = item.kind {
+                        let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity();
+                        if let hir::Defaultness::Default { has_value: true } =
+                            tcx.defaultness(item.id.owner_id)
+                            && self.infcx.can_eq(param_env, assoc_ty, found)
+                        {
+                            diag.span_label(
+                                item.span,
+                                "associated type is `default` and may be overridden",
+                            );
+                            return true;
+                        }
+                    }
+                }
+            }
+            _ => {}
+        }
+        false
+    }
+
+    /// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref`
+    /// requirement, provide a structured suggestion to constrain it to a given type `ty`.
+    ///
+    /// `is_bound_surely_present` indicates whether we know the bound we're looking for is
+    /// inside `bounds`. If that's the case then we can consider `bounds` containing only one
+    /// trait bound as the one we're looking for. This can help in cases where the associated
+    /// type is defined on a supertrait of the one present in the bounds.
+    fn constrain_generic_bound_associated_type_structured_suggestion(
+        &self,
+        diag: &mut Diag<'_>,
+        trait_ref: ty::TraitRef<'tcx>,
+        bounds: hir::GenericBounds<'_>,
+        assoc: ty::AssocItem,
+        assoc_args: &[ty::GenericArg<'tcx>],
+        ty: Ty<'tcx>,
+        msg: impl Fn() -> String,
+        is_bound_surely_present: bool,
+    ) -> bool {
+        // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting.
+
+        let trait_bounds = bounds.iter().filter_map(|bound| match bound {
+            hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => Some(ptr),
+            _ => None,
+        });
+
+        let matching_trait_bounds = trait_bounds
+            .clone()
+            .filter(|ptr| ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id))
+            .collect::<Vec<_>>();
+
+        let span = match &matching_trait_bounds[..] {
+            &[ptr] => ptr.span,
+            &[] if is_bound_surely_present => match &trait_bounds.collect::<Vec<_>>()[..] {
+                &[ptr] => ptr.span,
+                _ => return false,
+            },
+            _ => return false,
+        };
+
+        self.constrain_associated_type_structured_suggestion(diag, span, assoc, assoc_args, ty, msg)
+    }
+
+    /// Given a span corresponding to a bound, provide a structured suggestion to set an
+    /// associated type to a given type `ty`.
+    fn constrain_associated_type_structured_suggestion(
+        &self,
+        diag: &mut Diag<'_>,
+        span: Span,
+        assoc: ty::AssocItem,
+        assoc_args: &[ty::GenericArg<'tcx>],
+        ty: Ty<'tcx>,
+        msg: impl Fn() -> String,
+    ) -> bool {
+        let tcx = self.tcx;
+
+        if let Ok(has_params) =
+            tcx.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>'))
+        {
+            let (span, sugg) = if has_params {
+                let pos = span.hi() - BytePos(1);
+                let span = Span::new(pos, pos, span.ctxt(), span.parent());
+                (span, format!(", {} = {}", assoc.ident(tcx), ty))
+            } else {
+                let item_args = self.format_generic_args(assoc_args);
+                (span.shrink_to_hi(), format!("<{}{} = {}>", assoc.ident(tcx), item_args, ty))
+            };
+            diag.span_suggestion_verbose(span, msg(), sugg, MaybeIncorrect);
+            return true;
+        }
+        false
+    }
+
+    pub fn format_generic_args(&self, args: &[ty::GenericArg<'tcx>]) -> String {
+        FmtPrinter::print_string(self.tcx, hir::def::Namespace::TypeNS, |cx| {
+            cx.path_generic_args(|_| Ok(()), args)
+        })
+        .expect("could not write to `String`.")
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs
new file mode 100644
index 00000000000..e4a4ec125a5
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs
@@ -0,0 +1,1430 @@
+use std::iter;
+
+use rustc_data_structures::fx::FxIndexSet;
+use rustc_errors::{
+    struct_span_code_err, Applicability, Diag, Subdiagnostic, E0309, E0310, E0311, E0495,
+};
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::{self as hir, ParamName};
+use rustc_middle::bug;
+use rustc_middle::traits::ObligationCauseCode;
+use rustc_middle::ty::error::TypeError;
+use rustc_middle::ty::{self, IsSuggestable, Region, Ty, TyCtxt, TypeVisitableExt as _};
+use rustc_span::symbol::kw;
+use rustc_span::{BytePos, ErrorGuaranteed, Span, Symbol};
+use rustc_type_ir::Upcast as _;
+use tracing::{debug, instrument};
+
+use super::nice_region_error::find_anon_type;
+use super::ObligationCauseAsDiagArg;
+use crate::error_reporting::infer::ObligationCauseExt;
+use crate::error_reporting::TypeErrCtxt;
+use crate::errors::{
+    self, note_and_explain, FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound,
+    OutlivesContent, RefLongerThanData, RegionOriginNote, WhereClauseSuggestions,
+};
+use crate::fluent_generated as fluent;
+use crate::infer::region_constraints::GenericKind;
+use crate::infer::{self, InferCtxt, RegionResolutionError, RegionVariableOrigin, SubregionOrigin};
+
+impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
+    pub fn report_region_errors(
+        &self,
+        generic_param_scope: LocalDefId,
+        errors: &[RegionResolutionError<'tcx>],
+    ) -> ErrorGuaranteed {
+        assert!(!errors.is_empty());
+
+        if let Some(guaranteed) = self.infcx.tainted_by_errors() {
+            return guaranteed;
+        }
+
+        debug!("report_region_errors(): {} errors to start", errors.len());
+
+        // try to pre-process the errors, which will group some of them
+        // together into a `ProcessedErrors` group:
+        let errors = self.process_errors(errors);
+
+        debug!("report_region_errors: {} errors after preprocessing", errors.len());
+
+        let mut guar = None;
+        for error in errors {
+            debug!("report_region_errors: error = {:?}", error);
+
+            let e = if let Some(guar) =
+                self.try_report_nice_region_error(generic_param_scope, &error)
+            {
+                guar
+            } else {
+                match error.clone() {
+                    // These errors could indicate all manner of different
+                    // problems with many different solutions. Rather
+                    // than generate a "one size fits all" error, what we
+                    // attempt to do is go through a number of specific
+                    // scenarios and try to find the best way to present
+                    // the error. If all of these fails, we fall back to a rather
+                    // general bit of code that displays the error information
+                    RegionResolutionError::ConcreteFailure(origin, sub, sup) => {
+                        if sub.is_placeholder() || sup.is_placeholder() {
+                            self.report_placeholder_failure(generic_param_scope, origin, sub, sup)
+                                .emit()
+                        } else {
+                            self.report_concrete_failure(generic_param_scope, origin, sub, sup)
+                                .emit()
+                        }
+                    }
+
+                    RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => self
+                        .report_generic_bound_failure(
+                            generic_param_scope,
+                            origin.span(),
+                            Some(origin),
+                            param_ty,
+                            sub,
+                        ),
+
+                    RegionResolutionError::SubSupConflict(
+                        _,
+                        var_origin,
+                        sub_origin,
+                        sub_r,
+                        sup_origin,
+                        sup_r,
+                        _,
+                    ) => {
+                        if sub_r.is_placeholder() {
+                            self.report_placeholder_failure(
+                                generic_param_scope,
+                                sub_origin,
+                                sub_r,
+                                sup_r,
+                            )
+                            .emit()
+                        } else if sup_r.is_placeholder() {
+                            self.report_placeholder_failure(
+                                generic_param_scope,
+                                sup_origin,
+                                sub_r,
+                                sup_r,
+                            )
+                            .emit()
+                        } else {
+                            self.report_sub_sup_conflict(
+                                generic_param_scope,
+                                var_origin,
+                                sub_origin,
+                                sub_r,
+                                sup_origin,
+                                sup_r,
+                            )
+                        }
+                    }
+
+                    RegionResolutionError::UpperBoundUniverseConflict(
+                        _,
+                        _,
+                        _,
+                        sup_origin,
+                        sup_r,
+                    ) => {
+                        assert!(sup_r.is_placeholder());
+
+                        // Make a dummy value for the "sub region" --
+                        // this is the initial value of the
+                        // placeholder. In practice, we expect more
+                        // tailored errors that don't really use this
+                        // value.
+                        let sub_r = self.tcx.lifetimes.re_erased;
+
+                        self.report_placeholder_failure(
+                            generic_param_scope,
+                            sup_origin,
+                            sub_r,
+                            sup_r,
+                        )
+                        .emit()
+                    }
+
+                    RegionResolutionError::CannotNormalize(clause, origin) => {
+                        let clause: ty::Clause<'tcx> =
+                            clause.map_bound(ty::ClauseKind::TypeOutlives).upcast(self.tcx);
+                        self.tcx
+                            .dcx()
+                            .struct_span_err(origin.span(), format!("cannot normalize `{clause}`"))
+                            .emit()
+                    }
+                }
+            };
+
+            guar = Some(e)
+        }
+
+        guar.unwrap()
+    }
+
+    // This method goes through all the errors and try to group certain types
+    // of error together, for the purpose of suggesting explicit lifetime
+    // parameters to the user. This is done so that we can have a more
+    // complete view of what lifetimes should be the same.
+    // If the return value is an empty vector, it means that processing
+    // failed (so the return value of this method should not be used).
+    //
+    // The method also attempts to weed out messages that seem like
+    // duplicates that will be unhelpful to the end-user. But
+    // obviously it never weeds out ALL errors.
+    fn process_errors(
+        &self,
+        errors: &[RegionResolutionError<'tcx>],
+    ) -> Vec<RegionResolutionError<'tcx>> {
+        debug!("process_errors()");
+
+        // We want to avoid reporting generic-bound failures if we can
+        // avoid it: these have a very high rate of being unhelpful in
+        // practice. This is because they are basically secondary
+        // checks that test the state of the region graph after the
+        // rest of inference is done, and the other kinds of errors
+        // indicate that the region constraint graph is internally
+        // inconsistent, so these test results are likely to be
+        // meaningless.
+        //
+        // Therefore, we filter them out of the list unless they are
+        // the only thing in the list.
+
+        let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e {
+            RegionResolutionError::GenericBoundFailure(..) => true,
+            RegionResolutionError::ConcreteFailure(..)
+            | RegionResolutionError::SubSupConflict(..)
+            | RegionResolutionError::UpperBoundUniverseConflict(..)
+            | RegionResolutionError::CannotNormalize(..) => false,
+        };
+
+        let mut errors = if errors.iter().all(|e| is_bound_failure(e)) {
+            errors.to_owned()
+        } else {
+            errors.iter().filter(|&e| !is_bound_failure(e)).cloned().collect()
+        };
+
+        // sort the errors by span, for better error message stability.
+        errors.sort_by_key(|u| match *u {
+            RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(),
+            RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(),
+            RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _, _) => rvo.span(),
+            RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(),
+            RegionResolutionError::CannotNormalize(_, ref sro) => sro.span(),
+        });
+        errors
+    }
+
+    pub(super) fn note_region_origin(&self, err: &mut Diag<'_>, origin: &SubregionOrigin<'tcx>) {
+        match *origin {
+            infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
+                span: trace.cause.span,
+                requirement: ObligationCauseAsDiagArg(trace.cause.clone()),
+                expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)),
+            }
+            .add_to_diag(err),
+            infer::Reborrow(span) => {
+                RegionOriginNote::Plain { span, msg: fluent::trait_selection_reborrow }
+                    .add_to_diag(err)
+            }
+            infer::RelateObjectBound(span) => {
+                RegionOriginNote::Plain { span, msg: fluent::trait_selection_relate_object_bound }
+                    .add_to_diag(err);
+            }
+            infer::ReferenceOutlivesReferent(ty, span) => {
+                RegionOriginNote::WithName {
+                    span,
+                    msg: fluent::trait_selection_reference_outlives_referent,
+                    name: &self.ty_to_string(ty),
+                    continues: false,
+                }
+                .add_to_diag(err);
+            }
+            infer::RelateParamBound(span, ty, opt_span) => {
+                RegionOriginNote::WithName {
+                    span,
+                    msg: fluent::trait_selection_relate_param_bound,
+                    name: &self.ty_to_string(ty),
+                    continues: opt_span.is_some(),
+                }
+                .add_to_diag(err);
+                if let Some(span) = opt_span {
+                    RegionOriginNote::Plain {
+                        span,
+                        msg: fluent::trait_selection_relate_param_bound_2,
+                    }
+                    .add_to_diag(err);
+                }
+            }
+            infer::RelateRegionParamBound(span, _) => {
+                RegionOriginNote::Plain {
+                    span,
+                    msg: fluent::trait_selection_relate_region_param_bound,
+                }
+                .add_to_diag(err);
+            }
+            infer::CompareImplItemObligation { span, .. } => {
+                RegionOriginNote::Plain {
+                    span,
+                    msg: fluent::trait_selection_compare_impl_item_obligation,
+                }
+                .add_to_diag(err);
+            }
+            infer::CheckAssociatedTypeBounds { ref parent, .. } => {
+                self.note_region_origin(err, parent);
+            }
+            infer::AscribeUserTypeProvePredicate(span) => {
+                RegionOriginNote::Plain {
+                    span,
+                    msg: fluent::trait_selection_ascribe_user_type_prove_predicate,
+                }
+                .add_to_diag(err);
+            }
+        }
+    }
+
+    pub(super) fn report_concrete_failure(
+        &self,
+        generic_param_scope: LocalDefId,
+        origin: SubregionOrigin<'tcx>,
+        sub: Region<'tcx>,
+        sup: Region<'tcx>,
+    ) -> Diag<'a> {
+        let mut err = match origin {
+            infer::Subtype(box trace) => {
+                let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
+                let mut err = self.report_and_explain_type_error(trace, terr);
+                match (*sub, *sup) {
+                    (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {}
+                    (ty::RePlaceholder(_), _) => {
+                        note_and_explain_region(
+                            self.tcx,
+                            &mut err,
+                            generic_param_scope,
+                            "",
+                            sup,
+                            " doesn't meet the lifetime requirements",
+                            None,
+                        );
+                    }
+                    (_, ty::RePlaceholder(_)) => {
+                        note_and_explain_region(
+                            self.tcx,
+                            &mut err,
+                            generic_param_scope,
+                            "the required lifetime does not necessarily outlive ",
+                            sub,
+                            "",
+                            None,
+                        );
+                    }
+                    _ => {
+                        note_and_explain_region(
+                            self.tcx,
+                            &mut err,
+                            generic_param_scope,
+                            "",
+                            sup,
+                            "...",
+                            None,
+                        );
+                        note_and_explain_region(
+                            self.tcx,
+                            &mut err,
+                            generic_param_scope,
+                            "...does not necessarily outlive ",
+                            sub,
+                            "",
+                            None,
+                        );
+                    }
+                }
+                err
+            }
+            infer::Reborrow(span) => {
+                let reference_valid = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sub,
+                    None,
+                    note_and_explain::PrefixKind::RefValidFor,
+                    note_and_explain::SuffixKind::Continues,
+                );
+                let content_valid = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sup,
+                    None,
+                    note_and_explain::PrefixKind::ContentValidFor,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                self.dcx().create_err(OutlivesContent {
+                    span,
+                    notes: reference_valid.into_iter().chain(content_valid).collect(),
+                })
+            }
+            infer::RelateObjectBound(span) => {
+                let object_valid = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sub,
+                    None,
+                    note_and_explain::PrefixKind::TypeObjValidFor,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                let pointer_valid = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sup,
+                    None,
+                    note_and_explain::PrefixKind::SourcePointerValidFor,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                self.dcx().create_err(OutlivesBound {
+                    span,
+                    notes: object_valid.into_iter().chain(pointer_valid).collect(),
+                })
+            }
+            infer::RelateParamBound(span, ty, opt_span) => {
+                let prefix = match *sub {
+                    ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy,
+                    _ => note_and_explain::PrefixKind::TypeOutlive,
+                };
+                let suffix = if opt_span.is_some() {
+                    note_and_explain::SuffixKind::ReqByBinding
+                } else {
+                    note_and_explain::SuffixKind::Empty
+                };
+                let note = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sub,
+                    opt_span,
+                    prefix,
+                    suffix,
+                );
+                self.dcx().create_err(FulfillReqLifetime {
+                    span,
+                    ty: self.resolve_vars_if_possible(ty),
+                    note,
+                })
+            }
+            infer::RelateRegionParamBound(span, ty) => {
+                let param_instantiated = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sup,
+                    None,
+                    note_and_explain::PrefixKind::LfParamInstantiatedWith,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                let mut alt_span = None;
+                if let Some(ty) = ty
+                    && sub.is_static()
+                    && let ty::Dynamic(preds, _, ty::DynKind::Dyn) = ty.kind()
+                    && let Some(def_id) = preds.principal_def_id()
+                {
+                    for (clause, span) in
+                        self.tcx.predicates_of(def_id).instantiate_identity(self.tcx)
+                    {
+                        if let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(a, b)) =
+                            clause.kind().skip_binder()
+                            && let ty::Param(param) = a.kind()
+                            && param.name == kw::SelfUpper
+                            && b.is_static()
+                        {
+                            // Point at explicit `'static` bound on the trait (`trait T: 'static`).
+                            alt_span = Some(span);
+                        }
+                    }
+                }
+                let param_must_outlive = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sub,
+                    alt_span,
+                    note_and_explain::PrefixKind::LfParamMustOutlive,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                self.dcx().create_err(LfBoundNotSatisfied {
+                    span,
+                    notes: param_instantiated.into_iter().chain(param_must_outlive).collect(),
+                })
+            }
+            infer::ReferenceOutlivesReferent(ty, span) => {
+                let pointer_valid = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sub,
+                    None,
+                    note_and_explain::PrefixKind::PointerValidFor,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                let data_valid = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sup,
+                    None,
+                    note_and_explain::PrefixKind::DataValidFor,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                self.dcx().create_err(RefLongerThanData {
+                    span,
+                    ty: self.resolve_vars_if_possible(ty),
+                    notes: pointer_valid.into_iter().chain(data_valid).collect(),
+                })
+            }
+            infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => {
+                let mut err = self.report_extra_impl_obligation(
+                    span,
+                    impl_item_def_id,
+                    trait_item_def_id,
+                    &format!("`{sup}: {sub}`"),
+                );
+                // We should only suggest rewriting the `where` clause if the predicate is within that `where` clause
+                if let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id)
+                    && generics.where_clause_span.contains(span)
+                {
+                    self.suggest_copy_trait_method_bounds(
+                        trait_item_def_id,
+                        impl_item_def_id,
+                        &mut err,
+                    );
+                }
+                err
+            }
+            infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => {
+                let mut err = self.report_concrete_failure(generic_param_scope, *parent, sub, sup);
+
+                // Don't mention the item name if it's an RPITIT, since that'll just confuse
+                // folks.
+                if !self.tcx.is_impl_trait_in_trait(impl_item_def_id.to_def_id()) {
+                    let trait_item_span = self.tcx.def_span(trait_item_def_id);
+                    let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
+                    err.span_label(
+                        trait_item_span,
+                        format!("definition of `{item_name}` from trait"),
+                    );
+                }
+
+                self.suggest_copy_trait_method_bounds(
+                    trait_item_def_id,
+                    impl_item_def_id,
+                    &mut err,
+                );
+                err
+            }
+            infer::AscribeUserTypeProvePredicate(span) => {
+                let instantiated = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sup,
+                    None,
+                    note_and_explain::PrefixKind::LfInstantiatedWith,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                let must_outlive = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sub,
+                    None,
+                    note_and_explain::PrefixKind::LfMustOutlive,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                self.dcx().create_err(LfBoundNotSatisfied {
+                    span,
+                    notes: instantiated.into_iter().chain(must_outlive).collect(),
+                })
+            }
+        };
+        if sub.is_error() || sup.is_error() {
+            err.downgrade_to_delayed_bug();
+        }
+        err
+    }
+
+    pub fn suggest_copy_trait_method_bounds(
+        &self,
+        trait_item_def_id: DefId,
+        impl_item_def_id: LocalDefId,
+        err: &mut Diag<'_>,
+    ) {
+        // FIXME(compiler-errors): Right now this is only being used for region
+        // predicate mismatches. Ideally, we'd use it for *all* predicate mismatches,
+        // but right now it's not really very smart when it comes to implicit `Sized`
+        // predicates and bounds on the trait itself.
+
+        let Some(impl_def_id) = self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx)
+        else {
+            return;
+        };
+        let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else {
+            return;
+        };
+        let trait_args = trait_ref
+            .instantiate_identity()
+            // Replace the explicit self type with `Self` for better suggestion rendering
+            .with_self_ty(self.tcx, Ty::new_param(self.tcx, 0, kw::SelfUpper))
+            .args;
+        let trait_item_args = ty::GenericArgs::identity_for_item(self.tcx, impl_item_def_id)
+            .rebase_onto(self.tcx, impl_def_id, trait_args);
+
+        let Ok(trait_predicates) =
+            self.tcx
+                .explicit_predicates_of(trait_item_def_id)
+                .instantiate_own(self.tcx, trait_item_args)
+                .map(|(pred, _)| {
+                    if pred.is_suggestable(self.tcx, false) {
+                        Ok(pred.to_string())
+                    } else {
+                        Err(())
+                    }
+                })
+                .collect::<Result<Vec<_>, ()>>()
+        else {
+            return;
+        };
+
+        let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else {
+            return;
+        };
+
+        let suggestion = if trait_predicates.is_empty() {
+            WhereClauseSuggestions::Remove { span: generics.where_clause_span }
+        } else {
+            let space = if generics.where_clause_span.is_empty() { " " } else { "" };
+            WhereClauseSuggestions::CopyPredicates {
+                span: generics.where_clause_span,
+                space,
+                trait_predicates: trait_predicates.join(", "),
+            }
+        };
+        err.subdiagnostic(suggestion);
+    }
+
+    pub(super) fn report_placeholder_failure(
+        &self,
+        generic_param_scope: LocalDefId,
+        placeholder_origin: SubregionOrigin<'tcx>,
+        sub: Region<'tcx>,
+        sup: Region<'tcx>,
+    ) -> Diag<'a> {
+        // I can't think how to do better than this right now. -nikomatsakis
+        debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure");
+        match placeholder_origin {
+            infer::Subtype(box ref trace)
+                if matches!(
+                    &trace.cause.code().peel_derives(),
+                    ObligationCauseCode::WhereClause(..)
+                        | ObligationCauseCode::WhereClauseInExpr(..)
+                ) =>
+            {
+                // Hack to get around the borrow checker because trace.cause has an `Rc`.
+                if let ObligationCauseCode::WhereClause(_, span)
+                | ObligationCauseCode::WhereClauseInExpr(_, span, ..) =
+                    &trace.cause.code().peel_derives()
+                    && !span.is_dummy()
+                {
+                    let span = *span;
+                    self.report_concrete_failure(generic_param_scope, placeholder_origin, sub, sup)
+                        .with_span_note(span, "the lifetime requirement is introduced here")
+                } else {
+                    unreachable!(
+                        "control flow ensures we have a `BindingObligation` or `WhereClauseInExpr` here..."
+                    )
+                }
+            }
+            infer::Subtype(box trace) => {
+                let terr = TypeError::RegionsPlaceholderMismatch;
+                return self.report_and_explain_type_error(trace, terr);
+            }
+            _ => {
+                return self.report_concrete_failure(
+                    generic_param_scope,
+                    placeholder_origin,
+                    sub,
+                    sup,
+                );
+            }
+        }
+    }
+
+    pub fn report_generic_bound_failure(
+        &self,
+        generic_param_scope: LocalDefId,
+        span: Span,
+        origin: Option<SubregionOrigin<'tcx>>,
+        bound_kind: GenericKind<'tcx>,
+        sub: Region<'tcx>,
+    ) -> ErrorGuaranteed {
+        self.construct_generic_bound_failure(generic_param_scope, span, origin, bound_kind, sub)
+            .emit()
+    }
+
+    pub fn construct_generic_bound_failure(
+        &self,
+        generic_param_scope: LocalDefId,
+        span: Span,
+        origin: Option<SubregionOrigin<'tcx>>,
+        bound_kind: GenericKind<'tcx>,
+        sub: Region<'tcx>,
+    ) -> Diag<'a> {
+        if let Some(SubregionOrigin::CompareImplItemObligation {
+            span,
+            impl_item_def_id,
+            trait_item_def_id,
+        }) = origin
+        {
+            return self.report_extra_impl_obligation(
+                span,
+                impl_item_def_id,
+                trait_item_def_id,
+                &format!("`{bound_kind}: {sub}`"),
+            );
+        }
+
+        let labeled_user_string = match bound_kind {
+            GenericKind::Param(ref p) => format!("the parameter type `{p}`"),
+            GenericKind::Placeholder(ref p) => format!("the placeholder type `{p:?}`"),
+            GenericKind::Alias(ref p) => match p.kind(self.tcx) {
+                ty::Projection | ty::Inherent => {
+                    format!("the associated type `{p}`")
+                }
+                ty::Weak => format!("the type alias `{p}`"),
+                ty::Opaque => format!("the opaque type `{p}`"),
+            },
+        };
+
+        let mut err = self
+            .tcx
+            .dcx()
+            .struct_span_err(span, format!("{labeled_user_string} may not live long enough"));
+        err.code(match sub.kind() {
+            ty::ReEarlyParam(_) | ty::ReLateParam(_) if sub.has_name() => E0309,
+            ty::ReStatic => E0310,
+            _ => E0311,
+        });
+
+        '_explain: {
+            let (description, span) = match sub.kind() {
+                ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => {
+                    msg_span_from_named_region(self.tcx, generic_param_scope, sub, Some(span))
+                }
+                _ => (format!("lifetime `{sub}`"), Some(span)),
+            };
+            let prefix = format!("{labeled_user_string} must be valid for ");
+            label_msg_span(&mut err, &prefix, description, span, "...");
+            if let Some(origin) = origin {
+                self.note_region_origin(&mut err, &origin);
+            }
+        }
+
+        'suggestion: {
+            let msg = "consider adding an explicit lifetime bound";
+
+            if (bound_kind, sub).has_infer_regions()
+                || (bound_kind, sub).has_placeholders()
+                || !bound_kind.is_suggestable(self.tcx, false)
+            {
+                let lt_name = sub.get_name_or_anon().to_string();
+                err.help(format!("{msg} `{bound_kind}: {lt_name}`..."));
+                break 'suggestion;
+            }
+
+            let mut generic_param_scope = generic_param_scope;
+            while self.tcx.def_kind(generic_param_scope) == DefKind::OpaqueTy {
+                generic_param_scope = self.tcx.local_parent(generic_param_scope);
+            }
+
+            // type_param_sugg_span is (span, has_bounds, needs_parentheses)
+            let (type_scope, type_param_sugg_span) = match bound_kind {
+                GenericKind::Param(param) => {
+                    let generics = self.tcx.generics_of(generic_param_scope);
+                    let type_param = generics.type_param(param, self.tcx);
+                    let def_id = type_param.def_id.expect_local();
+                    let scope = self.tcx.local_def_id_to_hir_id(def_id).owner.def_id;
+                    // Get the `hir::Param` to verify whether it already has any bounds.
+                    // We do this to avoid suggesting code that ends up as `T: 'a'b`,
+                    // instead we suggest `T: 'a + 'b` in that case.
+                    let hir_generics = self.tcx.hir().get_generics(scope).unwrap();
+                    let sugg_span = match hir_generics.bounds_span_for_suggestions(def_id) {
+                        Some((span, open_paren_sp)) => Some((span, true, open_paren_sp)),
+                        // If `param` corresponds to `Self`, no usable suggestion span.
+                        None if generics.has_self && param.index == 0 => None,
+                        None => {
+                            let span = if let Some(param) =
+                                hir_generics.params.iter().find(|param| param.def_id == def_id)
+                                && let ParamName::Plain(ident) = param.name
+                            {
+                                ident.span.shrink_to_hi()
+                            } else {
+                                let span = self.tcx.def_span(def_id);
+                                span.shrink_to_hi()
+                            };
+                            Some((span, false, None))
+                        }
+                    };
+                    (scope, sugg_span)
+                }
+                _ => (generic_param_scope, None),
+            };
+            let suggestion_scope = {
+                let lifetime_scope = match sub.kind() {
+                    ty::ReStatic => hir::def_id::CRATE_DEF_ID,
+                    _ => match self.tcx.is_suitable_region(generic_param_scope, sub) {
+                        Some(info) => info.def_id,
+                        None => generic_param_scope,
+                    },
+                };
+                match self.tcx.is_descendant_of(type_scope.into(), lifetime_scope.into()) {
+                    true => type_scope,
+                    false => lifetime_scope,
+                }
+            };
+
+            let mut suggs = vec![];
+            let lt_name = self.suggest_name_region(generic_param_scope, sub, &mut suggs);
+
+            if let Some((sp, has_lifetimes, open_paren_sp)) = type_param_sugg_span
+                && suggestion_scope == type_scope
+            {
+                let suggestion =
+                    if has_lifetimes { format!(" + {lt_name}") } else { format!(": {lt_name}") };
+
+                if let Some(open_paren_sp) = open_paren_sp {
+                    suggs.push((open_paren_sp, "(".to_string()));
+                    suggs.push((sp, format!("){suggestion}")));
+                } else {
+                    suggs.push((sp, suggestion))
+                }
+            } else if let GenericKind::Alias(ref p) = bound_kind
+                && let ty::Projection = p.kind(self.tcx)
+                && let DefKind::AssocTy = self.tcx.def_kind(p.def_id)
+                && let Some(ty::ImplTraitInTraitData::Trait { .. }) =
+                    self.tcx.opt_rpitit_info(p.def_id)
+            {
+                // The lifetime found in the `impl` is longer than the one on the RPITIT.
+                // Do not suggest `<Type as Trait>::{opaque}: 'static`.
+            } else if let Some(generics) = self.tcx.hir().get_generics(suggestion_scope) {
+                let pred = format!("{bound_kind}: {lt_name}");
+                let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred);
+                suggs.push((generics.tail_span_for_predicate_suggestion(), suggestion))
+            } else {
+                let consider = format!("{msg} `{bound_kind}: {sub}`...");
+                err.help(consider);
+            }
+
+            if !suggs.is_empty() {
+                err.multipart_suggestion_verbose(
+                    msg,
+                    suggs,
+                    Applicability::MaybeIncorrect, // Issue #41966
+                );
+            }
+        }
+
+        err
+    }
+
+    pub fn suggest_name_region(
+        &self,
+        generic_param_scope: LocalDefId,
+        lifetime: Region<'tcx>,
+        add_lt_suggs: &mut Vec<(Span, String)>,
+    ) -> String {
+        struct LifetimeReplaceVisitor<'tcx, 'a> {
+            tcx: TyCtxt<'tcx>,
+            needle: hir::LifetimeName,
+            new_lt: &'a str,
+            add_lt_suggs: &'a mut Vec<(Span, String)>,
+        }
+
+        impl<'hir, 'tcx> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'tcx, '_> {
+            fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) {
+                if lt.res == self.needle {
+                    let (pos, span) = lt.suggestion_position();
+                    let new_lt = &self.new_lt;
+                    let sugg = match pos {
+                        hir::LifetimeSuggestionPosition::Normal => format!("{new_lt}"),
+                        hir::LifetimeSuggestionPosition::Ampersand => format!("{new_lt} "),
+                        hir::LifetimeSuggestionPosition::ElidedPath => format!("<{new_lt}>"),
+                        hir::LifetimeSuggestionPosition::ElidedPathArgument => {
+                            format!("{new_lt}, ")
+                        }
+                        hir::LifetimeSuggestionPosition::ObjectDefault => format!("+ {new_lt}"),
+                    };
+                    self.add_lt_suggs.push((span, sugg));
+                }
+            }
+
+            fn visit_ty(&mut self, ty: &'hir hir::Ty<'hir>) {
+                let hir::TyKind::OpaqueDef(item_id, _, _) = ty.kind else {
+                    return hir::intravisit::walk_ty(self, ty);
+                };
+                let opaque_ty = self.tcx.hir().item(item_id).expect_opaque_ty();
+                if let Some(&(_, b)) =
+                    opaque_ty.lifetime_mapping.iter().find(|&(a, _)| a.res == self.needle)
+                {
+                    let prev_needle =
+                        std::mem::replace(&mut self.needle, hir::LifetimeName::Param(b));
+                    for bound in opaque_ty.bounds {
+                        self.visit_param_bound(bound);
+                    }
+                    self.needle = prev_needle;
+                }
+            }
+        }
+
+        let (lifetime_def_id, lifetime_scope) =
+            match self.tcx.is_suitable_region(generic_param_scope, lifetime) {
+                Some(info) if !lifetime.has_name() => {
+                    (info.bound_region.get_id().unwrap().expect_local(), info.def_id)
+                }
+                _ => return lifetime.get_name_or_anon().to_string(),
+            };
+
+        let new_lt = {
+            let generics = self.tcx.generics_of(lifetime_scope);
+            let mut used_names =
+                iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p)))
+                    .flat_map(|g| &g.own_params)
+                    .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))
+                    .map(|p| p.name)
+                    .collect::<Vec<_>>();
+            let hir_id = self.tcx.local_def_id_to_hir_id(lifetime_scope);
+            // consider late-bound lifetimes ...
+            used_names.extend(self.tcx.late_bound_vars(hir_id).into_iter().filter_map(
+                |p| match p {
+                    ty::BoundVariableKind::Region(lt) => lt.get_name(),
+                    _ => None,
+                },
+            ));
+            (b'a'..=b'z')
+                .map(|c| format!("'{}", c as char))
+                .find(|candidate| !used_names.iter().any(|e| e.as_str() == candidate))
+                .unwrap_or("'lt".to_string())
+        };
+
+        let mut visitor = LifetimeReplaceVisitor {
+            tcx: self.tcx,
+            needle: hir::LifetimeName::Param(lifetime_def_id),
+            add_lt_suggs,
+            new_lt: &new_lt,
+        };
+        match self.tcx.expect_hir_owner_node(lifetime_scope) {
+            hir::OwnerNode::Item(i) => visitor.visit_item(i),
+            hir::OwnerNode::ForeignItem(i) => visitor.visit_foreign_item(i),
+            hir::OwnerNode::ImplItem(i) => visitor.visit_impl_item(i),
+            hir::OwnerNode::TraitItem(i) => visitor.visit_trait_item(i),
+            hir::OwnerNode::Crate(_) => bug!("OwnerNode::Crate doesn't not have generics"),
+            hir::OwnerNode::Synthetic => unreachable!(),
+        }
+
+        let ast_generics = self.tcx.hir().get_generics(lifetime_scope).unwrap();
+        let sugg = ast_generics
+            .span_for_lifetime_suggestion()
+            .map(|span| (span, format!("{new_lt}, ")))
+            .unwrap_or_else(|| (ast_generics.span, format!("<{new_lt}>")));
+        add_lt_suggs.push(sugg);
+
+        new_lt
+    }
+
+    fn report_sub_sup_conflict(
+        &self,
+        generic_param_scope: LocalDefId,
+        var_origin: RegionVariableOrigin,
+        sub_origin: SubregionOrigin<'tcx>,
+        sub_region: Region<'tcx>,
+        sup_origin: SubregionOrigin<'tcx>,
+        sup_region: Region<'tcx>,
+    ) -> ErrorGuaranteed {
+        let mut err = self.report_inference_failure(var_origin);
+
+        note_and_explain_region(
+            self.tcx,
+            &mut err,
+            generic_param_scope,
+            "first, the lifetime cannot outlive ",
+            sup_region,
+            "...",
+            None,
+        );
+
+        debug!("report_sub_sup_conflict: var_origin={:?}", var_origin);
+        debug!("report_sub_sup_conflict: sub_region={:?}", sub_region);
+        debug!("report_sub_sup_conflict: sub_origin={:?}", sub_origin);
+        debug!("report_sub_sup_conflict: sup_region={:?}", sup_region);
+        debug!("report_sub_sup_conflict: sup_origin={:?}", sup_origin);
+
+        if let infer::Subtype(ref sup_trace) = sup_origin
+            && let infer::Subtype(ref sub_trace) = sub_origin
+            && let Some((sup_expected, sup_found, _)) = self.values_str(sup_trace.values)
+            && let Some((sub_expected, sub_found, _)) = self.values_str(sub_trace.values)
+            && sub_expected == sup_expected
+            && sub_found == sup_found
+        {
+            note_and_explain_region(
+                self.tcx,
+                &mut err,
+                generic_param_scope,
+                "...but the lifetime must also be valid for ",
+                sub_region,
+                "...",
+                None,
+            );
+            err.span_note(
+                sup_trace.cause.span,
+                format!("...so that the {}", sup_trace.cause.as_requirement_str()),
+            );
+
+            err.note_expected_found(&"", sup_expected, &"", sup_found);
+            return if sub_region.is_error() | sup_region.is_error() {
+                err.delay_as_bug()
+            } else {
+                err.emit()
+            };
+        }
+
+        self.note_region_origin(&mut err, &sup_origin);
+
+        note_and_explain_region(
+            self.tcx,
+            &mut err,
+            generic_param_scope,
+            "but, the lifetime must be valid for ",
+            sub_region,
+            "...",
+            None,
+        );
+
+        self.note_region_origin(&mut err, &sub_origin);
+        if sub_region.is_error() | sup_region.is_error() { err.delay_as_bug() } else { err.emit() }
+    }
+
+    fn report_inference_failure(&self, var_origin: RegionVariableOrigin) -> Diag<'_> {
+        let br_string = |br: ty::BoundRegionKind| {
+            let mut s = match br {
+                ty::BrNamed(_, name) => name.to_string(),
+                _ => String::new(),
+            };
+            if !s.is_empty() {
+                s.push(' ');
+            }
+            s
+        };
+        let var_description = match var_origin {
+            infer::MiscVariable(_) => String::new(),
+            infer::PatternRegion(_) => " for pattern".to_string(),
+            infer::AddrOfRegion(_) => " for borrow expression".to_string(),
+            infer::Autoref(_) => " for autoref".to_string(),
+            infer::Coercion(_) => " for automatic coercion".to_string(),
+            infer::BoundRegion(_, br, infer::FnCall) => {
+                format!(" for lifetime parameter {}in function call", br_string(br))
+            }
+            infer::BoundRegion(_, br, infer::HigherRankedType) => {
+                format!(" for lifetime parameter {}in generic type", br_string(br))
+            }
+            infer::BoundRegion(_, br, infer::AssocTypeProjection(def_id)) => format!(
+                " for lifetime parameter {}in trait containing associated type `{}`",
+                br_string(br),
+                self.tcx.associated_item(def_id).name
+            ),
+            infer::RegionParameterDefinition(_, name) => {
+                format!(" for lifetime parameter `{name}`")
+            }
+            infer::UpvarRegion(ref upvar_id, _) => {
+                let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
+                format!(" for capture of `{var_name}` by closure")
+            }
+            infer::Nll(..) => bug!("NLL variable found in lexical phase"),
+        };
+
+        struct_span_code_err!(
+            self.dcx(),
+            var_origin.span(),
+            E0495,
+            "cannot infer an appropriate lifetime{} due to conflicting requirements",
+            var_description
+        )
+    }
+}
+
+pub(super) fn note_and_explain_region<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    err: &mut Diag<'_>,
+    generic_param_scope: LocalDefId,
+    prefix: &str,
+    region: ty::Region<'tcx>,
+    suffix: &str,
+    alt_span: Option<Span>,
+) {
+    let (description, span) = match *region {
+        ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReStatic => {
+            msg_span_from_named_region(tcx, generic_param_scope, region, alt_span)
+        }
+
+        ty::ReError(_) => return,
+
+        // FIXME(#125431): `ReVar` shouldn't reach here.
+        ty::ReVar(_) => (format!("lifetime `{region}`"), alt_span),
+
+        ty::ReBound(..) | ty::ReErased => {
+            bug!("unexpected region for note_and_explain_region: {:?}", region);
+        }
+    };
+
+    emit_msg_span(err, prefix, description, span, suffix);
+}
+
+fn explain_free_region<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    err: &mut Diag<'_>,
+    generic_param_scope: LocalDefId,
+    prefix: &str,
+    region: ty::Region<'tcx>,
+    suffix: &str,
+) {
+    let (description, span) = msg_span_from_named_region(tcx, generic_param_scope, region, None);
+
+    label_msg_span(err, prefix, description, span, suffix);
+}
+
+fn msg_span_from_named_region<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    generic_param_scope: LocalDefId,
+    region: ty::Region<'tcx>,
+    alt_span: Option<Span>,
+) -> (String, Option<Span>) {
+    match *region {
+        ty::ReEarlyParam(br) => {
+            let param_def_id = tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id;
+            let span = tcx.def_span(param_def_id);
+            let text = if br.has_name() {
+                format!("the lifetime `{}` as defined here", br.name)
+            } else {
+                "the anonymous lifetime as defined here".to_string()
+            };
+            (text, Some(span))
+        }
+        ty::ReLateParam(ref fr) => {
+            if !fr.bound_region.is_named()
+                && let Some((ty, _)) =
+                    find_anon_type(tcx, generic_param_scope, region, &fr.bound_region)
+            {
+                ("the anonymous lifetime defined here".to_string(), Some(ty.span))
+            } else {
+                match fr.bound_region {
+                    ty::BoundRegionKind::BrNamed(param_def_id, name) => {
+                        let span = tcx.def_span(param_def_id);
+                        let text = if name == kw::UnderscoreLifetime {
+                            "the anonymous lifetime as defined here".to_string()
+                        } else {
+                            format!("the lifetime `{name}` as defined here")
+                        };
+                        (text, Some(span))
+                    }
+                    ty::BrAnon => (
+                        "the anonymous lifetime as defined here".to_string(),
+                        Some(tcx.def_span(generic_param_scope)),
+                    ),
+                    _ => (
+                        format!("the lifetime `{region}` as defined here"),
+                        Some(tcx.def_span(generic_param_scope)),
+                    ),
+                }
+            }
+        }
+        ty::ReStatic => ("the static lifetime".to_owned(), alt_span),
+        ty::RePlaceholder(ty::PlaceholderRegion {
+            bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrNamed(def_id, name), .. },
+            ..
+        }) => (format!("the lifetime `{name}` as defined here"), Some(tcx.def_span(def_id))),
+        ty::RePlaceholder(ty::PlaceholderRegion {
+            bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon, .. },
+            ..
+        }) => ("an anonymous lifetime".to_owned(), None),
+        _ => bug!("{:?}", region),
+    }
+}
+
+fn emit_msg_span(
+    err: &mut Diag<'_>,
+    prefix: &str,
+    description: String,
+    span: Option<Span>,
+    suffix: &str,
+) {
+    let message = format!("{prefix}{description}{suffix}");
+
+    if let Some(span) = span {
+        err.span_note(span, message);
+    } else {
+        err.note(message);
+    }
+}
+
+fn label_msg_span(
+    err: &mut Diag<'_>,
+    prefix: &str,
+    description: String,
+    span: Option<Span>,
+    suffix: &str,
+) {
+    let message = format!("{prefix}{description}{suffix}");
+
+    if let Some(span) = span {
+        err.span_label(span, message);
+    } else {
+        err.note(message);
+    }
+}
+
+#[instrument(level = "trace", skip(infcx))]
+pub fn unexpected_hidden_region_diagnostic<'a, 'tcx>(
+    infcx: &'a InferCtxt<'tcx>,
+    generic_param_scope: LocalDefId,
+    span: Span,
+    hidden_ty: Ty<'tcx>,
+    hidden_region: ty::Region<'tcx>,
+    opaque_ty_key: ty::OpaqueTypeKey<'tcx>,
+) -> Diag<'a> {
+    let tcx = infcx.tcx;
+    let mut err = infcx.dcx().create_err(errors::OpaqueCapturesLifetime {
+        span,
+        opaque_ty: Ty::new_opaque(tcx, opaque_ty_key.def_id.to_def_id(), opaque_ty_key.args),
+        opaque_ty_span: tcx.def_span(opaque_ty_key.def_id),
+    });
+
+    // Explain the region we are capturing.
+    match *hidden_region {
+        ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => {
+            // Assuming regionck succeeded (*), we ought to always be
+            // capturing *some* region from the fn header, and hence it
+            // ought to be free. So under normal circumstances, we will go
+            // down this path which gives a decent human readable
+            // explanation.
+            //
+            // (*) if not, the `tainted_by_errors` field would be set to
+            // `Some(ErrorGuaranteed)` in any case, so we wouldn't be here at all.
+            explain_free_region(
+                tcx,
+                &mut err,
+                generic_param_scope,
+                &format!("hidden type `{hidden_ty}` captures "),
+                hidden_region,
+                "",
+            );
+            if let Some(_) = tcx.is_suitable_region(generic_param_scope, hidden_region) {
+                suggest_precise_capturing(tcx, opaque_ty_key.def_id, hidden_region, &mut err);
+            }
+        }
+        ty::RePlaceholder(_) => {
+            explain_free_region(
+                tcx,
+                &mut err,
+                generic_param_scope,
+                &format!("hidden type `{}` captures ", hidden_ty),
+                hidden_region,
+                "",
+            );
+        }
+        ty::ReError(_) => {
+            err.downgrade_to_delayed_bug();
+        }
+        _ => {
+            // Ugh. This is a painful case: the hidden region is not one
+            // that we can easily summarize or explain. This can happen
+            // in a case like
+            // `tests/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`:
+            //
+            // ```
+            // fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> {
+            //   if condition() { a } else { b }
+            // }
+            // ```
+            //
+            // Here the captured lifetime is the intersection of `'a` and
+            // `'b`, which we can't quite express.
+
+            // We can at least report a really cryptic error for now.
+            note_and_explain_region(
+                tcx,
+                &mut err,
+                generic_param_scope,
+                &format!("hidden type `{hidden_ty}` captures "),
+                hidden_region,
+                "",
+                None,
+            );
+        }
+    }
+
+    err
+}
+
+fn suggest_precise_capturing<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    opaque_def_id: LocalDefId,
+    captured_lifetime: ty::Region<'tcx>,
+    diag: &mut Diag<'_>,
+) {
+    let hir::OpaqueTy { bounds, origin, .. } =
+        tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty();
+
+    let hir::OpaqueTyOrigin::FnReturn(fn_def_id) = *origin else {
+        return;
+    };
+
+    let new_lifetime = Symbol::intern(&captured_lifetime.to_string());
+
+    if let Some((args, span)) = bounds.iter().find_map(|bound| match bound {
+        hir::GenericBound::Use(args, span) => Some((args, span)),
+        _ => None,
+    }) {
+        let last_lifetime_span = args.iter().rev().find_map(|arg| match arg {
+            hir::PreciseCapturingArg::Lifetime(lt) => Some(lt.ident.span),
+            _ => None,
+        });
+
+        let first_param_span = args.iter().find_map(|arg| match arg {
+            hir::PreciseCapturingArg::Param(p) => Some(p.ident.span),
+            _ => None,
+        });
+
+        let (span, pre, post) = if let Some(last_lifetime_span) = last_lifetime_span {
+            (last_lifetime_span.shrink_to_hi(), ", ", "")
+        } else if let Some(first_param_span) = first_param_span {
+            (first_param_span.shrink_to_lo(), "", ", ")
+        } else {
+            // If we have no args, then have `use<>` and need to fall back to using
+            // span math. This sucks, but should be reliable due to the construction
+            // of the `use<>` span.
+            (span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(), "", "")
+        };
+
+        diag.subdiagnostic(errors::AddPreciseCapturing::Existing { span, new_lifetime, pre, post });
+    } else {
+        let mut captured_lifetimes = FxIndexSet::default();
+        let mut captured_non_lifetimes = FxIndexSet::default();
+
+        let variances = tcx.variances_of(opaque_def_id);
+        let mut generics = tcx.generics_of(opaque_def_id);
+        let mut synthetics = vec![];
+        loop {
+            for param in &generics.own_params {
+                if variances[param.index as usize] == ty::Bivariant {
+                    continue;
+                }
+
+                match param.kind {
+                    ty::GenericParamDefKind::Lifetime => {
+                        captured_lifetimes.insert(param.name);
+                    }
+                    ty::GenericParamDefKind::Type { synthetic: true, .. } => {
+                        synthetics.push((tcx.def_span(param.def_id), param.name));
+                    }
+                    ty::GenericParamDefKind::Type { .. }
+                    | ty::GenericParamDefKind::Const { .. } => {
+                        captured_non_lifetimes.insert(param.name);
+                    }
+                }
+            }
+
+            if let Some(parent) = generics.parent {
+                generics = tcx.generics_of(parent);
+            } else {
+                break;
+            }
+        }
+
+        if !captured_lifetimes.insert(new_lifetime) {
+            // Uh, strange. This lifetime appears to already be captured...
+            return;
+        }
+
+        if synthetics.is_empty() {
+            let concatenated_bounds = captured_lifetimes
+                .into_iter()
+                .chain(captured_non_lifetimes)
+                .map(|sym| sym.to_string())
+                .collect::<Vec<_>>()
+                .join(", ");
+
+            diag.subdiagnostic(errors::AddPreciseCapturing::New {
+                span: tcx.def_span(opaque_def_id).shrink_to_hi(),
+                new_lifetime,
+                concatenated_bounds,
+            });
+        } else {
+            let mut next_fresh_param = || {
+                ["T", "U", "V", "W", "X", "Y", "A", "B", "C"]
+                    .into_iter()
+                    .map(Symbol::intern)
+                    .chain((0..).map(|i| Symbol::intern(&format!("T{i}"))))
+                    .find(|s| captured_non_lifetimes.insert(*s))
+                    .unwrap()
+            };
+
+            let mut new_params = String::new();
+            let mut suggs = vec![];
+            let mut apit_spans = vec![];
+
+            for (i, (span, name)) in synthetics.into_iter().enumerate() {
+                apit_spans.push(span);
+
+                let fresh_param = next_fresh_param();
+
+                // Suggest renaming.
+                suggs.push((span, fresh_param.to_string()));
+
+                // Super jank. Turn `impl Trait` into `T: Trait`.
+                //
+                // This currently involves stripping the `impl` from the name of
+                // the parameter, since APITs are always named after how they are
+                // rendered in the AST. This sucks! But to recreate the bound list
+                // from the APIT itself would be miserable, so we're stuck with
+                // this for now!
+                if i > 0 {
+                    new_params += ", ";
+                }
+                let name_as_bounds = name.as_str().trim_start_matches("impl").trim_start();
+                new_params += fresh_param.as_str();
+                new_params += ": ";
+                new_params += name_as_bounds;
+            }
+
+            let Some(generics) = tcx.hir().get_generics(fn_def_id) else {
+                // This shouldn't happen, but don't ICE.
+                return;
+            };
+
+            // Add generics or concatenate to the end of the list.
+            suggs.push(if let Some(params_span) = generics.span_for_param_suggestion() {
+                (params_span, format!(", {new_params}"))
+            } else {
+                (generics.span, format!("<{new_params}>"))
+            });
+
+            let concatenated_bounds = captured_lifetimes
+                .into_iter()
+                .chain(captured_non_lifetimes)
+                .map(|sym| sym.to_string())
+                .collect::<Vec<_>>()
+                .join(", ");
+
+            suggs.push((
+                tcx.def_span(opaque_def_id).shrink_to_hi(),
+                format!(" + use<{concatenated_bounds}>"),
+            ));
+
+            diag.subdiagnostic(errors::AddPreciseCapturingAndParams {
+                suggs,
+                new_lifetime,
+                apit_spans,
+            });
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/sub_relations.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/sub_relations.rs
new file mode 100644
index 00000000000..ef26a8ff7b8
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/sub_relations.rs
@@ -0,0 +1,81 @@
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::undo_log::NoUndo;
+use rustc_data_structures::unify as ut;
+use rustc_middle::ty;
+
+use crate::infer::InferCtxt;
+
+#[derive(Debug, Copy, Clone, PartialEq)]
+struct SubId(u32);
+impl ut::UnifyKey for SubId {
+    type Value = ();
+    #[inline]
+    fn index(&self) -> u32 {
+        self.0
+    }
+    #[inline]
+    fn from_index(i: u32) -> SubId {
+        SubId(i)
+    }
+    fn tag() -> &'static str {
+        "SubId"
+    }
+}
+
+/// When reporting ambiguity errors, we sometimes want to
+/// treat all inference vars which are subtypes of each
+/// others as if they are equal. For this case we compute
+/// the transitive closure of our subtype obligations here.
+///
+/// E.g. when encountering ambiguity errors, we want to suggest
+/// specifying some method argument or to add a type annotation
+/// to a local variable. Because subtyping cannot change the
+/// shape of a type, it's fine if the cause of the ambiguity error
+/// is only related to the suggested variable via subtyping.
+///
+/// Even for something like `let x = returns_arg(); x.method();` the
+/// type of `x` is only a supertype of the argument of `returns_arg`. We
+/// still want to suggest specifying the type of the argument.
+#[derive(Default)]
+pub struct SubRelations {
+    map: FxHashMap<ty::TyVid, SubId>,
+    table: ut::UnificationTableStorage<SubId>,
+}
+
+impl SubRelations {
+    fn get_id<'tcx>(&mut self, infcx: &InferCtxt<'tcx>, vid: ty::TyVid) -> SubId {
+        let root_vid = infcx.root_var(vid);
+        *self.map.entry(root_vid).or_insert_with(|| self.table.with_log(&mut NoUndo).new_key(()))
+    }
+
+    pub fn add_constraints<'tcx>(
+        &mut self,
+        infcx: &InferCtxt<'tcx>,
+        obls: impl IntoIterator<Item = ty::Predicate<'tcx>>,
+    ) {
+        for p in obls {
+            let (a, b) = match p.kind().skip_binder() {
+                ty::PredicateKind::Subtype(ty::SubtypePredicate { a_is_expected: _, a, b }) => {
+                    (a, b)
+                }
+                ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => (a, b),
+                _ => continue,
+            };
+
+            match (a.kind(), b.kind()) {
+                (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => {
+                    let a = self.get_id(infcx, a_vid);
+                    let b = self.get_id(infcx, b_vid);
+                    self.table.with_log(&mut NoUndo).unify_var_var(a, b).unwrap();
+                }
+                _ => continue,
+            }
+        }
+    }
+
+    pub fn unified<'tcx>(&mut self, infcx: &InferCtxt<'tcx>, a: ty::TyVid, b: ty::TyVid) -> bool {
+        let a = self.get_id(infcx, a);
+        let b = self.get_id(infcx, b);
+        self.table.with_log(&mut NoUndo).unioned(a, b)
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs
new file mode 100644
index 00000000000..50cbdcc6151
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs
@@ -0,0 +1,900 @@
+use core::ops::ControlFlow;
+
+use hir::def::CtorKind;
+use hir::intravisit::{walk_expr, walk_stmt, Visitor};
+use hir::{LetStmt, QPath};
+use rustc_data_structures::fx::FxIndexSet;
+use rustc_errors::{Applicability, Diag};
+use rustc_hir as hir;
+use rustc_hir::def::Res;
+use rustc_hir::{MatchSource, Node};
+use rustc_middle::traits::{
+    IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
+    StatementAsExpression,
+};
+use rustc_middle::ty::print::with_no_trimmed_paths;
+use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt};
+use rustc_span::{sym, Span};
+use tracing::debug;
+
+use crate::error_reporting::infer::hir::Path;
+use crate::error_reporting::TypeErrCtxt;
+use crate::errors::{
+    ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
+    FunctionPointerSuggestion, SuggestAccessingField, SuggestRemoveSemiOrReturnBinding,
+    SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags,
+};
+
+#[derive(Clone, Copy)]
+pub enum SuggestAsRefKind {
+    Option,
+    Result,
+}
+
+impl<'tcx> TypeErrCtxt<'_, 'tcx> {
+    pub(super) fn suggest_remove_semi_or_return_binding(
+        &self,
+        first_id: Option<hir::HirId>,
+        first_ty: Ty<'tcx>,
+        first_span: Span,
+        second_id: Option<hir::HirId>,
+        second_ty: Ty<'tcx>,
+        second_span: Span,
+    ) -> Option<SuggestRemoveSemiOrReturnBinding> {
+        let remove_semicolon = [
+            (first_id, self.resolve_vars_if_possible(second_ty)),
+            (second_id, self.resolve_vars_if_possible(first_ty)),
+        ]
+        .into_iter()
+        .find_map(|(id, ty)| {
+            let hir::Node::Block(blk) = self.tcx.hir_node(id?) else { return None };
+            self.could_remove_semicolon(blk, ty)
+        });
+        match remove_semicolon {
+            Some((sp, StatementAsExpression::NeedsBoxing)) => {
+                Some(SuggestRemoveSemiOrReturnBinding::RemoveAndBox {
+                    first_lo: first_span.shrink_to_lo(),
+                    first_hi: first_span.shrink_to_hi(),
+                    second_lo: second_span.shrink_to_lo(),
+                    second_hi: second_span.shrink_to_hi(),
+                    sp,
+                })
+            }
+            Some((sp, StatementAsExpression::CorrectType)) => {
+                Some(SuggestRemoveSemiOrReturnBinding::Remove { sp })
+            }
+            None => {
+                let mut ret = None;
+                for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] {
+                    if let Some(id) = id
+                        && let hir::Node::Block(blk) = self.tcx.hir_node(id)
+                        && let Some(diag) = self.consider_returning_binding_diag(blk, ty)
+                    {
+                        ret = Some(diag);
+                        break;
+                    }
+                }
+                ret
+            }
+        }
+    }
+
+    pub(super) fn suggest_tuple_pattern(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+        diag: &mut Diag<'_>,
+    ) {
+        // Heavily inspired by `FnCtxt::suggest_compatible_variants`, with
+        // some modifications due to that being in typeck and this being in infer.
+        if let ObligationCauseCode::Pattern { .. } = cause.code() {
+            if let ty::Adt(expected_adt, args) = exp_found.expected.kind() {
+                let compatible_variants: Vec<_> = expected_adt
+                    .variants()
+                    .iter()
+                    .filter(|variant| {
+                        variant.fields.len() == 1 && variant.ctor_kind() == Some(CtorKind::Fn)
+                    })
+                    .filter_map(|variant| {
+                        let sole_field = &variant.single_field();
+                        let sole_field_ty = sole_field.ty(self.tcx, args);
+                        if self.same_type_modulo_infer(sole_field_ty, exp_found.found) {
+                            let variant_path =
+                                with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
+                            // FIXME #56861: DRYer prelude filtering
+                            if let Some(path) = variant_path.strip_prefix("std::prelude::") {
+                                if let Some((_, path)) = path.split_once("::") {
+                                    return Some(path.to_string());
+                                }
+                            }
+                            Some(variant_path)
+                        } else {
+                            None
+                        }
+                    })
+                    .collect();
+                match &compatible_variants[..] {
+                    [] => {}
+                    [variant] => {
+                        let sugg = SuggestTuplePatternOne {
+                            variant: variant.to_owned(),
+                            span_low: cause.span.shrink_to_lo(),
+                            span_high: cause.span.shrink_to_hi(),
+                        };
+                        diag.subdiagnostic(sugg);
+                    }
+                    _ => {
+                        // More than one matching variant.
+                        let sugg = SuggestTuplePatternMany {
+                            path: self.tcx.def_path_str(expected_adt.did()),
+                            cause_span: cause.span,
+                            compatible_variants,
+                        };
+                        diag.subdiagnostic(sugg);
+                    }
+                }
+            }
+        }
+    }
+
+    /// A possible error is to forget to add `.await` when using futures:
+    ///
+    /// ```compile_fail,E0308
+    /// async fn make_u32() -> u32 {
+    ///     22
+    /// }
+    ///
+    /// fn take_u32(x: u32) {}
+    ///
+    /// async fn foo() {
+    ///     let x = make_u32();
+    ///     take_u32(x);
+    /// }
+    /// ```
+    ///
+    /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
+    /// expected type. If this is the case, and we are inside of an async body, it suggests adding
+    /// `.await` to the tail of the expression.
+    pub(super) fn suggest_await_on_expect_found(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        exp_span: Span,
+        exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+        diag: &mut Diag<'_>,
+    ) {
+        debug!(
+            "suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
+            exp_span, exp_found.expected, exp_found.found,
+        );
+
+        if let ObligationCauseCode::CompareImplItem { .. } = cause.code() {
+            return;
+        }
+
+        let subdiag = match (
+            self.get_impl_future_output_ty(exp_found.expected),
+            self.get_impl_future_output_ty(exp_found.found),
+        ) {
+            (Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause
+                .code()
+            {
+                ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
+                    let then_span = self.find_block_span_from_hir_id(*then_id);
+                    Some(ConsiderAddingAwait::BothFuturesSugg {
+                        first: then_span.shrink_to_hi(),
+                        second: exp_span.shrink_to_hi(),
+                    })
+                }
+                ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
+                    prior_non_diverging_arms,
+                    ..
+                }) => {
+                    if let [.., arm_span] = &prior_non_diverging_arms[..] {
+                        Some(ConsiderAddingAwait::BothFuturesSugg {
+                            first: arm_span.shrink_to_hi(),
+                            second: exp_span.shrink_to_hi(),
+                        })
+                    } else {
+                        Some(ConsiderAddingAwait::BothFuturesHelp)
+                    }
+                }
+                _ => Some(ConsiderAddingAwait::BothFuturesHelp),
+            },
+            (_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
+                // FIXME: Seems like we can't have a suggestion and a note with different spans in a single subdiagnostic
+                diag.subdiagnostic(ConsiderAddingAwait::FutureSugg {
+                    span: exp_span.shrink_to_hi(),
+                });
+                Some(ConsiderAddingAwait::FutureSuggNote { span: exp_span })
+            }
+            (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
+            {
+                ObligationCauseCode::Pattern { span: Some(then_span), origin_expr, .. } => {
+                    origin_expr.then_some(ConsiderAddingAwait::FutureSugg {
+                        span: then_span.shrink_to_hi(),
+                    })
+                }
+                ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
+                    let then_span = self.find_block_span_from_hir_id(*then_id);
+                    Some(ConsiderAddingAwait::FutureSugg { span: then_span.shrink_to_hi() })
+                }
+                ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
+                    ref prior_non_diverging_arms,
+                    ..
+                }) => Some({
+                    ConsiderAddingAwait::FutureSuggMultiple {
+                        spans: prior_non_diverging_arms
+                            .iter()
+                            .map(|arm| arm.shrink_to_hi())
+                            .collect(),
+                    }
+                }),
+                _ => None,
+            },
+            _ => None,
+        };
+        if let Some(subdiag) = subdiag {
+            diag.subdiagnostic(subdiag);
+        }
+    }
+
+    pub(super) fn suggest_accessing_field_where_appropriate(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+        diag: &mut Diag<'_>,
+    ) {
+        debug!(
+            "suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})",
+            cause, exp_found
+        );
+        if let ty::Adt(expected_def, expected_args) = exp_found.expected.kind() {
+            if expected_def.is_enum() {
+                return;
+            }
+
+            if let Some((name, ty)) = expected_def
+                .non_enum_variant()
+                .fields
+                .iter()
+                .filter(|field| field.vis.is_accessible_from(field.did, self.tcx))
+                .map(|field| (field.name, field.ty(self.tcx, expected_args)))
+                .find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found))
+            {
+                if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() {
+                    if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+                        let suggestion = if expected_def.is_struct() {
+                            SuggestAccessingField::Safe { span, snippet, name, ty }
+                        } else if expected_def.is_union() {
+                            SuggestAccessingField::Unsafe { span, snippet, name, ty }
+                        } else {
+                            return;
+                        };
+                        diag.subdiagnostic(suggestion);
+                    }
+                }
+            }
+        }
+    }
+
+    pub(super) fn suggest_turning_stmt_into_expr(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+        diag: &mut Diag<'_>,
+    ) {
+        let ty::error::ExpectedFound { expected, found } = exp_found;
+        if !found.peel_refs().is_unit() {
+            return;
+        }
+
+        let ObligationCauseCode::BlockTailExpression(hir_id, MatchSource::Normal) = cause.code()
+        else {
+            return;
+        };
+
+        let node = self.tcx.hir_node(*hir_id);
+        let mut blocks = vec![];
+        if let hir::Node::Block(block) = node
+            && let Some(expr) = block.expr
+            && let hir::ExprKind::Path(QPath::Resolved(_, Path { res, .. })) = expr.kind
+            && let Res::Local(local) = res
+            && let Node::LetStmt(LetStmt { init: Some(init), .. }) =
+                self.tcx.parent_hir_node(*local)
+        {
+            fn collect_blocks<'hir>(expr: &hir::Expr<'hir>, blocks: &mut Vec<&hir::Block<'hir>>) {
+                match expr.kind {
+                    // `blk1` and `blk2` must be have the same types, it will be reported before reaching here
+                    hir::ExprKind::If(_, blk1, Some(blk2)) => {
+                        collect_blocks(blk1, blocks);
+                        collect_blocks(blk2, blocks);
+                    }
+                    hir::ExprKind::Match(_, arms, _) => {
+                        // all arms must have same types
+                        for arm in arms.iter() {
+                            collect_blocks(arm.body, blocks);
+                        }
+                    }
+                    hir::ExprKind::Block(blk, _) => {
+                        blocks.push(blk);
+                    }
+                    _ => {}
+                }
+            }
+            collect_blocks(init, &mut blocks);
+        }
+
+        let expected_inner: Ty<'_> = expected.peel_refs();
+        for block in blocks.iter() {
+            self.consider_removing_semicolon(block, expected_inner, diag);
+        }
+    }
+
+    /// A common error is to add an extra semicolon:
+    ///
+    /// ```compile_fail,E0308
+    /// fn foo() -> usize {
+    ///     22;
+    /// }
+    /// ```
+    ///
+    /// This routine checks if the final statement in a block is an
+    /// expression with an explicit semicolon whose type is compatible
+    /// with `expected_ty`. If so, it suggests removing the semicolon.
+    pub fn consider_removing_semicolon(
+        &self,
+        blk: &'tcx hir::Block<'tcx>,
+        expected_ty: Ty<'tcx>,
+        diag: &mut Diag<'_>,
+    ) -> bool {
+        if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) {
+            if let StatementAsExpression::NeedsBoxing = boxed {
+                diag.span_suggestion_verbose(
+                    span_semi,
+                    "consider removing this semicolon and boxing the expression",
+                    "",
+                    Applicability::HasPlaceholders,
+                );
+            } else {
+                diag.span_suggestion_short(
+                    span_semi,
+                    "remove this semicolon to return this value",
+                    "",
+                    Applicability::MachineApplicable,
+                );
+            }
+            true
+        } else {
+            false
+        }
+    }
+
+    pub(super) fn suggest_function_pointers(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        span: Span,
+        exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+        diag: &mut Diag<'_>,
+    ) {
+        debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found);
+        let ty::error::ExpectedFound { expected, found } = exp_found;
+        let expected_inner = expected.peel_refs();
+        let found_inner = found.peel_refs();
+        if !expected_inner.is_fn() || !found_inner.is_fn() {
+            return;
+        }
+        match (expected_inner.kind(), found_inner.kind()) {
+            (ty::FnPtr(sig_tys, hdr), ty::FnDef(did, args)) => {
+                let sig = sig_tys.with(*hdr);
+                let expected_sig = &(self.normalize_fn_sig)(sig);
+                let found_sig =
+                    &(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
+
+                let fn_name = self.tcx.def_path_str_with_args(*did, args);
+
+                if !self.same_type_modulo_infer(*found_sig, *expected_sig)
+                    || !sig.is_suggestable(self.tcx, true)
+                    || self.tcx.intrinsic(*did).is_some()
+                {
+                    return;
+                }
+
+                let sugg = match (expected.is_ref(), found.is_ref()) {
+                    (true, false) => FunctionPointerSuggestion::UseRef { span, fn_name },
+                    (false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name },
+                    (true, true) => {
+                        diag.subdiagnostic(FnItemsAreDistinct);
+                        FunctionPointerSuggestion::CastRef { span, fn_name, sig }
+                    }
+                    (false, false) => {
+                        diag.subdiagnostic(FnItemsAreDistinct);
+                        FunctionPointerSuggestion::Cast { span, fn_name, sig }
+                    }
+                };
+                diag.subdiagnostic(sugg);
+            }
+            (ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => {
+                let expected_sig =
+                    &(self.normalize_fn_sig)(self.tcx.fn_sig(*did1).instantiate(self.tcx, args1));
+                let found_sig =
+                    &(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).instantiate(self.tcx, args2));
+
+                if self.same_type_modulo_infer(*expected_sig, *found_sig) {
+                    diag.subdiagnostic(FnUniqTypes);
+                }
+
+                if !self.same_type_modulo_infer(*found_sig, *expected_sig)
+                    || !found_sig.is_suggestable(self.tcx, true)
+                    || !expected_sig.is_suggestable(self.tcx, true)
+                    || self.tcx.intrinsic(*did1).is_some()
+                    || self.tcx.intrinsic(*did2).is_some()
+                {
+                    return;
+                }
+
+                let fn_name = self.tcx.def_path_str_with_args(*did2, args2);
+                let sug = if found.is_ref() {
+                    FunctionPointerSuggestion::CastBothRef {
+                        span,
+                        fn_name,
+                        found_sig: *found_sig,
+                        expected_sig: *expected_sig,
+                    }
+                } else {
+                    FunctionPointerSuggestion::CastBoth {
+                        span,
+                        fn_name,
+                        found_sig: *found_sig,
+                        expected_sig: *expected_sig,
+                    }
+                };
+
+                diag.subdiagnostic(sug);
+            }
+            (ty::FnDef(did, args), ty::FnPtr(sig_tys, hdr)) => {
+                let expected_sig =
+                    &(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
+                let found_sig = &(self.normalize_fn_sig)(sig_tys.with(*hdr));
+
+                if !self.same_type_modulo_infer(*found_sig, *expected_sig) {
+                    return;
+                }
+
+                let fn_name = self.tcx.def_path_str_with_args(*did, args);
+
+                let casting = if expected.is_ref() {
+                    format!("&({fn_name} as {found_sig})")
+                } else {
+                    format!("{fn_name} as {found_sig}")
+                };
+
+                diag.subdiagnostic(FnConsiderCasting { casting });
+            }
+            _ => {
+                return;
+            }
+        };
+    }
+
+    pub fn should_suggest_as_ref_kind(
+        &self,
+        expected: Ty<'tcx>,
+        found: Ty<'tcx>,
+    ) -> Option<SuggestAsRefKind> {
+        if let (ty::Adt(exp_def, exp_args), ty::Ref(_, found_ty, _)) =
+            (expected.kind(), found.kind())
+        {
+            if let ty::Adt(found_def, found_args) = *found_ty.kind() {
+                if exp_def == &found_def {
+                    let have_as_ref = &[
+                        (sym::Option, SuggestAsRefKind::Option),
+                        (sym::Result, SuggestAsRefKind::Result),
+                    ];
+                    if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
+                        self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
+                    }) {
+                        let mut show_suggestion = true;
+                        for (exp_ty, found_ty) in
+                            std::iter::zip(exp_args.types(), found_args.types())
+                        {
+                            match *exp_ty.kind() {
+                                ty::Ref(_, exp_ty, _) => {
+                                    match (exp_ty.kind(), found_ty.kind()) {
+                                        (_, ty::Param(_))
+                                        | (_, ty::Infer(_))
+                                        | (ty::Param(_), _)
+                                        | (ty::Infer(_), _) => {}
+                                        _ if self.same_type_modulo_infer(exp_ty, found_ty) => {}
+                                        _ => show_suggestion = false,
+                                    };
+                                }
+                                ty::Param(_) | ty::Infer(_) => {}
+                                _ => show_suggestion = false,
+                            }
+                        }
+                        if show_suggestion {
+                            return Some(*msg);
+                        }
+                    }
+                }
+            }
+        }
+        None
+    }
+
+    // FIXME: Remove once `rustc_hir_typeck` is migrated to diagnostic structs
+    pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
+        match self.should_suggest_as_ref_kind(expected, found) {
+            Some(SuggestAsRefKind::Option) => Some(
+                "you can convert from `&Option<T>` to `Option<&T>` using \
+            `.as_ref()`",
+            ),
+            Some(SuggestAsRefKind::Result) => Some(
+                "you can convert from `&Result<T, E>` to \
+            `Result<&T, &E>` using `.as_ref()`",
+            ),
+            None => None,
+        }
+    }
+    /// Try to find code with pattern `if Some(..) = expr`
+    /// use a `visitor` to mark the `if` which its span contains given error span,
+    /// and then try to find a assignment in the `cond` part, which span is equal with error span
+    pub(super) fn suggest_let_for_letchains(
+        &self,
+        cause: &ObligationCause<'_>,
+        span: Span,
+    ) -> Option<TypeErrorAdditionalDiags> {
+        /// Find the if expression with given span
+        struct IfVisitor {
+            pub found_if: bool,
+            pub err_span: Span,
+        }
+
+        impl<'v> Visitor<'v> for IfVisitor {
+            type Result = ControlFlow<()>;
+            fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) -> Self::Result {
+                match ex.kind {
+                    hir::ExprKind::If(cond, _, _) => {
+                        self.found_if = true;
+                        walk_expr(self, cond)?;
+                        self.found_if = false;
+                        ControlFlow::Continue(())
+                    }
+                    _ => walk_expr(self, ex),
+                }
+            }
+
+            fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result {
+                if let hir::StmtKind::Let(LetStmt {
+                    span,
+                    pat: hir::Pat { .. },
+                    ty: None,
+                    init: Some(_),
+                    ..
+                }) = &ex.kind
+                    && self.found_if
+                    && span.eq(&self.err_span)
+                {
+                    ControlFlow::Break(())
+                } else {
+                    walk_stmt(self, ex)
+                }
+            }
+        }
+
+        self.tcx.hir().maybe_body_owned_by(cause.body_id).and_then(|body| {
+            IfVisitor { err_span: span, found_if: false }
+                .visit_body(&body)
+                .is_break()
+                .then(|| TypeErrorAdditionalDiags::AddLetForLetChains { span: span.shrink_to_lo() })
+        })
+    }
+
+    /// For "one type is more general than the other" errors on closures, suggest changing the lifetime
+    /// of the parameters to accept all lifetimes.
+    pub(super) fn suggest_for_all_lifetime_closure(
+        &self,
+        span: Span,
+        hir: hir::Node<'_>,
+        exp_found: &ty::error::ExpectedFound<ty::TraitRef<'tcx>>,
+        diag: &mut Diag<'_>,
+    ) {
+        // 0. Extract fn_decl from hir
+        let hir::Node::Expr(hir::Expr {
+            kind: hir::ExprKind::Closure(hir::Closure { body, fn_decl, .. }),
+            ..
+        }) = hir
+        else {
+            return;
+        };
+        let hir::Body { params, .. } = self.tcx.hir().body(*body);
+
+        // 1. Get the args of the closure.
+        // 2. Assume exp_found is FnOnce / FnMut / Fn, we can extract function parameters from [1].
+        let Some(expected) = exp_found.expected.args.get(1) else {
+            return;
+        };
+        let Some(found) = exp_found.found.args.get(1) else {
+            return;
+        };
+        let expected = expected.unpack();
+        let found = found.unpack();
+        // 3. Extract the tuple type from Fn trait and suggest the change.
+        if let GenericArgKind::Type(expected) = expected
+            && let GenericArgKind::Type(found) = found
+            && let ty::Tuple(expected) = expected.kind()
+            && let ty::Tuple(found) = found.kind()
+            && expected.len() == found.len()
+        {
+            let mut suggestion = "|".to_string();
+            let mut is_first = true;
+            let mut has_suggestion = false;
+
+            for (((expected, found), param_hir), arg_hir) in
+                expected.iter().zip(found.iter()).zip(params.iter()).zip(fn_decl.inputs.iter())
+            {
+                if is_first {
+                    is_first = false;
+                } else {
+                    suggestion += ", ";
+                }
+
+                if let ty::Ref(expected_region, _, _) = expected.kind()
+                    && let ty::Ref(found_region, _, _) = found.kind()
+                    && expected_region.is_bound()
+                    && !found_region.is_bound()
+                    && let hir::TyKind::Infer = arg_hir.kind
+                {
+                    // If the expected region is late bound, the found region is not, and users are asking compiler
+                    // to infer the type, we can suggest adding `: &_`.
+                    if param_hir.pat.span == param_hir.ty_span {
+                        // for `|x|`, `|_|`, `|x: impl Foo|`
+                        let Ok(pat) =
+                            self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span)
+                        else {
+                            return;
+                        };
+                        suggestion += &format!("{pat}: &_");
+                    } else {
+                        // for `|x: ty|`, `|_: ty|`
+                        let Ok(pat) =
+                            self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span)
+                        else {
+                            return;
+                        };
+                        let Ok(ty) = self.tcx.sess.source_map().span_to_snippet(param_hir.ty_span)
+                        else {
+                            return;
+                        };
+                        suggestion += &format!("{pat}: &{ty}");
+                    }
+                    has_suggestion = true;
+                } else {
+                    let Ok(arg) = self.tcx.sess.source_map().span_to_snippet(param_hir.span) else {
+                        return;
+                    };
+                    // Otherwise, keep it as-is.
+                    suggestion += &arg;
+                }
+            }
+            suggestion += "|";
+
+            if has_suggestion {
+                diag.span_suggestion_verbose(
+                    span,
+                    "consider specifying the type of the closure parameters",
+                    suggestion,
+                    Applicability::MaybeIncorrect,
+                );
+            }
+        }
+    }
+}
+
+impl<'tcx> TypeErrCtxt<'_, 'tcx> {
+    /// Be helpful when the user wrote `{... expr; }` and taking the `;` off
+    /// is enough to fix the error.
+    pub fn could_remove_semicolon(
+        &self,
+        blk: &'tcx hir::Block<'tcx>,
+        expected_ty: Ty<'tcx>,
+    ) -> Option<(Span, StatementAsExpression)> {
+        let blk = blk.innermost_block();
+        // Do not suggest if we have a tail expr.
+        if blk.expr.is_some() {
+            return None;
+        }
+        let last_stmt = blk.stmts.last()?;
+        let hir::StmtKind::Semi(last_expr) = last_stmt.kind else {
+            return None;
+        };
+        let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(last_expr)?;
+        let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
+            _ if last_expr_ty.references_error() => return None,
+            _ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
+                StatementAsExpression::CorrectType
+            }
+            (
+                ty::Alias(ty::Opaque, ty::AliasTy { def_id: last_def_id, .. }),
+                ty::Alias(ty::Opaque, ty::AliasTy { def_id: exp_def_id, .. }),
+            ) if last_def_id == exp_def_id => StatementAsExpression::CorrectType,
+            (
+                ty::Alias(ty::Opaque, ty::AliasTy { def_id: last_def_id, args: last_bounds, .. }),
+                ty::Alias(ty::Opaque, ty::AliasTy { def_id: exp_def_id, args: exp_bounds, .. }),
+            ) => {
+                debug!(
+                    "both opaque, likely future {:?} {:?} {:?} {:?}",
+                    last_def_id, last_bounds, exp_def_id, exp_bounds
+                );
+
+                let last_local_id = last_def_id.as_local()?;
+                let exp_local_id = exp_def_id.as_local()?;
+
+                match (
+                    &self.tcx.hir().expect_item(last_local_id).kind,
+                    &self.tcx.hir().expect_item(exp_local_id).kind,
+                ) {
+                    (
+                        hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
+                        hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
+                    ) if std::iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| match (
+                        left, right,
+                    ) {
+                        (hir::GenericBound::Trait(tl, ml), hir::GenericBound::Trait(tr, mr))
+                            if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
+                                && ml == mr =>
+                        {
+                            true
+                        }
+                        _ => false,
+                    }) =>
+                    {
+                        StatementAsExpression::NeedsBoxing
+                    }
+                    _ => StatementAsExpression::CorrectType,
+                }
+            }
+            _ => return None,
+        };
+        let span = if last_stmt.span.from_expansion() {
+            let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
+            self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
+        } else {
+            self.tcx
+                .sess
+                .source_map()
+                .span_extend_while_whitespace(last_expr.span)
+                .shrink_to_hi()
+                .with_hi(last_stmt.span.hi())
+        };
+
+        Some((span, needs_box))
+    }
+
+    /// Suggest returning a local binding with a compatible type if the block
+    /// has no return expression.
+    pub fn consider_returning_binding_diag(
+        &self,
+        blk: &'tcx hir::Block<'tcx>,
+        expected_ty: Ty<'tcx>,
+    ) -> Option<SuggestRemoveSemiOrReturnBinding> {
+        let blk = blk.innermost_block();
+        // Do not suggest if we have a tail expr.
+        if blk.expr.is_some() {
+            return None;
+        }
+        let mut shadowed = FxIndexSet::default();
+        let mut candidate_idents = vec![];
+        let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
+            if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
+                && let Some(pat_ty) = self
+                    .typeck_results
+                    .as_ref()
+                    .and_then(|typeck_results| typeck_results.node_type_opt(*hir_id))
+            {
+                let pat_ty = self.resolve_vars_if_possible(pat_ty);
+                if self.same_type_modulo_infer(pat_ty, expected_ty)
+                    && !(pat_ty, expected_ty).references_error()
+                    && shadowed.insert(ident.name)
+                {
+                    candidate_idents.push((*ident, pat_ty));
+                }
+            }
+            true
+        };
+
+        let hir = self.tcx.hir();
+        for stmt in blk.stmts.iter().rev() {
+            let hir::StmtKind::Let(local) = &stmt.kind else {
+                continue;
+            };
+            local.pat.walk(&mut find_compatible_candidates);
+        }
+        match self.tcx.parent_hir_node(blk.hir_id) {
+            hir::Node::Expr(hir::Expr { hir_id, .. }) => match self.tcx.parent_hir_node(*hir_id) {
+                hir::Node::Arm(hir::Arm { pat, .. }) => {
+                    pat.walk(&mut find_compatible_candidates);
+                }
+
+                hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. })
+                | hir::Node::ImplItem(hir::ImplItem {
+                    kind: hir::ImplItemKind::Fn(_, body), ..
+                })
+                | hir::Node::TraitItem(hir::TraitItem {
+                    kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
+                    ..
+                })
+                | hir::Node::Expr(hir::Expr {
+                    kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
+                    ..
+                }) => {
+                    for param in hir.body(*body).params {
+                        param.pat.walk(&mut find_compatible_candidates);
+                    }
+                }
+                hir::Node::Expr(hir::Expr {
+                    kind:
+                        hir::ExprKind::If(
+                            hir::Expr { kind: hir::ExprKind::Let(let_), .. },
+                            then_block,
+                            _,
+                        ),
+                    ..
+                }) if then_block.hir_id == *hir_id => {
+                    let_.pat.walk(&mut find_compatible_candidates);
+                }
+                _ => {}
+            },
+            _ => {}
+        }
+
+        match &candidate_idents[..] {
+            [(ident, _ty)] => {
+                let sm = self.tcx.sess.source_map();
+                let (span, sugg) = if let Some(stmt) = blk.stmts.last() {
+                    let stmt_span = sm.stmt_span(stmt.span, blk.span);
+                    let sugg = if sm.is_multiline(blk.span)
+                        && let Some(spacing) = sm.indentation_before(stmt_span)
+                    {
+                        format!("\n{spacing}{ident}")
+                    } else {
+                        format!(" {ident}")
+                    };
+                    (stmt_span.shrink_to_hi(), sugg)
+                } else {
+                    let sugg = if sm.is_multiline(blk.span)
+                        && let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
+                    {
+                        format!("\n{spacing}    {ident}\n{spacing}")
+                    } else {
+                        format!(" {ident} ")
+                    };
+                    let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
+                    (sm.span_extend_while_whitespace(left_span), sugg)
+                };
+                Some(SuggestRemoveSemiOrReturnBinding::Add { sp: span, code: sugg, ident: *ident })
+            }
+            values if (1..3).contains(&values.len()) => {
+                let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
+                Some(SuggestRemoveSemiOrReturnBinding::AddOne { spans: spans.into() })
+            }
+            _ => None,
+        }
+    }
+
+    pub fn consider_returning_binding(
+        &self,
+        blk: &'tcx hir::Block<'tcx>,
+        expected_ty: Ty<'tcx>,
+        err: &mut Diag<'_>,
+    ) -> bool {
+        let diag = self.consider_returning_binding_diag(blk, expected_ty);
+        match diag {
+            Some(diag) => {
+                err.subdiagnostic(diag);
+                true
+            }
+            None => false,
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/mod.rs
index f6ac8fc7b61..cb7efeaae0b 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/mod.rs
@@ -1 +1,73 @@
+use std::ops::Deref;
+
+use rustc_errors::DiagCtxtHandle;
+use rustc_infer::infer::InferCtxt;
+use rustc_infer::traits::PredicateObligation;
+use rustc_macros::extension;
+use rustc_middle::bug;
+use rustc_middle::ty::{self, Ty};
+
+use crate::error_reporting::infer::sub_relations;
+
+pub mod infer;
 pub mod traits;
+
+/// A helper for building type related errors. The `typeck_results`
+/// field is only populated during an in-progress typeck.
+/// Get an instance by calling `InferCtxt::err_ctxt` or `FnCtxt::err_ctxt`.
+///
+/// You must only create this if you intend to actually emit an error (or
+/// perhaps a warning, though preferably not.) It provides a lot of utility
+/// methods which should not be used during the happy path.
+pub struct TypeErrCtxt<'a, 'tcx> {
+    pub infcx: &'a InferCtxt<'tcx>,
+    pub sub_relations: std::cell::RefCell<sub_relations::SubRelations>,
+
+    pub typeck_results: Option<std::cell::Ref<'a, ty::TypeckResults<'tcx>>>,
+    pub fallback_has_occurred: bool,
+
+    pub normalize_fn_sig: Box<dyn Fn(ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx> + 'a>,
+
+    pub autoderef_steps:
+        Box<dyn Fn(Ty<'tcx>) -> Vec<(Ty<'tcx>, Vec<PredicateObligation<'tcx>>)> + 'a>,
+}
+
+#[extension(pub trait InferCtxtErrorExt<'tcx>)]
+impl<'tcx> InferCtxt<'tcx> {
+    /// Creates a `TypeErrCtxt` for emitting various inference errors.
+    /// During typeck, use `FnCtxt::err_ctxt` instead.
+    fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> {
+        TypeErrCtxt {
+            infcx: self,
+            sub_relations: Default::default(),
+            typeck_results: None,
+            fallback_has_occurred: false,
+            normalize_fn_sig: Box::new(|fn_sig| fn_sig),
+            autoderef_steps: Box::new(|ty| {
+                debug_assert!(false, "shouldn't be using autoderef_steps outside of typeck");
+                vec![(ty, vec![])]
+            }),
+        }
+    }
+}
+
+impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
+    pub fn dcx(&self) -> DiagCtxtHandle<'a> {
+        self.infcx.dcx()
+    }
+
+    /// This is just to avoid a potential footgun of accidentally
+    /// dropping `typeck_results` by calling `InferCtxt::err_ctxt`
+    #[deprecated(note = "you already have a `TypeErrCtxt`")]
+    #[allow(unused)]
+    pub fn err_ctxt(&self) -> ! {
+        bug!("called `err_ctxt` on `TypeErrCtxt`. Try removing the call");
+    }
+}
+
+impl<'tcx> Deref for TypeErrCtxt<'_, 'tcx> {
+    type Target = InferCtxt<'tcx>;
+    fn deref(&self) -> &InferCtxt<'tcx> {
+        self.infcx
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs
index c301deac616..79c1f722280 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs
@@ -8,20 +8,18 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::Visitor as _;
 use rustc_hir::LangItem;
-use rustc_infer::infer::error_reporting::{TypeAnnotationNeeded, TypeErrCtxt};
 use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt};
 use rustc_infer::traits::util::elaborate;
 use rustc_infer::traits::{
     Obligation, ObligationCause, ObligationCauseCode, PolyTraitObligation, PredicateObligation,
 };
-use rustc_macros::extension;
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable as _, TypeVisitableExt as _};
 use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
+use tracing::{debug, instrument};
 
-use crate::error_reporting::traits::suggestions::TypeErrCtxtExt as _;
-use crate::error_reporting::traits::{
-    to_pretty_impl_header, FindExprBySpan, InferCtxtPrivExt as _,
-};
+use crate::error_reporting::infer::need_type_info::TypeAnnotationNeeded;
+use crate::error_reporting::traits::{to_pretty_impl_header, FindExprBySpan};
+use crate::error_reporting::TypeErrCtxt;
 use crate::traits::query::evaluate_obligation::InferCtxtExt;
 use crate::traits::ObligationCtxt;
 
@@ -152,10 +150,12 @@ pub fn compute_applicable_impls_for_diagnostics<'tcx>(
     ambiguities
 }
 
-#[extension(pub trait TypeErrCtxtAmbiguityExt<'a, 'tcx>)]
 impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     #[instrument(skip(self), level = "debug")]
-    fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) -> ErrorGuaranteed {
+    pub(super) fn maybe_report_ambiguity(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+    ) -> ErrorGuaranteed {
         // Unable to successfully determine, probably means
         // insufficient type information, but could mean
         // ambiguous impls. The latter *ought* to be a
@@ -389,39 +389,67 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                                 trait_impls.non_blanket_impls().values().flatten().count();
                             // If there is only one implementation of the trait, suggest using it.
                             // Otherwise, use a placeholder comment for the implementation.
-                            let (message, self_type) = if non_blanket_impl_count == 1 {
+                            let (message, self_types) = if non_blanket_impl_count == 1 {
                                 (
                                     "use the fully-qualified path to the only available \
                                      implementation",
-                                    format!(
+                                    vec![format!(
                                         "{}",
                                         self.tcx.type_of(impl_def_id).instantiate_identity()
-                                    ),
+                                    )],
+                                )
+                            } else if non_blanket_impl_count < 20 {
+                                (
+                                    "use a fully-qualified path to one of the available \
+                                     implementations",
+                                    trait_impls
+                                        .non_blanket_impls()
+                                        .values()
+                                        .flatten()
+                                        .map(|id| {
+                                            format!(
+                                                "{}",
+                                                self.tcx.type_of(id).instantiate_identity()
+                                            )
+                                        })
+                                        .collect::<Vec<String>>(),
                                 )
                             } else {
                                 (
                                     "use a fully-qualified path to a specific available \
                                      implementation",
-                                    "/* self type */".to_string(),
+                                    vec!["/* self type */".to_string()],
                                 )
                             };
-                            let mut suggestions =
-                                vec![(path.span.shrink_to_lo(), format!("<{self_type} as "))];
-                            if let Some(generic_arg) = trait_path_segment.args {
-                                let between_span =
-                                    trait_path_segment.ident.span.between(generic_arg.span_ext);
-                                // get rid of :: between Trait and <type>
-                                // must be '::' between them, otherwise the parser won't accept the code
-                                suggestions.push((between_span, "".to_string()));
-                                suggestions
-                                    .push((generic_arg.span_ext.shrink_to_hi(), ">".to_string()));
-                            } else {
-                                suggestions.push((
-                                    trait_path_segment.ident.span.shrink_to_hi(),
-                                    ">".to_string(),
-                                ));
-                            }
-                            err.multipart_suggestion(
+                            let suggestions: Vec<_> = self_types
+                                .into_iter()
+                                .map(|self_type| {
+                                    let mut suggestions = vec![(
+                                        path.span.shrink_to_lo(),
+                                        format!("<{self_type} as "),
+                                    )];
+                                    if let Some(generic_arg) = trait_path_segment.args {
+                                        let between_span = trait_path_segment
+                                            .ident
+                                            .span
+                                            .between(generic_arg.span_ext);
+                                        // get rid of :: between Trait and <type>
+                                        // must be '::' between them, otherwise the parser won't accept the code
+                                        suggestions.push((between_span, "".to_string()));
+                                        suggestions.push((
+                                            generic_arg.span_ext.shrink_to_hi(),
+                                            ">".to_string(),
+                                        ));
+                                    } else {
+                                        suggestions.push((
+                                            trait_path_segment.ident.span.shrink_to_hi(),
+                                            ">".to_string(),
+                                        ));
+                                    }
+                                    suggestions
+                                })
+                                .collect();
+                            err.multipart_suggestions(
                                 message,
                                 suggestions,
                                 Applicability::MaybeIncorrect,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index 2e6247b4640..5918686213a 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -1,33 +1,18 @@
-use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _};
-use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as _};
-use crate::error_reporting::traits::infer_ctxt_ext::InferCtxtExt;
-use crate::errors::{
-    AsyncClosureNotFn, ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch,
-};
-use crate::infer::error_reporting::TyCategory;
-use crate::infer::InferCtxtExt as _;
-use crate::infer::{self, InferCtxt};
-use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
-use crate::traits::NormalizeExt;
-use crate::traits::{
-    elaborate, MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode,
-    ObligationCtxt, Overflow, PredicateObligation, SelectionError, SignatureMismatch,
-    TraitNotObjectSafe,
-};
 use core::ops::ControlFlow;
+use std::borrow::Cow;
+
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::unord::UnordSet;
 use rustc_errors::codes::*;
-use rustc_errors::{pluralize, struct_span_code_err, Applicability, StringPart};
-use rustc_errors::{Diag, ErrorGuaranteed, StashKey};
+use rustc_errors::{
+    pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey,
+    StringPart,
+};
 use rustc_hir::def::Namespace;
-use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
 use rustc_hir::intravisit::Visitor;
-use rustc_hir::Node;
-use rustc_hir::{self as hir, LangItem};
-use rustc_infer::infer::error_reporting::TypeErrCtxt;
+use rustc_hir::{self as hir, LangItem, Node};
 use rustc_infer::infer::{InferOk, TypeTrace};
-use rustc_macros::extension;
 use rustc_middle::traits::select::OverflowError;
 use rustc_middle::traits::SignatureMismatchData;
 use rustc_middle::ty::abstract_const::NotConstEvaluatable;
@@ -43,20 +28,32 @@ use rustc_middle::ty::{
 use rustc_middle::{bug, span_bug};
 use rustc_span::symbol::sym;
 use rustc_span::{BytePos, Span, Symbol, DUMMY_SP};
-use std::borrow::Cow;
+use tracing::{debug, instrument};
 
+use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote};
+use super::suggestions::get_explanation_based_on_obligation;
 use super::{
     ArgKind, CandidateSimilarity, GetSafeTransmuteErrorAndReason, ImplCandidate, UnsatisfiedConst,
 };
+use crate::error_reporting::infer::TyCategory;
+use crate::error_reporting::traits::report_object_safety_error;
+use crate::error_reporting::TypeErrCtxt;
+use crate::errors::{
+    AsyncClosureNotFn, ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch,
+};
+use crate::infer::{self, InferCtxt, InferCtxtExt as _};
+use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
+use crate::traits::{
+    elaborate, MismatchedProjectionTypes, NormalizeExt, Obligation, ObligationCause,
+    ObligationCauseCode, ObligationCtxt, Overflow, PredicateObligation, SelectionError,
+    SignatureMismatch, TraitNotObjectSafe,
+};
 
-pub use rustc_infer::traits::error_reporting::*;
-
-#[extension(pub trait TypeErrCtxtSelectionErrExt<'a, 'tcx>)]
 impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     /// The `root_obligation` parameter should be the `root_obligation` field
     /// from a `FulfillmentError`. If no `FulfillmentError` is available,
     /// then it should be the same as `obligation`.
-    fn report_selection_error(
+    pub fn report_selection_error(
         &self,
         mut obligation: PredicateObligation<'tcx>,
         root_obligation: &PredicateObligation<'tcx>,
@@ -173,7 +170,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         {
                             return guar;
                         }
-                        // Silence redundant errors on binding acccess that are already
+                        // Silence redundant errors on binding access that are already
                         // reported on the binding definition (#56607).
                         if let Err(guar) = self.fn_arg_obligation(&obligation) {
                             return guar;
@@ -234,8 +231,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             post_message,
                         );
 
-                        let (err_msg, safe_transmute_explanation) = if Some(main_trait_ref.def_id())
-                            == self.tcx.lang_items().transmute_trait()
+                        let (err_msg, safe_transmute_explanation) = if self.tcx.is_lang_item(main_trait_ref.def_id(), LangItem::TransmuteTrait)
                         {
                             // Recompute the safe transmute reason and use that for the error reporting
                             match self.get_safe_transmute_error_and_reason(
@@ -379,7 +375,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         let impl_candidates = self.find_similar_impl_candidates(leaf_trait_predicate);
                         suggested = if let &[cand] = &impl_candidates[..] {
                             let cand = cand.trait_ref;
-                            if let (ty::FnPtr(_), ty::FnDef(..)) =
+                            if let (ty::FnPtr(..), ty::FnDef(..)) =
                                 (cand.self_ty().kind(), main_trait_ref.self_ty().skip_binder().kind())
                             {
                                 err.span_suggestion(
@@ -682,17 +678,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     }
 }
 
-#[extension(pub(super) trait TypeErrCtxtExt<'a, 'tcx>)]
 impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
-    fn apply_do_not_recommend(&self, obligation: &mut PredicateObligation<'tcx>) -> bool {
+    pub(super) fn apply_do_not_recommend(
+        &self,
+        obligation: &mut PredicateObligation<'tcx>,
+    ) -> bool {
         let mut base_cause = obligation.cause.code().clone();
         let mut applied_do_not_recommend = false;
         loop {
             if let ObligationCauseCode::ImplDerived(ref c) = base_cause {
-                if self.tcx.has_attrs_with_path(
-                    c.impl_or_alias_def_id,
-                    &[sym::diagnostic, sym::do_not_recommend],
-                ) {
+                if self.tcx.do_not_recommend_impl(c.impl_or_alias_def_id) {
                     let code = (*c.derived.parent_code).clone();
                     obligation.cause.map_code(|_| code);
                     obligation.predicate = c.derived.parent_trait_pred.upcast(self.tcx);
@@ -795,8 +790,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             // is unimplemented is because async closures don't implement `Fn`/`FnMut`
             // if they have captures.
             if let Some(by_ref_captures) = by_ref_captures
-                && let ty::FnPtr(sig) = by_ref_captures.kind()
-                && !sig.skip_binder().output().is_unit()
+                && let ty::FnPtr(sig_tys, _) = by_ref_captures.kind()
+                && !sig_tys.skip_binder().output().is_unit()
             {
                 let mut err = self.dcx().create_err(AsyncClosureNotFn {
                     span: self.tcx.def_span(closure_def_id),
@@ -903,7 +898,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         let mut suggested = false;
         let mut chain = vec![];
 
-        // The following logic is simlar to `point_at_chain`, but that's focused on associated types
+        // The following logic is similar to `point_at_chain`, but that's focused on associated types
         let mut expr = expr;
         while let hir::ExprKind::MethodCall(path_segment, rcvr_expr, args, span) = expr.kind {
             // Point at every method call in the chain with the `Result` type.
@@ -946,8 +941,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 // The current method call returns `Result<_, ()>`
                 && self.can_eq(obligation.param_env, ty, found_ty)
                 // There's a single argument in the method call and it is a closure
-                && args.len() == 1
-                && let Some(arg) = args.get(0)
+                && let [arg] = args
                 && let hir::ExprKind::Closure(closure) = arg.kind
                 // The closure has a block for its body with no tail expression
                 && let body = self.tcx.hir().body(closure.body)
@@ -1063,7 +1057,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     "`{ty}` is forbidden as the type of a const generic parameter",
                 )
             }
-            ty::FnPtr(_) => {
+            ty::FnPtr(..) => {
                 struct_span_code_err!(
                     self.dcx(),
                     span,
@@ -1142,7 +1136,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     }
 }
 
-#[extension(pub(super) trait InferCtxtPrivExt<'a, 'tcx>)]
 impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     fn can_match_trait(
         &self,
@@ -1182,7 +1175,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     // returns if `cond` not occurring implies that `error` does not occur - i.e., that
     // `error` occurring implies that `cond` occurs.
     #[instrument(level = "debug", skip(self), ret)]
-    fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool {
+    pub(super) fn error_implies(
+        &self,
+        cond: ty::Predicate<'tcx>,
+        error: ty::Predicate<'tcx>,
+    ) -> bool {
         if cond == error {
             return true;
         }
@@ -1205,7 +1202,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     }
 
     #[instrument(level = "debug", skip_all)]
-    fn report_projection_error(
+    pub(super) fn report_projection_error(
         &self,
         obligation: &PredicateObligation<'tcx>,
         error: &MismatchedProjectionTypes<'tcx>,
@@ -1455,7 +1452,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         }
     }
 
-    fn fuzzy_match_tys(
+    pub fn fuzzy_match_tys(
         &self,
         mut a: Ty<'tcx>,
         mut b: Ty<'tcx>,
@@ -1535,7 +1532,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         }
     }
 
-    fn describe_closure(&self, kind: hir::ClosureKind) -> &'static str {
+    pub(super) fn describe_closure(&self, kind: hir::ClosureKind) -> &'static str {
         match kind {
             hir::ClosureKind::Closure => "a closure",
             hir::ClosureKind::Coroutine(hir::CoroutineKind::Coroutine(_)) => "a coroutine",
@@ -1585,7 +1582,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         }
     }
 
-    fn find_similar_impl_candidates(
+    pub(super) fn find_similar_impl_candidates(
         &self,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) -> Vec<ImplCandidate<'tcx>> {
@@ -1615,7 +1612,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         candidates
     }
 
-    fn report_similar_impl_candidates(
+    pub(super) fn report_similar_impl_candidates(
         &self,
         impl_candidates: &[ImplCandidate<'tcx>],
         trait_ref: ty::PolyTraitRef<'tcx>,
@@ -1624,9 +1621,127 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         other: bool,
         param_env: ty::ParamEnv<'tcx>,
     ) -> bool {
-        // If we have a single implementation, try to unify it with the trait ref
-        // that failed. This should uncover a better hint for what *is* implemented.
+        let alternative_candidates = |def_id: DefId| {
+            let mut impl_candidates: Vec<_> = self
+                .tcx
+                .all_impls(def_id)
+                // ignore `do_not_recommend` items
+                .filter(|def_id| !self.tcx.do_not_recommend_impl(*def_id))
+                // Ignore automatically derived impls and `!Trait` impls.
+                .filter_map(|def_id| self.tcx.impl_trait_header(def_id))
+                .filter_map(|header| {
+                    (header.polarity != ty::ImplPolarity::Negative
+                        || self.tcx.is_automatically_derived(def_id))
+                    .then(|| header.trait_ref.instantiate_identity())
+                })
+                .filter(|trait_ref| {
+                    let self_ty = trait_ref.self_ty();
+                    // Avoid mentioning type parameters.
+                    if let ty::Param(_) = self_ty.kind() {
+                        false
+                    }
+                    // Avoid mentioning types that are private to another crate
+                    else if let ty::Adt(def, _) = self_ty.peel_refs().kind() {
+                        // FIXME(compiler-errors): This could be generalized, both to
+                        // be more granular, and probably look past other `#[fundamental]`
+                        // types, too.
+                        self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx)
+                    } else {
+                        true
+                    }
+                })
+                .collect();
+
+            impl_candidates.sort_by_key(|tr| tr.to_string());
+            impl_candidates.dedup();
+            impl_candidates
+        };
+
+        // We'll check for the case where the reason for the mismatch is that the trait comes from
+        // one crate version and the type comes from another crate version, even though they both
+        // are from the same crate.
+        let trait_def_id = trait_ref.def_id();
+        if let ty::Adt(def, _) = trait_ref.self_ty().skip_binder().peel_refs().kind()
+            && let found_type = def.did()
+            && trait_def_id.krate != found_type.krate
+            && self.tcx.crate_name(trait_def_id.krate) == self.tcx.crate_name(found_type.krate)
+        {
+            let name = self.tcx.crate_name(trait_def_id.krate);
+            let spans: Vec<_> = [trait_def_id, found_type]
+                .into_iter()
+                .filter_map(|def_id| self.tcx.extern_crate(def_id.krate))
+                .map(|data| {
+                    let dependency = if data.dependency_of == LOCAL_CRATE {
+                        "direct dependency of the current crate".to_string()
+                    } else {
+                        let dep = self.tcx.crate_name(data.dependency_of);
+                        format!("dependency of crate `{dep}`")
+                    };
+                    (
+                        data.span,
+                        format!("one version of crate `{name}` is used here, as a {dependency}"),
+                    )
+                })
+                .collect();
+            let mut span: MultiSpan = spans.iter().map(|(sp, _)| *sp).collect::<Vec<Span>>().into();
+            for (sp, label) in spans.into_iter() {
+                span.push_span_label(sp, label);
+            }
+            err.highlighted_span_help(
+                span,
+                vec![
+                    StringPart::normal("there are ".to_string()),
+                    StringPart::highlighted("multiple different versions".to_string()),
+                    StringPart::normal(" of crate `".to_string()),
+                    StringPart::highlighted(format!("{name}")),
+                    StringPart::normal("` in the dependency graph".to_string()),
+                ],
+            );
+            let candidates = if impl_candidates.is_empty() {
+                alternative_candidates(trait_def_id)
+            } else {
+                impl_candidates.into_iter().map(|cand| cand.trait_ref).collect()
+            };
+            if let Some((sp_candidate, sp_found)) = candidates.iter().find_map(|trait_ref| {
+                if let ty::Adt(def, _) = trait_ref.self_ty().peel_refs().kind()
+                    && let candidate_def_id = def.did()
+                    && let Some(name) = self.tcx.opt_item_name(candidate_def_id)
+                    && let Some(found) = self.tcx.opt_item_name(found_type)
+                    && name == found
+                    && candidate_def_id.krate != found_type.krate
+                    && self.tcx.crate_name(candidate_def_id.krate)
+                        == self.tcx.crate_name(found_type.krate)
+                {
+                    // A candidate was found of an item with the same name, from two separate
+                    // versions of the same crate, let's clarify.
+                    Some((self.tcx.def_span(candidate_def_id), self.tcx.def_span(found_type)))
+                } else {
+                    None
+                }
+            }) {
+                let mut span: MultiSpan = vec![sp_candidate, sp_found].into();
+                span.push_span_label(self.tcx.def_span(trait_def_id), "this is the required trait");
+                span.push_span_label(sp_candidate, "this type implements the required trait");
+                span.push_span_label(sp_found, "this type doesn't implement the required trait");
+                err.highlighted_span_note(
+                    span,
+                    vec![
+                        StringPart::normal(
+                            "two types coming from two different versions of the same crate are \
+                             different types "
+                                .to_string(),
+                        ),
+                        StringPart::highlighted("even if they look the same".to_string()),
+                    ],
+                );
+            }
+            err.help("you can use `cargo tree` to explore your dependency tree");
+            return true;
+        }
+
         if let [single] = &impl_candidates {
+            // If we have a single implementation, try to unify it with the trait ref
+            // that failed. This should uncover a better hint for what *is* implemented.
             if self.probe(|_| {
                 let ocx = ObligationCtxt::new(self);
 
@@ -1721,10 +1836,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             if let &[cand] = &candidates[..] {
                 let (desc, mention_castable) =
                     match (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind()) {
-                        (ty::FnPtr(_), ty::FnDef(..)) => {
+                        (ty::FnPtr(..), ty::FnDef(..)) => {
                             (" implemented for fn pointer `", ", cast using `as`")
                         }
-                        (ty::FnPtr(_), _) => (" implemented for fn pointer `", ""),
+                        (ty::FnPtr(..), _) => (" implemented for fn pointer `", ""),
                         _ => (" implemented for `", ""),
                     };
                 err.highlighted_help(vec![
@@ -1781,12 +1896,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         let impl_candidates = impl_candidates
             .into_iter()
             .cloned()
-            .filter(|cand| {
-                !self.tcx.has_attrs_with_path(
-                    cand.impl_def_id,
-                    &[sym::diagnostic, sym::do_not_recommend],
-                )
-            })
+            .filter(|cand| !self.tcx.do_not_recommend_impl(cand.impl_def_id))
             .collect::<Vec<_>>();
 
         let def_id = trait_ref.def_id();
@@ -1798,43 +1908,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 // Mentioning implementers of `Copy`, `Debug` and friends is not useful.
                 return false;
             }
-            let mut impl_candidates: Vec<_> = self
-                .tcx
-                .all_impls(def_id)
-                // ignore `do_not_recommend` items
-                .filter(|def_id| {
-                    !self
-                        .tcx
-                        .has_attrs_with_path(*def_id, &[sym::diagnostic, sym::do_not_recommend])
-                })
-                // Ignore automatically derived impls and `!Trait` impls.
-                .filter_map(|def_id| self.tcx.impl_trait_header(def_id))
-                .filter_map(|header| {
-                    (header.polarity != ty::ImplPolarity::Negative
-                        || self.tcx.is_automatically_derived(def_id))
-                    .then(|| header.trait_ref.instantiate_identity())
-                })
-                .filter(|trait_ref| {
-                    let self_ty = trait_ref.self_ty();
-                    // Avoid mentioning type parameters.
-                    if let ty::Param(_) = self_ty.kind() {
-                        false
-                    }
-                    // Avoid mentioning types that are private to another crate
-                    else if let ty::Adt(def, _) = self_ty.peel_refs().kind() {
-                        // FIXME(compiler-errors): This could be generalized, both to
-                        // be more granular, and probably look past other `#[fundamental]`
-                        // types, too.
-                        self.tcx.visibility(def.did()).is_accessible_from(body_def_id, self.tcx)
-                    } else {
-                        true
-                    }
-                })
-                .collect();
-
-            impl_candidates.sort_by_key(|tr| tr.to_string());
-            impl_candidates.dedup();
-            return report(impl_candidates, err);
+            return report(alternative_candidates(def_id), err);
         }
 
         // Sort impl candidates so that ordering is consistent for UI tests.
@@ -1989,7 +2063,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     /// `trait_ref`.
     ///
     /// For this to work, `new_self_ty` must have no escaping bound variables.
-    fn mk_trait_obligation_with_new_self_ty(
+    pub(super) fn mk_trait_obligation_with_new_self_ty(
         &self,
         param_env: ty::ParamEnv<'tcx>,
         trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>,
@@ -2041,7 +2115,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         })
     }
 
-    fn note_obligation_cause(&self, err: &mut Diag<'_>, obligation: &PredicateObligation<'tcx>) {
+    pub fn note_obligation_cause(
+        &self,
+        err: &mut Diag<'_>,
+        obligation: &PredicateObligation<'tcx>,
+    ) {
         // First, attempt to add note to this error with an async-await-specific
         // message, and fall back to regular note otherwise.
         if !self.maybe_note_obligation_cause_for_async_await(err, obligation) {
@@ -2067,7 +2145,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         }
     }
 
-    fn is_recursive_obligation(
+    pub(super) fn is_recursive_obligation(
         &self,
         obligated_types: &mut Vec<Ty<'tcx>>,
         cause_code: &ObligationCauseCode<'tcx>,
@@ -2596,7 +2674,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     })
                     .unwrap_or((found_span, None, found));
 
-                self.infcx.report_arg_count_mismatch(
+                self.report_arg_count_mismatch(
                     span,
                     closure_span,
                     expected,
@@ -2608,6 +2686,242 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         )
     }
 
+    /// Given some node representing a fn-like thing in the HIR map,
+    /// returns a span and `ArgKind` information that describes the
+    /// arguments it expects. This can be supplied to
+    /// `report_arg_count_mismatch`.
+    pub fn get_fn_like_arguments(
+        &self,
+        node: Node<'_>,
+    ) -> Option<(Span, Option<Span>, Vec<ArgKind>)> {
+        let sm = self.tcx.sess.source_map();
+        let hir = self.tcx.hir();
+        Some(match node {
+            Node::Expr(&hir::Expr {
+                kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }),
+                ..
+            }) => (
+                fn_decl_span,
+                fn_arg_span,
+                hir.body(body)
+                    .params
+                    .iter()
+                    .map(|arg| {
+                        if let hir::Pat { kind: hir::PatKind::Tuple(args, _), span, .. } = *arg.pat
+                        {
+                            Some(ArgKind::Tuple(
+                                Some(span),
+                                args.iter()
+                                    .map(|pat| {
+                                        sm.span_to_snippet(pat.span)
+                                            .ok()
+                                            .map(|snippet| (snippet, "_".to_owned()))
+                                    })
+                                    .collect::<Option<Vec<_>>>()?,
+                            ))
+                        } else {
+                            let name = sm.span_to_snippet(arg.pat.span).ok()?;
+                            Some(ArgKind::Arg(name, "_".to_owned()))
+                        }
+                    })
+                    .collect::<Option<Vec<ArgKind>>>()?,
+            ),
+            Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. })
+            | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. })
+            | Node::TraitItem(&hir::TraitItem {
+                kind: hir::TraitItemKind::Fn(ref sig, _), ..
+            })
+            | Node::ForeignItem(&hir::ForeignItem {
+                kind: hir::ForeignItemKind::Fn(ref sig, _, _),
+                ..
+            }) => (
+                sig.span,
+                None,
+                sig.decl
+                    .inputs
+                    .iter()
+                    .map(|arg| match arg.kind {
+                        hir::TyKind::Tup(tys) => ArgKind::Tuple(
+                            Some(arg.span),
+                            vec![("_".to_owned(), "_".to_owned()); tys.len()],
+                        ),
+                        _ => ArgKind::empty(),
+                    })
+                    .collect::<Vec<ArgKind>>(),
+            ),
+            Node::Ctor(variant_data) => {
+                let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id));
+                (span, None, vec![ArgKind::empty(); variant_data.fields().len()])
+            }
+            _ => panic!("non-FnLike node found: {node:?}"),
+        })
+    }
+
+    /// Reports an error when the number of arguments needed by a
+    /// trait match doesn't match the number that the expression
+    /// provides.
+    pub fn report_arg_count_mismatch(
+        &self,
+        span: Span,
+        found_span: Option<Span>,
+        expected_args: Vec<ArgKind>,
+        found_args: Vec<ArgKind>,
+        is_closure: bool,
+        closure_arg_span: Option<Span>,
+    ) -> Diag<'a> {
+        let kind = if is_closure { "closure" } else { "function" };
+
+        let args_str = |arguments: &[ArgKind], other: &[ArgKind]| {
+            let arg_length = arguments.len();
+            let distinct = matches!(other, &[ArgKind::Tuple(..)]);
+            match (arg_length, arguments.get(0)) {
+                (1, Some(ArgKind::Tuple(_, fields))) => {
+                    format!("a single {}-tuple as argument", fields.len())
+                }
+                _ => format!(
+                    "{} {}argument{}",
+                    arg_length,
+                    if distinct && arg_length > 1 { "distinct " } else { "" },
+                    pluralize!(arg_length)
+                ),
+            }
+        };
+
+        let expected_str = args_str(&expected_args, &found_args);
+        let found_str = args_str(&found_args, &expected_args);
+
+        let mut err = struct_span_code_err!(
+            self.dcx(),
+            span,
+            E0593,
+            "{} is expected to take {}, but it takes {}",
+            kind,
+            expected_str,
+            found_str,
+        );
+
+        err.span_label(span, format!("expected {kind} that takes {expected_str}"));
+
+        if let Some(found_span) = found_span {
+            err.span_label(found_span, format!("takes {found_str}"));
+
+            // Suggest to take and ignore the arguments with expected_args_length `_`s if
+            // found arguments is empty (assume the user just wants to ignore args in this case).
+            // For example, if `expected_args_length` is 2, suggest `|_, _|`.
+            if found_args.is_empty() && is_closure {
+                let underscores = vec!["_"; expected_args.len()].join(", ");
+                err.span_suggestion_verbose(
+                    closure_arg_span.unwrap_or(found_span),
+                    format!(
+                        "consider changing the closure to take and ignore the expected argument{}",
+                        pluralize!(expected_args.len())
+                    ),
+                    format!("|{underscores}|"),
+                    Applicability::MachineApplicable,
+                );
+            }
+
+            if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] {
+                if fields.len() == expected_args.len() {
+                    let sugg = fields
+                        .iter()
+                        .map(|(name, _)| name.to_owned())
+                        .collect::<Vec<String>>()
+                        .join(", ");
+                    err.span_suggestion_verbose(
+                        found_span,
+                        "change the closure to take multiple arguments instead of a single tuple",
+                        format!("|{sugg}|"),
+                        Applicability::MachineApplicable,
+                    );
+                }
+            }
+            if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..]
+                && fields.len() == found_args.len()
+                && is_closure
+            {
+                let sugg = format!(
+                    "|({}){}|",
+                    found_args
+                        .iter()
+                        .map(|arg| match arg {
+                            ArgKind::Arg(name, _) => name.to_owned(),
+                            _ => "_".to_owned(),
+                        })
+                        .collect::<Vec<String>>()
+                        .join(", "),
+                    // add type annotations if available
+                    if found_args.iter().any(|arg| match arg {
+                        ArgKind::Arg(_, ty) => ty != "_",
+                        _ => false,
+                    }) {
+                        format!(
+                            ": ({})",
+                            fields
+                                .iter()
+                                .map(|(_, ty)| ty.to_owned())
+                                .collect::<Vec<String>>()
+                                .join(", ")
+                        )
+                    } else {
+                        String::new()
+                    },
+                );
+                err.span_suggestion_verbose(
+                    found_span,
+                    "change the closure to accept a tuple instead of individual arguments",
+                    sugg,
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+
+        err
+    }
+
+    /// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce`
+    /// in that order, and returns the generic type corresponding to the
+    /// argument of that trait (corresponding to the closure arguments).
+    pub fn type_implements_fn_trait(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        ty: ty::Binder<'tcx, Ty<'tcx>>,
+        polarity: ty::PredicatePolarity,
+    ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> {
+        self.commit_if_ok(|_| {
+            for trait_def_id in [
+                self.tcx.lang_items().fn_trait(),
+                self.tcx.lang_items().fn_mut_trait(),
+                self.tcx.lang_items().fn_once_trait(),
+            ] {
+                let Some(trait_def_id) = trait_def_id else { continue };
+                // Make a fresh inference variable so we can determine what the generic parameters
+                // of the trait are.
+                let var = self.next_ty_var(DUMMY_SP);
+                // FIXME(effects)
+                let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty.skip_binder(), var]);
+                let obligation = Obligation::new(
+                    self.tcx,
+                    ObligationCause::dummy(),
+                    param_env,
+                    ty.rebind(ty::TraitPredicate { trait_ref, polarity }),
+                );
+                let ocx = ObligationCtxt::new(self);
+                ocx.register_obligation(obligation);
+                if ocx.select_all_or_error().is_empty() {
+                    return Ok((
+                        self.tcx
+                            .fn_trait_kind_from_def_id(trait_def_id)
+                            .expect("expected to map DefId to ClosureKind"),
+                        ty.rebind(self.resolve_vars_if_possible(var)),
+                    ));
+                }
+            }
+
+            Err(())
+        })
+    }
+
     fn report_not_const_evaluatable_error(
         &self,
         obligation: &PredicateObligation<'tcx>,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs
deleted file mode 100644
index e8d7e80ac56..00000000000
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs
+++ /dev/null
@@ -1,244 +0,0 @@
-// FIXME(error_reporting): This should be made into private methods on `TypeErrCtxt`.
-
-use crate::infer::InferCtxt;
-use crate::traits::{Obligation, ObligationCause, ObligationCtxt};
-use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, Diag};
-use rustc_hir as hir;
-use rustc_hir::Node;
-use rustc_macros::extension;
-use rustc_middle::ty::{self, Ty};
-use rustc_span::{Span, DUMMY_SP};
-
-use super::ArgKind;
-
-#[extension(pub trait InferCtxtExt<'tcx>)]
-impl<'tcx> InferCtxt<'tcx> {
-    /// Given some node representing a fn-like thing in the HIR map,
-    /// returns a span and `ArgKind` information that describes the
-    /// arguments it expects. This can be supplied to
-    /// `report_arg_count_mismatch`.
-    fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option<Span>, Vec<ArgKind>)> {
-        let sm = self.tcx.sess.source_map();
-        let hir = self.tcx.hir();
-        Some(match node {
-            Node::Expr(&hir::Expr {
-                kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }),
-                ..
-            }) => (
-                fn_decl_span,
-                fn_arg_span,
-                hir.body(body)
-                    .params
-                    .iter()
-                    .map(|arg| {
-                        if let hir::Pat { kind: hir::PatKind::Tuple(args, _), span, .. } = *arg.pat
-                        {
-                            Some(ArgKind::Tuple(
-                                Some(span),
-                                args.iter()
-                                    .map(|pat| {
-                                        sm.span_to_snippet(pat.span)
-                                            .ok()
-                                            .map(|snippet| (snippet, "_".to_owned()))
-                                    })
-                                    .collect::<Option<Vec<_>>>()?,
-                            ))
-                        } else {
-                            let name = sm.span_to_snippet(arg.pat.span).ok()?;
-                            Some(ArgKind::Arg(name, "_".to_owned()))
-                        }
-                    })
-                    .collect::<Option<Vec<ArgKind>>>()?,
-            ),
-            Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. })
-            | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. })
-            | Node::TraitItem(&hir::TraitItem {
-                kind: hir::TraitItemKind::Fn(ref sig, _), ..
-            }) => (
-                sig.span,
-                None,
-                sig.decl
-                    .inputs
-                    .iter()
-                    .map(|arg| match arg.kind {
-                        hir::TyKind::Tup(tys) => ArgKind::Tuple(
-                            Some(arg.span),
-                            vec![("_".to_owned(), "_".to_owned()); tys.len()],
-                        ),
-                        _ => ArgKind::empty(),
-                    })
-                    .collect::<Vec<ArgKind>>(),
-            ),
-            Node::Ctor(variant_data) => {
-                let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id));
-                (span, None, vec![ArgKind::empty(); variant_data.fields().len()])
-            }
-            _ => panic!("non-FnLike node found: {node:?}"),
-        })
-    }
-
-    /// Reports an error when the number of arguments needed by a
-    /// trait match doesn't match the number that the expression
-    /// provides.
-    fn report_arg_count_mismatch(
-        &self,
-        span: Span,
-        found_span: Option<Span>,
-        expected_args: Vec<ArgKind>,
-        found_args: Vec<ArgKind>,
-        is_closure: bool,
-        closure_arg_span: Option<Span>,
-    ) -> Diag<'_> {
-        let kind = if is_closure { "closure" } else { "function" };
-
-        let args_str = |arguments: &[ArgKind], other: &[ArgKind]| {
-            let arg_length = arguments.len();
-            let distinct = matches!(other, &[ArgKind::Tuple(..)]);
-            match (arg_length, arguments.get(0)) {
-                (1, Some(ArgKind::Tuple(_, fields))) => {
-                    format!("a single {}-tuple as argument", fields.len())
-                }
-                _ => format!(
-                    "{} {}argument{}",
-                    arg_length,
-                    if distinct && arg_length > 1 { "distinct " } else { "" },
-                    pluralize!(arg_length)
-                ),
-            }
-        };
-
-        let expected_str = args_str(&expected_args, &found_args);
-        let found_str = args_str(&found_args, &expected_args);
-
-        let mut err = struct_span_code_err!(
-            self.dcx(),
-            span,
-            E0593,
-            "{} is expected to take {}, but it takes {}",
-            kind,
-            expected_str,
-            found_str,
-        );
-
-        err.span_label(span, format!("expected {kind} that takes {expected_str}"));
-
-        if let Some(found_span) = found_span {
-            err.span_label(found_span, format!("takes {found_str}"));
-
-            // Suggest to take and ignore the arguments with expected_args_length `_`s if
-            // found arguments is empty (assume the user just wants to ignore args in this case).
-            // For example, if `expected_args_length` is 2, suggest `|_, _|`.
-            if found_args.is_empty() && is_closure {
-                let underscores = vec!["_"; expected_args.len()].join(", ");
-                err.span_suggestion_verbose(
-                    closure_arg_span.unwrap_or(found_span),
-                    format!(
-                        "consider changing the closure to take and ignore the expected argument{}",
-                        pluralize!(expected_args.len())
-                    ),
-                    format!("|{underscores}|"),
-                    Applicability::MachineApplicable,
-                );
-            }
-
-            if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] {
-                if fields.len() == expected_args.len() {
-                    let sugg = fields
-                        .iter()
-                        .map(|(name, _)| name.to_owned())
-                        .collect::<Vec<String>>()
-                        .join(", ");
-                    err.span_suggestion_verbose(
-                        found_span,
-                        "change the closure to take multiple arguments instead of a single tuple",
-                        format!("|{sugg}|"),
-                        Applicability::MachineApplicable,
-                    );
-                }
-            }
-            if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..]
-                && fields.len() == found_args.len()
-                && is_closure
-            {
-                let sugg = format!(
-                    "|({}){}|",
-                    found_args
-                        .iter()
-                        .map(|arg| match arg {
-                            ArgKind::Arg(name, _) => name.to_owned(),
-                            _ => "_".to_owned(),
-                        })
-                        .collect::<Vec<String>>()
-                        .join(", "),
-                    // add type annotations if available
-                    if found_args.iter().any(|arg| match arg {
-                        ArgKind::Arg(_, ty) => ty != "_",
-                        _ => false,
-                    }) {
-                        format!(
-                            ": ({})",
-                            fields
-                                .iter()
-                                .map(|(_, ty)| ty.to_owned())
-                                .collect::<Vec<String>>()
-                                .join(", ")
-                        )
-                    } else {
-                        String::new()
-                    },
-                );
-                err.span_suggestion_verbose(
-                    found_span,
-                    "change the closure to accept a tuple instead of individual arguments",
-                    sugg,
-                    Applicability::MachineApplicable,
-                );
-            }
-        }
-
-        err
-    }
-
-    /// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce`
-    /// in that order, and returns the generic type corresponding to the
-    /// argument of that trait (corresponding to the closure arguments).
-    fn type_implements_fn_trait(
-        &self,
-        param_env: ty::ParamEnv<'tcx>,
-        ty: ty::Binder<'tcx, Ty<'tcx>>,
-        polarity: ty::PredicatePolarity,
-    ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> {
-        self.commit_if_ok(|_| {
-            for trait_def_id in [
-                self.tcx.lang_items().fn_trait(),
-                self.tcx.lang_items().fn_mut_trait(),
-                self.tcx.lang_items().fn_once_trait(),
-            ] {
-                let Some(trait_def_id) = trait_def_id else { continue };
-                // Make a fresh inference variable so we can determine what the generic parameters
-                // of the trait are.
-                let var = self.next_ty_var(DUMMY_SP);
-                // FIXME(effects)
-                let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty.skip_binder(), var]);
-                let obligation = Obligation::new(
-                    self.tcx,
-                    ObligationCause::dummy(),
-                    param_env,
-                    ty.rebind(ty::TraitPredicate { trait_ref, polarity }),
-                );
-                let ocx = ObligationCtxt::new(self);
-                ocx.register_obligation(obligation);
-                if ocx.select_all_or_error().is_empty() {
-                    return Ok((
-                        self.tcx
-                            .fn_trait_kind_from_def_id(trait_def_id)
-                            .expect("expected to map DefId to ClosureKind"),
-                        ty.rebind(self.resolve_vars_if_possible(var)),
-                    ));
-                }
-            }
-
-            Err(())
-        })
-    }
-}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
index 2131e236401..9aa6d1f3d46 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
@@ -1,34 +1,28 @@
 pub mod ambiguity;
 mod fulfillment_errors;
-mod infer_ctxt_ext;
 pub mod on_unimplemented;
 mod overflow;
 pub mod suggestions;
 
-use std::iter;
+use std::{fmt, iter};
 
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
-use rustc_hir::def_id::DefId;
+use rustc_errors::{struct_span_code_err, Applicability, Diag, MultiSpan, E0038, E0276};
+use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::{self as hir, LangItem};
-use rustc_infer::infer::error_reporting::TypeErrCtxt;
 use rustc_infer::traits::{
-    Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionError,
+    ObjectSafetyViolation, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation,
+    SelectionError,
 };
-use rustc_macros::extension;
-use rustc_middle::ty::print::PrintTraitRefExt as _;
+use rustc_middle::ty::print::{with_no_trimmed_paths, PrintTraitRefExt as _};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::{ErrorGuaranteed, ExpnKind, Span};
+use tracing::{info, instrument};
 
-use ambiguity::TypeErrCtxtAmbiguityExt as _;
-use fulfillment_errors::TypeErrCtxtExt as _;
-use suggestions::TypeErrCtxtExt as _;
-
-use crate::traits::{FulfillmentError, FulfillmentErrorCode};
-
-pub use self::fulfillment_errors::*;
-pub use self::infer_ctxt_ext::*;
 pub use self::overflow::*;
+use crate::error_reporting::TypeErrCtxt;
+use crate::traits::{FulfillmentError, FulfillmentErrorCode};
 
 // When outputting impl candidates, prefer showing those that are more similar.
 //
@@ -137,9 +131,8 @@ pub enum DefIdOrName {
     Name(&'static str),
 }
 
-#[extension(pub trait TypeErrCtxtExt<'a, 'tcx>)]
 impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
-    fn report_fulfillment_errors(
+    pub fn report_fulfillment_errors(
         &self,
         mut errors: Vec<FulfillmentError<'tcx>>,
     ) -> ErrorGuaranteed {
@@ -383,3 +376,194 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti
     w.push(';');
     Some(w)
 }
+
+impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
+    pub fn report_extra_impl_obligation(
+        &self,
+        error_span: Span,
+        impl_item_def_id: LocalDefId,
+        trait_item_def_id: DefId,
+        requirement: &dyn fmt::Display,
+    ) -> Diag<'a> {
+        let mut err = struct_span_code_err!(
+            self.dcx(),
+            error_span,
+            E0276,
+            "impl has stricter requirements than trait"
+        );
+
+        if !self.tcx.is_impl_trait_in_trait(trait_item_def_id) {
+            if let Some(span) = self.tcx.hir().span_if_local(trait_item_def_id) {
+                let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
+                err.span_label(span, format!("definition of `{item_name}` from trait"));
+            }
+        }
+
+        err.span_label(error_span, format!("impl has extra requirement {requirement}"));
+
+        err
+    }
+}
+
+pub fn report_object_safety_error<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    span: Span,
+    hir_id: Option<hir::HirId>,
+    trait_def_id: DefId,
+    violations: &[ObjectSafetyViolation],
+) -> Diag<'tcx> {
+    let trait_str = tcx.def_path_str(trait_def_id);
+    let trait_span = tcx.hir().get_if_local(trait_def_id).and_then(|node| match node {
+        hir::Node::Item(item) => Some(item.ident.span),
+        _ => None,
+    });
+    let mut err = struct_span_code_err!(
+        tcx.dcx(),
+        span,
+        E0038,
+        "the trait `{}` cannot be made into an object",
+        trait_str
+    );
+    err.span_label(span, format!("`{trait_str}` cannot be made into an object"));
+
+    if let Some(hir_id) = hir_id
+        && let hir::Node::Ty(ty) = tcx.hir_node(hir_id)
+        && let hir::TyKind::TraitObject([trait_ref, ..], ..) = ty.kind
+    {
+        let mut hir_id = hir_id;
+        while let hir::Node::Ty(ty) = tcx.parent_hir_node(hir_id) {
+            hir_id = ty.hir_id;
+        }
+        if tcx.parent_hir_node(hir_id).fn_sig().is_some() {
+            // Do not suggest `impl Trait` when dealing with things like super-traits.
+            err.span_suggestion_verbose(
+                ty.span.until(trait_ref.0.span),
+                "consider using an opaque type instead",
+                "impl ",
+                Applicability::MaybeIncorrect,
+            );
+        }
+    }
+    let mut reported_violations = FxIndexSet::default();
+    let mut multi_span = vec![];
+    let mut messages = vec![];
+    for violation in violations {
+        if let ObjectSafetyViolation::SizedSelf(sp) = &violation
+            && !sp.is_empty()
+        {
+            // Do not report `SizedSelf` without spans pointing at `SizedSelf` obligations
+            // with a `Span`.
+            reported_violations.insert(ObjectSafetyViolation::SizedSelf(vec![].into()));
+        }
+        if reported_violations.insert(violation.clone()) {
+            let spans = violation.spans();
+            let msg = if trait_span.is_none() || spans.is_empty() {
+                format!("the trait cannot be made into an object because {}", violation.error_msg())
+            } else {
+                format!("...because {}", violation.error_msg())
+            };
+            if spans.is_empty() {
+                err.note(msg);
+            } else {
+                for span in spans {
+                    multi_span.push(span);
+                    messages.push(msg.clone());
+                }
+            }
+        }
+    }
+    let has_multi_span = !multi_span.is_empty();
+    let mut note_span = MultiSpan::from_spans(multi_span.clone());
+    if let (Some(trait_span), true) = (trait_span, has_multi_span) {
+        note_span.push_span_label(trait_span, "this trait cannot be made into an object...");
+    }
+    for (span, msg) in iter::zip(multi_span, messages) {
+        note_span.push_span_label(span, msg);
+    }
+    err.span_note(
+        note_span,
+        "for a trait to be \"object safe\" it needs to allow building a vtable to allow the call \
+         to be resolvable dynamically; for more information visit \
+         <https://doc.rust-lang.org/reference/items/traits.html#object-safety>",
+    );
+
+    // Only provide the help if its a local trait, otherwise it's not actionable.
+    if trait_span.is_some() {
+        let mut reported_violations: Vec<_> = reported_violations.into_iter().collect();
+        reported_violations.sort();
+
+        let mut potential_solutions: Vec<_> =
+            reported_violations.into_iter().map(|violation| violation.solution()).collect();
+        potential_solutions.sort();
+        // Allows us to skip suggesting that the same item should be moved to another trait multiple times.
+        potential_solutions.dedup();
+        for solution in potential_solutions {
+            solution.add_to(&mut err);
+        }
+    }
+
+    let impls_of = tcx.trait_impls_of(trait_def_id);
+    let impls = if impls_of.blanket_impls().is_empty() {
+        impls_of
+            .non_blanket_impls()
+            .values()
+            .flatten()
+            .filter(|def_id| {
+                !matches!(tcx.type_of(*def_id).instantiate_identity().kind(), ty::Dynamic(..))
+            })
+            .collect::<Vec<_>>()
+    } else {
+        vec![]
+    };
+    let externally_visible = if !impls.is_empty()
+        && let Some(def_id) = trait_def_id.as_local()
+        // We may be executing this during typeck, which would result in cycle
+        // if we used effective_visibilities query, which looks into opaque types
+        // (and therefore calls typeck).
+        && tcx.resolutions(()).effective_visibilities.is_exported(def_id)
+    {
+        true
+    } else {
+        false
+    };
+    match &impls[..] {
+        [] => {}
+        _ if impls.len() > 9 => {}
+        [only] if externally_visible => {
+            err.help(with_no_trimmed_paths!(format!(
+                "only type `{}` is seen to implement the trait in this crate, consider using it \
+                 directly instead",
+                tcx.type_of(*only).instantiate_identity(),
+            )));
+        }
+        [only] => {
+            err.help(with_no_trimmed_paths!(format!(
+                "only type `{}` implements the trait, consider using it directly instead",
+                tcx.type_of(*only).instantiate_identity(),
+            )));
+        }
+        impls => {
+            let types = impls
+                .iter()
+                .map(|t| {
+                    with_no_trimmed_paths!(format!("  {}", tcx.type_of(*t).instantiate_identity(),))
+                })
+                .collect::<Vec<_>>();
+            err.help(format!(
+                "the following types implement the trait, consider defining an enum where each \
+                 variant holds one of these types, implementing `{}` for this new enum and using \
+                 it instead:\n{}",
+                trait_str,
+                types.join("\n"),
+            ));
+        }
+    }
+    if externally_visible {
+        err.note(format!(
+            "`{trait_str}` can be implemented in other crates; if you want to support your users \
+             passing their own types here, you can't refer to a specific type",
+        ));
+    }
+
+    err
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
index e90fe8fb94d..41b0ee56a4c 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
@@ -1,30 +1,28 @@
-use super::{ObligationCauseCode, PredicateObligation};
-use crate::error_reporting::traits::fulfillment_errors::InferCtxtPrivExt;
-use crate::errors::{
-    EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
-};
-use crate::infer::error_reporting::TypeErrCtxt;
-use crate::infer::InferCtxtExt;
-use rustc_ast::AttrArgs;
-use rustc_ast::AttrArgsEq;
-use rustc_ast::AttrKind;
-use rustc_ast::{Attribute, MetaItem, NestedMetaItem};
-use rustc_attr as attr;
+use std::iter;
+use std::path::PathBuf;
+
+use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind, Attribute, MetaItem, NestedMetaItem};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{codes::*, struct_span_code_err, ErrorGuaranteed};
-use rustc_hir as hir;
+use rustc_errors::codes::*;
+use rustc_errors::{struct_span_code_err, ErrorGuaranteed};
 use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_macros::{extension, LintDiagnostic};
+use rustc_macros::LintDiagnostic;
 use rustc_middle::bug;
 use rustc_middle::ty::print::PrintTraitRefExt as _;
-use rustc_middle::ty::GenericArgsRef;
-use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
+use rustc_middle::ty::{self, GenericArgsRef, GenericParamDefKind, TyCtxt};
 use rustc_parse_format::{ParseMode, Parser, Piece, Position};
 use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
 use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::Span;
-use std::iter;
-use std::path::PathBuf;
+use tracing::{debug, info};
+use {rustc_attr as attr, rustc_hir as hir};
+
+use super::{ObligationCauseCode, PredicateObligation};
+use crate::error_reporting::TypeErrCtxt;
+use crate::errors::{
+    EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
+};
+use crate::infer::InferCtxtExt;
 
 /// The symbols which are always allowed in a format string
 static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[
@@ -41,7 +39,6 @@ static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[
     sym::Trait,
 ];
 
-#[extension(pub trait TypeErrCtxtExt<'tcx>)]
 impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn impl_similar_to(
         &self,
@@ -77,10 +74,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 }
             });
 
-            let impl_def_id_and_args = if self_match_impls.len() == 1 {
-                self_match_impls[0]
-            } else if fuzzy_match_impls.len() == 1 {
-                fuzzy_match_impls[0]
+            let impl_def_id_and_args = if let [impl_] = self_match_impls[..] {
+                impl_
+            } else if let [impl_] = fuzzy_match_impls[..] {
+                impl_
             } else {
                 return None;
             };
@@ -109,7 +106,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
     }
 
-    fn on_unimplemented_note(
+    pub fn on_unimplemented_note(
         &self,
         trait_ref: ty::PolyTraitRef<'tcx>,
         obligation: &PredicateObligation<'tcx>,
@@ -881,7 +878,7 @@ impl<'tcx> OnUnimplementedFormatString {
             }
         }
         // we cannot return errors from processing the format string as hard error here
-        // as the diagnostic namespace gurantees that malformed input cannot cause an error
+        // as the diagnostic namespace guarantees that malformed input cannot cause an error
         //
         // if we encounter any error while processing we nevertheless want to show it as warning
         // so that users are aware that something is not correct
@@ -989,10 +986,10 @@ impl<'tcx> OnUnimplementedFormatString {
             })
             .collect();
         // we cannot return errors from processing the format string as hard error here
-        // as the diagnostic namespace gurantees that malformed input cannot cause an error
+        // as the diagnostic namespace guarantees that malformed input cannot cause an error
         //
         // if we encounter any error while processing the format string
-        // we don't want to show the potentially half assembled formated string,
+        // we don't want to show the potentially half assembled formatted string,
         // therefore we fall back to just showing the input string in this case
         //
         // The actual parser errors are emitted earlier
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs
index 061a5a4be20..51fb9f3c622 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs
@@ -5,17 +5,15 @@ use rustc_errors::{
 };
 use rustc_hir::def::Namespace;
 use rustc_hir::def_id::LOCAL_CRATE;
-use rustc_infer::infer::error_reporting::TypeErrCtxt;
 use rustc_infer::traits::{Obligation, PredicateObligation};
-use rustc_macros::extension;
 use rustc_middle::ty::print::{FmtPrinter, Print};
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_session::Limit;
 use rustc_span::Span;
 use rustc_type_ir::Upcast;
+use tracing::debug;
 
-use super::InferCtxtPrivExt;
-use crate::error_reporting::traits::suggestions::TypeErrCtxtExt;
+use crate::error_reporting::TypeErrCtxt;
 
 pub enum OverflowCause<'tcx> {
     DeeplyNormalize(ty::AliasTerm<'tcx>),
@@ -38,7 +36,6 @@ pub fn suggest_new_overflow_limit<'tcx, G: EmissionGuarantee>(
     ));
 }
 
-#[extension(pub trait TypeErrCtxtOverflowExt<'a, 'tcx>)]
 impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     /// Reports that an overflow has occurred and halts compilation. We
     /// halt compilation unconditionally because it is important that
@@ -46,7 +43,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     /// whose result could not be truly determined and thus we can't say
     /// if the program type checks or not -- and they are unusual
     /// occurrences in any case.
-    fn report_overflow_error(
+    pub fn report_overflow_error(
         &self,
         cause: OverflowCause<'tcx>,
         span: Span,
@@ -59,7 +56,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         FatalError.raise();
     }
 
-    fn build_overflow_error(
+    pub fn build_overflow_error(
         &self,
         cause: OverflowCause<'tcx>,
         span: Span,
@@ -132,7 +129,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     /// whose result could not be truly determined and thus we can't say
     /// if the program type checks or not -- and they are unusual
     /// occurrences in any case.
-    fn report_overflow_obligation<T>(
+    pub fn report_overflow_obligation<T>(
         &self,
         obligation: &Obligation<'tcx, T>,
         suggest_increasing_limit: bool,
@@ -165,7 +162,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     /// that we can give a more helpful error message (and, in particular,
     /// we do not suggest increasing the overflow limit, which is not
     /// going to help).
-    fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! {
+    pub fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! {
         let cycle = self.resolve_vars_if_possible(cycle.to_owned());
         assert!(!cycle.is_empty());
 
@@ -179,7 +176,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         );
     }
 
-    fn report_overflow_no_abort(
+    pub fn report_overflow_no_abort(
         &self,
         obligation: PredicateObligation<'tcx>,
         suggest_increasing_limit: bool,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index 43bc925d09b..45e157b1080 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -1,35 +1,33 @@
 // ignore-tidy-filelength
 
-use super::{
-    DefIdOrName, FindExprBySpan, ImplCandidate, Obligation, ObligationCause, ObligationCauseCode,
-    PredicateObligation,
-};
-
-use crate::errors;
-use crate::infer::InferCtxt;
-use crate::traits::{ImplDerivedCause, NormalizeExt, ObligationCtxt};
+use std::assert_matches::debug_assert_matches;
+use std::borrow::Cow;
+use std::iter;
 
-use hir::def::CtorOf;
+use itertools::{EitherOrBoth, Itertools};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_errors::codes::*;
 use rustc_errors::{
-    codes::*, pluralize, struct_span_code_err, Applicability, Diag, EmissionGuarantee, MultiSpan,
-    Style, SuggestionStyle,
+    pluralize, struct_span_code_err, Applicability, Diag, EmissionGuarantee, MultiSpan, Style,
+    SuggestionStyle,
 };
 use rustc_hir as hir;
-use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::Visitor;
-use rustc_hir::is_range_literal;
 use rustc_hir::lang_items::LangItem;
-use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node};
-use rustc_infer::infer::error_reporting::TypeErrCtxt;
-use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk};
-use rustc_macros::extension;
+use rustc_hir::{
+    is_range_literal, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node,
+};
+use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk};
 use rustc_middle::hir::map;
 use rustc_middle::traits::IsConstable;
 use rustc_middle::ty::error::TypeError;
-use rustc_middle::ty::print::PrintPolyTraitRefExt;
+use rustc_middle::ty::print::{
+    with_forced_trimmed_paths, with_no_trimmed_paths, PrintPolyTraitPredicateExt as _,
+    PrintPolyTraitRefExt, PrintTraitPredicateExt as _,
+};
 use rustc_middle::ty::{
     self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, GenericArgs,
     InferTy, IsSuggestable, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder,
@@ -40,20 +38,17 @@ use rustc_span::def_id::LocalDefId;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span, DUMMY_SP};
 use rustc_target::spec::abi;
-use std::assert_matches::debug_assert_matches;
-use std::borrow::Cow;
-use std::iter;
+use tracing::{debug, instrument};
 
-use crate::error_reporting::traits::fulfillment_errors::InferCtxtPrivExt;
+use super::{
+    DefIdOrName, FindExprBySpan, ImplCandidate, Obligation, ObligationCause, ObligationCauseCode,
+    PredicateObligation,
+};
+use crate::error_reporting::TypeErrCtxt;
+use crate::errors;
 use crate::infer::InferCtxtExt as _;
 use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
-use rustc_middle::ty::print::{
-    with_forced_trimmed_paths, with_no_trimmed_paths, PrintPolyTraitPredicateExt as _,
-    PrintTraitPredicateExt as _,
-};
-
-use itertools::EitherOrBoth;
-use itertools::Itertools;
+use crate::traits::{ImplDerivedCause, NormalizeExt, ObligationCtxt};
 
 #[derive(Debug)]
 pub enum CoroutineInteriorOrUpvar {
@@ -241,9 +236,8 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>(
     }
 }
 
-#[extension(pub trait TypeErrCtxtExt<'a, 'tcx>)]
 impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
-    fn suggest_restricting_param_bound(
+    pub fn suggest_restricting_param_bound(
         &self,
         err: &mut Diag<'_>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
@@ -453,7 +447,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     /// When after several dereferencing, the reference satisfies the trait
     /// bound. This function provides dereference suggestion for this
     /// specific situation.
-    fn suggest_dereferences(
+    pub(super) fn suggest_dereferences(
         &self,
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diag<'_>,
@@ -466,7 +460,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             && let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(expr)
         {
             // Suggest dereferencing the argument to a function/method call if possible
-
             let mut real_trait_pred = trait_pred;
             while let Some((parent_code, parent_trait_pred)) = code.parent() {
                 code = parent_code;
@@ -553,6 +546,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         );
                         if self.predicate_may_hold(&obligation)
                             && self.predicate_must_hold_modulo_regions(&sized_obligation)
+                            // Do not suggest * if it is already a reference,
+                            // will suggest removing the borrow instead in that case.
+                            && !matches!(expr.kind, hir::ExprKind::AddrOf(..))
                         {
                             let call_node = self.tcx.hir_node(*call_hir_id);
                             let msg = "consider dereferencing here";
@@ -780,7 +776,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     /// We tried to apply the bound to an `fn` or closure. Check whether calling it would
     /// evaluate to a type that *would* satisfy the trait bound. If it would, suggest calling
     /// it: `bar(foo)` → `bar(foo())`. This case is *very* likely to be hit if `foo` is `async`.
-    fn suggest_fn_call(
+    pub(super) fn suggest_fn_call(
         &self,
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diag<'_>,
@@ -896,7 +892,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         true
     }
 
-    fn check_for_binding_assigned_block_without_tail_expression(
+    pub(super) fn check_for_binding_assigned_block_without_tail_expression(
         &self,
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diag<'_>,
@@ -972,7 +968,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         }
     }
 
-    fn suggest_add_clone_to_arg(
+    pub(super) fn suggest_add_clone_to_arg(
         &self,
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diag<'_>,
@@ -1072,7 +1068,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     /// Extracts information about a callable type for diagnostics. This is a
     /// heuristic -- it doesn't necessarily mean that a type is always callable,
     /// because the callable type must also be well-formed to be called.
-    fn extract_callable_info(
+    pub fn extract_callable_info(
         &self,
         body_id: LocalDefId,
         param_env: ty::ParamEnv<'tcx>,
@@ -1082,10 +1078,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         let Some((def_id_or_name, output, inputs)) =
             (self.autoderef_steps)(found).into_iter().find_map(|(found, _)| {
                 match *found.kind() {
-                    ty::FnPtr(fn_sig) => Some((
+                    ty::FnPtr(sig_tys, _) => Some((
                         DefIdOrName::Name("function pointer"),
-                        fn_sig.output(),
-                        fn_sig.inputs(),
+                        sig_tys.output(),
+                        sig_tys.inputs(),
                     )),
                     ty::FnDef(def_id, _) => {
                         let fn_sig = found.fn_sig(self.tcx);
@@ -1198,7 +1194,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
     }
 
-    fn suggest_add_reference_to_arg(
+    pub(super) fn suggest_add_reference_to_arg(
         &self,
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diag<'_>,
@@ -1420,7 +1416,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     }
 
     // Suggest borrowing the type
-    fn suggest_borrowing_for_object_cast(
+    pub(super) fn suggest_borrowing_for_object_cast(
         &self,
         err: &mut Diag<'_>,
         obligation: &PredicateObligation<'tcx>,
@@ -1455,7 +1451,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
     /// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`,
     /// suggest removing these references until we reach a type that implements the trait.
-    fn suggest_remove_reference(
+    pub(super) fn suggest_remove_reference(
         &self,
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diag<'_>,
@@ -1576,7 +1572,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         false
     }
 
-    fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>) {
+    pub(super) fn suggest_remove_await(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diag<'_>,
+    ) {
         let hir = self.tcx.hir();
         if let ObligationCauseCode::AwaitableExpr(hir_id) = obligation.cause.code().peel_derives()
             && let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id)
@@ -1642,7 +1642,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
     /// Check if the trait bound is implemented for a different mutability and note it in the
     /// final error.
-    fn suggest_change_mut(
+    pub(super) fn suggest_change_mut(
         &self,
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diag<'_>,
@@ -1718,7 +1718,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         }
     }
 
-    fn suggest_semicolon_removal(
+    pub(super) fn suggest_semicolon_removal(
         &self,
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diag<'_>,
@@ -1760,7 +1760,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         false
     }
 
-    fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> {
+    pub(super) fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> {
         let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. }) =
             self.tcx.hir_node_by_def_id(obligation.cause.body_id)
         else {
@@ -1773,7 +1773,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     /// If all conditions are met to identify a returned `dyn Trait`, suggest using `impl Trait` if
     /// applicable and signal that the error has been expanded appropriately and needs to be
     /// emitted.
-    fn suggest_impl_trait(
+    pub(super) fn suggest_impl_trait(
         &self,
         err: &mut Diag<'_>,
         obligation: &PredicateObligation<'tcx>,
@@ -1791,25 +1791,42 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         err.children.clear();
 
         let span = obligation.cause.span;
-        if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span)
+        let body = self.tcx.hir().body_owned_by(obligation.cause.body_id);
+
+        let mut visitor = ReturnsVisitor::default();
+        visitor.visit_body(&body);
+
+        let (pre, impl_span) = if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span)
             && snip.starts_with("dyn ")
         {
-            err.span_suggestion(
-                span.with_hi(span.lo() + BytePos(4)),
-                "return an `impl Trait` instead of a `dyn Trait`, \
-                if all returned values are the same type",
+            ("", span.with_hi(span.lo() + BytePos(4)))
+        } else {
+            ("dyn ", span.shrink_to_lo())
+        };
+        let alternatively = if visitor
+            .returns
+            .iter()
+            .map(|expr| self.typeck_results.as_ref().unwrap().expr_ty_adjusted_opt(expr))
+            .collect::<FxHashSet<_>>()
+            .len()
+            <= 1
+        {
+            err.span_suggestion_verbose(
+                impl_span,
+                "consider returning an `impl Trait` instead of a `dyn Trait`",
                 "impl ",
                 Applicability::MaybeIncorrect,
             );
-        }
-
-        let body = self.tcx.hir().body_owned_by(obligation.cause.body_id);
-
-        let mut visitor = ReturnsVisitor::default();
-        visitor.visit_body(&body);
+            "alternatively, "
+        } else {
+            err.help("if there were a single returned type, you could use `impl Trait` instead");
+            ""
+        };
 
-        let mut sugg =
-            vec![(span.shrink_to_lo(), "Box<".to_string()), (span.shrink_to_hi(), ">".to_string())];
+        let mut sugg = vec![
+            (span.shrink_to_lo(), format!("Box<{pre}")),
+            (span.shrink_to_hi(), ">".to_string()),
+        ];
         sugg.extend(visitor.returns.into_iter().flat_map(|expr| {
             let span =
                 expr.span.find_ancestor_in_same_ctxt(obligation.cause.span).unwrap_or(expr.span);
@@ -1835,7 +1852,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         }));
 
         err.multipart_suggestion(
-            "box the return type, and wrap all of the returned values in `Box::new`",
+            format!(
+                "{alternatively}box the return type, and wrap all of the returned values in \
+                 `Box::new`",
+            ),
             sugg,
             Applicability::MaybeIncorrect,
         );
@@ -1843,7 +1863,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         true
     }
 
-    fn point_at_returns_when_relevant(
+    pub(super) fn point_at_returns_when_relevant(
         &self,
         err: &mut Diag<'_>,
         obligation: &PredicateObligation<'tcx>,
@@ -1875,7 +1895,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         }
     }
 
-    fn report_closure_arg_mismatch(
+    pub(super) fn report_closure_arg_mismatch(
         &self,
         span: Span,
         found_span: Option<Span>,
@@ -1958,20 +1978,22 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = cause else {
             return;
         };
-        let ty::FnPtr(expected) = expected.kind() else {
+        let ty::FnPtr(sig_tys, hdr) = expected.kind() else {
             return;
         };
-        let ty::FnPtr(found) = found.kind() else {
+        let expected = sig_tys.with(*hdr);
+        let ty::FnPtr(sig_tys, hdr) = found.kind() else {
             return;
         };
+        let found = sig_tys.with(*hdr);
         let Node::Expr(arg) = self.tcx.hir_node(*arg_hir_id) else {
             return;
         };
         let hir::ExprKind::Path(path) = arg.kind else {
             return;
         };
-        let expected_inputs = self.tcx.instantiate_bound_regions_with_erased(*expected).inputs();
-        let found_inputs = self.tcx.instantiate_bound_regions_with_erased(*found).inputs();
+        let expected_inputs = self.tcx.instantiate_bound_regions_with_erased(expected).inputs();
+        let found_inputs = self.tcx.instantiate_bound_regions_with_erased(found).inputs();
         let both_tys = expected_inputs.iter().copied().zip(found_inputs.iter().copied());
 
         let arg_expr = |infcx: &InferCtxt<'tcx>, name, expected: Ty<'tcx>, found: Ty<'tcx>| {
@@ -2154,7 +2176,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         }
     }
 
-    fn suggest_fully_qualified_path(
+    pub(super) fn suggest_fully_qualified_path(
         &self,
         err: &mut Diag<'_>,
         item_def_id: DefId,
@@ -2221,7 +2243,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     ///
     /// Returns `true` if an async-await specific note was added to the diagnostic.
     #[instrument(level = "debug", skip_all, fields(?obligation.predicate, ?obligation.cause.span))]
-    fn maybe_note_obligation_cause_for_async_await<G: EmissionGuarantee>(
+    pub fn maybe_note_obligation_cause_for_async_await<G: EmissionGuarantee>(
         &self,
         err: &mut Diag<'_, G>,
         obligation: &PredicateObligation<'tcx>,
@@ -2690,7 +2712,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         );
     }
 
-    fn note_obligation_cause_code<G: EmissionGuarantee, T>(
+    pub(super) fn note_obligation_cause_code<G: EmissionGuarantee, T>(
         &self,
         body_id: LocalDefId,
         err: &mut Diag<'_, G>,
@@ -2706,6 +2728,20 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
         let tcx = self.tcx;
         let predicate = predicate.upcast(tcx);
+        let suggest_remove_deref = |err: &mut Diag<'_, G>, expr: &hir::Expr<'_>| {
+            if let Some(pred) = predicate.as_trait_clause()
+                && tcx.is_lang_item(pred.def_id(), LangItem::Sized)
+                && let hir::ExprKind::Unary(hir::UnOp::Deref, inner) = expr.kind
+            {
+                err.span_suggestion_verbose(
+                    expr.span.until(inner.span),
+                    "references are always `Sized`, even if they point to unsized data; consider \
+                     not dereferencing the expression",
+                    String::new(),
+                    Applicability::MaybeIncorrect,
+                );
+            }
+        };
         match *cause_code {
             ObligationCauseCode::ExprAssignable
             | ObligationCauseCode::MatchExpressionArm { .. }
@@ -2752,6 +2788,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             | ObligationCauseCode::WhereClauseInExpr(item_def_id, span, ..)
                 if !span.is_dummy() =>
             {
+                if let ObligationCauseCode::WhereClauseInExpr(_, _, hir_id, pos) = &cause_code {
+                    if let Node::Expr(expr) = tcx.parent_hir_node(*hir_id)
+                        && let hir::ExprKind::Call(_, args) = expr.kind
+                        && let Some(expr) = args.get(*pos)
+                    {
+                        suggest_remove_deref(err, &expr);
+                    } else if let Node::Expr(expr) = self.tcx.hir_node(*hir_id)
+                        && let hir::ExprKind::MethodCall(_, _, args, _) = expr.kind
+                        && let Some(expr) = args.get(*pos)
+                    {
+                        suggest_remove_deref(err, &expr);
+                    }
+                }
                 let item_name = tcx.def_path_str(item_def_id);
                 let short_item_name = with_forced_trimmed_paths!(tcx.def_path_str(item_def_id));
                 let mut multispan = MultiSpan::from(span);
@@ -2810,7 +2859,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                                     // Do not suggest relaxing if there is an explicit `Sized` obligation.
                                     && !bounds.iter()
                                         .filter_map(|bound| bound.trait_ref())
-                                        .any(|tr| tr.trait_def_id() == tcx.lang_items().sized_trait())
+                                        .any(|tr| tr.trait_def_id().is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Sized)))
                                 {
                                     let (span, separator) = if let [.., last] = bounds {
                                         (last.span().shrink_to_hi(), " +")
@@ -2949,6 +2998,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     ));
                     err.downgrade_to_delayed_bug();
                 }
+                let mut local = true;
                 match tcx.parent_hir_node(hir_id) {
                     Node::LetStmt(hir::LetStmt { ty: Some(ty), .. }) => {
                         err.span_suggestion_verbose(
@@ -2957,7 +3007,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             "&",
                             Applicability::MachineApplicable,
                         );
-                        err.note("all local variables must have a statically known size");
                     }
                     Node::LetStmt(hir::LetStmt {
                         init: Some(hir::Expr { kind: hir::ExprKind::Index(..), span, .. }),
@@ -2972,7 +3021,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             "&",
                             Applicability::MachineApplicable,
                         );
-                        err.note("all local variables must have a statically known size");
+                    }
+                    Node::LetStmt(hir::LetStmt { init: Some(expr), .. }) => {
+                        // When encountering an assignment of an unsized trait, like `let x = *"";`,
+                        // we check if the RHS is a deref operation, to suggest removing it.
+                        suggest_remove_deref(err, &expr);
                     }
                     Node::Param(param) => {
                         err.span_suggestion_verbose(
@@ -2982,10 +3035,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             "&",
                             Applicability::MachineApplicable,
                         );
+                        local = false;
                     }
-                    _ => {
-                        err.note("all local variables must have a statically known size");
-                    }
+                    _ => {}
+                }
+                if local {
+                    err.note("all local variables must have a statically known size");
                 }
                 if !tcx.features().unsized_locals {
                     err.help("unsized locals are gated as an unstable feature");
@@ -3017,11 +3072,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     match ty.kind {
                         hir::TyKind::TraitObject(traits, _, _) => {
                             let (span, kw) = match traits {
-                                [first, ..] if first.span.lo() == ty.span.lo() => {
+                                [(first, _), ..] if first.span.lo() == ty.span.lo() => {
                                     // Missing `dyn` in front of trait object.
                                     (ty.span.shrink_to_lo(), "dyn ")
                                 }
-                                [first, ..] => (ty.span.until(first.span), ""),
+                                [(first, _), ..] => (ty.span.until(first.span), ""),
                                 [] => span_bug!(ty.span, "trait object with no traits: {ty:?}"),
                             };
                             let needs_parens = traits.len() != 1;
@@ -3508,14 +3563,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 );
             }
             ObligationCauseCode::OpaqueReturnType(expr_info) => {
-                if let Some((expr_ty, expr_span)) = expr_info {
+                if let Some((expr_ty, hir_id)) = expr_info {
                     let expr_ty = self.tcx.short_ty_string(expr_ty, &mut long_ty_file);
+                    let expr = self.infcx.tcx.hir().expect_expr(hir_id);
                     err.span_label(
-                        expr_span,
+                        expr.span,
                         with_forced_trimmed_paths!(format!(
                             "return type was inferred to be `{expr_ty}` here",
                         )),
                     );
+                    suggest_remove_deref(err, &expr);
                 }
             }
         }
@@ -3532,7 +3589,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     #[instrument(
         level = "debug", skip(self, err), fields(trait_pred.self_ty = ?trait_pred.self_ty())
     )]
-    fn suggest_await_before_try(
+    pub(super) fn suggest_await_before_try(
         &self,
         err: &mut Diag<'_>,
         obligation: &PredicateObligation<'tcx>,
@@ -3589,7 +3646,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         }
     }
 
-    fn suggest_floating_point_literal(
+    pub(super) fn suggest_floating_point_literal(
         &self,
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diag<'_>,
@@ -3613,7 +3670,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         }
     }
 
-    fn suggest_derive(
+    pub fn suggest_derive(
         &self,
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diag<'_>,
@@ -3679,7 +3736,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         }
     }
 
-    fn suggest_dereferencing_index(
+    pub(super) fn suggest_dereferencing_index(
         &self,
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diag<'_>,
@@ -3810,6 +3867,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             {
                 if let Some(where_pred) = where_pred.as_trait_clause()
                     && let Some(failed_pred) = failed_pred.as_trait_clause()
+                    && where_pred.def_id() == failed_pred.def_id()
                 {
                     self.enter_forall(where_pred, |where_pred| {
                         let failed_pred = self.instantiate_binder_with_fresh_vars(
@@ -4300,7 +4358,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     /// If the type that failed selection is an array or a reference to an array,
     /// but the trait is implemented for slices, suggest that the user converts
     /// the array into a slice.
-    fn suggest_convert_to_slice(
+    pub(super) fn suggest_convert_to_slice(
         &self,
         err: &mut Diag<'_>,
         obligation: &PredicateObligation<'tcx>,
@@ -4372,7 +4430,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         }
     }
 
-    fn explain_hrtb_projection(
+    pub(super) fn explain_hrtb_projection(
         &self,
         diag: &mut Diag<'_>,
         pred: ty::PolyTraitPredicate<'tcx>,
@@ -4438,7 +4496,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         }
     }
 
-    fn suggest_desugaring_async_fn_in_trait(
+    pub(super) fn suggest_desugaring_async_fn_in_trait(
         &self,
         err: &mut Diag<'_>,
         trait_ref: ty::PolyTraitRef<'tcx>,
@@ -4522,7 +4580,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         );
     }
 
-    fn ty_kind_suggestion(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> Option<String> {
+    pub fn ty_kind_suggestion(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        ty: Ty<'tcx>,
+    ) -> Option<String> {
         let tcx = self.infcx.tcx;
         let implements_default = |ty| {
             let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else {
@@ -4584,7 +4646,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         })
     }
 
-    fn suggest_add_result_as_return_type(
+    // For E0277 when use `?` operator, suggest adding
+    // a suitable return type in `FnSig`, and a default
+    // return value at the end of the function's body.
+    pub(super) fn suggest_add_result_as_return_type(
         &self,
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diag<'_>,
@@ -4594,26 +4659,59 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             return;
         }
 
+        // Only suggest for local function and associated method,
+        // because this suggest adding both return type in
+        // the `FnSig` and a default return value in the body, so it
+        // is not suitable for foreign function without a local body,
+        // and neither for trait method which may be also implemented
+        // in other place, so shouldn't change it's FnSig.
+        fn choose_suggest_items<'tcx, 'hir>(
+            tcx: TyCtxt<'tcx>,
+            node: hir::Node<'hir>,
+        ) -> Option<(&'hir hir::FnDecl<'hir>, hir::BodyId)> {
+            match node {
+                hir::Node::Item(item) if let hir::ItemKind::Fn(sig, _, body_id) = item.kind => {
+                    Some((sig.decl, body_id))
+                }
+                hir::Node::ImplItem(item)
+                    if let hir::ImplItemKind::Fn(sig, body_id) = item.kind =>
+                {
+                    let parent = tcx.parent_hir_node(item.hir_id());
+                    if let hir::Node::Item(item) = parent
+                        && let hir::ItemKind::Impl(imp) = item.kind
+                        && imp.of_trait.is_none()
+                    {
+                        return Some((sig.decl, body_id));
+                    }
+                    None
+                }
+                _ => None,
+            }
+        }
+
         let node = self.tcx.hir_node_by_def_id(obligation.cause.body_id);
-        if let hir::Node::Item(item) = node
-            && let hir::ItemKind::Fn(sig, _, body_id) = item.kind
-            && let hir::FnRetTy::DefaultReturn(ret_span) = sig.decl.output
+        if let Some((fn_decl, body_id)) = choose_suggest_items(self.tcx, node)
+            && let hir::FnRetTy::DefaultReturn(ret_span) = fn_decl.output
             && self.tcx.is_diagnostic_item(sym::FromResidual, trait_pred.def_id())
             && trait_pred.skip_binder().trait_ref.args.type_at(0).is_unit()
             && let ty::Adt(def, _) = trait_pred.skip_binder().trait_ref.args.type_at(1).kind()
             && self.tcx.is_diagnostic_item(sym::Result, def.did())
         {
-            let body = self.tcx.hir().body(body_id);
             let mut sugg_spans =
                 vec![(ret_span, " -> Result<(), Box<dyn std::error::Error>>".to_string())];
-
+            let body = self.tcx.hir().body(body_id);
             if let hir::ExprKind::Block(b, _) = body.value.kind
                 && b.expr.is_none()
             {
+                // The span of '}' in the end of block.
+                let span = self.tcx.sess.source_map().end_point(b.span);
                 sugg_spans.push((
-                    // The span will point to the closing curly brace `}` of the block.
-                    b.span.shrink_to_hi().with_lo(b.span.hi() - BytePos(1)),
-                    "\n    Ok(())\n}".to_string(),
+                    span.shrink_to_lo(),
+                    format!(
+                        "{}{}",
+                        "    Ok(())\n",
+                        self.tcx.sess.source_map().indentation_before(span).unwrap_or_default(),
+                    ),
                 ));
             }
             err.multipart_suggestion_verbose(
@@ -4625,7 +4723,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     }
 
     #[instrument(level = "debug", skip_all)]
-    fn suggest_unsized_bound_if_applicable(
+    pub(super) fn suggest_unsized_bound_if_applicable(
         &self,
         err: &mut Diag<'_>,
         obligation: &PredicateObligation<'tcx>,
@@ -4766,13 +4864,13 @@ fn hint_missing_borrow<'tcx>(
     }
 
     let found_args = match found.kind() {
-        ty::FnPtr(f) => infcx.enter_forall(*f, |f| f.inputs().iter()),
+        ty::FnPtr(sig_tys, _) => infcx.enter_forall(*sig_tys, |sig_tys| sig_tys.inputs().iter()),
         kind => {
             span_bug!(span, "found was converted to a FnPtr above but is now {:?}", kind)
         }
     };
     let expected_args = match expected.kind() {
-        ty::FnPtr(f) => infcx.enter_forall(*f, |f| f.inputs().iter()),
+        ty::FnPtr(sig_tys, _) => infcx.enter_forall(*sig_tys, |sig_tys| sig_tys.inputs().iter()),
         kind => {
             span_bug!(span, "expected was converted to a FnPtr above but is now {:?}", kind)
         }
@@ -5284,7 +5382,8 @@ impl<'v> Visitor<'v> for FindTypeParam {
         match ty.kind {
             hir::TyKind::Ptr(_) | hir::TyKind::Ref(..) | hir::TyKind::TraitObject(..) => {}
             hir::TyKind::Path(hir::QPath::Resolved(None, path))
-                if path.segments.len() == 1 && path.segments[0].ident.name == self.param =>
+                if let [segment] = path.segments
+                    && segment.ident.name == self.param =>
             {
                 if !self.nested {
                     debug!(?ty, "FindTypeParam::visit_ty");
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
index a46cba35b2d..ebaec0b9059 100644
--- a/compiler/rustc_trait_selection/src/errors.rs
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -1,11 +1,27 @@
-use crate::fluent_generated as fluent;
+use std::path::PathBuf;
+
+use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::codes::*;
 use rustc_errors::{
-    codes::*, Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
-    SubdiagMessageOp, Subdiagnostic,
+    Applicability, Diag, DiagCtxtHandle, DiagMessage, DiagStyledString, Diagnostic,
+    EmissionGuarantee, IntoDiagArg, Level, MultiSpan, SubdiagMessageOp, Subdiagnostic,
 };
+use rustc_hir as hir;
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::intravisit::{walk_ty, Visitor};
+use rustc_hir::{FnRetTy, GenericParamKind};
 use rustc_macros::{Diagnostic, Subdiagnostic};
-use rustc_middle::ty::{self, print::PrintTraitRefExt as _, ClosureKind, PolyTraitRef, Ty};
-use rustc_span::{Span, Symbol};
+use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath};
+use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, PolyTraitRef, Region, Ty, TyCtxt};
+use rustc_span::symbol::{kw, Ident, Symbol};
+use rustc_span::{BytePos, Span};
+
+use crate::error_reporting::infer::need_type_info::UnderspecifiedArgKind;
+use crate::error_reporting::infer::nice_region_error::placeholder_error::Highlighted;
+use crate::error_reporting::infer::ObligationCauseAsDiagArg;
+use crate::fluent_generated as fluent;
+
+pub mod note_and_explain;
 
 #[derive(Diagnostic)]
 #[diag(trait_selection_dump_vtable_entries)]
@@ -170,3 +186,1612 @@ pub(crate) struct AsyncClosureNotFn {
     pub span: Span,
     pub kind: &'static str,
 }
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_type_annotations_needed, code = E0282)]
+pub struct AnnotationRequired<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub source_kind: &'static str,
+    pub source_name: &'a str,
+    #[label]
+    pub failure_span: Option<Span>,
+    #[subdiagnostic]
+    pub bad_label: Option<InferenceBadError<'a>>,
+    #[subdiagnostic]
+    pub infer_subdiags: Vec<SourceKindSubdiag<'a>>,
+    #[subdiagnostic]
+    pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>,
+    #[note(trait_selection_full_type_written)]
+    pub was_written: bool,
+    pub path: PathBuf,
+    #[note(trait_selection_type_annotations_needed_error_time)]
+    pub time_version: bool,
+}
+
+// Copy of `AnnotationRequired` for E0283
+#[derive(Diagnostic)]
+#[diag(trait_selection_type_annotations_needed, code = E0283)]
+pub struct AmbiguousImpl<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub source_kind: &'static str,
+    pub source_name: &'a str,
+    #[label]
+    pub failure_span: Option<Span>,
+    #[subdiagnostic]
+    pub bad_label: Option<InferenceBadError<'a>>,
+    #[subdiagnostic]
+    pub infer_subdiags: Vec<SourceKindSubdiag<'a>>,
+    #[subdiagnostic]
+    pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>,
+    #[note(trait_selection_full_type_written)]
+    pub was_written: bool,
+    pub path: PathBuf,
+}
+
+// Copy of `AnnotationRequired` for E0284
+#[derive(Diagnostic)]
+#[diag(trait_selection_type_annotations_needed, code = E0284)]
+pub struct AmbiguousReturn<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub source_kind: &'static str,
+    pub source_name: &'a str,
+    #[label]
+    pub failure_span: Option<Span>,
+    #[subdiagnostic]
+    pub bad_label: Option<InferenceBadError<'a>>,
+    #[subdiagnostic]
+    pub infer_subdiags: Vec<SourceKindSubdiag<'a>>,
+    #[subdiagnostic]
+    pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>,
+    #[note(trait_selection_full_type_written)]
+    pub was_written: bool,
+    pub path: PathBuf,
+}
+
+// Used when a better one isn't available
+#[derive(Subdiagnostic)]
+#[label(trait_selection_label_bad)]
+pub struct InferenceBadError<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub bad_kind: &'static str,
+    pub prefix_kind: UnderspecifiedArgKind,
+    pub has_parent: bool,
+    pub prefix: &'a str,
+    pub parent_prefix: &'a str,
+    pub parent_name: String,
+    pub name: String,
+}
+
+#[derive(Subdiagnostic)]
+pub enum SourceKindSubdiag<'a> {
+    #[suggestion(
+        trait_selection_source_kind_subdiag_let,
+        style = "verbose",
+        code = ": {type_name}",
+        applicability = "has-placeholders"
+    )]
+    LetLike {
+        #[primary_span]
+        span: Span,
+        name: String,
+        type_name: String,
+        kind: &'static str,
+        x_kind: &'static str,
+        prefix_kind: UnderspecifiedArgKind,
+        prefix: &'a str,
+        arg_name: String,
+    },
+    #[label(trait_selection_source_kind_subdiag_generic_label)]
+    GenericLabel {
+        #[primary_span]
+        span: Span,
+        is_type: bool,
+        param_name: String,
+        parent_exists: bool,
+        parent_prefix: String,
+        parent_name: String,
+    },
+    #[suggestion(
+        trait_selection_source_kind_subdiag_generic_suggestion,
+        style = "verbose",
+        code = "::<{args}>",
+        applicability = "has-placeholders"
+    )]
+    GenericSuggestion {
+        #[primary_span]
+        span: Span,
+        arg_count: usize,
+        args: String,
+    },
+}
+
+#[derive(Subdiagnostic)]
+pub enum SourceKindMultiSuggestion<'a> {
+    #[multipart_suggestion(
+        trait_selection_source_kind_fully_qualified,
+        style = "verbose",
+        applicability = "has-placeholders"
+    )]
+    FullyQualified {
+        #[suggestion_part(code = "{def_path}({adjustment}")]
+        span_lo: Span,
+        #[suggestion_part(code = "{successor_pos}")]
+        span_hi: Span,
+        def_path: String,
+        adjustment: &'a str,
+        successor_pos: &'a str,
+    },
+    #[multipart_suggestion(
+        trait_selection_source_kind_closure_return,
+        style = "verbose",
+        applicability = "has-placeholders"
+    )]
+    ClosureReturn {
+        #[suggestion_part(code = "{start_span_code}")]
+        start_span: Span,
+        start_span_code: String,
+        #[suggestion_part(code = " }}")]
+        end_span: Option<Span>,
+    },
+}
+
+impl<'a> SourceKindMultiSuggestion<'a> {
+    pub fn new_fully_qualified(
+        span: Span,
+        def_path: String,
+        adjustment: &'a str,
+        successor: (&'a str, BytePos),
+    ) -> Self {
+        Self::FullyQualified {
+            span_lo: span.shrink_to_lo(),
+            span_hi: span.shrink_to_hi().with_hi(successor.1),
+            def_path,
+            adjustment,
+            successor_pos: successor.0,
+        }
+    }
+
+    pub fn new_closure_return(
+        ty_info: String,
+        data: &'a FnRetTy<'a>,
+        should_wrap_expr: Option<Span>,
+    ) -> Self {
+        let arrow = match data {
+            FnRetTy::DefaultReturn(_) => " -> ",
+            _ => "",
+        };
+        let (start_span, start_span_code, end_span) = match should_wrap_expr {
+            Some(end_span) => (data.span(), format!("{arrow}{ty_info} {{"), Some(end_span)),
+            None => (data.span(), format!("{arrow}{ty_info}"), None),
+        };
+        Self::ClosureReturn { start_span, start_span_code, end_span }
+    }
+}
+
+pub enum RegionOriginNote<'a> {
+    Plain {
+        span: Span,
+        msg: DiagMessage,
+    },
+    WithName {
+        span: Span,
+        msg: DiagMessage,
+        name: &'a str,
+        continues: bool,
+    },
+    WithRequirement {
+        span: Span,
+        requirement: ObligationCauseAsDiagArg<'a>,
+        expected_found: Option<(DiagStyledString, DiagStyledString)>,
+    },
+}
+
+impl Subdiagnostic for RegionOriginNote<'_> {
+    fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
+        self,
+        diag: &mut Diag<'_, G>,
+        _f: &F,
+    ) {
+        let mut label_or_note = |span, msg: DiagMessage| {
+            let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count();
+            let expanded_sub_count = diag.children.iter().filter(|d| !d.span.is_dummy()).count();
+            let span_is_primary = diag.span.primary_spans().iter().all(|&sp| sp == span);
+            if span_is_primary && sub_count == 0 && expanded_sub_count == 0 {
+                diag.span_label(span, msg);
+            } else if span_is_primary && expanded_sub_count == 0 {
+                diag.note(msg);
+            } else {
+                diag.span_note(span, msg);
+            }
+        };
+        match self {
+            RegionOriginNote::Plain { span, msg } => {
+                label_or_note(span, msg);
+            }
+            RegionOriginNote::WithName { span, msg, name, continues } => {
+                label_or_note(span, msg);
+                diag.arg("name", name);
+                diag.arg("continues", continues);
+            }
+            RegionOriginNote::WithRequirement {
+                span,
+                requirement,
+                expected_found: Some((expected, found)),
+            } => {
+                label_or_note(span, fluent::trait_selection_subtype);
+                diag.arg("requirement", requirement);
+
+                diag.note_expected_found(&"", expected, &"", found);
+            }
+            RegionOriginNote::WithRequirement { span, requirement, expected_found: None } => {
+                // FIXME: this really should be handled at some earlier stage. Our
+                // handling of region checking when type errors are present is
+                // *terrible*.
+                label_or_note(span, fluent::trait_selection_subtype_2);
+                diag.arg("requirement", requirement);
+            }
+        };
+    }
+}
+
+pub enum LifetimeMismatchLabels {
+    InRet {
+        param_span: Span,
+        ret_span: Span,
+        span: Span,
+        label_var1: Option<Ident>,
+    },
+    Normal {
+        hir_equal: bool,
+        ty_sup: Span,
+        ty_sub: Span,
+        span: Span,
+        sup: Option<Ident>,
+        sub: Option<Ident>,
+    },
+}
+
+impl Subdiagnostic for LifetimeMismatchLabels {
+    fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
+        self,
+        diag: &mut Diag<'_, G>,
+        _f: &F,
+    ) {
+        match self {
+            LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => {
+                diag.span_label(param_span, fluent::trait_selection_declared_different);
+                diag.span_label(ret_span, fluent::trait_selection_nothing);
+                diag.span_label(span, fluent::trait_selection_data_returned);
+                diag.arg("label_var1_exists", label_var1.is_some());
+                diag.arg("label_var1", label_var1.map(|x| x.to_string()).unwrap_or_default());
+            }
+            LifetimeMismatchLabels::Normal {
+                hir_equal,
+                ty_sup,
+                ty_sub,
+                span,
+                sup: label_var1,
+                sub: label_var2,
+            } => {
+                if hir_equal {
+                    diag.span_label(ty_sup, fluent::trait_selection_declared_multiple);
+                    diag.span_label(ty_sub, fluent::trait_selection_nothing);
+                    diag.span_label(span, fluent::trait_selection_data_lifetime_flow);
+                } else {
+                    diag.span_label(ty_sup, fluent::trait_selection_types_declared_different);
+                    diag.span_label(ty_sub, fluent::trait_selection_nothing);
+                    diag.span_label(span, fluent::trait_selection_data_flows);
+                    diag.arg("label_var1_exists", label_var1.is_some());
+                    diag.arg("label_var1", label_var1.map(|x| x.to_string()).unwrap_or_default());
+                    diag.arg("label_var2_exists", label_var2.is_some());
+                    diag.arg("label_var2", label_var2.map(|x| x.to_string()).unwrap_or_default());
+                }
+            }
+        }
+    }
+}
+
+pub struct AddLifetimeParamsSuggestion<'a> {
+    pub tcx: TyCtxt<'a>,
+    pub generic_param_scope: LocalDefId,
+    pub sub: Region<'a>,
+    pub ty_sup: &'a hir::Ty<'a>,
+    pub ty_sub: &'a hir::Ty<'a>,
+    pub add_note: bool,
+}
+
+impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
+    fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
+        self,
+        diag: &mut Diag<'_, G>,
+        _f: &F,
+    ) {
+        let mut mk_suggestion = || {
+            let Some(anon_reg) = self.tcx.is_suitable_region(self.generic_param_scope, self.sub)
+            else {
+                return false;
+            };
+
+            let node = self.tcx.hir_node_by_def_id(anon_reg.def_id);
+            let is_impl = matches!(&node, hir::Node::ImplItem(_));
+            let (generics, parent_generics) = match node {
+                hir::Node::Item(&hir::Item {
+                    kind: hir::ItemKind::Fn(_, ref generics, ..),
+                    ..
+                })
+                | hir::Node::TraitItem(&hir::TraitItem { ref generics, .. })
+                | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => (
+                    generics,
+                    match self.tcx.parent_hir_node(self.tcx.local_def_id_to_hir_id(anon_reg.def_id))
+                    {
+                        hir::Node::Item(hir::Item {
+                            kind: hir::ItemKind::Trait(_, _, ref generics, ..),
+                            ..
+                        })
+                        | hir::Node::Item(hir::Item {
+                            kind: hir::ItemKind::Impl(hir::Impl { ref generics, .. }),
+                            ..
+                        }) => Some(generics),
+                        _ => None,
+                    },
+                ),
+                _ => return false,
+            };
+
+            let suggestion_param_name = generics
+                .params
+                .iter()
+                .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }))
+                .map(|p| p.name.ident().name)
+                .find(|i| *i != kw::UnderscoreLifetime);
+            let introduce_new = suggestion_param_name.is_none();
+
+            let mut default = "'a".to_string();
+            if let Some(parent_generics) = parent_generics {
+                let used: FxHashSet<_> = parent_generics
+                    .params
+                    .iter()
+                    .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }))
+                    .map(|p| p.name.ident().name)
+                    .filter(|i| *i != kw::UnderscoreLifetime)
+                    .map(|l| l.to_string())
+                    .collect();
+                if let Some(lt) =
+                    ('a'..='z').map(|it| format!("'{it}")).find(|it| !used.contains(it))
+                {
+                    // We want a lifetime that *isn't* present in the `trait` or `impl` that assoc
+                    // `fn` belongs to. We could suggest reusing one of their lifetimes, but it is
+                    // likely to be an over-constraining lifetime requirement, so we always add a
+                    // lifetime to the `fn`.
+                    default = lt;
+                }
+            }
+            let suggestion_param_name =
+                suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| default);
+
+            struct ImplicitLifetimeFinder {
+                suggestions: Vec<(Span, String)>,
+                suggestion_param_name: String,
+            }
+
+            impl<'v> Visitor<'v> for ImplicitLifetimeFinder {
+                fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
+                    let make_suggestion = |ident: Ident| {
+                        if ident.name == kw::Empty && ident.span.is_empty() {
+                            format!("{}, ", self.suggestion_param_name)
+                        } else if ident.name == kw::UnderscoreLifetime && ident.span.is_empty() {
+                            format!("{} ", self.suggestion_param_name)
+                        } else {
+                            self.suggestion_param_name.clone()
+                        }
+                    };
+                    match ty.kind {
+                        hir::TyKind::Path(hir::QPath::Resolved(_, path)) => {
+                            for segment in path.segments {
+                                if let Some(args) = segment.args {
+                                    if args.args.iter().all(|arg| {
+                                        matches!(
+                                            arg,
+                                            hir::GenericArg::Lifetime(lifetime)
+                                            if lifetime.ident.name == kw::Empty
+                                        )
+                                    }) {
+                                        self.suggestions.push((
+                                            segment.ident.span.shrink_to_hi(),
+                                            format!(
+                                                "<{}>",
+                                                args.args
+                                                    .iter()
+                                                    .map(|_| self.suggestion_param_name.clone())
+                                                    .collect::<Vec<_>>()
+                                                    .join(", ")
+                                            ),
+                                        ));
+                                    } else {
+                                        for arg in args.args {
+                                            if let hir::GenericArg::Lifetime(lifetime) = arg
+                                                && lifetime.is_anonymous()
+                                            {
+                                                self.suggestions.push((
+                                                    lifetime.ident.span,
+                                                    make_suggestion(lifetime.ident),
+                                                ));
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                        hir::TyKind::Ref(lifetime, ..) if lifetime.is_anonymous() => {
+                            self.suggestions
+                                .push((lifetime.ident.span, make_suggestion(lifetime.ident)));
+                        }
+                        _ => {}
+                    }
+                    walk_ty(self, ty);
+                }
+            }
+            let mut visitor = ImplicitLifetimeFinder {
+                suggestions: vec![],
+                suggestion_param_name: suggestion_param_name.clone(),
+            };
+            if let Some(fn_decl) = node.fn_decl()
+                && let hir::FnRetTy::Return(ty) = fn_decl.output
+            {
+                visitor.visit_ty(ty);
+            }
+            if visitor.suggestions.is_empty() {
+                // Do not suggest constraining the `&self` param, but rather the return type.
+                // If that is wrong (because it is not sufficient), a follow up error will tell the
+                // user to fix it. This way we lower the chances of *over* constraining, but still
+                // get the cake of "correctly" contrained in two steps.
+                visitor.visit_ty(self.ty_sup);
+            }
+            visitor.visit_ty(self.ty_sub);
+            if visitor.suggestions.is_empty() {
+                return false;
+            }
+            if introduce_new {
+                let new_param_suggestion = if let Some(first) =
+                    generics.params.iter().find(|p| !p.name.ident().span.is_empty())
+                {
+                    (first.span.shrink_to_lo(), format!("{suggestion_param_name}, "))
+                } else {
+                    (generics.span, format!("<{suggestion_param_name}>"))
+                };
+
+                visitor.suggestions.push(new_param_suggestion);
+            }
+            diag.multipart_suggestion_verbose(
+                fluent::trait_selection_lifetime_param_suggestion,
+                visitor.suggestions,
+                Applicability::MaybeIncorrect,
+            );
+            diag.arg("is_impl", is_impl);
+            diag.arg("is_reuse", !introduce_new);
+
+            true
+        };
+        if mk_suggestion() && self.add_note {
+            diag.note(fluent::trait_selection_lifetime_param_suggestion_elided);
+        }
+    }
+}
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_lifetime_mismatch, code = E0623)]
+pub struct LifetimeMismatch<'a> {
+    #[primary_span]
+    pub span: Span,
+    #[subdiagnostic]
+    pub labels: LifetimeMismatchLabels,
+    #[subdiagnostic]
+    pub suggestion: AddLifetimeParamsSuggestion<'a>,
+}
+
+pub struct IntroducesStaticBecauseUnmetLifetimeReq {
+    pub unmet_requirements: MultiSpan,
+    pub binding_span: Span,
+}
+
+impl Subdiagnostic for IntroducesStaticBecauseUnmetLifetimeReq {
+    fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
+        mut self,
+        diag: &mut Diag<'_, G>,
+        _f: &F,
+    ) {
+        self.unmet_requirements
+            .push_span_label(self.binding_span, fluent::trait_selection_msl_introduces_static);
+        diag.span_note(self.unmet_requirements, fluent::trait_selection_msl_unmet_req);
+    }
+}
+
+// FIXME(#100717): replace with a `Option<Span>` when subdiagnostic supports that
+#[derive(Subdiagnostic)]
+pub enum DoesNotOutliveStaticFromImpl {
+    #[note(trait_selection_does_not_outlive_static_from_impl)]
+    Spanned {
+        #[primary_span]
+        span: Span,
+    },
+    #[note(trait_selection_does_not_outlive_static_from_impl)]
+    Unspanned,
+}
+
+#[derive(Subdiagnostic)]
+pub enum ImplicitStaticLifetimeSubdiag {
+    #[note(trait_selection_implicit_static_lifetime_note)]
+    Note {
+        #[primary_span]
+        span: Span,
+    },
+    #[suggestion(
+        trait_selection_implicit_static_lifetime_suggestion,
+        style = "verbose",
+        code = " + '_",
+        applicability = "maybe-incorrect"
+    )]
+    Sugg {
+        #[primary_span]
+        span: Span,
+    },
+}
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_mismatched_static_lifetime)]
+pub struct MismatchedStaticLifetime<'a> {
+    #[primary_span]
+    pub cause_span: Span,
+    #[subdiagnostic]
+    pub unmet_lifetime_reqs: IntroducesStaticBecauseUnmetLifetimeReq,
+    #[subdiagnostic]
+    pub expl: Option<note_and_explain::RegionExplanation<'a>>,
+    #[subdiagnostic]
+    pub does_not_outlive_static_from_impl: DoesNotOutliveStaticFromImpl,
+    #[subdiagnostic]
+    pub implicit_static_lifetimes: Vec<ImplicitStaticLifetimeSubdiag>,
+}
+
+#[derive(Diagnostic)]
+pub enum ExplicitLifetimeRequired<'a> {
+    #[diag(trait_selection_explicit_lifetime_required_with_ident, code = E0621)]
+    WithIdent {
+        #[primary_span]
+        #[label]
+        span: Span,
+        simple_ident: Ident,
+        named: String,
+        #[suggestion(
+            trait_selection_explicit_lifetime_required_sugg_with_ident,
+            code = "{new_ty}",
+            applicability = "unspecified"
+        )]
+        new_ty_span: Span,
+        #[skip_arg]
+        new_ty: Ty<'a>,
+    },
+    #[diag(trait_selection_explicit_lifetime_required_with_param_type, code = E0621)]
+    WithParamType {
+        #[primary_span]
+        #[label]
+        span: Span,
+        named: String,
+        #[suggestion(
+            trait_selection_explicit_lifetime_required_sugg_with_param_type,
+            code = "{new_ty}",
+            applicability = "unspecified"
+        )]
+        new_ty_span: Span,
+        #[skip_arg]
+        new_ty: Ty<'a>,
+    },
+}
+
+pub enum TyOrSig<'tcx> {
+    Ty(Highlighted<'tcx, Ty<'tcx>>),
+    ClosureSig(Highlighted<'tcx, Binder<'tcx, FnSig<'tcx>>>),
+}
+
+impl IntoDiagArg for TyOrSig<'_> {
+    fn into_diag_arg(self) -> rustc_errors::DiagArgValue {
+        match self {
+            TyOrSig::Ty(ty) => ty.into_diag_arg(),
+            TyOrSig::ClosureSig(sig) => sig.into_diag_arg(),
+        }
+    }
+}
+
+#[derive(Subdiagnostic)]
+pub enum ActualImplExplNotes<'tcx> {
+    #[note(trait_selection_actual_impl_expl_expected_signature_two)]
+    ExpectedSignatureTwo {
+        leading_ellipsis: bool,
+        ty_or_sig: TyOrSig<'tcx>,
+        trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
+        lifetime_1: usize,
+        lifetime_2: usize,
+    },
+    #[note(trait_selection_actual_impl_expl_expected_signature_any)]
+    ExpectedSignatureAny {
+        leading_ellipsis: bool,
+        ty_or_sig: TyOrSig<'tcx>,
+        trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
+        lifetime_1: usize,
+    },
+    #[note(trait_selection_actual_impl_expl_expected_signature_some)]
+    ExpectedSignatureSome {
+        leading_ellipsis: bool,
+        ty_or_sig: TyOrSig<'tcx>,
+        trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
+        lifetime_1: usize,
+    },
+    #[note(trait_selection_actual_impl_expl_expected_signature_nothing)]
+    ExpectedSignatureNothing {
+        leading_ellipsis: bool,
+        ty_or_sig: TyOrSig<'tcx>,
+        trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
+    },
+    #[note(trait_selection_actual_impl_expl_expected_passive_two)]
+    ExpectedPassiveTwo {
+        leading_ellipsis: bool,
+        ty_or_sig: TyOrSig<'tcx>,
+        trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
+        lifetime_1: usize,
+        lifetime_2: usize,
+    },
+    #[note(trait_selection_actual_impl_expl_expected_passive_any)]
+    ExpectedPassiveAny {
+        leading_ellipsis: bool,
+        ty_or_sig: TyOrSig<'tcx>,
+        trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
+        lifetime_1: usize,
+    },
+    #[note(trait_selection_actual_impl_expl_expected_passive_some)]
+    ExpectedPassiveSome {
+        leading_ellipsis: bool,
+        ty_or_sig: TyOrSig<'tcx>,
+        trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
+        lifetime_1: usize,
+    },
+    #[note(trait_selection_actual_impl_expl_expected_passive_nothing)]
+    ExpectedPassiveNothing {
+        leading_ellipsis: bool,
+        ty_or_sig: TyOrSig<'tcx>,
+        trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
+    },
+    #[note(trait_selection_actual_impl_expl_expected_other_two)]
+    ExpectedOtherTwo {
+        leading_ellipsis: bool,
+        ty_or_sig: TyOrSig<'tcx>,
+        trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
+        lifetime_1: usize,
+        lifetime_2: usize,
+    },
+    #[note(trait_selection_actual_impl_expl_expected_other_any)]
+    ExpectedOtherAny {
+        leading_ellipsis: bool,
+        ty_or_sig: TyOrSig<'tcx>,
+        trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
+        lifetime_1: usize,
+    },
+    #[note(trait_selection_actual_impl_expl_expected_other_some)]
+    ExpectedOtherSome {
+        leading_ellipsis: bool,
+        ty_or_sig: TyOrSig<'tcx>,
+        trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
+        lifetime_1: usize,
+    },
+    #[note(trait_selection_actual_impl_expl_expected_other_nothing)]
+    ExpectedOtherNothing {
+        leading_ellipsis: bool,
+        ty_or_sig: TyOrSig<'tcx>,
+        trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
+    },
+    #[note(trait_selection_actual_impl_expl_but_actually_implements_trait)]
+    ButActuallyImplementsTrait {
+        trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
+        has_lifetime: bool,
+        lifetime: usize,
+    },
+    #[note(trait_selection_actual_impl_expl_but_actually_implemented_for_ty)]
+    ButActuallyImplementedForTy {
+        trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
+        has_lifetime: bool,
+        lifetime: usize,
+        ty: String,
+    },
+    #[note(trait_selection_actual_impl_expl_but_actually_ty_implements)]
+    ButActuallyTyImplements {
+        trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
+        has_lifetime: bool,
+        lifetime: usize,
+        ty: String,
+    },
+}
+
+pub enum ActualImplExpectedKind {
+    Signature,
+    Passive,
+    Other,
+}
+
+pub enum ActualImplExpectedLifetimeKind {
+    Two,
+    Any,
+    Some,
+    Nothing,
+}
+
+impl<'tcx> ActualImplExplNotes<'tcx> {
+    pub fn new_expected(
+        kind: ActualImplExpectedKind,
+        lt_kind: ActualImplExpectedLifetimeKind,
+        leading_ellipsis: bool,
+        ty_or_sig: TyOrSig<'tcx>,
+        trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
+        lifetime_1: usize,
+        lifetime_2: usize,
+    ) -> Self {
+        match (kind, lt_kind) {
+            (ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Two) => {
+                Self::ExpectedSignatureTwo {
+                    leading_ellipsis,
+                    ty_or_sig,
+                    trait_path,
+                    lifetime_1,
+                    lifetime_2,
+                }
+            }
+            (ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Any) => {
+                Self::ExpectedSignatureAny { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 }
+            }
+            (ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Some) => {
+                Self::ExpectedSignatureSome { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 }
+            }
+            (ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Nothing) => {
+                Self::ExpectedSignatureNothing { leading_ellipsis, ty_or_sig, trait_path }
+            }
+            (ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Two) => {
+                Self::ExpectedPassiveTwo {
+                    leading_ellipsis,
+                    ty_or_sig,
+                    trait_path,
+                    lifetime_1,
+                    lifetime_2,
+                }
+            }
+            (ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Any) => {
+                Self::ExpectedPassiveAny { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 }
+            }
+            (ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Some) => {
+                Self::ExpectedPassiveSome { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 }
+            }
+            (ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Nothing) => {
+                Self::ExpectedPassiveNothing { leading_ellipsis, ty_or_sig, trait_path }
+            }
+            (ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Two) => {
+                Self::ExpectedOtherTwo {
+                    leading_ellipsis,
+                    ty_or_sig,
+                    trait_path,
+                    lifetime_1,
+                    lifetime_2,
+                }
+            }
+            (ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Any) => {
+                Self::ExpectedOtherAny { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 }
+            }
+            (ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Some) => {
+                Self::ExpectedOtherSome { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 }
+            }
+            (ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Nothing) => {
+                Self::ExpectedOtherNothing { leading_ellipsis, ty_or_sig, trait_path }
+            }
+        }
+    }
+}
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_trait_placeholder_mismatch)]
+pub struct TraitPlaceholderMismatch<'tcx> {
+    #[primary_span]
+    pub span: Span,
+    #[label(trait_selection_label_satisfy)]
+    pub satisfy_span: Option<Span>,
+    #[label(trait_selection_label_where)]
+    pub where_span: Option<Span>,
+    #[label(trait_selection_label_dup)]
+    pub dup_span: Option<Span>,
+    pub def_id: String,
+    pub trait_def_id: String,
+
+    #[subdiagnostic]
+    pub actual_impl_expl_notes: Vec<ActualImplExplNotes<'tcx>>,
+}
+
+pub struct ConsiderBorrowingParamHelp {
+    pub spans: Vec<Span>,
+}
+
+impl Subdiagnostic for ConsiderBorrowingParamHelp {
+    fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
+        self,
+        diag: &mut Diag<'_, G>,
+        f: &F,
+    ) {
+        let mut type_param_span: MultiSpan = self.spans.clone().into();
+        for &span in &self.spans {
+            // Seems like we can't call f() here as Into<DiagMessage> is required
+            type_param_span.push_span_label(span, fluent::trait_selection_tid_consider_borrowing);
+        }
+        let msg = f(diag, fluent::trait_selection_tid_param_help.into());
+        diag.span_help(type_param_span, msg);
+    }
+}
+
+#[derive(Subdiagnostic)]
+#[help(trait_selection_tid_rel_help)]
+pub struct RelationshipHelp;
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_trait_impl_diff)]
+pub struct TraitImplDiff {
+    #[primary_span]
+    #[label(trait_selection_found)]
+    pub sp: Span,
+    #[label(trait_selection_expected)]
+    pub trait_sp: Span,
+    #[note(trait_selection_expected_found)]
+    pub note: (),
+    #[subdiagnostic]
+    pub param_help: ConsiderBorrowingParamHelp,
+    #[subdiagnostic]
+    // Seems like subdiagnostics are always pushed to the end, so this one
+    // also has to be a subdiagnostic to maintain order.
+    pub rel_help: Option<RelationshipHelp>,
+    pub expected: String,
+    pub found: String,
+}
+
+pub struct DynTraitConstraintSuggestion {
+    pub span: Span,
+    pub ident: Ident,
+}
+
+impl Subdiagnostic for DynTraitConstraintSuggestion {
+    fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
+        self,
+        diag: &mut Diag<'_, G>,
+        f: &F,
+    ) {
+        let mut multi_span: MultiSpan = vec![self.span].into();
+        multi_span.push_span_label(self.span, fluent::trait_selection_dtcs_has_lifetime_req_label);
+        multi_span
+            .push_span_label(self.ident.span, fluent::trait_selection_dtcs_introduces_requirement);
+        let msg = f(diag, fluent::trait_selection_dtcs_has_req_note.into());
+        diag.span_note(multi_span, msg);
+        let msg = f(diag, fluent::trait_selection_dtcs_suggestion.into());
+        diag.span_suggestion_verbose(
+            self.span.shrink_to_hi(),
+            msg,
+            " + '_",
+            Applicability::MaybeIncorrect,
+        );
+    }
+}
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_but_calling_introduces, code = E0772)]
+pub struct ButCallingIntroduces {
+    #[label(trait_selection_label1)]
+    pub param_ty_span: Span,
+    #[primary_span]
+    #[label(trait_selection_label2)]
+    pub cause_span: Span,
+
+    pub has_param_name: bool,
+    pub param_name: String,
+    pub has_lifetime: bool,
+    pub lifetime: String,
+    pub assoc_item: Symbol,
+    pub has_impl_path: bool,
+    pub impl_path: String,
+}
+
+pub struct ReqIntroducedLocations {
+    pub span: MultiSpan,
+    pub spans: Vec<Span>,
+    pub fn_decl_span: Span,
+    pub cause_span: Span,
+    pub add_label: bool,
+}
+
+impl Subdiagnostic for ReqIntroducedLocations {
+    fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
+        mut self,
+        diag: &mut Diag<'_, G>,
+        f: &F,
+    ) {
+        for sp in self.spans {
+            self.span.push_span_label(sp, fluent::trait_selection_ril_introduced_here);
+        }
+
+        if self.add_label {
+            self.span.push_span_label(self.fn_decl_span, fluent::trait_selection_ril_introduced_by);
+        }
+        self.span.push_span_label(self.cause_span, fluent::trait_selection_ril_because_of);
+        let msg = f(diag, fluent::trait_selection_ril_static_introduced_by.into());
+        diag.span_note(self.span, msg);
+    }
+}
+
+pub struct MoreTargeted {
+    pub ident: Symbol,
+}
+
+impl Subdiagnostic for MoreTargeted {
+    fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
+        self,
+        diag: &mut Diag<'_, G>,
+        _f: &F,
+    ) {
+        diag.code(E0772);
+        diag.primary_message(fluent::trait_selection_more_targeted);
+        diag.arg("ident", self.ident);
+    }
+}
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_but_needs_to_satisfy, code = E0759)]
+pub struct ButNeedsToSatisfy {
+    #[primary_span]
+    pub sp: Span,
+    #[label(trait_selection_influencer)]
+    pub influencer_point: Span,
+    #[label(trait_selection_used_here)]
+    pub spans: Vec<Span>,
+    #[label(trait_selection_require)]
+    pub require_span_as_label: Option<Span>,
+    #[note(trait_selection_require)]
+    pub require_span_as_note: Option<Span>,
+    #[note(trait_selection_introduced_by_bound)]
+    pub bound: Option<Span>,
+
+    #[subdiagnostic]
+    pub req_introduces_loc: Option<ReqIntroducedLocations>,
+
+    pub has_param_name: bool,
+    pub param_name: String,
+    pub spans_empty: bool,
+    pub has_lifetime: bool,
+    pub lifetime: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_outlives_content, code = E0312)]
+pub struct OutlivesContent<'a> {
+    #[primary_span]
+    pub span: Span,
+    #[subdiagnostic]
+    pub notes: Vec<note_and_explain::RegionExplanation<'a>>,
+}
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_outlives_bound, code = E0476)]
+pub struct OutlivesBound<'a> {
+    #[primary_span]
+    pub span: Span,
+    #[subdiagnostic]
+    pub notes: Vec<note_and_explain::RegionExplanation<'a>>,
+}
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_fulfill_req_lifetime, code = E0477)]
+pub struct FulfillReqLifetime<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub ty: Ty<'a>,
+    #[subdiagnostic]
+    pub note: Option<note_and_explain::RegionExplanation<'a>>,
+}
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_lf_bound_not_satisfied, code = E0478)]
+pub struct LfBoundNotSatisfied<'a> {
+    #[primary_span]
+    pub span: Span,
+    #[subdiagnostic]
+    pub notes: Vec<note_and_explain::RegionExplanation<'a>>,
+}
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_ref_longer_than_data, code = E0491)]
+pub struct RefLongerThanData<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub ty: Ty<'a>,
+    #[subdiagnostic]
+    pub notes: Vec<note_and_explain::RegionExplanation<'a>>,
+}
+
+#[derive(Subdiagnostic)]
+pub enum WhereClauseSuggestions {
+    #[suggestion(
+        trait_selection_where_remove,
+        code = "",
+        applicability = "machine-applicable",
+        style = "verbose"
+    )]
+    Remove {
+        #[primary_span]
+        span: Span,
+    },
+    #[suggestion(
+        trait_selection_where_copy_predicates,
+        code = "{space}where {trait_predicates}",
+        applicability = "machine-applicable",
+        style = "verbose"
+    )]
+    CopyPredicates {
+        #[primary_span]
+        span: Span,
+        space: &'static str,
+        trait_predicates: String,
+    },
+}
+
+#[derive(Subdiagnostic)]
+pub enum SuggestRemoveSemiOrReturnBinding {
+    #[multipart_suggestion(
+        trait_selection_srs_remove_and_box,
+        applicability = "machine-applicable"
+    )]
+    RemoveAndBox {
+        #[suggestion_part(code = "Box::new(")]
+        first_lo: Span,
+        #[suggestion_part(code = ")")]
+        first_hi: Span,
+        #[suggestion_part(code = "Box::new(")]
+        second_lo: Span,
+        #[suggestion_part(code = ")")]
+        second_hi: Span,
+        #[suggestion_part(code = "")]
+        sp: Span,
+    },
+    #[suggestion(
+        trait_selection_srs_remove,
+        style = "short",
+        code = "",
+        applicability = "machine-applicable"
+    )]
+    Remove {
+        #[primary_span]
+        sp: Span,
+    },
+    #[suggestion(
+        trait_selection_srs_add,
+        style = "verbose",
+        code = "{code}",
+        applicability = "maybe-incorrect"
+    )]
+    Add {
+        #[primary_span]
+        sp: Span,
+        code: String,
+        ident: Ident,
+    },
+    #[note(trait_selection_srs_add_one)]
+    AddOne {
+        #[primary_span]
+        spans: MultiSpan,
+    },
+}
+
+#[derive(Subdiagnostic)]
+pub enum ConsiderAddingAwait {
+    #[help(trait_selection_await_both_futures)]
+    BothFuturesHelp,
+    #[multipart_suggestion(trait_selection_await_both_futures, applicability = "maybe-incorrect")]
+    BothFuturesSugg {
+        #[suggestion_part(code = ".await")]
+        first: Span,
+        #[suggestion_part(code = ".await")]
+        second: Span,
+    },
+    #[suggestion(
+        trait_selection_await_future,
+        code = ".await",
+        style = "verbose",
+        applicability = "maybe-incorrect"
+    )]
+    FutureSugg {
+        #[primary_span]
+        span: Span,
+    },
+    #[note(trait_selection_await_note)]
+    FutureSuggNote {
+        #[primary_span]
+        span: Span,
+    },
+    #[multipart_suggestion(
+        trait_selection_await_future,
+        style = "verbose",
+        applicability = "maybe-incorrect"
+    )]
+    FutureSuggMultiple {
+        #[suggestion_part(code = ".await")]
+        spans: Vec<Span>,
+    },
+}
+
+#[derive(Diagnostic)]
+pub enum PlaceholderRelationLfNotSatisfied {
+    #[diag(trait_selection_lf_bound_not_satisfied)]
+    HasBoth {
+        #[primary_span]
+        span: Span,
+        #[note(trait_selection_prlf_defined_with_sub)]
+        sub_span: Span,
+        #[note(trait_selection_prlf_must_outlive_with_sup)]
+        sup_span: Span,
+        sub_symbol: Symbol,
+        sup_symbol: Symbol,
+        #[note(trait_selection_prlf_known_limitation)]
+        note: (),
+    },
+    #[diag(trait_selection_lf_bound_not_satisfied)]
+    HasSub {
+        #[primary_span]
+        span: Span,
+        #[note(trait_selection_prlf_defined_with_sub)]
+        sub_span: Span,
+        #[note(trait_selection_prlf_must_outlive_without_sup)]
+        sup_span: Span,
+        sub_symbol: Symbol,
+        #[note(trait_selection_prlf_known_limitation)]
+        note: (),
+    },
+    #[diag(trait_selection_lf_bound_not_satisfied)]
+    HasSup {
+        #[primary_span]
+        span: Span,
+        #[note(trait_selection_prlf_defined_without_sub)]
+        sub_span: Span,
+        #[note(trait_selection_prlf_must_outlive_with_sup)]
+        sup_span: Span,
+        sup_symbol: Symbol,
+        #[note(trait_selection_prlf_known_limitation)]
+        note: (),
+    },
+    #[diag(trait_selection_lf_bound_not_satisfied)]
+    HasNone {
+        #[primary_span]
+        span: Span,
+        #[note(trait_selection_prlf_defined_without_sub)]
+        sub_span: Span,
+        #[note(trait_selection_prlf_must_outlive_without_sup)]
+        sup_span: Span,
+        #[note(trait_selection_prlf_known_limitation)]
+        note: (),
+    },
+    #[diag(trait_selection_lf_bound_not_satisfied)]
+    OnlyPrimarySpan {
+        #[primary_span]
+        span: Span,
+        #[note(trait_selection_prlf_known_limitation)]
+        note: (),
+    },
+}
+
+#[derive(Diagnostic)]
+#[diag(trait_selection_opaque_captures_lifetime, code = E0700)]
+pub struct OpaqueCapturesLifetime<'tcx> {
+    #[primary_span]
+    pub span: Span,
+    #[label]
+    pub opaque_ty_span: Span,
+    pub opaque_ty: Ty<'tcx>,
+}
+
+#[derive(Subdiagnostic)]
+pub enum FunctionPointerSuggestion<'a> {
+    #[suggestion(
+        trait_selection_fps_use_ref,
+        code = "&{fn_name}",
+        style = "verbose",
+        applicability = "maybe-incorrect"
+    )]
+    UseRef {
+        #[primary_span]
+        span: Span,
+        #[skip_arg]
+        fn_name: String,
+    },
+    #[suggestion(
+        trait_selection_fps_remove_ref,
+        code = "{fn_name}",
+        style = "verbose",
+        applicability = "maybe-incorrect"
+    )]
+    RemoveRef {
+        #[primary_span]
+        span: Span,
+        #[skip_arg]
+        fn_name: String,
+    },
+    #[suggestion(
+        trait_selection_fps_cast,
+        code = "&({fn_name} as {sig})",
+        style = "verbose",
+        applicability = "maybe-incorrect"
+    )]
+    CastRef {
+        #[primary_span]
+        span: Span,
+        #[skip_arg]
+        fn_name: String,
+        #[skip_arg]
+        sig: Binder<'a, FnSig<'a>>,
+    },
+    #[suggestion(
+        trait_selection_fps_cast,
+        code = "{fn_name} as {sig}",
+        style = "verbose",
+        applicability = "maybe-incorrect"
+    )]
+    Cast {
+        #[primary_span]
+        span: Span,
+        #[skip_arg]
+        fn_name: String,
+        #[skip_arg]
+        sig: Binder<'a, FnSig<'a>>,
+    },
+    #[suggestion(
+        trait_selection_fps_cast_both,
+        code = "{fn_name} as {found_sig}",
+        style = "hidden",
+        applicability = "maybe-incorrect"
+    )]
+    CastBoth {
+        #[primary_span]
+        span: Span,
+        #[skip_arg]
+        fn_name: String,
+        #[skip_arg]
+        found_sig: Binder<'a, FnSig<'a>>,
+        expected_sig: Binder<'a, FnSig<'a>>,
+    },
+    #[suggestion(
+        trait_selection_fps_cast_both,
+        code = "&({fn_name} as {found_sig})",
+        style = "hidden",
+        applicability = "maybe-incorrect"
+    )]
+    CastBothRef {
+        #[primary_span]
+        span: Span,
+        #[skip_arg]
+        fn_name: String,
+        #[skip_arg]
+        found_sig: Binder<'a, FnSig<'a>>,
+        expected_sig: Binder<'a, FnSig<'a>>,
+    },
+}
+
+#[derive(Subdiagnostic)]
+#[note(trait_selection_fps_items_are_distinct)]
+pub struct FnItemsAreDistinct;
+
+#[derive(Subdiagnostic)]
+#[note(trait_selection_fn_uniq_types)]
+pub struct FnUniqTypes;
+
+#[derive(Subdiagnostic)]
+#[help(trait_selection_fn_consider_casting)]
+pub struct FnConsiderCasting {
+    pub casting: String,
+}
+
+#[derive(Subdiagnostic)]
+pub enum SuggestAccessingField<'a> {
+    #[suggestion(
+        trait_selection_suggest_accessing_field,
+        code = "{snippet}.{name}",
+        applicability = "maybe-incorrect"
+    )]
+    Safe {
+        #[primary_span]
+        span: Span,
+        snippet: String,
+        name: Symbol,
+        ty: Ty<'a>,
+    },
+    #[suggestion(
+        trait_selection_suggest_accessing_field,
+        code = "unsafe {{ {snippet}.{name} }}",
+        applicability = "maybe-incorrect"
+    )]
+    Unsafe {
+        #[primary_span]
+        span: Span,
+        snippet: String,
+        name: Symbol,
+        ty: Ty<'a>,
+    },
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(trait_selection_stp_wrap_one, applicability = "maybe-incorrect")]
+pub struct SuggestTuplePatternOne {
+    pub variant: String,
+    #[suggestion_part(code = "{variant}(")]
+    pub span_low: Span,
+    #[suggestion_part(code = ")")]
+    pub span_high: Span,
+}
+
+pub struct SuggestTuplePatternMany {
+    pub path: String,
+    pub cause_span: Span,
+    pub compatible_variants: Vec<String>,
+}
+
+impl Subdiagnostic for SuggestTuplePatternMany {
+    fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
+        self,
+        diag: &mut Diag<'_, G>,
+        f: &F,
+    ) {
+        diag.arg("path", self.path);
+        let message = f(diag, crate::fluent_generated::trait_selection_stp_wrap_many.into());
+        diag.multipart_suggestions(
+            message,
+            self.compatible_variants.into_iter().map(|variant| {
+                vec![
+                    (self.cause_span.shrink_to_lo(), format!("{variant}(")),
+                    (self.cause_span.shrink_to_hi(), ")".to_string()),
+                ]
+            }),
+            rustc_errors::Applicability::MaybeIncorrect,
+        );
+    }
+}
+
+#[derive(Subdiagnostic)]
+pub enum TypeErrorAdditionalDiags {
+    #[suggestion(
+        trait_selection_meant_byte_literal,
+        code = "b'{code}'",
+        applicability = "machine-applicable"
+    )]
+    MeantByteLiteral {
+        #[primary_span]
+        span: Span,
+        code: String,
+    },
+    #[suggestion(
+        trait_selection_meant_char_literal,
+        code = "'{code}'",
+        applicability = "machine-applicable"
+    )]
+    MeantCharLiteral {
+        #[primary_span]
+        span: Span,
+        code: String,
+    },
+    #[multipart_suggestion(trait_selection_meant_str_literal, applicability = "machine-applicable")]
+    MeantStrLiteral {
+        #[suggestion_part(code = "\"")]
+        start: Span,
+        #[suggestion_part(code = "\"")]
+        end: Span,
+    },
+    #[suggestion(
+        trait_selection_consider_specifying_length,
+        code = "{length}",
+        applicability = "maybe-incorrect"
+    )]
+    ConsiderSpecifyingLength {
+        #[primary_span]
+        span: Span,
+        length: u64,
+    },
+    #[note(trait_selection_try_cannot_convert)]
+    TryCannotConvert { found: String, expected: String },
+    #[suggestion(
+        trait_selection_tuple_trailing_comma,
+        code = ",",
+        applicability = "machine-applicable"
+    )]
+    TupleOnlyComma {
+        #[primary_span]
+        span: Span,
+    },
+    #[multipart_suggestion(
+        trait_selection_tuple_trailing_comma,
+        applicability = "machine-applicable"
+    )]
+    TupleAlsoParentheses {
+        #[suggestion_part(code = "(")]
+        span_low: Span,
+        #[suggestion_part(code = ",)")]
+        span_high: Span,
+    },
+    #[suggestion(
+        trait_selection_suggest_add_let_for_letchains,
+        style = "verbose",
+        applicability = "machine-applicable",
+        code = "let "
+    )]
+    AddLetForLetChains {
+        #[primary_span]
+        span: Span,
+    },
+}
+
+#[derive(Diagnostic)]
+pub enum ObligationCauseFailureCode {
+    #[diag(trait_selection_oc_method_compat, code = E0308)]
+    MethodCompat {
+        #[primary_span]
+        span: Span,
+        #[subdiagnostic]
+        subdiags: Vec<TypeErrorAdditionalDiags>,
+    },
+    #[diag(trait_selection_oc_type_compat, code = E0308)]
+    TypeCompat {
+        #[primary_span]
+        span: Span,
+        #[subdiagnostic]
+        subdiags: Vec<TypeErrorAdditionalDiags>,
+    },
+    #[diag(trait_selection_oc_const_compat, code = E0308)]
+    ConstCompat {
+        #[primary_span]
+        span: Span,
+        #[subdiagnostic]
+        subdiags: Vec<TypeErrorAdditionalDiags>,
+    },
+    #[diag(trait_selection_oc_try_compat, code = E0308)]
+    TryCompat {
+        #[primary_span]
+        span: Span,
+        #[subdiagnostic]
+        subdiags: Vec<TypeErrorAdditionalDiags>,
+    },
+    #[diag(trait_selection_oc_match_compat, code = E0308)]
+    MatchCompat {
+        #[primary_span]
+        span: Span,
+        #[subdiagnostic]
+        subdiags: Vec<TypeErrorAdditionalDiags>,
+    },
+    #[diag(trait_selection_oc_if_else_different, code = E0308)]
+    IfElseDifferent {
+        #[primary_span]
+        span: Span,
+        #[subdiagnostic]
+        subdiags: Vec<TypeErrorAdditionalDiags>,
+    },
+    #[diag(trait_selection_oc_no_else, code = E0317)]
+    NoElse {
+        #[primary_span]
+        span: Span,
+    },
+    #[diag(trait_selection_oc_no_diverge, code = E0308)]
+    NoDiverge {
+        #[primary_span]
+        span: Span,
+        #[subdiagnostic]
+        subdiags: Vec<TypeErrorAdditionalDiags>,
+    },
+    #[diag(trait_selection_oc_fn_main_correct_type, code = E0580)]
+    FnMainCorrectType {
+        #[primary_span]
+        span: Span,
+    },
+    #[diag(trait_selection_oc_fn_start_correct_type, code = E0308)]
+    FnStartCorrectType {
+        #[primary_span]
+        span: Span,
+        #[subdiagnostic]
+        subdiags: Vec<TypeErrorAdditionalDiags>,
+    },
+    #[diag(trait_selection_oc_fn_lang_correct_type, code = E0308)]
+    FnLangCorrectType {
+        #[primary_span]
+        span: Span,
+        #[subdiagnostic]
+        subdiags: Vec<TypeErrorAdditionalDiags>,
+        lang_item_name: Symbol,
+    },
+    #[diag(trait_selection_oc_intrinsic_correct_type, code = E0308)]
+    IntrinsicCorrectType {
+        #[primary_span]
+        span: Span,
+        #[subdiagnostic]
+        subdiags: Vec<TypeErrorAdditionalDiags>,
+    },
+    #[diag(trait_selection_oc_method_correct_type, code = E0308)]
+    MethodCorrectType {
+        #[primary_span]
+        span: Span,
+        #[subdiagnostic]
+        subdiags: Vec<TypeErrorAdditionalDiags>,
+    },
+    #[diag(trait_selection_oc_closure_selfref, code = E0644)]
+    ClosureSelfref {
+        #[primary_span]
+        span: Span,
+    },
+    #[diag(trait_selection_oc_cant_coerce, code = E0308)]
+    CantCoerce {
+        #[primary_span]
+        span: Span,
+        #[subdiagnostic]
+        subdiags: Vec<TypeErrorAdditionalDiags>,
+    },
+    #[diag(trait_selection_oc_generic, code = E0308)]
+    Generic {
+        #[primary_span]
+        span: Span,
+        #[subdiagnostic]
+        subdiags: Vec<TypeErrorAdditionalDiags>,
+    },
+}
+
+#[derive(Subdiagnostic)]
+pub enum AddPreciseCapturing {
+    #[suggestion(
+        trait_selection_precise_capturing_new,
+        style = "verbose",
+        code = " + use<{concatenated_bounds}>",
+        applicability = "machine-applicable"
+    )]
+    New {
+        #[primary_span]
+        span: Span,
+        new_lifetime: Symbol,
+        concatenated_bounds: String,
+    },
+    #[suggestion(
+        trait_selection_precise_capturing_existing,
+        style = "verbose",
+        code = "{pre}{new_lifetime}{post}",
+        applicability = "machine-applicable"
+    )]
+    Existing {
+        #[primary_span]
+        span: Span,
+        new_lifetime: Symbol,
+        pre: &'static str,
+        post: &'static str,
+    },
+}
+
+pub struct AddPreciseCapturingAndParams {
+    pub suggs: Vec<(Span, String)>,
+    pub new_lifetime: Symbol,
+    pub apit_spans: Vec<Span>,
+}
+
+impl Subdiagnostic for AddPreciseCapturingAndParams {
+    fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
+        self,
+        diag: &mut Diag<'_, G>,
+        _f: &F,
+    ) {
+        diag.arg("new_lifetime", self.new_lifetime);
+        diag.multipart_suggestion_verbose(
+            fluent::trait_selection_precise_capturing_new_but_apit,
+            self.suggs,
+            Applicability::MaybeIncorrect,
+        );
+        diag.span_note(self.apit_spans, fluent::trait_selection_warn_removing_apit_params);
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/errors/note_and_explain.rs b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs
new file mode 100644
index 00000000000..b1477763028
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs
@@ -0,0 +1,185 @@
+use rustc_errors::{Diag, EmissionGuarantee, IntoDiagArg, SubdiagMessageOp, Subdiagnostic};
+use rustc_hir::def_id::LocalDefId;
+use rustc_middle::bug;
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_span::symbol::kw;
+use rustc_span::Span;
+
+use crate::error_reporting::infer::nice_region_error::find_anon_type;
+use crate::fluent_generated as fluent;
+
+struct DescriptionCtx<'a> {
+    span: Option<Span>,
+    kind: &'a str,
+    arg: String,
+}
+
+impl<'a> DescriptionCtx<'a> {
+    fn new<'tcx>(
+        tcx: TyCtxt<'tcx>,
+        generic_param_scope: LocalDefId,
+        region: ty::Region<'tcx>,
+        alt_span: Option<Span>,
+    ) -> Option<Self> {
+        let (span, kind, arg) = match *region {
+            ty::ReEarlyParam(br) => {
+                let scope = tcx
+                    .parent(tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id)
+                    .expect_local();
+                let span = if let Some(param) =
+                    tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name))
+                {
+                    param.span
+                } else {
+                    tcx.def_span(scope)
+                };
+                if br.has_name() {
+                    (Some(span), "as_defined", br.name.to_string())
+                } else {
+                    (Some(span), "as_defined_anon", String::new())
+                }
+            }
+            ty::ReLateParam(ref fr) => {
+                if !fr.bound_region.is_named()
+                    && let Some((ty, _)) =
+                        find_anon_type(tcx, generic_param_scope, region, &fr.bound_region)
+                {
+                    (Some(ty.span), "defined_here", String::new())
+                } else {
+                    let scope = fr.scope.expect_local();
+                    match fr.bound_region {
+                        ty::BoundRegionKind::BrNamed(_, name) => {
+                            let span = if let Some(param) = tcx
+                                .hir()
+                                .get_generics(scope)
+                                .and_then(|generics| generics.get_named(name))
+                            {
+                                param.span
+                            } else {
+                                tcx.def_span(scope)
+                            };
+                            if name == kw::UnderscoreLifetime {
+                                (Some(span), "as_defined_anon", String::new())
+                            } else {
+                                (Some(span), "as_defined", name.to_string())
+                            }
+                        }
+                        ty::BrAnon => {
+                            let span = Some(tcx.def_span(scope));
+                            (span, "defined_here", String::new())
+                        }
+                        _ => (Some(tcx.def_span(scope)), "defined_here_reg", region.to_string()),
+                    }
+                }
+            }
+
+            ty::ReStatic => (alt_span, "restatic", String::new()),
+
+            ty::RePlaceholder(_) | ty::ReError(_) => return None,
+
+            ty::ReVar(_) | ty::ReBound(..) | ty::ReErased => {
+                bug!("unexpected region for DescriptionCtx: {:?}", region);
+            }
+        };
+        Some(DescriptionCtx { span, kind, arg })
+    }
+}
+
+pub enum PrefixKind {
+    Empty,
+    RefValidFor,
+    ContentValidFor,
+    TypeObjValidFor,
+    SourcePointerValidFor,
+    TypeSatisfy,
+    TypeOutlive,
+    LfParamInstantiatedWith,
+    LfParamMustOutlive,
+    LfInstantiatedWith,
+    LfMustOutlive,
+    PointerValidFor,
+    DataValidFor,
+}
+
+pub enum SuffixKind {
+    Empty,
+    Continues,
+    ReqByBinding,
+}
+
+impl IntoDiagArg for PrefixKind {
+    fn into_diag_arg(self) -> rustc_errors::DiagArgValue {
+        let kind = match self {
+            Self::Empty => "empty",
+            Self::RefValidFor => "ref_valid_for",
+            Self::ContentValidFor => "content_valid_for",
+            Self::TypeObjValidFor => "type_obj_valid_for",
+            Self::SourcePointerValidFor => "source_pointer_valid_for",
+            Self::TypeSatisfy => "type_satisfy",
+            Self::TypeOutlive => "type_outlive",
+            Self::LfParamInstantiatedWith => "lf_param_instantiated_with",
+            Self::LfParamMustOutlive => "lf_param_must_outlive",
+            Self::LfInstantiatedWith => "lf_instantiated_with",
+            Self::LfMustOutlive => "lf_must_outlive",
+            Self::PointerValidFor => "pointer_valid_for",
+            Self::DataValidFor => "data_valid_for",
+        }
+        .into();
+        rustc_errors::DiagArgValue::Str(kind)
+    }
+}
+
+impl IntoDiagArg for SuffixKind {
+    fn into_diag_arg(self) -> rustc_errors::DiagArgValue {
+        let kind = match self {
+            Self::Empty => "empty",
+            Self::Continues => "continues",
+            Self::ReqByBinding => "req_by_binding",
+        }
+        .into();
+        rustc_errors::DiagArgValue::Str(kind)
+    }
+}
+
+pub struct RegionExplanation<'a> {
+    desc: DescriptionCtx<'a>,
+    prefix: PrefixKind,
+    suffix: SuffixKind,
+}
+
+impl RegionExplanation<'_> {
+    pub fn new<'tcx>(
+        tcx: TyCtxt<'tcx>,
+        generic_param_scope: LocalDefId,
+        region: ty::Region<'tcx>,
+        alt_span: Option<Span>,
+        prefix: PrefixKind,
+        suffix: SuffixKind,
+    ) -> Option<Self> {
+        Some(Self {
+            desc: DescriptionCtx::new(tcx, generic_param_scope, region, alt_span)?,
+            prefix,
+            suffix,
+        })
+    }
+}
+
+impl Subdiagnostic for RegionExplanation<'_> {
+    fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
+        self,
+        diag: &mut Diag<'_, G>,
+        f: &F,
+    ) {
+        diag.arg("pref_kind", self.prefix);
+        diag.arg("suff_kind", self.suffix);
+        diag.arg("desc_kind", self.desc.kind);
+        diag.arg("desc_arg", self.desc.arg);
+
+        let msg = f(diag, fluent::trait_selection_region_explanation.into());
+        if let Some(span) = self.desc.span {
+            diag.span_note(span, msg);
+        } else {
+            diag.note(msg);
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs
index ad087620ae0..f232a896f96 100644
--- a/compiler/rustc_trait_selection/src/infer.rs
+++ b/compiler/rustc_trait_selection/src/infer.rs
@@ -1,20 +1,19 @@
-use crate::infer::at::ToTrace;
-use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
-use crate::traits::{self, Obligation, ObligationCause, ObligationCtxt, SelectionContext};
+use std::fmt::Debug;
 
 use rustc_hir::def_id::DefId;
 use rustc_hir::lang_items::LangItem;
+pub use rustc_infer::infer::*;
 use rustc_macros::extension;
 use rustc_middle::arena::ArenaAllocatable;
 use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryResponse};
 use rustc_middle::traits::query::NoSolution;
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
-use rustc_middle::ty::{GenericArg, Upcast};
+use rustc_middle::ty::{self, GenericArg, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast};
 use rustc_span::DUMMY_SP;
+use tracing::instrument;
 
-use std::fmt::Debug;
-
-pub use rustc_infer::infer::*;
+use crate::infer::at::ToTrace;
+use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
+use crate::traits::{self, Obligation, ObligationCause, ObligationCtxt, SelectionContext};
 
 #[extension(pub trait InferCtxtExt<'tcx>)]
 impl<'tcx> InferCtxt<'tcx> {
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
index d0a12d73941..a17c007debd 100644
--- a/compiler/rustc_trait_selection/src/lib.rs
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -19,20 +19,22 @@
 #![feature(assert_matches)]
 #![feature(associated_type_defaults)]
 #![feature(box_patterns)]
+#![feature(cfg_version)]
 #![feature(control_flow_enum)]
 #![feature(extract_if)]
 #![feature(if_let_guard)]
+#![feature(iter_intersperse)]
 #![feature(let_chains)]
 #![feature(never_type)]
 #![feature(rustdoc_internals)]
+#![feature(try_blocks)]
 #![feature(type_alias_impl_trait)]
 #![feature(unwrap_infallible)]
+#![feature(yeet_expr)]
 #![recursion_limit = "512"] // For rustdoc
+#![warn(unreachable_pub)] // For rustdoc
 // tidy-alphabetical-end
 
-#[macro_use]
-extern crate tracing;
-
 pub mod error_reporting;
 pub mod errors;
 pub mod infer;
diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs
index 5f986e22f51..65762cfcd2e 100644
--- a/compiler/rustc_trait_selection/src/regions.rs
+++ b/compiler/rustc_trait_selection/src/regions.rs
@@ -1,10 +1,11 @@
-use crate::traits::ScrubbedTraitError;
 use rustc_infer::infer::outlives::env::OutlivesEnvironment;
 use rustc_infer::infer::{InferCtxt, RegionResolutionError};
 use rustc_macros::extension;
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::traits::ObligationCause;
 
+use crate::traits::ScrubbedTraitError;
+
 #[extension(pub trait InferCtxtRegionExt<'tcx>)]
 impl<'tcx> InferCtxt<'tcx> {
     /// Resolve regions, using the deep normalizer to normalize any type-outlives
diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs
index f67518a577e..a7b0719d8d4 100644
--- a/compiler/rustc_trait_selection/src/solve/delegate.rs
+++ b/compiler/rustc_trait_selection/src/solve/delegate.rs
@@ -13,6 +13,7 @@ use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt as _};
 use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
 use rustc_type_ir::solve::{Certainty, NoSolution, SolverMode};
+use tracing::trace;
 
 use crate::traits::specialization_graph;
 
@@ -87,12 +88,12 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
     ) -> Option<ty::Const<'tcx>> {
         use rustc_middle::mir::interpret::ErrorHandled;
         match self.const_eval_resolve(param_env, unevaluated, DUMMY_SP) {
-            Ok(Some(val)) => Some(ty::Const::new_value(
+            Ok(Ok(val)) => Some(ty::Const::new_value(
                 self.tcx,
                 val,
                 self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args),
             )),
-            Ok(None) | Err(ErrorHandled::TooGeneric(_)) => None,
+            Ok(Err(_)) | Err(ErrorHandled::TooGeneric(_)) => None,
             Err(ErrorHandled::Reported(e, _)) => Some(ty::Const::new_error(self.tcx, e.into())),
         }
     }
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 76b88aeb0f7..f5f36f40f7e 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -13,13 +13,12 @@ use rustc_middle::bug;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as _};
-use rustc_span::symbol::sym;
-
-use crate::traits::{FulfillmentError, FulfillmentErrorCode, ScrubbedTraitError};
+use tracing::instrument;
 
 use super::delegate::SolverDelegate;
 use super::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor};
 use super::Certainty;
+use crate::traits::{FulfillmentError, FulfillmentErrorCode, ScrubbedTraitError};
 
 /// A trait engine using the new trait solver.
 ///
@@ -441,10 +440,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
             source: CandidateSource::Impl(impl_def_id),
             result: _,
         } = candidate.kind()
-            && goal
-                .infcx()
-                .tcx
-                .has_attrs_with_path(impl_def_id, &[sym::diagnostic, sym::do_not_recommend])
+            && goal.infcx().tcx.do_not_recommend_impl(impl_def_id)
         {
             return ControlFlow::Break(self.obligation.clone());
         }
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
index e8de8457440..49c37a684b5 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
@@ -9,6 +9,8 @@
 //! coherence right now and was annoying to implement, so I am leaving it
 //! as is until we start using it for something else.
 
+use std::assert_matches::assert_matches;
+
 use rustc_ast_ir::try_visit;
 use rustc_ast_ir::visit::VisitorResult;
 use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
@@ -21,6 +23,7 @@ use rustc_next_trait_solver::resolve::EagerResolver;
 use rustc_next_trait_solver::solve::inspect::{self, instantiate_canonical_state};
 use rustc_next_trait_solver::solve::{GenerateProofTree, MaybeCause, SolverDelegateEvalExt as _};
 use rustc_span::{Span, DUMMY_SP};
+use tracing::instrument;
 
 use crate::solve::delegate::SolverDelegate;
 use crate::traits::ObligationCtxt;
@@ -273,10 +276,10 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
                     steps.push(step)
                 }
                 inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => {
-                    assert!(matches!(
+                    assert_matches!(
                         shallow_certainty.replace(c),
                         None | Some(Certainty::Maybe(MaybeCause::Ambiguity))
-                    ));
+                    );
                 }
                 inspect::ProbeStep::NestedProbe(ref probe) => {
                     match probe.kind {
@@ -332,13 +335,9 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
 
     pub fn candidates(&'a self) -> Vec<InspectCandidate<'a, 'tcx>> {
         let mut candidates = vec![];
-        let last_eval_step = match self.evaluation_kind {
-            inspect::CanonicalGoalEvaluationKind::Overflow
-            | inspect::CanonicalGoalEvaluationKind::CycleInStack
-            | inspect::CanonicalGoalEvaluationKind::ProvisionalCacheHit => {
-                warn!("unexpected root evaluation: {:?}", self.evaluation_kind);
-                return vec![];
-            }
+        let last_eval_step = match &self.evaluation_kind {
+            // An annoying edge case in case the recursion limit is 0.
+            inspect::CanonicalGoalEvaluationKind::Overflow => return vec![],
             inspect::CanonicalGoalEvaluationKind::Evaluation { final_revision } => final_revision,
         };
 
diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs
index ca313590265..938ba2dde84 100644
--- a/compiler/rustc_trait_selection/src/solve/normalize.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalize.rs
@@ -1,19 +1,23 @@
+use std::assert_matches::assert_matches;
 use std::fmt::Debug;
 use std::marker::PhantomData;
 
-use crate::error_reporting::traits::{OverflowCause, TypeErrCtxtOverflowExt};
-use crate::traits::query::evaluate_obligation::InferCtxtExt;
-use crate::traits::{BoundVarReplacer, PlaceholderReplacer, ScrubbedTraitError};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_infer::infer::at::At;
 use rustc_infer::infer::InferCtxt;
 use rustc_infer::traits::{FromSolverError, Obligation, TraitEngine};
 use rustc_middle::traits::ObligationCause;
-use rustc_middle::ty::{self, Ty, TyCtxt, UniverseIndex};
-use rustc_middle::ty::{FallibleTypeFolder, TypeFolder, TypeSuperFoldable};
-use rustc_middle::ty::{TypeFoldable, TypeVisitableExt};
+use rustc_middle::ty::{
+    self, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
+    TypeVisitableExt, UniverseIndex,
+};
+use tracing::instrument;
 
 use super::{FulfillmentCtxt, NextSolverError};
+use crate::error_reporting::traits::OverflowCause;
+use crate::error_reporting::InferCtxtErrorExt;
+use crate::traits::query::evaluate_obligation::InferCtxtExt;
+use crate::traits::{BoundVarReplacer, PlaceholderReplacer, ScrubbedTraitError};
 
 /// Deeply normalize all aliases in `value`. This does not handle inference and expects
 /// its input to be already fully resolved.
@@ -61,7 +65,7 @@ where
     E: FromSolverError<'tcx, NextSolverError<'tcx>>,
 {
     fn normalize_alias_ty(&mut self, alias_ty: Ty<'tcx>) -> Result<Ty<'tcx>, Vec<E>> {
-        assert!(matches!(alias_ty.kind(), ty::Alias(..)));
+        assert_matches!(alias_ty.kind(), ty::Alias(..));
 
         let infcx = self.at.infcx;
         let tcx = infcx.tcx;
diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
index 1d32ef2ccd9..f68e0583307 100644
--- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs
+++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -1,20 +1,20 @@
 //! Support code for rustdoc and external tools.
 //! You really don't want to be using this unless you need to.
 
-use super::*;
-
-use crate::errors::UnableToConstructConstantValue;
-use crate::infer::region_constraints::{Constraint, RegionConstraintData};
-use crate::traits::project::ProjectAndUnifyResult;
+use std::collections::VecDeque;
+use std::iter;
 
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry};
 use rustc_data_structures::unord::UnordSet;
 use rustc_infer::infer::DefineOpaqueTypes;
 use rustc_middle::mir::interpret::ErrorHandled;
 use rustc_middle::ty::{Region, RegionVid};
+use tracing::debug;
 
-use std::collections::VecDeque;
-use std::iter;
+use super::*;
+use crate::errors::UnableToConstructConstantValue;
+use crate::infer::region_constraints::{Constraint, RegionConstraintData};
+use crate::traits::project::ProjectAndUnifyResult;
 
 // FIXME(twk): this is obviously not nice to duplicate like that
 #[derive(Eq, PartialEq, Hash, Copy, Clone, Debug)]
@@ -765,13 +765,13 @@ impl<'tcx> AutoTraitFinder<'tcx> {
                                 unevaluated,
                                 obligation.cause.span,
                             ) {
-                                Ok(Some(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args))),
-                                Ok(None) => {
+                                Ok(Ok(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args))),
+                                Ok(Err(_)) => {
                                     let tcx = self.tcx;
                                     let reported =
                                         tcx.dcx().emit_err(UnableToConstructConstantValue {
                                             span: tcx.def_span(unevaluated.def),
-                                            unevaluated: unevaluated,
+                                            unevaluated,
                                         });
                                     Err(ErrorHandled::Reported(reported.into(), tcx.def_span(unevaluated.def)))
                                 }
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 7e996c5c5ef..8558520897b 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -4,15 +4,8 @@
 //! [trait-resolution]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html
 //! [trait-specialization]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html
 
-use crate::infer::outlives::env::OutlivesEnvironment;
-use crate::infer::InferOk;
-use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor};
-use crate::solve::{deeply_normalize_for_diagnostics, inspect};
-use crate::traits::select::IntercrateAmbiguityCause;
-use crate::traits::NormalizeExt;
-use crate::traits::SkipLeakCheck;
-use crate::traits::{util, FulfillmentErrorCode};
-use crate::traits::{Obligation, ObligationCause, PredicateObligation, SelectionContext};
+use std::fmt::Debug;
+
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_errors::{Diag, EmissionGuarantee};
 use rustc_hir::def::DefKind;
@@ -28,10 +21,19 @@ use rustc_middle::ty::{self, Ty, TyCtxt};
 pub use rustc_next_trait_solver::coherence::*;
 use rustc_span::symbol::sym;
 use rustc_span::{Span, DUMMY_SP};
-use std::fmt::Debug;
+use tracing::{debug, instrument, warn};
 
 use super::ObligationCtxt;
 use crate::error_reporting::traits::suggest_new_overflow_limit;
+use crate::infer::outlives::env::OutlivesEnvironment;
+use crate::infer::InferOk;
+use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor};
+use crate::solve::{deeply_normalize_for_diagnostics, inspect};
+use crate::traits::select::IntercrateAmbiguityCause;
+use crate::traits::{
+    util, FulfillmentErrorCode, NormalizeExt, Obligation, ObligationCause, PredicateObligation,
+    SelectionContext, SkipLeakCheck,
+};
 
 pub struct OverlapResult<'tcx> {
     pub impl_header: ty::ImplHeader<'tcx>,
@@ -52,7 +54,7 @@ pub fn add_placeholder_note<G: EmissionGuarantee>(err: &mut Diag<'_, G>) {
     );
 }
 
-pub fn suggest_increasing_recursion_limit<'tcx, G: EmissionGuarantee>(
+pub(crate) fn suggest_increasing_recursion_limit<'tcx, G: EmissionGuarantee>(
     tcx: TyCtxt<'tcx>,
     err: &mut Diag<'_, G>,
     overflowing_predicates: &[ty::Predicate<'tcx>],
@@ -721,7 +723,7 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> {
                     // FIXME: While this matches the behavior of the
                     // old solver, it is not the only way in which the unknowable
                     // candidates *weaken* coherence, they can also force otherwise
-                    // sucessful normalization to be ambiguous.
+                    // successful normalization to be ambiguous.
                     Ok(Certainty::Maybe(_) | Certainty::Yes) => {
                         ambiguity_cause = None;
                         break;
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index f93bd0a396d..4289384725f 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -17,6 +17,7 @@ use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::abstract_const::NotConstEvaluatable;
 use rustc_middle::ty::{self, TyCtxt, TypeVisitable, TypeVisitableExt, TypeVisitor};
 use rustc_span::{Span, DUMMY_SP};
+use tracing::{debug, instrument};
 
 use crate::traits::ObligationCtxt;
 
diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs
index bdc27e734f9..de1d4ef15ac 100644
--- a/compiler/rustc_trait_selection/src/traits/engine.rs
+++ b/compiler/rustc_trait_selection/src/traits/engine.rs
@@ -1,16 +1,6 @@
 use std::cell::RefCell;
 use std::fmt::Debug;
 
-use super::{FromSolverError, TraitEngine};
-use super::{FulfillmentContext, ScrubbedTraitError};
-use crate::error_reporting::traits::TypeErrCtxtExt;
-use crate::regions::InferCtxtRegionExt;
-use crate::solve::FulfillmentCtxt as NextFulfillmentCtxt;
-use crate::solve::NextSolverError;
-use crate::traits::fulfill::OldSolverError;
-use crate::traits::NormalizeExt;
-use crate::traits::StructurallyNormalizeExt;
-use crate::traits::{FulfillmentError, Obligation, ObligationCause, PredicateObligation};
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -19,16 +9,22 @@ use rustc_infer::infer::canonical::{
     Canonical, CanonicalQueryResponse, CanonicalVarValues, QueryResponse,
 };
 use rustc_infer::infer::outlives::env::OutlivesEnvironment;
-use rustc_infer::infer::RegionResolutionError;
-use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
+use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError};
 use rustc_macros::extension;
 use rustc_middle::arena::ArenaAllocatable;
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::ty::error::TypeError;
-use rustc_middle::ty::TypeFoldable;
-use rustc_middle::ty::Upcast;
-use rustc_middle::ty::Variance;
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, Upcast, Variance};
+
+use super::{FromSolverError, FulfillmentContext, ScrubbedTraitError, TraitEngine};
+use crate::error_reporting::InferCtxtErrorExt;
+use crate::regions::InferCtxtRegionExt;
+use crate::solve::{FulfillmentCtxt as NextFulfillmentCtxt, NextSolverError};
+use crate::traits::fulfill::OldSolverError;
+use crate::traits::{
+    FulfillmentError, NormalizeExt, Obligation, ObligationCause, PredicateObligation,
+    StructurallyNormalizeExt,
+};
 
 #[extension(pub trait TraitEngineExt<'tcx, E>)]
 impl<'tcx, E> dyn TraitEngine<'tcx, E>
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 5597c8be592..16ba06f8667 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -1,32 +1,30 @@
-use crate::error_reporting::traits::TypeErrCtxtOverflowExt;
-use crate::infer::{InferCtxt, TyOrConstInferVar};
-use crate::traits::normalize::normalize_with_depth_to;
+use std::marker::PhantomData;
+
 use rustc_data_structures::captures::Captures;
-use rustc_data_structures::obligation_forest::ProcessResult;
-use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome};
-use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor};
+use rustc_data_structures::obligation_forest::{
+    Error, ForestObligation, ObligationForest, ObligationProcessor, Outcome, ProcessResult,
+};
 use rustc_infer::infer::DefineOpaqueTypes;
-use rustc_infer::traits::{FromSolverError, ProjectionCacheKey};
-use rustc_infer::traits::{PolyTraitObligation, SelectionError, TraitEngine};
+use rustc_infer::traits::{
+    FromSolverError, PolyTraitObligation, ProjectionCacheKey, SelectionError, TraitEngine,
+};
 use rustc_middle::bug;
 use rustc_middle::mir::interpret::ErrorHandled;
 use rustc_middle::ty::abstract_const::NotConstEvaluatable;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
-use rustc_middle::ty::GenericArgsRef;
-use rustc_middle::ty::{self, Binder, Const, TypeVisitableExt};
-use std::marker::PhantomData;
+use rustc_middle::ty::{self, Binder, Const, GenericArgsRef, TypeVisitableExt};
+use tracing::{debug, debug_span, instrument};
 
 use super::project::{self, ProjectAndUnifyResult};
 use super::select::SelectionContext;
-use super::wf;
-use super::EvaluationResult;
-use super::PredicateObligation;
-use super::Unimplemented;
-use super::{const_evaluatable, ScrubbedTraitError};
-use super::{FulfillmentError, FulfillmentErrorCode};
-
-use crate::traits::project::PolyProjectionObligation;
-use crate::traits::project::ProjectionCacheKeyExt as _;
+use super::{
+    const_evaluatable, wf, EvaluationResult, FulfillmentError, FulfillmentErrorCode,
+    PredicateObligation, ScrubbedTraitError, Unimplemented,
+};
+use crate::error_reporting::InferCtxtErrorExt;
+use crate::infer::{InferCtxt, TyOrConstInferVar};
+use crate::traits::normalize::normalize_with_depth_to;
+use crate::traits::project::{PolyProjectionObligation, ProjectionCacheKeyExt as _};
 use crate::traits::query::evaluate_obligation::InferCtxtExt;
 
 impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> {
@@ -474,7 +472,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                     };
 
                     match infcx.at(&obligation.cause, obligation.param_env).eq(
-                        // Only really excercised by generic_const_exprs
+                        // Only really exercised by generic_const_exprs
                         DefineOpaqueTypes::Yes,
                         ct_ty,
                         ty,
diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs
index baec2268629..3e65194577e 100644
--- a/compiler/rustc_trait_selection/src/traits/misc.rs
+++ b/compiler/rustc_trait_selection/src/traits/misc.rs
@@ -1,9 +1,9 @@
 //! Miscellaneous type-system utilities that are too small to deserve their own modules.
 
-use crate::regions::InferCtxtRegionExt;
-use crate::traits::{self, FulfillmentError, ObligationCause};
+use std::assert_matches::assert_matches;
 
 use hir::LangItem;
+use rustc_ast::Mutability;
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_hir as hir;
 use rustc_infer::infer::outlives::env::OutlivesEnvironment;
@@ -11,6 +11,8 @@ use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
 use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeVisitableExt};
 
 use super::outlives_bounds::InferCtxtExt;
+use crate::regions::InferCtxtRegionExt;
+use crate::traits::{self, FulfillmentError, ObligationCause};
 
 pub enum CopyImplementationError<'tcx> {
     InfringingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
@@ -19,6 +21,8 @@ pub enum CopyImplementationError<'tcx> {
 }
 
 pub enum ConstParamTyImplementationError<'tcx> {
+    UnsizedConstParamsFeatureRequired,
+    InvalidInnerTyOfBuiltinTy(Vec<(Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
     InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
     NotAnAdtOrBuiltinAllowed,
 }
@@ -77,9 +81,9 @@ pub fn type_allowed_to_implement_copy<'tcx>(
     Ok(())
 }
 
-/// Checks that the fields of the type (an ADT) all implement `ConstParamTy`.
+/// Checks that the fields of the type (an ADT) all implement `(Unsized?)ConstParamTy`.
 ///
-/// If fields don't implement `ConstParamTy`, return an error containing a list of
+/// If fields don't implement `(Unsized?)ConstParamTy`, return an error containing a list of
 /// those violating fields.
 ///
 /// If it's not an ADT, int ty, `bool` or `char`, returns `Err(NotAnAdtOrBuiltinAllowed)`.
@@ -87,35 +91,95 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>(
     tcx: TyCtxt<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     self_type: Ty<'tcx>,
+    lang_item: LangItem,
     parent_cause: ObligationCause<'tcx>,
 ) -> Result<(), ConstParamTyImplementationError<'tcx>> {
-    let (adt, args) = match self_type.kind() {
-        // `core` provides these impls.
-        ty::Uint(_)
-        | ty::Int(_)
-        | ty::Bool
-        | ty::Char
-        | ty::Str
-        | ty::Array(..)
-        | ty::Slice(_)
-        | ty::Ref(.., hir::Mutability::Not)
-        | ty::Tuple(_) => return Ok(()),
+    assert_matches!(lang_item, LangItem::ConstParamTy | LangItem::UnsizedConstParamTy);
 
-        &ty::Adt(adt, args) => (adt, args),
+    let inner_tys: Vec<_> = match *self_type.kind() {
+        // Trivially okay as these types are all:
+        // - Sized
+        // - Contain no nested types
+        // - Have structural equality
+        ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => return Ok(()),
+
+        // Handle types gated under `feature(unsized_const_params)`
+        // FIXME(unsized_const_params): Make `const N: [u8]` work then forbid references
+        ty::Slice(inner_ty) | ty::Ref(_, inner_ty, Mutability::Not)
+            if lang_item == LangItem::UnsizedConstParamTy =>
+        {
+            vec![inner_ty]
+        }
+        ty::Str if lang_item == LangItem::UnsizedConstParamTy => {
+            vec![Ty::new_slice(tcx, tcx.types.u8)]
+        }
+        ty::Str | ty::Slice(..) | ty::Ref(_, _, Mutability::Not) => {
+            return Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired);
+        }
+
+        ty::Array(inner_ty, _) => vec![inner_ty],
+
+        // `str` morally acts like a newtype around `[u8]`
+        ty::Tuple(inner_tys) => inner_tys.into_iter().collect(),
+
+        ty::Adt(adt, args) if adt.is_enum() || adt.is_struct() => {
+            all_fields_implement_trait(
+                tcx,
+                param_env,
+                self_type,
+                adt,
+                args,
+                parent_cause.clone(),
+                lang_item,
+            )
+            .map_err(ConstParamTyImplementationError::InfrigingFields)?;
+
+            vec![]
+        }
 
         _ => return Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed),
     };
 
-    all_fields_implement_trait(
-        tcx,
-        param_env,
-        self_type,
-        adt,
-        args,
-        parent_cause,
-        hir::LangItem::ConstParamTy,
-    )
-    .map_err(ConstParamTyImplementationError::InfrigingFields)?;
+    let mut infringing_inner_tys = vec![];
+    for inner_ty in inner_tys {
+        // We use an ocx per inner ty for better diagnostics
+        let infcx = tcx.infer_ctxt().build();
+        let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx);
+
+        ocx.register_bound(
+            parent_cause.clone(),
+            param_env,
+            inner_ty,
+            tcx.require_lang_item(lang_item, Some(parent_cause.span)),
+        );
+
+        let errors = ocx.select_all_or_error();
+        if !errors.is_empty() {
+            infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Fulfill(errors)));
+            continue;
+        }
+
+        // Check regions assuming the self type of the impl is WF
+        let outlives_env = OutlivesEnvironment::with_bounds(
+            param_env,
+            infcx.implied_bounds_tys(
+                param_env,
+                parent_cause.body_id,
+                &FxIndexSet::from_iter([self_type]),
+            ),
+        );
+        let errors = infcx.resolve_regions(&outlives_env);
+        if !errors.is_empty() {
+            infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Regions(errors)));
+            continue;
+        }
+    }
+
+    if !infringing_inner_tys.is_empty() {
+        return Err(ConstParamTyImplementationError::InvalidInnerTyOfBuiltinTy(
+            infringing_inner_tys,
+        ));
+    }
 
     Ok(())
 }
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index d28982ed849..c82eaa5143d 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -16,59 +16,62 @@ pub mod query;
 #[allow(hidden_glob_reexports)]
 mod select;
 mod specialize;
-mod structural_match;
 mod structural_normalize;
 #[allow(hidden_glob_reexports)]
 mod util;
 pub mod vtable;
 pub mod wf;
 
-use crate::error_reporting::traits::TypeErrCtxtExt as _;
-use crate::infer::outlives::env::OutlivesEnvironment;
-use crate::infer::{InferCtxt, TyCtxtInferExt};
-use crate::regions::InferCtxtRegionExt;
-use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
+use std::fmt::Debug;
+use std::ops::ControlFlow;
+
 use rustc_errors::ErrorGuaranteed;
+pub use rustc_infer::traits::*;
 use rustc_middle::query::Providers;
 use rustc_middle::span_bug;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeFolder, TypeSuperVisitable, Upcast};
-use rustc_middle::ty::{GenericArgs, GenericArgsRef};
+use rustc_middle::ty::{
+    self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFolder, TypeSuperVisitable, Upcast,
+};
 use rustc_span::def_id::DefId;
 use rustc_span::Span;
+use tracing::{debug, instrument};
 
-use std::fmt::Debug;
-use std::ops::ControlFlow;
-
-pub use self::coherence::{add_placeholder_note, orphan_check_trait_ref, overlapping_impls};
-pub use self::coherence::{InCrate, IsFirstInputType, UncoveredTyParams};
-pub use self::coherence::{OrphanCheckErr, OrphanCheckMode, OverlapResult};
+pub use self::coherence::{
+    add_placeholder_note, orphan_check_trait_ref, overlapping_impls, InCrate, IsFirstInputType,
+    OrphanCheckErr, OrphanCheckMode, OverlapResult, UncoveredTyParams,
+};
 pub use self::engine::{ObligationCtxt, TraitEngineExt};
 pub use self::fulfill::{FulfillmentContext, OldSolverError, PendingPredicateObligation};
 pub use self::normalize::NormalizeExt;
-pub use self::object_safety::hir_ty_lowering_object_safety_violations;
-pub use self::object_safety::is_vtable_safe_method;
-pub use self::object_safety::object_safety_violations_for_assoc_item;
-pub use self::object_safety::ObjectSafetyViolation;
+pub use self::object_safety::{
+    hir_ty_lowering_object_safety_violations, is_vtable_safe_method,
+    object_safety_violations_for_assoc_item, ObjectSafetyViolation,
+};
 pub use self::project::{normalize_inherent_projection, normalize_projection_ty};
-pub use self::select::{EvaluationCache, SelectionCache, SelectionContext};
-pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError};
-pub use self::specialize::specialization_graph::FutureCompatOverlapError;
-pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind;
+pub use self::select::{
+    EvaluationCache, EvaluationResult, IntercrateAmbiguityCause, OverflowError, SelectionCache,
+    SelectionContext,
+};
+pub use self::specialize::specialization_graph::{
+    FutureCompatOverlapError, FutureCompatOverlapErrorKind,
+};
 pub use self::specialize::{
     specialization_graph, translate_args, translate_args_with_cause, OverlapError,
 };
-pub use self::structural_match::search_for_structural_match_violation;
 pub use self::structural_normalize::StructurallyNormalizeExt;
-pub use self::util::elaborate;
-pub use self::util::{expand_trait_aliases, TraitAliasExpander, TraitAliasExpansionInfo};
-pub use self::util::{impl_item_is_final, upcast_choices};
-pub use self::util::{supertraits, transitive_bounds, transitive_bounds_that_define_assoc_item};
-pub use self::util::{with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer};
-
-pub use rustc_infer::traits::*;
+pub use self::util::{
+    elaborate, expand_trait_aliases, impl_item_is_final, supertraits,
+    transitive_bounds_that_define_assoc_item, upcast_choices, with_replaced_escaping_bound_vars,
+    BoundVarReplacer, PlaceholderReplacer, TraitAliasExpander, TraitAliasExpansionInfo,
+};
+use crate::error_reporting::InferCtxtErrorExt;
+use crate::infer::outlives::env::OutlivesEnvironment;
+use crate::infer::{InferCtxt, TyCtxtInferExt};
+use crate::regions::InferCtxtRegionExt;
+use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
 
 pub struct FulfillmentError<'tcx> {
     pub obligation: PredicateObligation<'tcx>,
diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs
index 01ba8c02ea6..aad47df7369 100644
--- a/compiler/rustc_trait_selection/src/traits/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/normalize.rs
@@ -1,20 +1,25 @@
 //! Deeply normalize types using the old trait solver.
 
-use super::SelectionContext;
-use super::{project, with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer};
-use crate::error_reporting::traits::OverflowCause;
-use crate::error_reporting::traits::TypeErrCtxtOverflowExt;
-use crate::solve::NextSolverError;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_infer::infer::at::At;
 use rustc_infer::infer::InferOk;
-use rustc_infer::traits::FromSolverError;
-use rustc_infer::traits::PredicateObligation;
-use rustc_infer::traits::{Normalized, Obligation, TraitEngine};
+use rustc_infer::traits::{
+    FromSolverError, Normalized, Obligation, PredicateObligation, TraitEngine,
+};
 use rustc_macros::extension;
 use rustc_middle::traits::{ObligationCause, ObligationCauseCode, Reveal};
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeFolder};
-use rustc_middle::ty::{TypeFoldable, TypeSuperFoldable, TypeVisitable, TypeVisitableExt};
+use rustc_middle::ty::{
+    self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, TypeVisitableExt,
+};
+use tracing::{debug, instrument};
+
+use super::{
+    project, with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer,
+    SelectionContext,
+};
+use crate::error_reporting::traits::OverflowCause;
+use crate::error_reporting::InferCtxtErrorExt;
+use crate::solve::NextSolverError;
 
 #[extension(pub trait NormalizeExt<'tcx>)]
 impl<'tcx> At<'_, 'tcx> {
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index 1c6993bdd37..a3d5c530797 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -8,30 +8,29 @@
 //!   - not reference the erased type `Self` except for in this receiver;
 //!   - not have generic type parameters.
 
-use super::elaborate;
+use std::iter;
+use std::ops::ControlFlow;
 
-use crate::infer::TyCtxtInferExt;
-use crate::traits::query::evaluate_obligation::InferCtxtExt;
-use crate::traits::{self, Obligation, ObligationCause};
 use rustc_errors::FatalError;
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{
-    self, EarlyBinder, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeSuperVisitable,
-    TypeVisitable, TypeVisitor,
+    self, EarlyBinder, ExistentialPredicateStableCmpExt as _, GenericArgs, Ty, TyCtxt,
+    TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable,
+    TypeVisitableExt, TypeVisitor, Upcast,
 };
-use rustc_middle::ty::{GenericArg, GenericArgs};
-use rustc_middle::ty::{TypeVisitableExt, Upcast};
 use rustc_span::symbol::Symbol;
 use rustc_span::Span;
 use rustc_target::abi::Abi;
 use smallvec::SmallVec;
+use tracing::{debug, instrument};
 
-use std::iter;
-use std::ops::ControlFlow;
-
-pub use crate::traits::{MethodViolationCode, ObjectSafetyViolation};
+use super::elaborate;
+use crate::infer::TyCtxtInferExt;
+use crate::traits::query::evaluate_obligation::InferCtxtExt;
+pub use crate::traits::ObjectSafetyViolation;
+use crate::traits::{util, MethodViolationCode, Obligation, ObligationCause};
 
 /// Returns the object safety violations that affect HIR ty lowering.
 ///
@@ -187,15 +186,20 @@ fn predicates_reference_self(
 ) -> SmallVec<[Span; 1]> {
     let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_def_id));
     let predicates = if supertraits_only {
-        tcx.explicit_super_predicates_of(trait_def_id)
+        tcx.explicit_super_predicates_of(trait_def_id).skip_binder()
     } else {
-        tcx.predicates_of(trait_def_id)
+        tcx.predicates_of(trait_def_id).predicates
     };
     predicates
-        .predicates
         .iter()
         .map(|&(predicate, sp)| (predicate.instantiate_supertrait(tcx, trait_ref), sp))
-        .filter_map(|predicate| predicate_references_self(tcx, predicate))
+        .filter_map(|(clause, sp)| {
+            // Super predicates cannot allow self projections, since they're
+            // impossible to make into existential bounds without eager resolution
+            // or something.
+            // e.g. `trait A: B<Item = Self::Assoc>`.
+            predicate_references_self(tcx, trait_def_id, clause, sp, AllowSelfProjections::No)
+        })
         .collect()
 }
 
@@ -204,20 +208,25 @@ fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span
         .in_definition_order()
         .filter(|item| item.kind == ty::AssocKind::Type)
         .flat_map(|item| tcx.explicit_item_bounds(item.def_id).iter_identity_copied())
-        .filter_map(|c| predicate_references_self(tcx, c))
+        .filter_map(|(clause, sp)| {
+            // Item bounds *can* have self projections, since they never get
+            // their self type erased.
+            predicate_references_self(tcx, trait_def_id, clause, sp, AllowSelfProjections::Yes)
+        })
         .collect()
 }
 
 fn predicate_references_self<'tcx>(
     tcx: TyCtxt<'tcx>,
-    (predicate, sp): (ty::Clause<'tcx>, Span),
+    trait_def_id: DefId,
+    predicate: ty::Clause<'tcx>,
+    sp: Span,
+    allow_self_projections: AllowSelfProjections,
 ) -> Option<Span> {
-    let self_ty = tcx.types.self_param;
-    let has_self_ty = |arg: &GenericArg<'tcx>| arg.walk().any(|arg| arg == self_ty.into());
     match predicate.kind().skip_binder() {
         ty::ClauseKind::Trait(ref data) => {
             // In the case of a trait predicate, we can skip the "self" type.
-            data.trait_ref.args[1..].iter().any(has_self_ty).then_some(sp)
+            data.trait_ref.args[1..].iter().any(|&arg| contains_illegal_self_type_reference(tcx, trait_def_id, arg, allow_self_projections)).then_some(sp)
         }
         ty::ClauseKind::Projection(ref data) => {
             // And similarly for projections. This should be redundant with
@@ -235,9 +244,9 @@ fn predicate_references_self<'tcx>(
             //
             // This is ALT2 in issue #56288, see that for discussion of the
             // possible alternatives.
-            data.projection_term.args[1..].iter().any(has_self_ty).then_some(sp)
+            data.projection_term.args[1..].iter().any(|&arg| contains_illegal_self_type_reference(tcx, trait_def_id, arg, allow_self_projections)).then_some(sp)
         }
-        ty::ClauseKind::ConstArgHasType(_ct, ty) => has_self_ty(&ty.into()).then_some(sp),
+        ty::ClauseKind::ConstArgHasType(_ct, ty) => contains_illegal_self_type_reference(tcx, trait_def_id, ty, allow_self_projections).then_some(sp),
 
         ty::ClauseKind::WellFormed(..)
         | ty::ClauseKind::TypeOutlives(..)
@@ -257,9 +266,8 @@ fn super_predicates_have_non_lifetime_binders(
         return SmallVec::new();
     }
     tcx.explicit_super_predicates_of(trait_def_id)
-        .predicates
-        .iter()
-        .filter_map(|(pred, span)| pred.has_non_region_bound_vars().then_some(*span))
+        .iter_identity_copied()
+        .filter_map(|(pred, span)| pred.has_non_region_bound_vars().then_some(span))
         .collect()
 }
 
@@ -383,7 +391,12 @@ fn virtual_call_violations_for_method<'tcx>(
     let mut errors = Vec::new();
 
     for (i, &input_ty) in sig.skip_binder().inputs().iter().enumerate().skip(1) {
-        if contains_illegal_self_type_reference(tcx, trait_def_id, sig.rebind(input_ty)) {
+        if contains_illegal_self_type_reference(
+            tcx,
+            trait_def_id,
+            sig.rebind(input_ty),
+            AllowSelfProjections::Yes,
+        ) {
             let span = if let Some(hir::Node::TraitItem(hir::TraitItem {
                 kind: hir::TraitItemKind::Fn(sig, _),
                 ..
@@ -396,7 +409,12 @@ fn virtual_call_violations_for_method<'tcx>(
             errors.push(MethodViolationCode::ReferencesSelfInput(span));
         }
     }
-    if contains_illegal_self_type_reference(tcx, trait_def_id, sig.output()) {
+    if contains_illegal_self_type_reference(
+        tcx,
+        trait_def_id,
+        sig.output(),
+        AllowSelfProjections::Yes,
+    ) {
         errors.push(MethodViolationCode::ReferencesSelfOutput);
     }
     if let Some(code) = contains_illegal_impl_trait_in_trait(tcx, method.def_id, sig.output()) {
@@ -482,7 +500,7 @@ fn virtual_call_violations_for_method<'tcx>(
             return false;
         }
 
-        contains_illegal_self_type_reference(tcx, trait_def_id, pred)
+        contains_illegal_self_type_reference(tcx, trait_def_id, pred, AllowSelfProjections::Yes)
     }) {
         errors.push(MethodViolationCode::WhereClauseReferencesSelf);
     }
@@ -494,7 +512,7 @@ fn virtual_call_violations_for_method<'tcx>(
 ///
 /// This check is outlined from the object safety check to avoid cycles with
 /// layout computation, which relies on knowing whether methods are object safe.
-pub fn check_receiver_correct<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, method: ty::AssocItem) {
+fn check_receiver_correct<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, method: ty::AssocItem) {
     if !is_vtable_safe_method(tcx, trait_def_id, method) {
         return;
     }
@@ -711,124 +729,184 @@ fn receiver_is_dispatchable<'tcx>(
     infcx.predicate_must_hold_modulo_regions(&obligation)
 }
 
+#[derive(Copy, Clone)]
+enum AllowSelfProjections {
+    Yes,
+    No,
+}
+
+/// This is somewhat subtle. In general, we want to forbid
+/// references to `Self` in the argument and return types,
+/// since the value of `Self` is erased. However, there is one
+/// exception: it is ok to reference `Self` in order to access
+/// an associated type of the current trait, since we retain
+/// the value of those associated types in the object type
+/// itself.
+///
+/// ```rust,ignore (example)
+/// trait SuperTrait {
+///     type X;
+/// }
+///
+/// trait Trait : SuperTrait {
+///     type Y;
+///     fn foo(&self, x: Self) // bad
+///     fn foo(&self) -> Self // bad
+///     fn foo(&self) -> Option<Self> // bad
+///     fn foo(&self) -> Self::Y // OK, desugars to next example
+///     fn foo(&self) -> <Self as Trait>::Y // OK
+///     fn foo(&self) -> Self::X // OK, desugars to next example
+///     fn foo(&self) -> <Self as SuperTrait>::X // OK
+/// }
+/// ```
+///
+/// However, it is not as simple as allowing `Self` in a projected
+/// type, because there are illegal ways to use `Self` as well:
+///
+/// ```rust,ignore (example)
+/// trait Trait : SuperTrait {
+///     ...
+///     fn foo(&self) -> <Self as SomeOtherTrait>::X;
+/// }
+/// ```
+///
+/// Here we will not have the type of `X` recorded in the
+/// object type, and we cannot resolve `Self as SomeOtherTrait`
+/// without knowing what `Self` is.
 fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
     tcx: TyCtxt<'tcx>,
     trait_def_id: DefId,
     value: T,
+    allow_self_projections: AllowSelfProjections,
 ) -> bool {
-    // This is somewhat subtle. In general, we want to forbid
-    // references to `Self` in the argument and return types,
-    // since the value of `Self` is erased. However, there is one
-    // exception: it is ok to reference `Self` in order to access
-    // an associated type of the current trait, since we retain
-    // the value of those associated types in the object type
-    // itself.
-    //
-    // ```rust
-    // trait SuperTrait {
-    //     type X;
-    // }
-    //
-    // trait Trait : SuperTrait {
-    //     type Y;
-    //     fn foo(&self, x: Self) // bad
-    //     fn foo(&self) -> Self // bad
-    //     fn foo(&self) -> Option<Self> // bad
-    //     fn foo(&self) -> Self::Y // OK, desugars to next example
-    //     fn foo(&self) -> <Self as Trait>::Y // OK
-    //     fn foo(&self) -> Self::X // OK, desugars to next example
-    //     fn foo(&self) -> <Self as SuperTrait>::X // OK
-    // }
-    // ```
-    //
-    // However, it is not as simple as allowing `Self` in a projected
-    // type, because there are illegal ways to use `Self` as well:
-    //
-    // ```rust
-    // trait Trait : SuperTrait {
-    //     ...
-    //     fn foo(&self) -> <Self as SomeOtherTrait>::X;
-    // }
-    // ```
-    //
-    // Here we will not have the type of `X` recorded in the
-    // object type, and we cannot resolve `Self as SomeOtherTrait`
-    // without knowing what `Self` is.
-
-    struct IllegalSelfTypeVisitor<'tcx> {
-        tcx: TyCtxt<'tcx>,
-        trait_def_id: DefId,
-        supertraits: Option<Vec<DefId>>,
-    }
+    value
+        .visit_with(&mut IllegalSelfTypeVisitor {
+            tcx,
+            trait_def_id,
+            supertraits: None,
+            allow_self_projections,
+        })
+        .is_break()
+}
+
+struct IllegalSelfTypeVisitor<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    trait_def_id: DefId,
+    supertraits: Option<Vec<ty::TraitRef<'tcx>>>,
+    allow_self_projections: AllowSelfProjections,
+}
 
-    impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IllegalSelfTypeVisitor<'tcx> {
-        type Result = ControlFlow<()>;
+impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IllegalSelfTypeVisitor<'tcx> {
+    type Result = ControlFlow<()>;
 
-        fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
-            match t.kind() {
-                ty::Param(_) => {
-                    if t == self.tcx.types.self_param {
-                        ControlFlow::Break(())
-                    } else {
-                        ControlFlow::Continue(())
-                    }
-                }
-                ty::Alias(ty::Projection, ref data)
-                    if self.tcx.is_impl_trait_in_trait(data.def_id) =>
-                {
-                    // We'll deny these later in their own pass
+    fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
+        match t.kind() {
+            ty::Param(_) => {
+                if t == self.tcx.types.self_param {
+                    ControlFlow::Break(())
+                } else {
                     ControlFlow::Continue(())
                 }
-                ty::Alias(ty::Projection, ref data) => {
-                    // This is a projected type `<Foo as SomeTrait>::X`.
-
-                    // Compute supertraits of current trait lazily.
-                    if self.supertraits.is_none() {
-                        let trait_ref =
-                            ty::Binder::dummy(ty::TraitRef::identity(self.tcx, self.trait_def_id));
-                        self.supertraits = Some(
-                            traits::supertraits(self.tcx, trait_ref).map(|t| t.def_id()).collect(),
-                        );
-                    }
+            }
+            ty::Alias(ty::Projection, ref data) if self.tcx.is_impl_trait_in_trait(data.def_id) => {
+                // We'll deny these later in their own pass
+                ControlFlow::Continue(())
+            }
+            ty::Alias(ty::Projection, ref data) => {
+                match self.allow_self_projections {
+                    AllowSelfProjections::Yes => {
+                        // This is a projected type `<Foo as SomeTrait>::X`.
+
+                        // Compute supertraits of current trait lazily.
+                        if self.supertraits.is_none() {
+                            self.supertraits = Some(
+                                util::supertraits(
+                                    self.tcx,
+                                    ty::Binder::dummy(ty::TraitRef::identity(
+                                        self.tcx,
+                                        self.trait_def_id,
+                                    )),
+                                )
+                                .map(|trait_ref| {
+                                    self.tcx.erase_regions(
+                                        self.tcx.instantiate_bound_regions_with_erased(trait_ref),
+                                    )
+                                })
+                                .collect(),
+                            );
+                        }
 
-                    // Determine whether the trait reference `Foo as
-                    // SomeTrait` is in fact a supertrait of the
-                    // current trait. In that case, this type is
-                    // legal, because the type `X` will be specified
-                    // in the object type. Note that we can just use
-                    // direct equality here because all of these types
-                    // are part of the formal parameter listing, and
-                    // hence there should be no inference variables.
-                    let is_supertrait_of_current_trait = self
-                        .supertraits
-                        .as_ref()
-                        .unwrap()
-                        .contains(&data.trait_ref(self.tcx).def_id);
-
-                    // only walk contained types if it's not a super trait
-                    if is_supertrait_of_current_trait {
-                        ControlFlow::Continue(())
-                    } else {
-                        t.super_visit_with(self) // POSSIBLY reporting an error
+                        // Determine whether the trait reference `Foo as
+                        // SomeTrait` is in fact a supertrait of the
+                        // current trait. In that case, this type is
+                        // legal, because the type `X` will be specified
+                        // in the object type. Note that we can just use
+                        // direct equality here because all of these types
+                        // are part of the formal parameter listing, and
+                        // hence there should be no inference variables.
+                        let is_supertrait_of_current_trait =
+                            self.supertraits.as_ref().unwrap().contains(
+                                &data.trait_ref(self.tcx).fold_with(
+                                    &mut EraseEscapingBoundRegions {
+                                        tcx: self.tcx,
+                                        binder: ty::INNERMOST,
+                                    },
+                                ),
+                            );
+
+                        // only walk contained types if it's not a super trait
+                        if is_supertrait_of_current_trait {
+                            ControlFlow::Continue(())
+                        } else {
+                            t.super_visit_with(self) // POSSIBLY reporting an error
+                        }
                     }
+                    AllowSelfProjections::No => t.super_visit_with(self),
                 }
-                _ => t.super_visit_with(self), // walk contained types, if any
             }
+            _ => t.super_visit_with(self),
         }
+    }
 
-        fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result {
-            // Constants can only influence object safety if they are generic and reference `Self`.
-            // This is only possible for unevaluated constants, so we walk these here.
-            self.tcx.expand_abstract_consts(ct).super_visit_with(self)
-        }
+    fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result {
+        // Constants can only influence object safety if they are generic and reference `Self`.
+        // This is only possible for unevaluated constants, so we walk these here.
+        self.tcx.expand_abstract_consts(ct).super_visit_with(self)
     }
+}
 
-    value
-        .visit_with(&mut IllegalSelfTypeVisitor { tcx, trait_def_id, supertraits: None })
-        .is_break()
+struct EraseEscapingBoundRegions<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    binder: ty::DebruijnIndex,
+}
+
+impl<'tcx> TypeFolder<TyCtxt<'tcx>> for EraseEscapingBoundRegions<'tcx> {
+    fn cx(&self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+
+    fn fold_binder<T>(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T>
+    where
+        T: TypeFoldable<TyCtxt<'tcx>>,
+    {
+        self.binder.shift_in(1);
+        let result = t.super_fold_with(self);
+        self.binder.shift_out(1);
+        result
+    }
+
+    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        if let ty::ReBound(debruijn, _) = *r
+            && debruijn < self.binder
+        {
+            r
+        } else {
+            self.tcx.lifetimes.re_erased
+        }
+    }
 }
 
-pub fn contains_illegal_impl_trait_in_trait<'tcx>(
+fn contains_illegal_impl_trait_in_trait<'tcx>(
     tcx: TyCtxt<'tcx>,
     fn_def_id: DefId,
     ty: ty::Binder<'tcx, Ty<'tcx>>,
@@ -852,7 +930,7 @@ pub fn contains_illegal_impl_trait_in_trait<'tcx>(
     })
 }
 
-pub fn provide(providers: &mut Providers) {
+pub(crate) fn provide(providers: &mut Providers) {
     *providers = Providers {
         object_safety_violations,
         is_object_safe,
diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
index 1dc2ebfaa7a..ee1b0fc6f2e 100644
--- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
@@ -1,15 +1,16 @@
-use crate::infer::InferCtxt;
-use crate::traits::{ObligationCause, ObligationCtxt};
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_infer::infer::resolve::OpportunisticRegionResolver;
 use rustc_infer::infer::InferOk;
 use rustc_macros::extension;
 use rustc_middle::infer::canonical::{OriginalQueryValues, QueryRegionConstraints};
 use rustc_middle::span_bug;
+pub use rustc_middle::traits::query::OutlivesBound;
 use rustc_middle::ty::{self, ParamEnv, Ty, TypeFolder, TypeVisitableExt};
 use rustc_span::def_id::LocalDefId;
+use tracing::{debug, instrument};
 
-pub use rustc_middle::traits::query::OutlivesBound;
+use crate::infer::InferCtxt;
+use crate::traits::{ObligationCause, ObligationCtxt};
 
 pub type BoundsCompat<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
 pub type Bounds<'a, 'tcx: 'a> = impl Iterator<Item = OutlivesBound<'tcx>> + 'a;
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 1d7a0515044..4702fd866c1 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -2,29 +2,6 @@
 
 use std::ops::ControlFlow;
 
-use super::specialization_graph;
-use super::translate_args;
-use super::util;
-use super::MismatchedProjectionTypes;
-use super::Obligation;
-use super::ObligationCause;
-use super::PredicateObligation;
-use super::Selection;
-use super::SelectionContext;
-use super::SelectionError;
-use super::{Normalized, NormalizedTerm, ProjectionCacheEntry, ProjectionCacheKey};
-use rustc_infer::traits::ObligationCauseCode;
-use rustc_middle::traits::BuiltinImplSource;
-use rustc_middle::traits::ImplSource;
-use rustc_middle::traits::ImplSourceUserDefinedData;
-use rustc_middle::{bug, span_bug};
-
-use crate::errors::InherentProjectionNormalizationOverflow;
-use crate::infer::{BoundRegionConversionTime, InferOk};
-use crate::traits::normalize::normalize_with_depth;
-use crate::traits::normalize::normalize_with_depth_to;
-use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
-use crate::traits::select::ProjectionMatchesProjection;
 use rustc_data_structures::sso::SsoHashSet;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::ErrorGuaranteed;
@@ -32,13 +9,27 @@ use rustc_hir::def::DefKind;
 use rustc_hir::lang_items::LangItem;
 use rustc_infer::infer::resolve::OpportunisticRegionResolver;
 use rustc_infer::infer::DefineOpaqueTypes;
+use rustc_infer::traits::ObligationCauseCode;
 use rustc_middle::traits::select::OverflowError;
+pub use rustc_middle::traits::Reveal;
+use rustc_middle::traits::{BuiltinImplSource, ImplSource, ImplSourceUserDefinedData};
 use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable, TypeVisitableExt};
 use rustc_middle::ty::{self, Term, Ty, TyCtxt, Upcast};
+use rustc_middle::{bug, span_bug};
 use rustc_span::symbol::sym;
+use tracing::{debug, instrument};
 
-pub use rustc_middle::traits::Reveal;
+use super::{
+    specialization_graph, translate_args, util, MismatchedProjectionTypes, Normalized,
+    NormalizedTerm, Obligation, ObligationCause, PredicateObligation, ProjectionCacheEntry,
+    ProjectionCacheKey, Selection, SelectionContext, SelectionError,
+};
+use crate::errors::InherentProjectionNormalizationOverflow;
+use crate::infer::{BoundRegionConversionTime, InferOk};
+use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to};
+use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
+use crate::traits::select::ProjectionMatchesProjection;
 
 pub type PolyProjectionObligation<'tcx> = Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>;
 
@@ -1120,7 +1111,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                         | ty::Error(_) => false,
                     }
                 } else if tcx.is_lang_item(trait_ref.def_id, LangItem::PointeeTrait) {
-                    let tail = selcx.tcx().struct_tail_with_normalize(
+                    let tail = selcx.tcx().struct_tail_raw(
                         self_ty,
                         |ty| {
                             // We throw away any obligations we get from this, since we normalize
@@ -1159,10 +1150,10 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                         | ty::Never
                         // Extern types have unit metadata, according to RFC 2850
                         | ty::Foreign(_)
-                        // If returned by `struct_tail_without_normalization` this is a unit struct
+                        // If returned by `struct_tail` this is a unit struct
                         // without any fields, or not a struct, and therefore is Sized.
                         | ty::Adt(..)
-                        // If returned by `struct_tail_without_normalization` this is the empty tuple.
+                        // If returned by `struct_tail` this is the empty tuple.
                         | ty::Tuple(..)
                         // Integers and floats are always Sized, and so have unit type metadata.
                         | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true,
@@ -1202,6 +1193,12 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                             false
                         }
                     }
+                } else if tcx.trait_is_auto(trait_ref.def_id) {
+                    tcx.dcx().span_delayed_bug(
+                        tcx.def_span(obligation.predicate.def_id),
+                        "associated types not allowed on auto traits",
+                    );
+                    false
                 } else {
                     bug!("unexpected builtin trait with associated type: {trait_ref:?}")
                 }
@@ -1640,7 +1637,7 @@ fn confirm_fn_pointer_candidate<'cx, 'tcx>(
             .generics_of(def_id)
             .host_effect_index
             .map_or(tcx.consts.true_, |idx| args.const_at(idx)),
-        ty::FnPtr(_) => tcx.consts.true_,
+        ty::FnPtr(..) => tcx.consts.true_,
         _ => unreachable!("only expected FnPtr or FnDef in `confirm_fn_pointer_candidate`"),
     };
 
diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
index 7dc051e6fe9..7036df02465 100644
--- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
@@ -1,11 +1,12 @@
-use crate::traits::query::normalize::QueryNormalizeExt;
-use crate::traits::query::NoSolution;
-use crate::traits::{Normalized, ObligationCause, ObligationCtxt};
-
 use rustc_data_structures::fx::FxHashSet;
 use rustc_middle::traits::query::{DropckConstraint, DropckOutlivesResult};
 use rustc_middle::ty::{self, EarlyBinder, ParamEnvAnd, Ty, TyCtxt};
 use rustc_span::{Span, DUMMY_SP};
+use tracing::{debug, instrument};
+
+use crate::traits::query::normalize::QueryNormalizeExt;
+use crate::traits::query::NoSolution;
+use crate::traits::{Normalized, ObligationCause, ObligationCtxt};
 
 /// This returns true if the type `ty` is "trivial" for
 /// dropck-outlives -- that is, if it doesn't require any types to
@@ -33,7 +34,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
         | ty::Float(_)
         | ty::Never
         | ty::FnDef(..)
-        | ty::FnPtr(_)
+        | ty::FnPtr(..)
         | ty::Char
         | ty::CoroutineWitness(..)
         | ty::RawPtr(_, _)
@@ -42,8 +43,15 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
         | ty::Foreign(..)
         | ty::Error(_) => true,
 
-        // `T is PAT`, `[T; N]`, and `[T]` have same properties as T.
-        ty::Pat(ty, _) | ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, *ty),
+        // `T is PAT` and `[T]` have same properties as T.
+        ty::Pat(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, *ty),
+        ty::Array(ty, size) => {
+            // Empty array never has a dtor. See issue #110288.
+            match size.try_to_target_usize(tcx) {
+                Some(0) => true,
+                _ => trivial_dropck_outlives(tcx, *ty),
+            }
+        }
 
         // (T1..Tn) and closures have same properties as T1..Tn --
         // check if *all* of them are trivial.
@@ -217,7 +225,7 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
         | ty::RawPtr(..)
         | ty::Ref(..)
         | ty::FnDef(..)
-        | ty::FnPtr(_)
+        | ty::FnPtr(..)
         | ty::CoroutineWitness(..) => {
             // these types never have a destructor
         }
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index c11e86abef8..f1b524d1325 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -2,26 +2,27 @@
 //! which folds deeply, invoking the underlying
 //! `normalize_canonicalized_projection_ty` query when it encounters projections.
 
-use crate::error_reporting::traits::OverflowCause;
-use crate::error_reporting::traits::TypeErrCtxtOverflowExt;
-use crate::infer::at::At;
-use crate::infer::canonical::OriginalQueryValues;
-use crate::infer::{InferCtxt, InferOk};
-use crate::traits::normalize::needs_normalization;
-use crate::traits::Normalized;
-use crate::traits::{BoundVarReplacer, PlaceholderReplacer, ScrubbedTraitError};
-use crate::traits::{ObligationCause, PredicateObligation, Reveal};
 use rustc_data_structures::sso::SsoHashMap;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_macros::extension;
+pub use rustc_middle::traits::query::NormalizationResult;
 use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable};
 use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt};
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitor};
 use rustc_span::DUMMY_SP;
+use tracing::{debug, info, instrument};
 
 use super::NoSolution;
-
-pub use rustc_middle::traits::query::NormalizationResult;
+use crate::error_reporting::traits::OverflowCause;
+use crate::error_reporting::InferCtxtErrorExt;
+use crate::infer::at::At;
+use crate::infer::canonical::OriginalQueryValues;
+use crate::infer::{InferCtxt, InferOk};
+use crate::traits::normalize::needs_normalization;
+use crate::traits::{
+    BoundVarReplacer, Normalized, ObligationCause, PlaceholderReplacer, PredicateObligation,
+    Reveal, ScrubbedTraitError,
+};
 
 #[extension(pub trait QueryNormalizeExt<'tcx>)]
 impl<'cx, 'tcx> At<'cx, 'tcx> {
@@ -333,14 +334,14 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
             return Ok(constant);
         }
 
-        let constant = constant.try_super_fold_with(self)?;
-        debug!(?constant, ?self.param_env);
-        Ok(crate::traits::with_replaced_escaping_bound_vars(
+        let constant = crate::traits::with_replaced_escaping_bound_vars(
             self.infcx,
             &mut self.universes,
             constant,
             |constant| constant.normalize(self.infcx.tcx, self.param_env),
-        ))
+        );
+        debug!(?constant, ?self.param_env);
+        constant.try_super_fold_with(self)
     }
 
     #[inline]
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs
index aca16950223..2b3c11d4c48 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs
@@ -1,13 +1,14 @@
-use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
-use crate::traits::ObligationCtxt;
 use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
 use rustc_infer::traits::Obligation;
+pub use rustc_middle::traits::query::type_op::AscribeUserType;
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
 use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, UserArgs, UserSelfTy, UserType};
-
-pub use rustc_middle::traits::query::type_op::AscribeUserType;
 use rustc_span::{Span, DUMMY_SP};
+use tracing::{debug, instrument};
+
+use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
+use crate::traits::ObligationCtxt;
 
 impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> {
     type QueryResponse = ();
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
index d533e69a4fa..6efc2d07843 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
@@ -1,14 +1,16 @@
-use crate::infer::canonical::query_response;
-use crate::infer::InferCtxt;
-use crate::traits::query::type_op::TypeOpOutput;
-use crate::traits::ObligationCtxt;
+use std::fmt;
+
 use rustc_errors::ErrorGuaranteed;
 use rustc_infer::infer::region_constraints::RegionConstraintData;
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::ty::{TyCtxt, TypeFoldable};
 use rustc_span::Span;
+use tracing::info;
 
-use std::fmt;
+use crate::infer::canonical::query_response;
+use crate::infer::InferCtxt;
+use crate::traits::query::type_op::TypeOpOutput;
+use crate::traits::ObligationCtxt;
 
 pub struct CustomTypeOp<F> {
     closure: F,
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs
deleted file mode 100644
index 57e649f3e43..00000000000
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
-use crate::traits::ObligationCtxt;
-use rustc_middle::traits::query::NoSolution;
-use rustc_middle::traits::ObligationCause;
-use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
-
-pub use rustc_middle::traits::query::type_op::Eq;
-
-impl<'tcx> super::QueryTypeOp<'tcx> for Eq<'tcx> {
-    type QueryResponse = ();
-
-    fn try_fast_path(
-        _tcx: TyCtxt<'tcx>,
-        key: &ParamEnvAnd<'tcx, Eq<'tcx>>,
-    ) -> Option<Self::QueryResponse> {
-        if key.value.a == key.value.b { Some(()) } else { None }
-    }
-
-    fn perform_query(
-        tcx: TyCtxt<'tcx>,
-        canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
-    ) -> Result<CanonicalQueryResponse<'tcx, ()>, NoSolution> {
-        tcx.type_op_eq(canonicalized)
-    }
-
-    fn perform_locally_with_next_solver(
-        ocx: &ObligationCtxt<'_, 'tcx>,
-        key: ParamEnvAnd<'tcx, Self>,
-    ) -> Result<Self::QueryResponse, NoSolution> {
-        ocx.eq(&ObligationCause::dummy(), key.param_env, key.value.a, key.value.b)?;
-        Ok(())
-    }
-}
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
index 8525215a3bc..a493615a1df 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
@@ -1,7 +1,3 @@
-use crate::traits::query::NoSolution;
-use crate::traits::wf;
-use crate::traits::ObligationCtxt;
-
 use rustc_infer::infer::canonical::Canonical;
 use rustc_infer::infer::resolve::OpportunisticRegionResolver;
 use rustc_infer::traits::query::OutlivesBound;
@@ -13,6 +9,10 @@ use rustc_span::def_id::CRATE_DEF_ID;
 use rustc_span::DUMMY_SP;
 use rustc_type_ir::outlives::{push_outlives_components, Component};
 use smallvec::{smallvec, SmallVec};
+use tracing::debug;
+
+use crate::traits::query::NoSolution;
+use crate::traits::{wf, ObligationCtxt};
 
 #[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)]
 pub struct ImpliedOutlivesBounds<'tcx> {
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
index c1b1bfd300b..a765de92afd 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
@@ -1,8 +1,5 @@
-use crate::infer::canonical::{
-    Canonical, CanonicalQueryResponse, OriginalQueryValues, QueryRegionConstraints,
-};
-use crate::infer::{InferCtxt, InferOk};
-use crate::traits::{ObligationCause, ObligationCtxt};
+use std::fmt;
+
 use rustc_errors::ErrorGuaranteed;
 use rustc_infer::infer::canonical::Certainty;
 use rustc_infer::traits::PredicateObligation;
@@ -10,16 +7,19 @@ use rustc_middle::traits::query::NoSolution;
 use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
 use rustc_span::Span;
-use std::fmt;
+
+use crate::infer::canonical::{
+    Canonical, CanonicalQueryResponse, OriginalQueryValues, QueryRegionConstraints,
+};
+use crate::infer::{InferCtxt, InferOk};
+use crate::traits::{ObligationCause, ObligationCtxt};
 
 pub mod ascribe_user_type;
 pub mod custom;
-pub mod eq;
 pub mod implied_outlives_bounds;
 pub mod normalize;
 pub mod outlives;
 pub mod prove_predicate;
-pub mod subtype;
 
 pub use rustc_middle::traits::query::type_op::*;
 
@@ -168,44 +168,12 @@ where
         // collecting region constraints via `region_constraints`.
         let (mut output, _) = scrape_region_constraints(
             infcx,
-            |_ocx| {
-                let (output, ei, mut obligations, _) =
+            |ocx| {
+                let (output, ei, obligations, _) =
                     Q::fully_perform_into(self, infcx, &mut region_constraints, span)?;
                 error_info = ei;
 
-                // Typically, instantiating NLL query results does not
-                // create obligations. However, in some cases there
-                // are unresolved type variables, and unify them *can*
-                // create obligations. In that case, we have to go
-                // fulfill them. We do this via a (recursive) query.
-                while !obligations.is_empty() {
-                    trace!("{:#?}", obligations);
-                    let mut progress = false;
-                    for obligation in std::mem::take(&mut obligations) {
-                        let obligation = infcx.resolve_vars_if_possible(obligation);
-                        match ProvePredicate::fully_perform_into(
-                            obligation.param_env.and(ProvePredicate::new(obligation.predicate)),
-                            infcx,
-                            &mut region_constraints,
-                            span,
-                        ) {
-                            Ok(((), _, new, certainty)) => {
-                                obligations.extend(new);
-                                progress = true;
-                                if let Certainty::Ambiguous = certainty {
-                                    obligations.push(obligation);
-                                }
-                            }
-                            Err(_) => obligations.push(obligation),
-                        }
-                    }
-                    if !progress {
-                        infcx.dcx().span_bug(
-                            span,
-                            format!("ambiguity processing {obligations:?} from {self:?}"),
-                        );
-                    }
-                }
+                ocx.register_obligations(obligations);
                 Ok(output)
             },
             "fully_perform",
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs
index e9948bf1f71..41c34f6da29 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs
@@ -1,12 +1,13 @@
-use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
-use crate::traits::ObligationCtxt;
+use std::fmt;
+
+pub use rustc_middle::traits::query::type_op::Normalize;
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
-use std::fmt;
 
-pub use rustc_middle::traits::query::type_op::Normalize;
+use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
+use crate::traits::ObligationCtxt;
 
 impl<'tcx, T> super::QueryTypeOp<'tcx> for Normalize<T>
 where
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs
index 3e7aa52dcfe..49d324fa62e 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs
@@ -1,11 +1,12 @@
+use rustc_macros::{HashStable, TypeFoldable, TypeVisitable};
+use rustc_middle::traits::query::{DropckOutlivesResult, NoSolution};
+use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt};
+
 use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
 use crate::traits::query::dropck_outlives::{
     compute_dropck_outlives_inner, trivial_dropck_outlives,
 };
 use crate::traits::ObligationCtxt;
-use rustc_macros::{HashStable, TypeFoldable, TypeVisitable};
-use rustc_middle::traits::query::{DropckOutlivesResult, NoSolution};
-use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt};
 
 #[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)]
 pub struct DropckOutlives<'tcx> {
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs
index 63289746f5e..d6687c762c3 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs
@@ -1,11 +1,12 @@
-use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
-use crate::traits::ObligationCtxt;
+use rustc_hir::LangItem;
 use rustc_infer::traits::Obligation;
+pub use rustc_middle::traits::query::type_op::ProvePredicate;
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt};
 
-pub use rustc_middle::traits::query::type_op::ProvePredicate;
+use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
+use crate::traits::ObligationCtxt;
 
 impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> {
     type QueryResponse = ();
@@ -20,8 +21,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> {
         // such cases.
         if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) =
             key.value.predicate.kind().skip_binder()
-            && let Some(sized_def_id) = tcx.lang_items().sized_trait()
-            && trait_ref.def_id() == sized_def_id
+            && tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized)
             && trait_ref.self_ty().is_trivially_sized(tcx)
         {
             return Some(());
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs
deleted file mode 100644
index ae11b0825bd..00000000000
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-use crate::infer::canonical::{Canonical, CanonicalQueryResponse};
-use crate::traits::ObligationCtxt;
-use rustc_middle::traits::query::NoSolution;
-use rustc_middle::traits::ObligationCause;
-use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
-
-pub use rustc_middle::traits::query::type_op::Subtype;
-
-impl<'tcx> super::QueryTypeOp<'tcx> for Subtype<'tcx> {
-    type QueryResponse = ();
-
-    fn try_fast_path(_tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option<()> {
-        if key.value.sub == key.value.sup { Some(()) } else { None }
-    }
-
-    fn perform_query(
-        tcx: TyCtxt<'tcx>,
-        canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>,
-    ) -> Result<CanonicalQueryResponse<'tcx, ()>, NoSolution> {
-        tcx.type_op_subtype(canonicalized)
-    }
-
-    fn perform_locally_with_next_solver(
-        ocx: &ObligationCtxt<'_, 'tcx>,
-        key: ParamEnvAnd<'tcx, Self>,
-    ) -> Result<Self::QueryResponse, NoSolution> {
-        ocx.sub(&ObligationCause::dummy(), key.param_env, key.value.sub, key.value.sup)?;
-        Ok(())
-    }
-}
diff --git a/compiler/rustc_trait_selection/src/traits/select/_match.rs b/compiler/rustc_trait_selection/src/traits/select/_match.rs
index 50d8e96aaf9..f77f83ed447 100644
--- a/compiler/rustc_trait_selection/src/traits/select/_match.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/_match.rs
@@ -3,7 +3,7 @@ use rustc_infer::infer::relate::{
 };
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::{self, InferConst, Ty, TyCtxt};
-use tracing::{debug, instrument};
+use tracing::instrument;
 
 /// A type "A" *matches* "B" if the fresh types in B could be
 /// instantiated with values so as to make it equal to A. Matching is
@@ -21,22 +21,18 @@ use tracing::{debug, instrument};
 /// Like subtyping, matching is really a binary relation, so the only
 /// important thing about the result is Ok/Err. Also, matching never
 /// affects any type variables or unification state.
-pub struct MatchAgainstFreshVars<'tcx> {
+pub(crate) struct MatchAgainstFreshVars<'tcx> {
     tcx: TyCtxt<'tcx>,
 }
 
 impl<'tcx> MatchAgainstFreshVars<'tcx> {
-    pub fn new(tcx: TyCtxt<'tcx>) -> MatchAgainstFreshVars<'tcx> {
+    pub(crate) fn new(tcx: TyCtxt<'tcx>) -> MatchAgainstFreshVars<'tcx> {
         MatchAgainstFreshVars { tcx }
     }
 }
 
 impl<'tcx> TypeRelation<TyCtxt<'tcx>> for MatchAgainstFreshVars<'tcx> {
-    fn tag(&self) -> &'static str {
-        "MatchAgainstFreshVars"
-    }
-
-    fn tcx(&self) -> TyCtxt<'tcx> {
+    fn cx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
 
@@ -50,7 +46,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for MatchAgainstFreshVars<'tcx> {
         self.relate(a, b)
     }
 
-    #[instrument(skip(self), level = "debug")]
+    #[instrument(skip(self), level = "trace")]
     fn regions(
         &mut self,
         a: ty::Region<'tcx>,
@@ -59,7 +55,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for MatchAgainstFreshVars<'tcx> {
         Ok(a)
     }
 
-    #[instrument(skip(self), level = "debug")]
+    #[instrument(skip(self), level = "trace")]
     fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
         if a == b {
             return Ok(a);
@@ -77,18 +73,18 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for MatchAgainstFreshVars<'tcx> {
                 Err(TypeError::Sorts(ExpectedFound::new(true, a, b)))
             }
 
-            (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(Ty::new_error(self.tcx(), guar)),
+            (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(Ty::new_error(self.cx(), guar)),
 
             _ => structurally_relate_tys(self, a, b),
         }
     }
 
+    #[instrument(skip(self), level = "trace")]
     fn consts(
         &mut self,
         a: ty::Const<'tcx>,
         b: ty::Const<'tcx>,
     ) -> RelateResult<'tcx, ty::Const<'tcx>> {
-        debug!("{}.consts({:?}, {:?})", self.tag(), a, b);
         if a == b {
             return Ok(a);
         }
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index 4c3d833b0f9..96faa5236b1 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -12,20 +12,18 @@ use hir::def_id::DefId;
 use hir::LangItem;
 use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
 use rustc_hir as hir;
-use rustc_infer::traits::ObligationCause;
-use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError};
+use rustc_infer::traits::{Obligation, ObligationCause, PolyTraitObligation, SelectionError};
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
 use rustc_middle::ty::{self, ToPolyTraitRef, Ty, TypeVisitableExt};
 use rustc_middle::{bug, span_bug};
+use tracing::{debug, instrument, trace};
 
+use super::SelectionCandidate::*;
+use super::{BuiltinImplConditions, SelectionCandidateSet, SelectionContext, TraitObligationStack};
 use crate::traits;
 use crate::traits::query::evaluate_obligation::InferCtxtExt;
 use crate::traits::util;
 
-use super::BuiltinImplConditions;
-use super::SelectionCandidate::*;
-use super::{SelectionCandidateSet, SelectionContext, TraitObligationStack};
-
 impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     #[instrument(skip(self, stack), level = "debug")]
     pub(super) fn assemble_candidates<'o>(
@@ -470,8 +468,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 }
                 candidates.vec.push(AsyncClosureCandidate);
             }
-            ty::FnDef(..) | ty::FnPtr(..) => {
-                candidates.vec.push(AsyncClosureCandidate);
+            // Provide an impl, but only for suitable `fn` pointers.
+            ty::FnPtr(sig_tys, hdr) => {
+                if sig_tys.with(hdr).is_fn_trait_compatible() {
+                    candidates.vec.push(AsyncClosureCandidate);
+                }
+            }
+            // Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396).
+            ty::FnDef(def_id, _) => {
+                let tcx = self.tcx();
+                if tcx.fn_sig(def_id).skip_binder().is_fn_trait_compatible()
+                    && tcx.codegen_fn_attrs(def_id).target_features.is_empty()
+                {
+                    candidates.vec.push(AsyncClosureCandidate);
+                }
             }
             _ => {}
         }
@@ -526,8 +536,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 candidates.ambiguous = true; // Could wind up being a fn() type.
             }
             // Provide an impl, but only for suitable `fn` pointers.
-            ty::FnPtr(sig) => {
-                if sig.is_fn_trait_compatible() {
+            ty::FnPtr(sig_tys, hdr) => {
+                if sig_tys.with(hdr).is_fn_trait_compatible() {
                     candidates
                         .vec
                         .push(FnPointerCandidate { fn_host_effect: self.tcx().consts.true_ });
@@ -772,7 +782,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     );
                 }
 
-                ty::Alias(ty::Opaque, _) => {
+                ty::Alias(ty::Opaque, alias) => {
                     if candidates.vec.iter().any(|c| matches!(c, ProjectionCandidate(_))) {
                         // We do not generate an auto impl candidate for `impl Trait`s which already
                         // reference our auto trait.
@@ -787,6 +797,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                         // We do not emit auto trait candidates for opaque types in coherence.
                         // Doing so can result in weird dependency cycles.
                         candidates.ambiguous = true;
+                    } else if self.infcx.can_define_opaque_ty(alias.def_id) {
+                        // We do not emit auto trait candidates for opaque types in their defining scope, as
+                        // we need to know the hidden type first, which we can't reliably know within the defining
+                        // scope.
+                        candidates.ambiguous = true;
                     } else {
                         candidates.vec.push(AutoImplCandidate)
                     }
@@ -805,7 +820,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 | ty::RawPtr(_, _)
                 | ty::Ref(..)
                 | ty::FnDef(..)
-                | ty::FnPtr(_)
+                | ty::FnPtr(..)
                 | ty::Closure(..)
                 | ty::CoroutineClosure(..)
                 | ty::Coroutine(..)
@@ -1193,7 +1208,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             | ty::RawPtr(_, _)
             | ty::Ref(..)
             | ty::FnDef(..)
-            | ty::FnPtr(_)
+            | ty::FnPtr(..)
             | ty::Never
             | ty::Foreign(_)
             | ty::Array(..)
@@ -1276,7 +1291,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             | ty::Ref(_, _, _)
             | ty::FnDef(_, _)
             | ty::Pat(_, _)
-            | ty::FnPtr(_)
+            | ty::FnPtr(..)
             | ty::Dynamic(_, _, _)
             | ty::Closure(..)
             | ty::CoroutineClosure(..)
@@ -1325,7 +1340,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let self_ty = self.infcx.resolve_vars_if_possible(obligation.self_ty());
 
         match self_ty.skip_binder().kind() {
-            ty::FnPtr(_) => candidates.vec.push(BuiltinCandidate { has_nested: false }),
+            ty::FnPtr(..) => candidates.vec.push(BuiltinCandidate { has_nested: false }),
             ty::Bool
             | ty::Char
             | ty::Int(_)
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 9508a3e8e15..daf0700ec0c 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -7,20 +7,24 @@
 //! [rustc dev guide]:
 //! https://rustc-dev-guide.rust-lang.org/traits/resolution.html#confirmation
 
+use std::iter;
+use std::ops::ControlFlow;
+
 use rustc_ast::Mutability;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_hir::lang_items::LangItem;
-use rustc_infer::infer::HigherRankedType;
-use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
+use rustc_infer::infer::{DefineOpaqueTypes, HigherRankedType, InferOk};
 use rustc_infer::traits::ObligationCauseCode;
 use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData};
 use rustc_middle::ty::{
-    self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, TraitPredicate, Ty,
-    TyCtxt, Upcast,
+    self, GenericArgs, GenericArgsRef, GenericParamDefKind, ToPolyTraitRef, Ty, TyCtxt, Upcast,
 };
 use rustc_middle::{bug, span_bug};
 use rustc_span::def_id::DefId;
+use tracing::{debug, instrument};
 
+use super::SelectionCandidate::{self, *};
+use super::{BuiltinImplConditions, 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::{
@@ -29,13 +33,6 @@ use crate::traits::{
     SignatureMismatch, TraitNotObjectSafe, TraitObligation, Unimplemented,
 };
 
-use super::BuiltinImplConditions;
-use super::SelectionCandidate::{self, *};
-use super::SelectionContext;
-
-use std::iter;
-use std::ops::ControlFlow;
-
 impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     #[instrument(level = "debug", skip(self))]
     pub(super) fn confirm_candidate(
@@ -295,90 +292,120 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         &mut self,
         obligation: &PolyTraitObligation<'tcx>,
     ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
-        use rustc_transmute::{Answer, Condition};
-        #[instrument(level = "debug", skip(tcx, obligation, predicate))]
+        use rustc_transmute::{Answer, Assume, Condition};
+
+        /// Generate sub-obligations for reference-to-reference transmutations.
+        fn reference_obligations<'tcx>(
+            tcx: TyCtxt<'tcx>,
+            obligation: &PolyTraitObligation<'tcx>,
+            (src_lifetime, src_ty, src_mut): (ty::Region<'tcx>, Ty<'tcx>, Mutability),
+            (dst_lifetime, dst_ty, dst_mut): (ty::Region<'tcx>, Ty<'tcx>, Mutability),
+            assume: Assume,
+        ) -> Vec<PredicateObligation<'tcx>> {
+            let make_transmute_obl = |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),
+                    ],
+                );
+                Obligation::with_depth(
+                    tcx,
+                    obligation.cause.clone(),
+                    obligation.recursion_depth + 1,
+                    obligation.param_env,
+                    obligation.predicate.rebind(trait_ref),
+                )
+            };
+
+            let make_freeze_obl = |ty| {
+                let trait_ref = ty::TraitRef::new(
+                    tcx,
+                    tcx.require_lang_item(LangItem::Freeze, None),
+                    [ty::GenericArg::from(ty)],
+                );
+                Obligation::with_depth(
+                    tcx,
+                    obligation.cause.clone(),
+                    obligation.recursion_depth + 1,
+                    obligation.param_env,
+                    trait_ref,
+                )
+            };
+
+            let make_outlives_obl = |target, region| {
+                let outlives = ty::OutlivesPredicate(target, region);
+                Obligation::with_depth(
+                    tcx,
+                    obligation.cause.clone(),
+                    obligation.recursion_depth + 1,
+                    obligation.param_env,
+                    obligation.predicate.rebind(outlives),
+                )
+            };
+
+            // Given a transmutation from `&'a (mut) Src` and `&'dst (mut) Dst`,
+            // it is always the case that `Src` must be transmutable into `Dst`,
+            // and that that `'src` must outlive `'dst`.
+            let mut obls = vec![make_transmute_obl(src_ty, dst_ty)];
+            if !assume.lifetimes {
+                obls.push(make_outlives_obl(src_lifetime, dst_lifetime));
+            }
+
+            // Given a transmutation from `&Src`, both `Src` and `Dst` must be
+            // `Freeze`, otherwise, using the transmuted value could lead to
+            // data races.
+            if src_mut == Mutability::Not {
+                obls.extend([make_freeze_obl(src_ty), make_freeze_obl(dst_ty)])
+            }
+
+            // Given a transmutation into `&'dst mut Dst`, it also must be the
+            // case that `Dst` is transmutable into `Src`. For example,
+            // transmuting bool -> u8 is OK as long as you can't update that u8
+            // to be > 1, because you could later transmute the u8 back to a
+            // bool and get undefined behavior. It also must be the case that
+            // `'dst` lives exactly as long as `'src`.
+            if dst_mut == Mutability::Mut {
+                obls.push(make_transmute_obl(dst_ty, src_ty));
+                if !assume.lifetimes {
+                    obls.push(make_outlives_obl(dst_lifetime, src_lifetime));
+                }
+            }
+
+            obls
+        }
+
+        /// 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>,
-            predicate: TraitPredicate<'tcx>,
             cond: Condition<rustc_transmute::layout::rustc::Ref<'tcx>>,
+            assume: Assume,
         ) -> Vec<PredicateObligation<'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, predicate, cond))
+                    .flat_map(|cond| flatten_answer_tree(tcx, obligation, cond, assume))
                     .collect(),
-                Condition::IfTransmutable { src, dst } => {
-                    let transmute_trait = obligation.predicate.def_id();
-                    let assume_const = predicate.trait_ref.args.const_at(2);
-                    let make_transmute_obl = |from_ty, to_ty| {
-                        let trait_ref = ty::TraitRef::new(
-                            tcx,
-                            transmute_trait,
-                            [
-                                ty::GenericArg::from(to_ty),
-                                ty::GenericArg::from(from_ty),
-                                ty::GenericArg::from(assume_const),
-                            ],
-                        );
-                        Obligation::with_depth(
-                            tcx,
-                            obligation.cause.clone(),
-                            obligation.recursion_depth + 1,
-                            obligation.param_env,
-                            trait_ref,
-                        )
-                    };
-
-                    let make_freeze_obl = |ty| {
-                        let trait_ref = ty::TraitRef::new(
-                            tcx,
-                            tcx.require_lang_item(LangItem::Freeze, None),
-                            [ty::GenericArg::from(ty)],
-                        );
-                        Obligation::with_depth(
-                            tcx,
-                            obligation.cause.clone(),
-                            obligation.recursion_depth + 1,
-                            obligation.param_env,
-                            trait_ref,
-                        )
-                    };
-
-                    let mut obls = vec![];
-
-                    // If the source is a shared reference, it must be `Freeze`;
-                    // otherwise, transmuting could lead to data races.
-                    if src.mutability == Mutability::Not {
-                        obls.extend([make_freeze_obl(src.ty), make_freeze_obl(dst.ty)])
-                    }
-
-                    // If Dst is mutable, check bidirectionally.
-                    // For example, transmuting bool -> u8 is OK as long as you can't update that u8
-                    // to be > 1, because you could later transmute the u8 back to a bool and get UB.
-                    match dst.mutability {
-                        Mutability::Not => obls.push(make_transmute_obl(src.ty, dst.ty)),
-                        Mutability::Mut => obls.extend([
-                            make_transmute_obl(src.ty, dst.ty),
-                            make_transmute_obl(dst.ty, src.ty),
-                        ]),
-                    }
-
-                    obls
-                }
+                Condition::IfTransmutable { src, dst } => reference_obligations(
+                    tcx,
+                    obligation,
+                    (src.lifetime, src.ty, src.mutability),
+                    (dst.lifetime, dst.ty, dst.mutability),
+                    assume,
+                ),
             }
         }
 
-        // We erase regions here because transmutability calls layout queries,
-        // which does not handle inference regions and doesn't particularly
-        // care about other regions. Erasing late-bound regions is equivalent
-        // to instantiating the binder with placeholders then erasing those
-        // placeholder regions.
-        let predicate = self
-            .tcx()
-            .erase_regions(self.tcx().instantiate_bound_regions_with_erased(obligation.predicate));
+        let predicate = obligation.predicate.skip_binder();
 
         let Some(assume) = rustc_transmute::Assume::from_const(
             self.infcx.tcx,
@@ -390,6 +417,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         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);
         let maybe_transmutable = transmute_env.is_transmutable(
@@ -400,7 +428,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         let fully_flattened = match maybe_transmutable {
             Answer::No(_) => Err(Unimplemented)?,
-            Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation, predicate, cond),
+            Answer::If(cond) => flatten_answer_tree(self.tcx(), obligation, cond, assume),
             Answer::Yes => vec![],
         };
 
@@ -573,21 +601,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         // Check supertraits hold. This is so that their associated type bounds
         // will be checked in the code below.
-        for super_trait in tcx
+        for (supertrait, _) in tcx
             .explicit_super_predicates_of(trait_predicate.def_id())
-            .instantiate(tcx, trait_predicate.trait_ref.args)
-            .predicates
-            .into_iter()
+            .iter_instantiated_copied(tcx, trait_predicate.trait_ref.args)
         {
-            let normalized_super_trait = normalize_with_depth_to(
+            let normalized_supertrait = normalize_with_depth_to(
                 self,
                 obligation.param_env,
                 obligation.cause.clone(),
                 obligation.recursion_depth + 1,
-                super_trait,
+                supertrait,
                 &mut nested,
             );
-            nested.push(obligation.with(tcx, normalized_super_trait));
+            nested.push(obligation.with(tcx, normalized_supertrait));
         }
 
         let assoc_types: Vec<_> = tcx
@@ -1401,7 +1427,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 | ty::RawPtr(_, _)
                 | ty::Ref(..)
                 | ty::FnDef(..)
-                | ty::FnPtr(_)
+                | ty::FnPtr(..)
                 | ty::Never
                 | ty::Foreign(_) => {}
 
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 7a93f59f163..241c3a3d141 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -2,31 +2,12 @@
 //!
 //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html#selection
 
-use self::EvaluationResult::*;
-use self::SelectionCandidate::*;
-
-use super::coherence::{self, Conflict};
-use super::const_evaluatable;
-use super::project;
-use super::project::ProjectionTermObligation;
-use super::util;
-use super::util::closure_trait_ref_and_return_type;
-use super::wf;
-use super::{
-    ImplDerivedCause, Normalized, Obligation, ObligationCause, ObligationCauseCode, Overflow,
-    PolyTraitObligation, PredicateObligation, Selection, SelectionError, SelectionResult,
-    TraitQueryMode,
-};
+use std::cell::{Cell, RefCell};
+use std::fmt::{self, Display};
+use std::ops::ControlFlow;
+use std::{cmp, iter};
 
-use crate::error_reporting::traits::TypeErrCtxtOverflowExt;
-use crate::infer::{InferCtxt, InferCtxtExt, InferOk, TypeFreshener};
-use crate::solve::InferCtxtSelectExt as _;
-use crate::traits::normalize::normalize_with_depth;
-use crate::traits::normalize::normalize_with_depth_to;
-use crate::traits::project::ProjectAndUnifyResult;
-use crate::traits::project::ProjectionCacheKeyExt;
-use crate::traits::ProjectionCacheKey;
-use crate::traits::Unimplemented;
+use hir::def::DefKind;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::{Diag, EmissionGuarantee};
@@ -34,31 +15,40 @@ use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::LangItem;
 use rustc_infer::infer::relate::TypeRelation;
-use rustc_infer::infer::BoundRegionConversionTime;
-use rustc_infer::infer::BoundRegionConversionTime::HigherRankedType;
+use rustc_infer::infer::BoundRegionConversionTime::{self, HigherRankedType};
 use rustc_infer::infer::DefineOpaqueTypes;
 use rustc_infer::traits::TraitObligation;
 use rustc_middle::bug;
-use rustc_middle::dep_graph::dep_kinds;
-use rustc_middle::dep_graph::DepNodeIndex;
+use rustc_middle::dep_graph::{dep_kinds, DepNodeIndex};
 use rustc_middle::mir::interpret::ErrorHandled;
+pub use rustc_middle::traits::select::*;
 use rustc_middle::ty::abstract_const::NotConstEvaluatable;
 use rustc_middle::ty::error::TypeErrorToStringExt;
-use rustc_middle::ty::print::PrintTraitRefExt as _;
-use rustc_middle::ty::GenericArgsRef;
-use rustc_middle::ty::{self, PolyProjectionPredicate, Upcast};
-use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
+use rustc_middle::ty::print::{with_no_trimmed_paths, PrintTraitRefExt as _};
+use rustc_middle::ty::{
+    self, GenericArgsRef, PolyProjectionPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitableExt,
+    Upcast,
+};
 use rustc_span::symbol::sym;
 use rustc_span::Symbol;
+use tracing::{debug, instrument, trace};
 
-use std::cell::{Cell, RefCell};
-use std::cmp;
-use std::fmt::{self, Display};
-use std::iter;
-use std::ops::ControlFlow;
-
-pub use rustc_middle::traits::select::*;
-use rustc_middle::ty::print::with_no_trimmed_paths;
+use self::EvaluationResult::*;
+use self::SelectionCandidate::*;
+use super::coherence::{self, Conflict};
+use super::project::ProjectionTermObligation;
+use super::util::closure_trait_ref_and_return_type;
+use super::{
+    const_evaluatable, project, util, wf, ImplDerivedCause, Normalized, Obligation,
+    ObligationCause, ObligationCauseCode, Overflow, PolyTraitObligation, PredicateObligation,
+    Selection, SelectionError, SelectionResult, TraitQueryMode,
+};
+use crate::error_reporting::InferCtxtErrorExt;
+use crate::infer::{InferCtxt, InferCtxtExt, InferOk, TypeFreshener};
+use crate::solve::InferCtxtSelectExt as _;
+use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to};
+use crate::traits::project::{ProjectAndUnifyResult, ProjectionCacheKeyExt};
+use crate::traits::{ProjectionCacheKey, Unimplemented};
 
 mod _match;
 mod candidate_assembly;
@@ -1001,7 +991,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     };
 
                     match self.infcx.at(&obligation.cause, obligation.param_env).eq(
-                        // Only really excercised by generic_const_exprs
+                        // Only really exercised by generic_const_exprs
                         DefineOpaqueTypes::Yes,
                         ct_ty,
                         ty,
@@ -1498,7 +1488,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             return false;
         }
 
-        // Avoid using the master cache during coherence and just rely
+        // Avoid using the global cache during coherence and just rely
         // on the local cache. This effectively disables caching
         // during coherence. It is really just a simplification to
         // avoid us having to fear that coherence results "pollute"
@@ -1509,6 +1499,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             return false;
         }
 
+        // Avoid using the global cache when we're defining opaque types
+        // as their hidden type may impact the result of candidate selection.
+        if !self.infcx.defining_opaque_types().is_empty() {
+            return false;
+        }
+
         // Otherwise, we can use the global cache.
         true
     }
@@ -2118,7 +2114,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             | ty::Bool
             | ty::Float(_)
             | ty::FnDef(..)
-            | ty::FnPtr(_)
+            | ty::FnPtr(..)
             | ty::RawPtr(..)
             | ty::Char
             | ty::Ref(..)
@@ -2175,7 +2171,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
         use self::BuiltinImplConditions::{Ambiguous, None, Where};
 
         match *self_ty.kind() {
-            ty::FnDef(..) | ty::FnPtr(_) | ty::Error(_) => Where(ty::Binder::dummy(Vec::new())),
+            ty::FnDef(..) | ty::FnPtr(..) | ty::Error(_) => Where(ty::Binder::dummy(Vec::new())),
 
             ty::Uint(_)
             | ty::Int(_)
@@ -2256,8 +2252,21 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 }
             }
 
-            // FIXME(async_closures): These are never clone, for now.
-            ty::CoroutineClosure(_, _) => None,
+            ty::CoroutineClosure(_, args) => {
+                // (*) binder moved here
+                let ty = self.infcx.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty());
+                if let ty::Infer(ty::TyVar(_)) = ty.kind() {
+                    // Not yet resolved.
+                    Ambiguous
+                } else {
+                    Where(
+                        obligation
+                            .predicate
+                            .rebind(args.as_coroutine_closure().upvar_tys().to_vec()),
+                    )
+                }
+            }
+
             // `Copy` and `Clone` are automatically implemented for an anonymous adt
             // if all of its fields are `Copy` and `Clone`
             ty::Adt(adt, args) if adt.is_anonymous() => {
@@ -2324,7 +2333,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             | ty::Bool
             | ty::Float(_)
             | ty::FnDef(..)
-            | ty::FnPtr(_)
+            | ty::FnPtr(..)
             | ty::Error(_)
             | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
             | ty::Never
@@ -2380,13 +2389,17 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             }
 
             ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
-                // We can resolve the `impl Trait` to its concrete type,
-                // which enforces a DAG between the functions requiring
-                // the auto trait bounds in question.
-                match self.tcx().type_of_opaque(def_id) {
-                    Ok(ty) => t.rebind(vec![ty.instantiate(self.tcx(), args)]),
-                    Err(_) => {
-                        return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id));
+                if self.infcx.can_define_opaque_ty(def_id) {
+                    unreachable!()
+                } else {
+                    // We can resolve the `impl Trait` to its concrete type,
+                    // which enforces a DAG between the functions requiring
+                    // the auto trait bounds in question.
+                    match self.tcx().type_of_opaque(def_id) {
+                        Ok(ty) => t.rebind(vec![ty.instantiate(self.tcx(), args)]),
+                        Err(_) => {
+                            return Err(SelectionError::OpaqueTypeAutoTraitLeakageUnknown(def_id));
+                        }
                     }
                 }
             }
@@ -2787,6 +2800,26 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             });
         }
 
+        // Register any outlives obligations from the trait here, cc #124336.
+        if matches!(tcx.def_kind(def_id), DefKind::Impl { of_trait: true }) {
+            for clause in tcx.impl_super_outlives(def_id).iter_instantiated(tcx, args) {
+                let clause = normalize_with_depth_to(
+                    self,
+                    param_env,
+                    cause.clone(),
+                    recursion_depth,
+                    clause,
+                    &mut obligations,
+                );
+                obligations.push(Obligation {
+                    cause: cause.clone(),
+                    recursion_depth,
+                    param_env,
+                    predicate: clause.as_predicate(),
+                });
+            }
+        }
+
         obligations
     }
 }
@@ -3094,7 +3127,7 @@ impl<'o, 'tcx> fmt::Debug for TraitObligationStack<'o, 'tcx> {
     }
 }
 
-pub enum ProjectionMatchesProjection {
+pub(crate) enum ProjectionMatchesProjection {
     Yes,
     Ambiguous,
     No,
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index 3c33d13567d..7337b59f870 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -10,28 +10,27 @@
 //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html
 
 pub mod specialization_graph;
+
+use rustc_data_structures::fx::FxIndexSet;
+use rustc_errors::codes::*;
+use rustc_errors::{Diag, EmissionGuarantee};
+use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_infer::infer::DefineOpaqueTypes;
+use rustc_middle::bug;
+use rustc_middle::query::LocalCrate;
 use rustc_middle::ty::print::PrintTraitRefExt as _;
+use rustc_middle::ty::{self, GenericArgsRef, ImplSubject, Ty, TyCtxt, TypeVisitableExt};
+use rustc_session::lint::builtin::{COHERENCE_LEAK_CHECK, ORDER_DEPENDENT_TRAIT_OBJECTS};
+use rustc_span::{sym, ErrorGuaranteed, Span, DUMMY_SP};
 use specialization_graph::GraphExt;
+use tracing::{debug, instrument};
 
+use super::{util, SelectionContext};
 use crate::error_reporting::traits::to_pretty_impl_header;
 use crate::errors::NegativePositiveConflict;
 use crate::infer::{InferCtxt, InferOk, TyCtxtInferExt};
 use crate::traits::select::IntercrateAmbiguityCause;
 use crate::traits::{coherence, FutureCompatOverlapErrorKind, ObligationCause, ObligationCtxt};
-use rustc_data_structures::fx::FxIndexSet;
-use rustc_errors::{codes::*, Diag, EmissionGuarantee};
-use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_middle::bug;
-use rustc_middle::query::LocalCrate;
-use rustc_middle::ty::GenericArgsRef;
-use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitableExt};
-use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK;
-use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS;
-use rustc_span::{sym, ErrorGuaranteed, Span, DUMMY_SP};
-
-use super::util;
-use super::SelectionContext;
 
 /// Information pertinent to an overlapping impl error.
 #[derive(Debug)]
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs
index 90f2c7ad213..0fdaf40b136 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs
@@ -1,14 +1,14 @@
-use super::OverlapError;
-
-use crate::traits;
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def_id::DefId;
 use rustc_macros::extension;
 use rustc_middle::bug;
+pub use rustc_middle::traits::specialization_graph::*;
 use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams};
 use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
+use tracing::{debug, instrument};
 
-pub use rustc_middle::traits::specialization_graph::*;
+use super::OverlapError;
+use crate::traits;
 
 #[derive(Copy, Clone, Debug)]
 pub enum FutureCompatOverlapErrorKind {
diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs
deleted file mode 100644
index d4535db951e..00000000000
--- a/compiler/rustc_trait_selection/src/traits/structural_match.rs
+++ /dev/null
@@ -1,173 +0,0 @@
-use rustc_data_structures::fx::FxHashSet;
-use rustc_hir as hir;
-use rustc_middle::bug;
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
-use std::ops::ControlFlow;
-
-/// This method traverses the structure of `ty`, trying to find an
-/// instance of an ADT (i.e. struct or enum) that doesn't implement
-/// the structural-match traits, or a generic type parameter
-/// (which cannot be determined to be structural-match).
-///
-/// The "structure of a type" includes all components that would be
-/// considered when doing a pattern match on a constant of that
-/// type.
-///
-///  * This means this method descends into fields of structs/enums,
-///    and also descends into the inner type `T` of `&T` and `&mut T`
-///
-///  * The traversal doesn't dereference unsafe pointers (`*const T`,
-///    `*mut T`), and it does not visit the type arguments of an
-///    instantiated generic like `PhantomData<T>`.
-///
-/// The reason we do this search is Rust currently require all ADTs
-/// reachable from a constant's type to implement the
-/// structural-match traits, which essentially say that
-/// the implementation of `PartialEq::eq` behaves *equivalently* to a
-/// comparison against the unfolded structure.
-///
-/// For more background on why Rust has this requirement, and issues
-/// that arose when the requirement was not enforced completely, see
-/// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307.
-pub fn search_for_structural_match_violation<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    ty: Ty<'tcx>,
-) -> Option<Ty<'tcx>> {
-    ty.visit_with(&mut Search { tcx, seen: FxHashSet::default() }).break_value()
-}
-
-/// This implements the traversal over the structure of a given type to try to
-/// find instances of ADTs (specifically structs or enums) that do not implement
-/// `StructuralPartialEq`.
-struct Search<'tcx> {
-    tcx: TyCtxt<'tcx>,
-
-    /// Tracks ADTs previously encountered during search, so that
-    /// we will not recur on them again.
-    seen: FxHashSet<hir::def_id::DefId>,
-}
-
-impl<'tcx> Search<'tcx> {
-    fn type_marked_structural(&self, adt_ty: Ty<'tcx>) -> bool {
-        adt_ty.is_structural_eq_shallow(self.tcx)
-    }
-}
-
-impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for Search<'tcx> {
-    type Result = ControlFlow<Ty<'tcx>>;
-
-    fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
-        debug!("Search visiting ty: {:?}", ty);
-
-        let (adt_def, args) = match *ty.kind() {
-            ty::Adt(adt_def, args) => (adt_def, args),
-            ty::Param(_) => {
-                return ControlFlow::Break(ty);
-            }
-            ty::Dynamic(..) => {
-                return ControlFlow::Break(ty);
-            }
-            ty::Foreign(_) => {
-                return ControlFlow::Break(ty);
-            }
-            ty::Alias(..) => {
-                return ControlFlow::Break(ty);
-            }
-            ty::Closure(..) => {
-                return ControlFlow::Break(ty);
-            }
-            ty::CoroutineClosure(..) => {
-                return ControlFlow::Break(ty);
-            }
-            ty::Coroutine(..) | ty::CoroutineWitness(..) => {
-                return ControlFlow::Break(ty);
-            }
-            ty::FnDef(..) => {
-                // Types of formals and return in `fn(_) -> _` are also irrelevant;
-                // so we do not recur into them via `super_visit_with`
-                return ControlFlow::Continue(());
-            }
-            ty::Array(_, n)
-                if { n.try_eval_target_usize(self.tcx, ty::ParamEnv::reveal_all()) == Some(0) } =>
-            {
-                // rust-lang/rust#62336: ignore type of contents
-                // for empty array.
-                return ControlFlow::Continue(());
-            }
-            ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Str | ty::Never => {
-                // These primitive types are always structural match.
-                //
-                // `Never` is kind of special here, but as it is not inhabitable, this should be fine.
-                return ControlFlow::Continue(());
-            }
-
-            ty::FnPtr(..) => {
-                return ControlFlow::Continue(());
-            }
-
-            ty::RawPtr(..) => {
-                // structural-match ignores substructure of
-                // `*const _`/`*mut _`, so skip `super_visit_with`.
-                //
-                // For example, if you have:
-                // ```
-                // struct NonStructural;
-                // #[derive(PartialEq, Eq)]
-                // struct T(*const NonStructural);
-                // const C: T = T(std::ptr::null());
-                // ```
-                //
-                // Even though `NonStructural` does not implement `PartialEq`,
-                // structural equality on `T` does not recur into the raw
-                // pointer. Therefore, one can still use `C` in a pattern.
-                return ControlFlow::Continue(());
-            }
-
-            ty::Float(_) => {
-                return ControlFlow::Continue(());
-            }
-
-            ty::Pat(..) | ty::Array(..) | ty::Slice(_) | ty::Ref(..) | ty::Tuple(..) => {
-                // First check all contained types and then tell the caller to continue searching.
-                return ty.super_visit_with(self);
-            }
-            ty::Infer(_) | ty::Placeholder(_) | ty::Bound(..) => {
-                bug!("unexpected type during structural-match checking: {:?}", ty);
-            }
-            ty::Error(_) => {
-                // We still want to check other types after encountering an error,
-                // as this may still emit relevant errors.
-                return ControlFlow::Continue(());
-            }
-        };
-
-        if !self.seen.insert(adt_def.did()) {
-            debug!("Search already seen adt_def: {:?}", adt_def);
-            return ControlFlow::Continue(());
-        }
-
-        if !self.type_marked_structural(ty) {
-            debug!("Search found ty: {:?}", ty);
-            return ControlFlow::Break(ty);
-        }
-
-        // structural-match does not care about the
-        // instantiation of the generics in an ADT (it
-        // instead looks directly at its fields outside
-        // this match), so we skip super_visit_with.
-        //
-        // (Must not recur on args for `PhantomData<T>` cf
-        // rust-lang/rust#55028 and rust-lang/rust#55837; but also
-        // want to skip args when only uses of generic are
-        // behind unsafe pointers `*const T`/`*mut T`.)
-
-        // even though we skip super_visit_with, we must recur on
-        // fields of ADT.
-        let tcx = self.tcx;
-        adt_def.all_fields().map(|field| field.ty(tcx, args)).try_for_each(|field_ty| {
-            let ty = self.tcx.normalize_erasing_regions(ty::ParamEnv::empty(), field_ty);
-            debug!("structural-match ADT: field_ty={:?}, ty={:?}", field_ty, ty);
-            ty.visit_with(self)
-        })
-    }
-}
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index 951af4b0920..0fb13799e67 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -1,19 +1,20 @@
 use std::collections::BTreeMap;
 
-use super::NormalizeExt;
-use super::{ObligationCause, PredicateObligation, SelectionContext};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::Diag;
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::{InferCtxt, InferOk};
+pub use rustc_infer::traits::util::*;
 use rustc_middle::bug;
-use rustc_middle::ty::GenericArgsRef;
-use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitableExt, Upcast};
-use rustc_middle::ty::{TypeFoldable, TypeFolder, TypeSuperFoldable};
+use rustc_middle::ty::{
+    self, GenericArgsRef, ImplSubject, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
+    TypeVisitableExt, Upcast,
+};
 use rustc_span::Span;
 use smallvec::{smallvec, SmallVec};
+use tracing::debug;
 
-pub use rustc_infer::traits::util::*;
+use super::{NormalizeExt, ObligationCause, PredicateObligation, SelectionContext};
 
 ///////////////////////////////////////////////////////////////////////////
 // `TraitAliasExpander` iterator
@@ -131,7 +132,7 @@ impl<'tcx> TraitAliasExpander<'tcx> {
         let predicates = tcx.explicit_super_predicates_of(trait_ref.def_id());
         debug!(?predicates);
 
-        let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| {
+        let items = predicates.skip_binder().iter().rev().filter_map(|(pred, span)| {
             pred.instantiate_supertrait(tcx, trait_ref)
                 .as_trait_clause()
                 .map(|trait_ref| item.clone_and_push(trait_ref.map_bound(|t| t.trait_ref), *span))
@@ -168,7 +169,7 @@ impl<'tcx> Iterator for TraitAliasExpander<'tcx> {
 /// Instantiate all bound parameters of the impl subject with the given args,
 /// returning the resulting subject and all obligations that arise.
 /// The obligations are closed under normalization.
-pub fn impl_subject_and_oblig<'a, 'tcx>(
+pub(crate) fn impl_subject_and_oblig<'a, 'tcx>(
     selcx: &SelectionContext<'a, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     impl_def_id: DefId,
@@ -208,7 +209,7 @@ pub fn upcast_choices<'tcx>(
     supertraits(tcx, source_trait_ref).filter(|r| r.def_id() == target_trait_def_id).collect()
 }
 
-pub fn closure_trait_ref_and_return_type<'tcx>(
+pub(crate) fn closure_trait_ref_and_return_type<'tcx>(
     tcx: TyCtxt<'tcx>,
     fn_trait_def_id: DefId,
     self_ty: Ty<'tcx>,
@@ -237,7 +238,7 @@ pub fn closure_trait_ref_and_return_type<'tcx>(
     sig.map_bound(|sig| (trait_ref, sig.output()))
 }
 
-pub fn coroutine_trait_ref_and_outputs<'tcx>(
+pub(crate) fn coroutine_trait_ref_and_outputs<'tcx>(
     tcx: TyCtxt<'tcx>,
     fn_trait_def_id: DefId,
     self_ty: Ty<'tcx>,
@@ -248,7 +249,7 @@ pub fn coroutine_trait_ref_and_outputs<'tcx>(
     (trait_ref, sig.yield_ty, sig.return_ty)
 }
 
-pub fn future_trait_ref_and_outputs<'tcx>(
+pub(crate) fn future_trait_ref_and_outputs<'tcx>(
     tcx: TyCtxt<'tcx>,
     fn_trait_def_id: DefId,
     self_ty: Ty<'tcx>,
@@ -259,7 +260,7 @@ pub fn future_trait_ref_and_outputs<'tcx>(
     (trait_ref, sig.return_ty)
 }
 
-pub fn iterator_trait_ref_and_outputs<'tcx>(
+pub(crate) fn iterator_trait_ref_and_outputs<'tcx>(
     tcx: TyCtxt<'tcx>,
     iterator_def_id: DefId,
     self_ty: Ty<'tcx>,
@@ -270,7 +271,7 @@ pub fn iterator_trait_ref_and_outputs<'tcx>(
     (trait_ref, sig.yield_ty)
 }
 
-pub fn async_iterator_trait_ref_and_outputs<'tcx>(
+pub(crate) fn async_iterator_trait_ref_and_outputs<'tcx>(
     tcx: TyCtxt<'tcx>,
     async_iterator_def_id: DefId,
     self_ty: Ty<'tcx>,
@@ -286,7 +287,7 @@ pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool {
         && tcx.defaultness(assoc_item.container_id(tcx)).is_final()
 }
 
-pub enum TupleArgumentsFlag {
+pub(crate) enum TupleArgumentsFlag {
     Yes,
     No,
 }
diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs
index 8f56f9c0f3e..f525c17e053 100644
--- a/compiler/rustc_trait_selection/src/traits/vtable.rs
+++ b/compiler/rustc_trait_selection/src/traits/vtable.rs
@@ -1,16 +1,19 @@
-use crate::errors::DumpVTableEntries;
-use crate::traits::{impossible_predicates, is_vtable_safe_method};
+use std::fmt::Debug;
+use std::ops::ControlFlow;
+
 use rustc_hir::def_id::DefId;
 use rustc_infer::traits::util::PredicateSet;
 use rustc_middle::bug;
 use rustc_middle::query::Providers;
-use rustc_middle::ty::{self, GenericParamDefKind, Ty, TyCtxt, Upcast, VtblEntry};
-use rustc_middle::ty::{GenericArgs, TypeVisitableExt};
+use rustc_middle::ty::{
+    self, GenericArgs, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt, Upcast, VtblEntry,
+};
 use rustc_span::{sym, Span, DUMMY_SP};
 use smallvec::{smallvec, SmallVec};
+use tracing::debug;
 
-use std::fmt::Debug;
-use std::ops::ControlFlow;
+use crate::errors::DumpVTableEntries;
+use crate::traits::{impossible_predicates, is_vtable_safe_method};
 
 #[derive(Clone, Debug)]
 pub enum VtblSegment<'tcx> {
@@ -118,8 +121,7 @@ fn prepare_vtable_segments_inner<'tcx, T>(
 
             let mut direct_super_traits_iter = tcx
                 .explicit_super_predicates_of(inner_most_trait_ref.def_id())
-                .predicates
-                .into_iter()
+                .iter_identity_copied()
                 .filter_map(move |(pred, _)| {
                     pred.instantiate_supertrait(tcx, inner_most_trait_ref).as_trait_clause()
                 });
@@ -364,7 +366,9 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe
 }
 
 /// Given a `dyn Subtrait` and `dyn Supertrait` trait object, find the slot of
-/// // the trait vptr in the subtrait's vtable.
+/// the trait vptr in the subtrait's vtable.
+///
+/// A return value of `None` means that the original vtable can be reused.
 pub(crate) fn supertrait_vtable_slot<'tcx>(
     tcx: TyCtxt<'tcx>,
     key: (
@@ -373,20 +377,22 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
     ),
 ) -> Option<usize> {
     debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param());
-
     let (source, target) = key;
-    let ty::Dynamic(source, _, _) = *source.kind() else {
+
+    // If the target principal is `None`, we can just return `None`.
+    let ty::Dynamic(target, _, _) = *target.kind() else {
         bug!();
     };
-    let source_principal = tcx
-        .normalize_erasing_regions(ty::ParamEnv::reveal_all(), source.principal().unwrap())
+    let target_principal = tcx
+        .normalize_erasing_regions(ty::ParamEnv::reveal_all(), target.principal()?)
         .with_self_ty(tcx, tcx.types.trait_object_dummy_self);
 
-    let ty::Dynamic(target, _, _) = *target.kind() else {
+    // Given that we have a target principal, it is a bug for there not to be a source principal.
+    let ty::Dynamic(source, _, _) = *source.kind() else {
         bug!();
     };
-    let target_principal = tcx
-        .normalize_erasing_regions(ty::ParamEnv::reveal_all(), target.principal().unwrap())
+    let source_principal = tcx
+        .normalize_erasing_regions(ty::ParamEnv::reveal_all(), source.principal().unwrap())
         .with_self_ty(tcx, tcx.types.trait_object_dummy_self);
 
     let vtable_segment_callback = {
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index f071dc6c784..889e4ed7fcc 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -1,17 +1,19 @@
-use crate::infer::InferCtxt;
-use crate::traits;
+use std::iter;
+
 use rustc_hir as hir;
 use rustc_hir::lang_items::LangItem;
 use rustc_infer::traits::ObligationCauseCode;
 use rustc_middle::bug;
 use rustc_middle::ty::{
-    self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
+    self, GenericArg, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable,
+    TypeVisitable, TypeVisitableExt, TypeVisitor,
 };
-use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef};
 use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
 use rustc_span::{Span, DUMMY_SP};
+use tracing::{debug, instrument, trace};
 
-use std::iter;
+use crate::infer::InferCtxt;
+use crate::traits;
 /// Returns the set of obligations needed to make `arg` well-formed.
 /// If `arg` contains unresolved inference variables, this may include
 /// further WF obligations. However, if `arg` IS an unresolved
@@ -672,9 +674,21 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
                 self.require_sized(subty, ObligationCauseCode::SliceOrArrayElem);
             }
 
-            ty::Array(subty, _) => {
+            ty::Array(subty, len) => {
                 self.require_sized(subty, ObligationCauseCode::SliceOrArrayElem);
-                // Note that we handle the len is implicitly checked while walking `arg`.
+                // Note that the len being WF is implicitly checked while visiting.
+                // Here we just check that it's of type usize.
+                let cause = self.cause(ObligationCauseCode::Misc);
+                self.out.push(traits::Obligation::with_depth(
+                    tcx,
+                    cause,
+                    self.recursion_depth,
+                    self.param_env,
+                    ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(
+                        len,
+                        tcx.types.usize,
+                    ))),
+                ));
             }
 
             ty::Pat(subty, _) => {
@@ -799,7 +813,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
                 return upvars.visit_with(self);
             }
 
-            ty::FnPtr(_) => {
+            ty::FnPtr(..) => {
                 // Let the visitor iterate into the argument/return
                 // types appearing in the fn signature.
             }