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.rs116
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs89
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs9
-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.rs4
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/region.rs10
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs48
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs28
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs216
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs14
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs6
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs12
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs172
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs29
-rw-r--r--compiler/rustc_trait_selection/src/regions.rs80
-rw-r--r--compiler/rustc_trait_selection/src/solve/delegate.rs26
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs500
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs531
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/analyse.rs89
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalize.rs31
-rw-r--r--compiler/rustc_trait_selection/src/traits/auto_trait.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs59
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs113
-rw-r--r--compiler/rustc_trait_selection/src/traits/engine.rs16
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/misc.rs23
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs45
-rw-r--r--compiler/rustc_trait_selection/src/traits/outlives_bounds.rs44
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs96
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs27
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs21
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs60
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs117
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs177
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs17
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/vtable.rs195
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs78
45 files changed, 1695 insertions, 1858 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
index bcb6ac13b8f..091773009e9 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
@@ -196,7 +196,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
 
         self.tcx
-            .explicit_item_super_predicates(def_id)
+            .explicit_item_self_bounds(def_id)
             .iter_instantiated_copied(self.tcx, args)
             .find_map(|(predicate, _)| {
                 predicate
@@ -435,7 +435,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>,
         terr: TypeError<'tcx>,
         param_env: Option<ParamEnv<'tcx>>,
-        path: &mut Option<PathBuf>,
     ) {
         match *cause.code() {
             ObligationCauseCode::Pattern {
@@ -458,7 +457,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             format!("this is an iterator with items of type `{}`", args.type_at(0)),
                         );
                     } else {
-                        let expected_ty = self.tcx.short_ty_string(expected_ty, path);
+                        let expected_ty = self.tcx.short_string(expected_ty, err.long_ty_path());
                         err.span_label(span, format!("this expression has type `{expected_ty}`"));
                     }
                 }
@@ -1392,9 +1391,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         mut values: Option<ty::ParamEnvAnd<'tcx, ValuePairs<'tcx>>>,
         terr: TypeError<'tcx>,
         prefer_label: bool,
+        override_span: Option<Span>,
     ) {
-        let span = cause.span;
-
+        // We use `override_span` when we want the error to point at a `Span` other than
+        // `cause.span`. This is used in E0271, when a closure is passed in where the return type
+        // isn't what was expected. We want to point at the closure's return type (or expression),
+        // instead of the expression where the closure is passed as call argument.
+        let span = override_span.unwrap_or(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 {
@@ -1541,7 +1544,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         (false, Mismatch::Fixed("existential projection"))
                     }
                 };
-                let Some(vals) = self.values_str(values, cause) else {
+                let Some(vals) = self.values_str(values, cause, diag.long_ty_path()) else {
                     // Derived error. Cancel the emitter.
                     // NOTE(eddyb) this was `.cancel()`, but `diag`
                     // is borrowed, so we can't fully defuse it.
@@ -1596,9 +1599,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             return;
         }
 
-        let mut path = None;
-        if let Some((expected, found, p)) = expected_found {
-            path = p;
+        if let Some((expected, found)) = expected_found {
             let (expected_label, found_label, exp_found) = match exp_found {
                 Mismatch::Variable(ef) => (
                     ef.expected.prefix_string(self.tcx),
@@ -1721,32 +1722,42 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     }
                 }
                 TypeError::Sorts(values) => {
-                    let extra = expected == found;
+                    let extra = expected == found
+                        // Ensure that we don't ever say something like
+                        // expected `impl Trait` (opaque type `impl Trait`)
+                        //    found `impl Trait` (opaque type `impl Trait`)
+                        && values.expected.sort_string(self.tcx)
+                            != values.found.sort_string(self.tcx);
                     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!(
+                            DiagStyledString::normal(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!(
+                            DiagStyledString::normal(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(),
+                        (true, _) => {
+                            let mut s = DiagStyledString::normal(" (");
+                            s.push_highlighted(ty.sort_string(self.tcx));
+                            s.push_normal(")");
+                            s
+                        }
+                        (false, _) => DiagStyledString::normal(""),
                     };
                     if !(values.expected.is_simple_text() && values.found.is_simple_text())
                         || (exp_found.is_some_and(|ef| {
@@ -1763,23 +1774,23 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             }
                         }))
                     {
-                        if let Some(ExpectedFound { found: found_ty, .. }) = exp_found {
+                        if let Some(ExpectedFound { found: found_ty, .. }) = exp_found
+                            && !self.tcx.ty_is_opaque_future(found_ty)
+                        {
                             // `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),
-                                );
-                            }
+                            diag.note_expected_found_extra(
+                                &expected_label,
+                                expected,
+                                &found_label,
+                                found,
+                                sort_string(values.expected),
+                                sort_string(values.found),
+                            );
                         }
                     }
                 }
@@ -1844,7 +1855,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 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_function_pointers(cause, span, &exp_found, terr, diag);
                 self.suggest_turning_stmt_into_expr(cause, &exp_found, diag);
             }
         }
@@ -1874,11 +1885,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
         // It reads better to have the error origin as the final
         // thing.
-        self.note_error_origin(diag, cause, exp_found, terr, param_env, &mut path);
-        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");
-        }
+        self.note_error_origin(diag, cause, exp_found, terr, param_env);
 
         debug!(?diag);
     }
@@ -1887,6 +1894,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         &self,
         trace: &TypeTrace<'tcx>,
         terr: TypeError<'tcx>,
+        path: &mut Option<PathBuf>,
     ) -> Vec<TypeErrorAdditionalDiags> {
         let mut suggestions = Vec::new();
         let span = trace.cause.span;
@@ -1965,7 +1973,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         })
         | ObligationCauseCode::BlockTailExpression(.., source)) = code
             && let hir::MatchSource::TryDesugar(_) = source
-            && let Some((expected_ty, found_ty, _)) = self.values_str(trace.values, &trace.cause)
+            && let Some((expected_ty, found_ty)) = self.values_str(trace.values, &trace.cause, path)
         {
             suggestions.push(TypeErrorAdditionalDiags::TryCannotConvert {
                 found: found_ty.content(),
@@ -2023,14 +2031,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             _ => None,
         };
         if let Some(tykind) = tykind
-            && let hir::TyKind::Array(_, length) = tykind
-            && let Some((scalar, ty)) = sz.found.try_to_scalar()
-            && ty == self.tcx.types.usize
+            && let hir::TyKind::Array(_, length_arg) = tykind
+            && let Some(length_val) = sz.found.try_to_target_usize(self.tcx)
         {
-            let span = length.span();
             Some(TypeErrorAdditionalDiags::ConsiderSpecifyingLength {
-                span,
-                length: scalar.to_target_usize(&self.tcx).unwrap(),
+                span: length_arg.span(),
+                length: length_val,
             })
         } else {
             None
@@ -2046,12 +2052,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr);
 
         let span = trace.cause.span;
+        let mut path = None;
         let failure_code = trace.cause.as_failure_code_diag(
             terr,
             span,
-            self.type_error_additional_suggestions(&trace, terr),
+            self.type_error_additional_suggestions(&trace, terr, &mut path),
         );
         let mut diag = self.dcx().create_err(failure_code);
+        *diag.long_ty_path() = path;
         self.note_type_err(
             &mut diag,
             &trace.cause,
@@ -2059,6 +2067,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             Some(param_env.and(trace.values)),
             terr,
             false,
+            None,
         );
         diag
     }
@@ -2095,10 +2104,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         &self,
         values: ValuePairs<'tcx>,
         cause: &ObligationCause<'tcx>,
-    ) -> Option<(DiagStyledString, DiagStyledString, Option<PathBuf>)> {
+        file: &mut Option<PathBuf>,
+    ) -> Option<(DiagStyledString, DiagStyledString)> {
         match values {
             ValuePairs::Regions(exp_found) => self.expected_found_str(exp_found),
-            ValuePairs::Terms(exp_found) => self.expected_found_str_term(exp_found),
+            ValuePairs::Terms(exp_found) => self.expected_found_str_term(exp_found, file),
             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),
@@ -2108,7 +2118,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     found: exp_found.found.print_trait_sugared(),
                 };
                 match self.expected_found_str(pretty_exp_found) {
-                    Some((expected, found, _)) if expected == found => {
+                    Some((expected, found)) if expected == found => {
                         self.expected_found_str(exp_found)
                     }
                     ret => ret,
@@ -2130,9 +2140,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     (None, None)
                 };
 
-                let (exp, fnd) =
-                    self.cmp_fn_sig(&exp_found.expected, fn_def1, &exp_found.found, fn_def2);
-                Some((exp, fnd, None))
+                Some(self.cmp_fn_sig(&exp_found.expected, fn_def1, &exp_found.found, fn_def2))
             }
         }
     }
@@ -2140,7 +2148,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     fn expected_found_str_term(
         &self,
         exp_found: ty::error::ExpectedFound<ty::Term<'tcx>>,
-    ) -> Option<(DiagStyledString, DiagStyledString, Option<PathBuf>)> {
+        path: &mut Option<PathBuf>,
+    ) -> Option<(DiagStyledString, DiagStyledString)> {
         let exp_found = self.resolve_vars_if_possible(exp_found);
         if exp_found.references_error() {
             return None;
@@ -2155,21 +2164,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 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);
+                    let exp_s = self.tcx.short_string(expected, path);
                     exp = DiagStyledString::highlighted(exp_s);
                 }
                 if fnd_s.len() > len {
-                    let fnd_s = self.tcx.short_ty_string(found, &mut path);
+                    let fnd_s = self.tcx.short_string(found, path);
                     fnd = DiagStyledString::highlighted(fnd_s);
                 }
-                (exp, fnd, path)
+                (exp, fnd)
             }
             _ => (
                 DiagStyledString::highlighted(exp_found.expected.to_string()),
                 DiagStyledString::highlighted(exp_found.found.to_string()),
-                None,
             ),
         })
     }
@@ -2178,7 +2185,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     fn expected_found_str<T: fmt::Display + TypeFoldable<TyCtxt<'tcx>>>(
         &self,
         exp_found: ty::error::ExpectedFound<T>,
-    ) -> Option<(DiagStyledString, DiagStyledString, Option<PathBuf>)> {
+    ) -> Option<(DiagStyledString, DiagStyledString)> {
         let exp_found = self.resolve_vars_if_possible(exp_found);
         if exp_found.references_error() {
             return None;
@@ -2187,7 +2194,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         Some((
             DiagStyledString::highlighted(exp_found.expected.to_string()),
             DiagStyledString::highlighted(exp_found.found.to_string()),
-            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
index 68a53381230..9e7e96dddd7 100644
--- 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
@@ -18,6 +18,8 @@ use rustc_middle::ty::{
     TypeFoldable, TypeFolder, TypeSuperFoldable, TypeckResults,
 };
 use rustc_span::{BytePos, DUMMY_SP, FileName, Ident, Span, sym};
+use rustc_type_ir::inherent::*;
+use rustc_type_ir::visit::TypeVisitableExt;
 use tracing::{debug, instrument, warn};
 
 use super::nice_region_error::placeholder_error::Highlighted;
@@ -155,27 +157,92 @@ impl UnderspecifiedArgKind {
     }
 }
 
-struct ClosureEraser<'tcx> {
-    tcx: TyCtxt<'tcx>,
+struct ClosureEraser<'a, 'tcx> {
+    infcx: &'a InferCtxt<'tcx>,
 }
 
-impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ClosureEraser<'tcx> {
+impl<'a, 'tcx> ClosureEraser<'a, 'tcx> {
+    fn new_infer(&mut self) -> Ty<'tcx> {
+        self.infcx.next_ty_var(DUMMY_SP)
+    }
+}
+
+impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for ClosureEraser<'a, 'tcx> {
     fn cx(&self) -> TyCtxt<'tcx> {
-        self.tcx
+        self.infcx.tcx
     }
 
     fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
         match ty.kind() {
             ty::Closure(_, args) => {
+                // For a closure type, we turn it into a function pointer so that it gets rendered
+                // as `fn(args) -> Ret`.
                 let closure_sig = args.as_closure().sig();
                 Ty::new_fn_ptr(
-                    self.tcx,
-                    self.tcx.signature_unclosure(closure_sig, hir::Safety::Safe),
+                    self.cx(),
+                    self.cx().signature_unclosure(closure_sig, hir::Safety::Safe),
                 )
             }
-            _ => ty.super_fold_with(self),
+            ty::Adt(_, args) if !args.iter().any(|a| a.has_infer()) => {
+                // We have a type that doesn't have any inference variables, so we replace
+                // the whole thing with `_`. The type system already knows about this type in
+                // its entirety and it is redundant to specify it for the user. The user only
+                // needs to specify the type parameters that we *couldn't* figure out.
+                self.new_infer()
+            }
+            ty::Adt(def, args) => {
+                let generics = self.cx().generics_of(def.did());
+                let generics: Vec<bool> = generics
+                    .own_params
+                    .iter()
+                    .map(|param| param.default_value(self.cx()).is_some())
+                    .collect();
+                let ty = Ty::new_adt(
+                    self.cx(),
+                    *def,
+                    self.cx().mk_args_from_iter(generics.into_iter().zip(args.iter()).map(
+                        |(has_default, arg)| {
+                            if arg.has_infer() {
+                                // This param has an unsubstituted type variable, meaning that this
+                                // type has a (potentially deeply nested) type parameter from the
+                                // corresponding type's definition. We have explicitly asked this
+                                // type to not be hidden. In either case, we keep the type and don't
+                                // substitute with `_` just yet.
+                                arg.fold_with(self)
+                            } else if has_default {
+                                // We have a type param that has a default type, like the allocator
+                                // in Vec. We decided to show `Vec` itself, because it hasn't yet
+                                // been replaced by an `_` `Infer`, but we want to ensure that the
+                                // type parameter with default types does *not* get replaced with
+                                // `_` because then we'd end up with `Vec<_, _>`, instead of
+                                // `Vec<_>`.
+                                arg
+                            } else if let GenericArgKind::Type(_) = arg.kind() {
+                                // We don't replace lifetime or const params, only type params.
+                                self.new_infer().into()
+                            } else {
+                                arg.fold_with(self)
+                            }
+                        },
+                    )),
+                );
+                ty
+            }
+            _ if ty.has_infer() => {
+                // This type has a (potentially nested) type parameter that we couldn't figure out.
+                // We will print this depth of type, so at least the type name and at least one of
+                // its type parameters.
+                ty.super_fold_with(self)
+            }
+            // We don't have an unknown type parameter anywhere, replace with `_`.
+            _ => self.new_infer(),
         }
     }
+
+    fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
+        // Avoid accidentally erasing the type of the const.
+        c
+    }
 }
 
 fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'tcx>, ns: Namespace) -> FmtPrinter<'a, 'tcx> {
@@ -219,9 +286,9 @@ fn ty_to_string<'tcx>(
 ) -> 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 });
+    // We use `fn` ptr syntax for closures, but this only works when the closure does not capture
+    // anything. We also remove all type parameters that are fully known to the type system.
+    let ty = ty.fold_with(&mut ClosureEraser { infcx });
 
     match (ty.kind(), called_method_def_id) {
         // We don't want the regular output for `fn`s because it includes its path in
@@ -713,7 +780,7 @@ impl<'tcx> InferSourceKind<'tcx> {
                 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)
+                    ("normal", infcx.tcx.short_string(ty, &mut path), path)
                 } else {
                     ("other", String::new(), path)
                 }
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
index dfbef39e9e1..039e21cb556 100644
--- 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
@@ -323,9 +323,12 @@ pub fn suggest_new_region_bound(
                             .params
                             .iter()
                             .filter(|p| {
-                                matches!(p.kind, GenericParamKind::Lifetime {
-                                    kind: hir::LifetimeParamKind::Explicit
-                                })
+                                matches!(
+                                    p.kind,
+                                    GenericParamKind::Lifetime {
+                                        kind: hir::LifetimeParamKind::Explicit
+                                    }
+                                )
                             })
                             .map(|p| {
                                 if let hir::ParamName::Plain(name) = p.name {
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs
deleted file mode 100644
index beae9962f7f..00000000000
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs
+++ /dev/null
@@ -1,422 +0,0 @@
-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::kw;
-use tracing::debug;
-
-use super::ObligationCauseAsDiagArg;
-use crate::error_reporting::infer::{TypeErrCtxt, note_and_explain_region};
-use crate::errors::{
-    FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent, RefLongerThanData,
-    RegionOriginNote, WhereClauseSuggestions, note_and_explain,
-};
-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
index 1dd09fe7aaf..e8d14b89d69 100644
--- 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
@@ -293,7 +293,7 @@ impl<T> Trait<T> for X {
                     (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)
+                                .explicit_item_self_bounds(alias.def_id)
                                 .skip_binder()
                                 .iter()
                                 .any(|(pred, _span)| match pred.kind().skip_binder() {
@@ -422,7 +422,7 @@ impl<T> Trait<T> for X {
                             ty::Alias(..) => values.expected,
                             _ => values.found,
                         };
-                        let preds = tcx.explicit_item_super_predicates(opaque_ty.def_id);
+                        let preds = tcx.explicit_item_self_bounds(opaque_ty.def_id);
                         for (pred, _span) in preds.skip_binder() {
                             let ty::ClauseKind::Trait(trait_predicate) = pred.kind().skip_binder()
                             else {
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs
index 3acca47025c..f35a5349ecb 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs
@@ -221,7 +221,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
                 span: trace.cause.span,
                 requirement: ObligationCauseAsDiagArg(trace.cause.clone()),
-                expected_found: self.values_str(trace.values, &trace.cause).map(|(e, f, _)| (e, f)),
+                expected_found: self.values_str(trace.values, &trace.cause, err.long_ty_path()),
             }
             .add_to_diag(err),
             infer::Reborrow(span) => {
@@ -946,10 +946,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
         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, &sup_trace.cause)
-            && let Some((sub_expected, sub_found, _)) =
-                self.values_str(sub_trace.values, &sup_trace.cause)
+            && let Some((sup_expected, sup_found)) =
+                self.values_str(sup_trace.values, &sup_trace.cause, err.long_ty_path())
+            && let Some((sub_expected, sub_found)) =
+                self.values_str(sub_trace.values, &sup_trace.cause, err.long_ty_path())
             && sub_expected == sup_expected
             && sub_found == sup_found
         {
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs
index af7e56961b7..562000e28ac 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs
@@ -12,6 +12,7 @@ use rustc_middle::traits::{
     IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
     StatementAsExpression,
 };
+use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt};
 use rustc_span::{Span, sym};
@@ -20,7 +21,7 @@ use tracing::debug;
 use crate::error_reporting::TypeErrCtxt;
 use crate::error_reporting::infer::hir::Path;
 use crate::errors::{
-    ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
+    ConsiderAddingAwait, FnConsiderCasting, FnConsiderCastingBoth, FnItemsAreDistinct, FnUniqTypes,
     FunctionPointerSuggestion, SuggestAccessingField, SuggestRemoveSemiOrReturnBinding,
     SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags,
 };
@@ -381,14 +382,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
     }
 
-    pub(super) fn suggest_function_pointers(
+    pub fn suggest_function_pointers_impl(
         &self,
-        cause: &ObligationCause<'tcx>,
-        span: Span,
+        span: Option<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();
@@ -411,8 +410,17 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     return;
                 }
 
+                let Some(span) = span else {
+                    let casting = format!("{fn_name} as {sig}");
+                    diag.subdiagnostic(FnItemsAreDistinct);
+                    diag.subdiagnostic(FnConsiderCasting { casting });
+                    return;
+                };
+
                 let sugg = match (expected.is_ref(), found.is_ref()) {
-                    (true, false) => FunctionPointerSuggestion::UseRef { span, fn_name },
+                    (true, false) => {
+                        FunctionPointerSuggestion::UseRef { span: span.shrink_to_lo() }
+                    }
                     (false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name },
                     (true, true) => {
                         diag.subdiagnostic(FnItemsAreDistinct);
@@ -420,7 +428,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     }
                     (false, false) => {
                         diag.subdiagnostic(FnItemsAreDistinct);
-                        FunctionPointerSuggestion::Cast { span, fn_name, sig }
+                        FunctionPointerSuggestion::Cast { span: span.shrink_to_hi(), sig }
                     }
                 };
                 diag.subdiagnostic(sugg);
@@ -445,6 +453,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 }
 
                 let fn_name = self.tcx.def_path_str_with_args(*did2, args2);
+
+                let Some(span) = span else {
+                    diag.subdiagnostic(FnConsiderCastingBoth { sig: *expected_sig });
+                    return;
+                };
+
                 let sug = if found.is_ref() {
                     FunctionPointerSuggestion::CastBothRef {
                         span,
@@ -454,8 +468,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     }
                 } else {
                     FunctionPointerSuggestion::CastBoth {
-                        span,
-                        fn_name,
+                        span: span.shrink_to_hi(),
                         found_sig: *found_sig,
                         expected_sig: *expected_sig,
                     }
@@ -488,6 +501,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         };
     }
 
+    pub(super) fn suggest_function_pointers(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        span: Span,
+        exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+        terr: TypeError<'tcx>,
+        diag: &mut Diag<'_>,
+    ) {
+        debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found);
+
+        if exp_found.expected.peel_refs().is_fn() && exp_found.found.peel_refs().is_fn() {
+            self.suggest_function_pointers_impl(Some(span), exp_found, diag);
+        } else if let TypeError::Sorts(exp_found) = terr {
+            self.suggest_function_pointers_impl(None, &exp_found, diag);
+        }
+    }
+
     pub fn should_suggest_as_ref_kind(
         &self,
         expected: Ty<'tcx>,
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 b4d294a70c0..6beb108bc3a 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs
@@ -163,6 +163,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
         let predicate = self.resolve_vars_if_possible(obligation.predicate);
         let span = obligation.cause.span;
+        let mut file = None;
 
         debug!(?predicate, obligation.cause.code = ?obligation.cause.code());
 
@@ -179,7 +180,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     return e;
                 }
 
-                if let Err(guar) = self.tcx.ensure().coherent_trait(trait_pred.def_id()) {
+                if let Err(guar) = self.tcx.ensure_ok().coherent_trait(trait_pred.def_id()) {
                     // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case
                     // other `Foo` impls are incoherent.
                     return guar;
@@ -245,7 +246,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         span,
                         E0283,
                         "type annotations needed: cannot satisfy `{}`",
-                        predicate,
+                        self.tcx.short_string(predicate, &mut file),
                     )
                 };
 
@@ -292,7 +293,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         err.cancel();
                         return e;
                     }
-                    err.note(format!("cannot satisfy `{predicate}`"));
+                    let pred = self.tcx.short_string(predicate, &mut file);
+                    err.note(format!("cannot satisfy `{pred}`"));
                     let impl_candidates =
                         self.find_similar_impl_candidates(predicate.as_trait_clause().unwrap());
                     if impl_candidates.len() < 40 {
@@ -511,8 +513,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     return e;
                 }
 
-                if let Err(guar) =
-                    self.tcx.ensure().coherent_trait(self.tcx.parent(data.projection_term.def_id))
+                if let Err(guar) = self
+                    .tcx
+                    .ensure_ok()
+                    .coherent_trait(self.tcx.parent(data.projection_term.def_id))
                 {
                     // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case
                     // other `Foo` impls are incoherent.
@@ -524,6 +528,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     .iter()
                     .chain(Some(data.term.into_arg()))
                     .find(|g| g.has_non_region_infer());
+                let predicate = self.tcx.short_string(predicate, &mut file);
                 if let Some(arg) = arg {
                     self.emit_inference_failure_err(
                         obligation.cause.body_id,
@@ -539,8 +544,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         self.dcx(),
                         span,
                         E0284,
-                        "type annotations needed: cannot satisfy `{}`",
-                        predicate,
+                        "type annotations needed: cannot satisfy `{predicate}`",
                     )
                     .with_span_label(span, format!("cannot satisfy `{predicate}`"))
                 }
@@ -565,12 +569,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     err
                 } else {
                     // If we can't find a generic parameter, just print a generic error
+                    let predicate = self.tcx.short_string(predicate, &mut file);
                     struct_span_code_err!(
                         self.dcx(),
                         span,
                         E0284,
-                        "type annotations needed: cannot satisfy `{}`",
-                        predicate,
+                        "type annotations needed: cannot satisfy `{predicate}`",
                     )
                     .with_span_label(span, format!("cannot satisfy `{predicate}`"))
                 }
@@ -590,6 +594,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 if let Some(e) = self.tainted_by_errors() {
                     return e;
                 }
+                let alias = self.tcx.short_string(alias, &mut file);
                 struct_span_code_err!(
                     self.dcx(),
                     span,
@@ -603,16 +608,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 if let Some(e) = self.tainted_by_errors() {
                     return e;
                 }
+                let predicate = self.tcx.short_string(predicate, &mut file);
                 struct_span_code_err!(
                     self.dcx(),
                     span,
                     E0284,
-                    "type annotations needed: cannot satisfy `{}`",
-                    predicate,
+                    "type annotations needed: cannot satisfy `{predicate}`",
                 )
                 .with_span_label(span, format!("cannot satisfy `{predicate}`"))
             }
         };
+        *err.long_ty_path() = file;
         self.note_obligation_cause(&mut err, obligation);
         err.emit()
     }
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 5021fd8bf83..1b43820bac0 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,5 +1,6 @@
 use core::ops::ControlFlow;
 use std::borrow::Cow;
+use std::path::PathBuf;
 
 use rustc_ast::TraitObjectSyntax;
 use rustc_data_structures::fx::FxHashMap;
@@ -9,7 +10,6 @@ use rustc_errors::{
     Applicability, Diag, ErrorGuaranteed, Level, MultiSpan, StashKey, StringPart, Suggestions,
     pluralize, struct_span_code_err,
 };
-use rustc_hir::def::Namespace;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::{self as hir, LangItem, Node};
@@ -20,8 +20,8 @@ use rustc_middle::ty::abstract_const::NotConstEvaluatable;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
 use rustc_middle::ty::print::{
-    FmtPrinter, Print, PrintPolyTraitPredicateExt, PrintTraitPredicateExt as _,
-    PrintTraitRefExt as _, with_forced_trimmed_paths,
+    PrintPolyTraitPredicateExt, PrintTraitPredicateExt as _, PrintTraitRefExt as _,
+    with_forced_trimmed_paths,
 };
 use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast};
 use rustc_middle::{bug, span_bug};
@@ -60,6 +60,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     ) -> ErrorGuaranteed {
         let tcx = self.tcx;
         let mut span = obligation.cause.span;
+        let mut long_ty_file = None;
 
         let mut err = match *error {
             SelectionError::Unimplemented => {
@@ -169,11 +170,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         if let Err(guar) = self.fn_arg_obligation(&obligation) {
                             return guar;
                         }
-                        let mut file = None;
                         let (post_message, pre_message, type_def) = self
                             .get_parent_trait_ref(obligation.cause.code())
                             .map(|(t, s)| {
-                                let t = self.tcx.short_ty_string(t, &mut file);
+                                let t = self.tcx.short_string(t, &mut long_ty_file);
                                 (
                                     format!(" in `{t}`"),
                                     format!("within `{t}`, "),
@@ -181,12 +181,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                                 )
                             })
                             .unwrap_or_default();
-                        let file_note = file.as_ref().map(|file| format!(
-                            "the full trait has been written to '{}'",
-                            file.display(),
-                        ));
-
-                        let mut long_ty_file = None;
 
                         let OnUnimplementedNote {
                             message,
@@ -223,6 +217,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             None,
                             append_const_msg,
                             post_message,
+                            &mut long_ty_file,
                         );
 
                         let (err_msg, safe_transmute_explanation) = if self.tcx.is_lang_item(main_trait_predicate.def_id(), LangItem::TransmuteTrait)
@@ -251,14 +246,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         };
 
                         let mut err = struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg);
+                        *err.long_ty_path() = long_ty_file;
 
-                        if let Some(long_ty_file) = long_ty_file {
-                            err.note(format!(
-                                "the full name for the type has been written to '{}'",
-                                long_ty_file.display(),
-                            ));
-                            err.note("consider using `--verbose` to print the full type name to the console");
-                        }
                         let mut suggested = false;
                         if is_try_conversion {
                             suggested = self.try_conversion_context(&obligation, main_trait_predicate, &mut err);
@@ -309,7 +298,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             return err.emit();
                         }
 
-                        file_note.map(|note| err.note(note));
                         if let Some(s) = label {
                             // If it has a custom `#[rustc_on_unimplemented]`
                             // error message, let's display it as the label!
@@ -708,13 +696,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     None,
                     TypeError::Sorts(ty::error::ExpectedFound::new(expected_ty, ct_ty)),
                     false,
+                    None,
                 );
                 diag
             }
         };
 
         self.note_obligation_cause(&mut err, &obligation);
-        self.point_at_returns_when_relevant(&mut err, &obligation);
         err.emit()
     }
 }
@@ -761,14 +749,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             trait_ref: predicate.trait_ref,
             polarity: ty::PredicatePolarity::Positive,
         });
+        let mut file = None;
         let err_msg = self.get_standard_error_message(
             trait_ref,
             None,
             Some(predicate.constness()),
             None,
             String::new(),
+            &mut file,
         );
         let mut diag = struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg);
+        *diag.long_ty_path() = file;
         if !self.predicate_may_hold(&Obligation::new(
             self.tcx,
             ObligationCause::dummy(),
@@ -814,7 +805,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     "Async",
                 );
                 self.note_obligation_cause(&mut err, &obligation);
-                self.point_at_returns_when_relevant(&mut err, &obligation);
                 return Some(err.emit());
             }
         }
@@ -860,7 +850,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     "",
                 );
                 self.note_obligation_cause(&mut err, &obligation);
-                self.point_at_returns_when_relevant(&mut err, &obligation);
                 return Some(err.emit());
             }
 
@@ -876,7 +865,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     kind: expected_kind.as_str(),
                 });
                 self.note_obligation_cause(&mut err, &obligation);
-                self.point_at_returns_when_relevant(&mut err, &obligation);
                 return Some(err.emit());
             }
         }
@@ -931,14 +919,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             }
         }
         let hir_id = self.tcx.local_def_id_to_hir_id(obligation.cause.body_id);
-        let body_id = match self.tcx.hir_node(hir_id) {
-            hir::Node::Item(hir::Item {
-                kind: hir::ItemKind::Fn { body: body_id, .. }, ..
-            }) => body_id,
-            _ => return false,
-        };
-        let ControlFlow::Break(expr) = (FindMethodSubexprOfTry { search_span: span })
-            .visit_body(self.tcx.hir().body(*body_id))
+        let Some(body_id) = self.tcx.hir_node(hir_id).body_id() else { return false };
+        let ControlFlow::Break(expr) =
+            (FindMethodSubexprOfTry { search_span: span }).visit_body(self.tcx.hir().body(body_id))
         else {
             return false;
         };
@@ -1385,22 +1368,53 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 _ => (None, error.err),
             };
 
-            let msg = values
+            let mut file = None;
+            let (msg, span, closure_span) = values
                 .and_then(|(predicate, normalized_term, expected_term)| {
-                    self.maybe_detailed_projection_msg(predicate, normalized_term, expected_term)
+                    self.maybe_detailed_projection_msg(
+                        obligation.cause.span,
+                        predicate,
+                        normalized_term,
+                        expected_term,
+                        &mut file,
+                    )
                 })
                 .unwrap_or_else(|| {
-                    let mut cx = FmtPrinter::new_with_limit(
-                        self.tcx,
-                        Namespace::TypeNS,
-                        rustc_session::Limit(10),
-                    );
-                    with_forced_trimmed_paths!(format!("type mismatch resolving `{}`", {
-                        self.resolve_vars_if_possible(predicate).print(&mut cx).unwrap();
-                        cx.into_buffer()
-                    }))
+                    (
+                        with_forced_trimmed_paths!(format!(
+                            "type mismatch resolving `{}`",
+                            self.tcx
+                                .short_string(self.resolve_vars_if_possible(predicate), &mut file),
+                        )),
+                        obligation.cause.span,
+                        None,
+                    )
                 });
-            let mut diag = struct_span_code_err!(self.dcx(), obligation.cause.span, E0271, "{msg}");
+            let mut diag = struct_span_code_err!(self.dcx(), span, E0271, "{msg}");
+            *diag.long_ty_path() = file;
+            if let Some(span) = closure_span {
+                // Mark the closure decl so that it is seen even if we are pointing at the return
+                // type or expression.
+                //
+                // error[E0271]: expected `{closure@foo.rs:41:16}` to be a closure that returns
+                //               `Unit3`, but it returns `Unit4`
+                //   --> $DIR/foo.rs:43:17
+                //    |
+                // LL |     let v = Unit2.m(
+                //    |                   - required by a bound introduced by this call
+                // ...
+                // LL |             f: |x| {
+                //    |                --- /* this span */
+                // LL |                 drop(x);
+                // LL |                 Unit4
+                //    |                 ^^^^^ expected `Unit3`, found `Unit4`
+                //    |
+                diag.span_label(span, "this closure");
+                if !span.overlaps(obligation.cause.span) {
+                    // Point at the binding corresponding to the closure where it is used.
+                    diag.span_label(obligation.cause.span, "closure used here");
+                }
+            }
 
             let secondary_span = (|| {
                 let ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) =
@@ -1443,15 +1457,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         ty.span,
                         with_forced_trimmed_paths!(Cow::from(format!(
                             "type mismatch resolving `{}`",
-                            {
-                                let mut cx = FmtPrinter::new_with_limit(
-                                    self.tcx,
-                                    Namespace::TypeNS,
-                                    rustc_session::Limit(5),
-                                );
-                                self.resolve_vars_if_possible(predicate).print(&mut cx).unwrap();
-                                cx.into_buffer()
-                            }
+                            self.tcx.short_string(
+                                self.resolve_vars_if_possible(predicate),
+                                diag.long_ty_path()
+                            ),
                         ))),
                         true,
                     )),
@@ -1471,6 +1480,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 }),
                 err,
                 false,
+                Some(span),
             );
             self.note_obligation_cause(&mut diag, obligation);
             diag.emit()
@@ -1479,34 +1489,65 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
     fn maybe_detailed_projection_msg(
         &self,
+        mut span: Span,
         projection_term: ty::AliasTerm<'tcx>,
         normalized_ty: ty::Term<'tcx>,
         expected_ty: ty::Term<'tcx>,
-    ) -> Option<String> {
+        file: &mut Option<PathBuf>,
+    ) -> Option<(String, Span, Option<Span>)> {
         let trait_def_id = projection_term.trait_def_id(self.tcx);
         let self_ty = projection_term.self_ty();
 
         with_forced_trimmed_paths! {
             if self.tcx.is_lang_item(projection_term.def_id, LangItem::FnOnceOutput) {
-                let fn_kind = self_ty.prefix_string(self.tcx);
+                let (span, closure_span) = if let ty::Closure(def_id, _) = self_ty.kind() {
+                    let def_span = self.tcx.def_span(def_id);
+                    if let Some(local_def_id) = def_id.as_local()
+                        && let node = self.tcx.hir_node_by_def_id(local_def_id)
+                        && let Some(fn_decl) = node.fn_decl()
+                        && let Some(id) = node.body_id()
+                    {
+                        span = match fn_decl.output {
+                            hir::FnRetTy::Return(ty) => ty.span,
+                            hir::FnRetTy::DefaultReturn(_) => {
+                                let body = self.tcx.hir().body(id);
+                                match body.value.kind {
+                                    hir::ExprKind::Block(
+                                        hir::Block { expr: Some(expr), .. },
+                                        _,
+                                    ) => expr.span,
+                                    hir::ExprKind::Block(
+                                        hir::Block {
+                                            expr: None, stmts: [.., last], ..
+                                        },
+                                        _,
+                                    ) => last.span,
+                                    _ => body.value.span,
+                                }
+                            }
+                        };
+                    }
+                    (span, Some(def_span))
+                } else {
+                    (span, None)
+                };
                 let item = match self_ty.kind() {
                     ty::FnDef(def, _) => self.tcx.item_name(*def).to_string(),
-                    _ => self_ty.to_string(),
+                    _ => self.tcx.short_string(self_ty, file),
                 };
-                Some(format!(
-                    "expected `{item}` to be a {fn_kind} that returns `{expected_ty}`, but it \
-                     returns `{normalized_ty}`",
-                ))
+                Some((format!(
+                    "expected `{item}` to return `{expected_ty}`, but it returns `{normalized_ty}`",
+                ), span, closure_span))
             } else if self.tcx.is_lang_item(trait_def_id, LangItem::Future) {
-                Some(format!(
+                Some((format!(
                     "expected `{self_ty}` to be a future that resolves to `{expected_ty}`, but it \
                      resolves to `{normalized_ty}`"
-                ))
+                ), span, None))
             } else if Some(trait_def_id) == self.tcx.get_diagnostic_item(sym::Iterator) {
-                Some(format!(
+                Some((format!(
                     "expected `{self_ty}` to be an iterator that yields `{expected_ty}`, but it \
                      yields `{normalized_ty}`"
-                ))
+                ), span, None))
             } else {
                 None
             }
@@ -1796,13 +1837,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 }
             }
             span.push_span_label(self.tcx.def_span(other_trait_def_id), "this is the found trait");
-            err.highlighted_span_note(span, vec![
-                StringPart::normal("there are ".to_string()),
-                StringPart::highlighted("multiple different versions".to_string()),
-                StringPart::normal(" of crate `".to_string()),
-                StringPart::highlighted(format!("{crate_name}")),
-                StringPart::normal("` in the dependency graph\n".to_string()),
-            ]);
+            err.highlighted_span_note(
+                span,
+                vec![
+                    StringPart::normal("there are ".to_string()),
+                    StringPart::highlighted("multiple different versions".to_string()),
+                    StringPart::normal(" of crate `".to_string()),
+                    StringPart::highlighted(format!("{crate_name}")),
+                    StringPart::normal("` in the dependency graph\n".to_string()),
+                ],
+            );
             if points_at_type {
                 // We only clarify that the same type from different crate versions are not the
                 // same when we *find* the same type coming from different crate versions, otherwise
@@ -1923,8 +1967,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         StringPart::normal(" implemented for `"),
                     ]);
                     if types_content.0 == types_content.1 {
-                        let ty =
-                            self.tcx.short_ty_string(obligation_trait_ref.self_ty(), &mut None);
+                        let ty = self
+                            .tcx
+                            .short_string(obligation_trait_ref.self_ty(), err.long_ty_path());
                         msg.push(StringPart::normal(ty));
                     } else {
                         msg.extend(types.0.0);
@@ -1969,6 +2014,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             StringPart::highlighted(exp_found.found.to_string()),
                             StringPart::normal("`"),
                         ]);
+                        self.suggest_function_pointers_impl(None, &exp_found, err);
                     }
 
                     true
@@ -2280,7 +2326,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, '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) {
-            let mut long_ty_file = None;
             self.note_obligation_cause_code(
                 obligation.cause.body_id,
                 err,
@@ -2289,15 +2334,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 obligation.cause.code(),
                 &mut vec![],
                 &mut Default::default(),
-                &mut long_ty_file,
             );
-            if let Some(file) = long_ty_file {
-                err.note(format!(
-                    "the full name for the type has been written to '{}'",
-                    file.display(),
-                ));
-                err.note("consider using `--verbose` to print the full type name to the console");
-            }
             self.suggest_unsized_bound_if_applicable(err, obligation);
             if let Some(span) = err.span.primary_span()
                 && let Some(mut diag) =
@@ -2341,6 +2378,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         predicate_constness: Option<ty::BoundConstness>,
         append_const_msg: Option<AppendConstMessage>,
         post_message: String,
+        long_ty_file: &mut Option<PathBuf>,
     ) -> String {
         message
             .and_then(|cannot_do_this| {
@@ -2364,7 +2402,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             .unwrap_or_else(|| {
                 format!(
                     "the trait bound `{}` is not satisfied{post_message}",
-                    trait_predicate.print_with_bound_constness(predicate_constness)
+                    self.tcx.short_string(
+                        trait_predicate.print_with_bound_constness(predicate_constness),
+                        long_ty_file,
+                    ),
                 )
             })
     }
@@ -2784,7 +2825,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         err.span_note(self.tcx.def_span(def_id), "opaque type is declared here");
 
         self.note_obligation_cause(&mut err, &obligation);
-        self.point_at_returns_when_relevant(&mut err, &obligation);
         self.dcx().try_steal_replace_and_emit_err(self.tcx.def_span(def_id), StashKey::Cycle, err)
     }
 
@@ -2940,12 +2980,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     .inputs
                     .iter()
                     .map(|arg| match arg.kind {
-                        hir::TyKind::Tup(tys) => {
-                            ArgKind::Tuple(Some(arg.span), vec![
-                                ("_".to_owned(), "_".to_owned());
-                                tys.len()
-                            ])
-                        }
+                        hir::TyKind::Tup(tys) => ArgKind::Tuple(
+                            Some(arg.span),
+                            vec![("_".to_owned(), "_".to_owned()); tys.len()],
+                        ),
                         _ => ArgKind::empty(),
                     })
                     .collect::<Vec<ArgKind>>(),
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 8111983c539..658fb4009d5 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
@@ -306,7 +306,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 if let ObligationCauseCode::WhereClause(..)
                 | ObligationCauseCode::WhereClauseInExpr(..) = code
                 {
-                    let mut long_ty_file = None;
                     self.note_obligation_cause_code(
                         error.obligation.cause.body_id,
                         &mut diag,
@@ -315,17 +314,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         code,
                         &mut vec![],
                         &mut Default::default(),
-                        &mut long_ty_file,
                     );
-                    if let Some(file) = long_ty_file {
-                        diag.note(format!(
-                            "the full name for the type has been written to '{}'",
-                            file.display(),
-                        ));
-                        diag.note(
-                            "consider using `--verbose` to print the full type name to the console",
-                        );
-                    }
                 }
                 diag.emit()
             }
@@ -482,11 +471,10 @@ pub fn report_dyn_incompatibility<'tcx>(
     for (span, msg) in iter::zip(multi_span, messages) {
         note_span.push_span_label(span, msg);
     }
-    // FIXME(dyn_compat_renaming): Update the URL.
     err.span_note(
         note_span,
         "for a trait to be dyn compatible it needs to allow building a vtable\n\
-        for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>",
+        for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>",
     );
 
     // Only provide the help if its a local trait, otherwise it's not actionable.
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 4e0b097db4c..518323f6526 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
@@ -799,7 +799,7 @@ impl<'tcx> OnUnimplementedFormatString {
         let mut result = Ok(());
         for token in &mut parser {
             match token {
-                Piece::String(_) => (), // Normal string, no need to check it
+                Piece::Lit(_) => (), // Normal string, no need to check it
                 Piece::NextArgument(a) => {
                     let format_spec = a.format;
                     if self.is_diagnostic_namespace_variant
@@ -932,7 +932,7 @@ impl<'tcx> OnUnimplementedFormatString {
                 let value = match param.kind {
                     GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
                         if let Some(ty) = trait_ref.args[param.index as usize].as_type() {
-                            tcx.short_ty_string(ty, long_ty_file)
+                            tcx.short_string(ty, long_ty_file)
                         } else {
                             trait_ref.args[param.index as usize].to_string()
                         }
@@ -950,7 +950,7 @@ impl<'tcx> OnUnimplementedFormatString {
         let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string);
         let constructed_message = (&mut parser)
             .map(|p| match p {
-                Piece::String(s) => s.to_owned(),
+                Piece::Lit(s) => s.to_owned(),
                 Piece::NextArgument(a) => match a.position {
                     Position::ArgumentNamed(arg) => {
                         let s = Symbol::intern(arg);
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 d82acc4e054..c5ed74420d4 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs
@@ -141,7 +141,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             obligation.cause.span,
             suggest_increasing_limit,
             |err| {
-                let mut long_ty_file = None;
                 self.note_obligation_cause_code(
                     obligation.cause.body_id,
                     err,
@@ -150,17 +149,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     obligation.cause.code(),
                     &mut vec![],
                     &mut Default::default(),
-                    &mut long_ty_file,
                 );
-                if let Some(file) = long_ty_file {
-                    err.note(format!(
-                        "the full name for the type has been written to '{}'",
-                        file.display(),
-                    ));
-                    err.note(
-                        "consider using `--verbose` to print the full type name to the console",
-                    );
-                }
             },
         );
     }
@@ -196,7 +185,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             suggest_increasing_limit,
         );
         self.note_obligation_cause(&mut err, &obligation);
-        self.point_at_returns_when_relevant(&mut err, &obligation);
         err.emit()
     }
 }
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 471105773e2..527d2e54e43 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -3,7 +3,6 @@
 use std::assert_matches::debug_assert_matches;
 use std::borrow::Cow;
 use std::iter;
-use std::path::PathBuf;
 
 use itertools::{EitherOrBoth, Itertools};
 use rustc_abi::ExternAbi;
@@ -641,10 +640,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     }
                     // Empty suggestions with empty spans ICE with debug assertions
                     if steps == 0 {
-                        return (msg.trim_end_matches(" and dereferencing instead"), vec![(
-                            prefix_span,
-                            String::new(),
-                        )]);
+                        return (
+                            msg.trim_end_matches(" and dereferencing instead"),
+                            vec![(prefix_span, String::new())],
+                        );
                     }
                     let derefs = "*".repeat(steps);
                     let needs_parens = steps > 0
@@ -1087,28 +1086,27 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         sig_parts.map_bound(|sig| sig.tupled_inputs_ty.tuple_fields().as_slice()),
                     ))
                 }
-                ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => self
-                    .tcx
-                    .item_super_predicates(def_id)
-                    .instantiate(self.tcx, args)
-                    .iter()
-                    .find_map(|pred| {
-                        if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
+                ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
+                    self.tcx.item_self_bounds(def_id).instantiate(self.tcx, args).iter().find_map(
+                        |pred| {
+                            if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
                             && self
                                 .tcx
                                 .is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput)
                             // args tuple will always be args[1]
                             && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()
-                        {
-                            Some((
-                                DefIdOrName::DefId(def_id),
-                                pred.kind().rebind(proj.term.expect_type()),
-                                pred.kind().rebind(args.as_slice()),
-                            ))
-                        } else {
-                            None
-                        }
-                    }),
+                            {
+                                Some((
+                                    DefIdOrName::DefId(def_id),
+                                    pred.kind().rebind(proj.term.expect_type()),
+                                    pred.kind().rebind(args.as_slice()),
+                                ))
+                            } else {
+                                None
+                            }
+                        },
+                    )
+                }
                 ty::Dynamic(data, _, ty::Dyn) => data.iter().find_map(|pred| {
                     if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
                         && self.tcx.is_lang_item(proj.def_id, LangItem::FnOnceOutput)
@@ -1298,30 +1296,24 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     // Because of this, we modify the error to refer to the original obligation and
                     // return early in the caller.
 
-                    let msg = format!("the trait bound `{old_pred}` is not satisfied");
+                    let msg = format!(
+                        "the trait bound `{}` is not satisfied",
+                        self.tcx.short_string(old_pred, err.long_ty_path()),
+                    );
+                    let self_ty_str =
+                        self.tcx.short_string(old_pred.self_ty().skip_binder(), err.long_ty_path());
                     if has_custom_message {
                         err.note(msg);
                     } else {
                         err.messages = vec![(rustc_errors::DiagMessage::from(msg), Style::NoStyle)];
                     }
-                    let mut file = None;
                     err.span_label(
                         span,
                         format!(
-                            "the trait `{}` is not implemented for `{}`",
-                            old_pred.print_modifiers_and_trait_path(),
-                            self.tcx.short_ty_string(old_pred.self_ty().skip_binder(), &mut file),
+                            "the trait `{}` is not implemented for `{self_ty_str}`",
+                            old_pred.print_modifiers_and_trait_path()
                         ),
                     );
-                    if let Some(file) = file {
-                        err.note(format!(
-                            "the full type name has been written to '{}'",
-                            file.display()
-                        ));
-                        err.note(
-                            "consider using `--verbose` to print full type name to the console",
-                        );
-                    }
 
                     if imm_ref_self_ty_satisfies_pred && mut_ref_self_ty_satisfies_pred {
                         err.span_suggestions(
@@ -1773,7 +1765,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         };
 
         err.code(E0746);
-        err.primary_message("return type cannot have an unboxed trait object");
+        err.primary_message("return type cannot be a trait object without pointer indirection");
         err.children.clear();
 
         let span = obligation.cause.span;
@@ -1789,25 +1781,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         } 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,
-            );
-            "alternatively, "
-        } else {
-            err.help("if there were a single returned type, you could use `impl Trait` instead");
-            ""
-        };
+
+        err.span_suggestion_verbose(
+            impl_span,
+            "consider returning an `impl Trait` instead of a `dyn Trait`",
+            "impl ",
+            Applicability::MaybeIncorrect,
+        );
 
         let mut sugg = vec![
             (span.shrink_to_lo(), format!("Box<{pre}")),
@@ -1839,7 +1819,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
         err.multipart_suggestion(
             format!(
-                "{alternatively}box the return type, and wrap all of the returned values in \
+                "alternatively, box the return type, and wrap all of the returned values in \
                  `Box::new`",
             ),
             sugg,
@@ -1849,41 +1829,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         true
     }
 
-    pub(super) fn point_at_returns_when_relevant(
-        &self,
-        err: &mut Diag<'_>,
-        obligation: &PredicateObligation<'tcx>,
-    ) {
-        match obligation.cause.code().peel_derives() {
-            ObligationCauseCode::SizedReturnType => {}
-            _ => return,
-        }
-
-        let hir = self.tcx.hir();
-        let node = self.tcx.hir_node_by_def_id(obligation.cause.body_id);
-        if let hir::Node::Item(hir::Item {
-            kind: hir::ItemKind::Fn { body: body_id, .. }, ..
-        }) = node
-        {
-            let body = hir.body(*body_id);
-            // Point at all the `return`s in the function as they have failed trait bounds.
-            let mut visitor = ReturnsVisitor::default();
-            visitor.visit_body(body);
-            let typeck_results = self.typeck_results.as_ref().unwrap();
-            for expr in &visitor.returns {
-                if let Some(returned_ty) = typeck_results.node_type_opt(expr.hir_id) {
-                    let ty = self.resolve_vars_if_possible(returned_ty);
-                    if ty.references_error() {
-                        // don't print out the [type error] here
-                        err.downgrade_to_delayed_bug();
-                    } else {
-                        err.span_label(expr.span, format!("this returned value is of type `{ty}`"));
-                    }
-                }
-            }
-        }
-    }
-
     pub(super) fn report_closure_arg_mismatch(
         &self,
         span: Span,
@@ -2690,7 +2635,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         // Add a note for the item obligation that remains - normally a note pointing to the
         // bound that introduced the obligation (e.g. `T: Send`).
         debug!(?next_code);
-        let mut long_ty_file = None;
         self.note_obligation_cause_code(
             obligation.cause.body_id,
             err,
@@ -2699,7 +2643,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             next_code.unwrap(),
             &mut Vec::new(),
             &mut Default::default(),
-            &mut long_ty_file,
         );
     }
 
@@ -2712,7 +2655,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         cause_code: &ObligationCauseCode<'tcx>,
         obligated_types: &mut Vec<Ty<'tcx>>,
         seen_requirements: &mut FxHashSet<DefId>,
-        long_ty_file: &mut Option<PathBuf>,
     ) where
         T: Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>>,
     {
@@ -2770,6 +2712,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             ObligationCauseCode::SliceOrArrayElem => {
                 err.note("slice and array elements must have `Sized` type");
             }
+            ObligationCauseCode::ArrayLen(array_ty) => {
+                err.note(format!("the length of array `{array_ty}` must be type `usize`"));
+            }
             ObligationCauseCode::TupleElem => {
                 err.note("only the last element of a tuple may have a dynamically sized type");
             }
@@ -2963,9 +2908,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             }
             ObligationCauseCode::Coercion { source, target } => {
                 let source =
-                    tcx.short_ty_string(self.resolve_vars_if_possible(source), long_ty_file);
+                    tcx.short_string(self.resolve_vars_if_possible(source), err.long_ty_path());
                 let target =
-                    tcx.short_ty_string(self.resolve_vars_if_possible(target), long_ty_file);
+                    tcx.short_string(self.resolve_vars_if_possible(target), err.long_ty_path());
                 err.note(with_forced_trimmed_paths!(format!(
                     "required for the cast from `{source}` to `{target}`",
                 )));
@@ -3250,7 +3195,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 };
 
                 if !is_upvar_tys_infer_tuple {
-                    let ty_str = tcx.short_ty_string(ty, long_ty_file);
+                    let ty_str = tcx.short_string(ty, err.long_ty_path());
                     let msg = format!("required because it appears within the type `{ty_str}`");
                     match ty.kind() {
                         ty::Adt(def, _) => match tcx.opt_item_ident(def.did()) {
@@ -3328,7 +3273,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             &data.parent_code,
                             obligated_types,
                             seen_requirements,
-                            long_ty_file,
                         )
                     });
                 } else {
@@ -3341,7 +3285,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             cause_code.peel_derives(),
                             obligated_types,
                             seen_requirements,
-                            long_ty_file,
                         )
                     });
                 }
@@ -3351,7 +3294,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     self.resolve_vars_if_possible(data.derived.parent_trait_pred);
                 let parent_def_id = parent_trait_pred.def_id();
                 let self_ty_str =
-                    tcx.short_ty_string(parent_trait_pred.skip_binder().self_ty(), long_ty_file);
+                    tcx.short_string(parent_trait_pred.skip_binder().self_ty(), err.long_ty_path());
                 let trait_name = parent_trait_pred.print_modifiers_and_trait_path().to_string();
                 let msg = format!("required for `{self_ty_str}` to implement `{trait_name}`");
                 let mut is_auto_trait = false;
@@ -3447,8 +3390,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         count,
                         pluralize!(count)
                     ));
-                    let self_ty = tcx
-                        .short_ty_string(parent_trait_pred.skip_binder().self_ty(), long_ty_file);
+                    let self_ty = tcx.short_string(
+                        parent_trait_pred.skip_binder().self_ty(),
+                        err.long_ty_path(),
+                    );
                     err.note(format!(
                         "required for `{self_ty}` to implement `{}`",
                         parent_trait_pred.print_modifiers_and_trait_path()
@@ -3464,7 +3409,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         &data.parent_code,
                         obligated_types,
                         seen_requirements,
-                        long_ty_file,
                     )
                 });
             }
@@ -3503,7 +3447,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         &data.derived.parent_code,
                         obligated_types,
                         seen_requirements,
-                        long_ty_file,
                     )
                 });
             }
@@ -3517,7 +3460,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         &data.parent_code,
                         obligated_types,
                         seen_requirements,
-                        long_ty_file,
                     )
                 });
             }
@@ -3534,7 +3476,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         &data.parent_code,
                         obligated_types,
                         seen_requirements,
-                        long_ty_file,
                     )
                 });
             }
@@ -3549,7 +3490,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         nested,
                         obligated_types,
                         seen_requirements,
-                        long_ty_file,
                     )
                 });
                 let mut multispan = MultiSpan::from(span);
@@ -3580,7 +3520,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         parent_code,
                         obligated_types,
                         seen_requirements,
-                        long_ty_file,
                     )
                 });
             }
@@ -3613,14 +3552,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             }
             ObligationCauseCode::TrivialBound => {
                 err.help("see issue #48214");
-                tcx.disabled_nightly_features(err, Some(tcx.local_def_id_to_hir_id(body_id)), [(
-                    String::new(),
-                    sym::trivial_bounds,
-                )]);
+                tcx.disabled_nightly_features(
+                    err,
+                    Some(tcx.local_def_id_to_hir_id(body_id)),
+                    [(String::new(), sym::trivial_bounds)],
+                );
             }
             ObligationCauseCode::OpaqueReturnType(expr_info) => {
                 let (expr_ty, expr) = if let Some((expr_ty, hir_id)) = expr_info {
-                    let expr_ty = tcx.short_ty_string(expr_ty, long_ty_file);
+                    let expr_ty = tcx.short_string(expr_ty, err.long_ty_path());
                     let expr = tcx.hir().expect_expr(hir_id);
                     (expr_ty, expr)
                 } else if let Some(body_id) = tcx.hir_node_by_def_id(body_id).body_id()
@@ -3635,7 +3575,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     && let ty::ClauseKind::Trait(pred) = pred.kind().skip_binder()
                     && self.can_eq(param_env, pred.self_ty(), expr_ty)
                 {
-                    let expr_ty = tcx.short_ty_string(expr_ty, long_ty_file);
+                    let expr_ty = tcx.short_string(expr_ty, err.long_ty_path());
                     (expr_ty, expr)
                 } else {
                     return;
@@ -5229,7 +5169,7 @@ pub(super) fn get_explanation_based_on_obligation<'tcx>(
             format!(
                 "{pre_message}the trait `{}` is not implemented for{desc} `{}`",
                 trait_predicate.print_modifiers_and_trait_path(),
-                tcx.short_ty_string(trait_predicate.self_ty().skip_binder(), &mut None),
+                tcx.short_string(trait_predicate.self_ty().skip_binder(), &mut None),
             )
         } else {
             // "the trait bound `T: !Send` is not satisfied" reads better than "`!Send` is
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
index 2dfa72972ba..62cac5b17bd 100644
--- a/compiler/rustc_trait_selection/src/errors.rs
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -12,7 +12,7 @@ use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty};
 use rustc_hir::{self as hir, AmbigArg, FnRetTy, GenericParamKind, Node};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath};
-use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, PolyTraitRef, Region, Ty, TyCtxt};
+use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, Region, Ty, TyCtxt};
 use rustc_span::{BytePos, Ident, Span, Symbol, kw};
 
 use crate::error_reporting::infer::ObligationCauseAsDiagArg;
@@ -23,15 +23,6 @@ use crate::fluent_generated as fluent;
 pub mod note_and_explain;
 
 #[derive(Diagnostic)]
-#[diag(trait_selection_dump_vtable_entries)]
-pub struct DumpVTableEntries<'a> {
-    #[primary_span]
-    pub span: Span,
-    pub trait_ref: PolyTraitRef<'a>,
-    pub entries: String,
-}
-
-#[derive(Diagnostic)]
 #[diag(trait_selection_unable_to_construct_constant_value)]
 pub struct UnableToConstructConstantValue<'a> {
     #[primary_span]
@@ -1400,15 +1391,13 @@ pub struct OpaqueCapturesLifetime<'tcx> {
 pub enum FunctionPointerSuggestion<'a> {
     #[suggestion(
         trait_selection_fps_use_ref,
-        code = "&{fn_name}",
+        code = "&",
         style = "verbose",
         applicability = "maybe-incorrect"
     )]
     UseRef {
         #[primary_span]
         span: Span,
-        #[skip_arg]
-        fn_name: String,
     },
     #[suggestion(
         trait_selection_fps_remove_ref,
@@ -1438,7 +1427,7 @@ pub enum FunctionPointerSuggestion<'a> {
     },
     #[suggestion(
         trait_selection_fps_cast,
-        code = "{fn_name} as {sig}",
+        code = " as {sig}",
         style = "verbose",
         applicability = "maybe-incorrect"
     )]
@@ -1446,13 +1435,11 @@ pub enum FunctionPointerSuggestion<'a> {
         #[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}",
+        code = " as {found_sig}",
         style = "hidden",
         applicability = "maybe-incorrect"
     )]
@@ -1460,8 +1447,6 @@ pub enum FunctionPointerSuggestion<'a> {
         #[primary_span]
         span: Span,
         #[skip_arg]
-        fn_name: String,
-        #[skip_arg]
         found_sig: Binder<'a, FnSig<'a>>,
         expected_sig: Binder<'a, FnSig<'a>>,
     },
@@ -1497,6 +1482,12 @@ pub struct FnConsiderCasting {
 }
 
 #[derive(Subdiagnostic)]
+#[help(trait_selection_fn_consider_casting_both)]
+pub struct FnConsiderCastingBoth<'a> {
+    pub sig: Binder<'a, FnSig<'a>>,
+}
+
+#[derive(Subdiagnostic)]
 pub enum SuggestAccessingField<'a> {
     #[suggestion(
         trait_selection_suggest_accessing_field,
diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs
index 863b6e293ff..55171754618 100644
--- a/compiler/rustc_trait_selection/src/regions.rs
+++ b/compiler/rustc_trait_selection/src/regions.rs
@@ -1,10 +1,73 @@
+use rustc_hir::def_id::LocalDefId;
 use rustc_infer::infer::outlives::env::OutlivesEnvironment;
 use rustc_infer::infer::{InferCtxt, RegionResolutionError};
 use rustc_macros::extension;
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::traits::query::NoSolution;
+use rustc_middle::ty::{self, Ty};
 
 use crate::traits::ScrubbedTraitError;
+use crate::traits::outlives_bounds::InferCtxtExt;
+
+#[extension(pub trait OutlivesEnvironmentBuildExt<'tcx>)]
+impl<'tcx> OutlivesEnvironment<'tcx> {
+    fn new(
+        infcx: &InferCtxt<'tcx>,
+        body_id: LocalDefId,
+        param_env: ty::ParamEnv<'tcx>,
+        assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>,
+    ) -> Self {
+        Self::new_with_implied_bounds_compat(
+            infcx,
+            body_id,
+            param_env,
+            assumed_wf_tys,
+            !infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat,
+        )
+    }
+
+    fn new_with_implied_bounds_compat(
+        infcx: &InferCtxt<'tcx>,
+        body_id: LocalDefId,
+        param_env: ty::ParamEnv<'tcx>,
+        assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>,
+        implied_bounds_compat: bool,
+    ) -> Self {
+        let mut bounds = vec![];
+
+        for bound in param_env.caller_bounds() {
+            if let Some(mut type_outlives) = bound.as_type_outlives_clause() {
+                if infcx.next_trait_solver() {
+                    match crate::solve::deeply_normalize::<_, ScrubbedTraitError<'tcx>>(
+                        infcx.at(&ObligationCause::dummy(), param_env),
+                        type_outlives,
+                    ) {
+                        Ok(new) => type_outlives = new,
+                        Err(_) => {
+                            infcx.dcx().delayed_bug(format!("could not normalize `{bound}`"));
+                        }
+                    }
+                }
+                bounds.push(type_outlives);
+            }
+        }
+
+        // FIXME: This needs to be modified so that we normalize the known type
+        // outlives obligations then elaborate them into their region/type components.
+        // Otherwise, `<W<'a> as Mirror>::Assoc: 'b` will not imply `'a: 'b` even
+        // if we can normalize `'a`.
+        OutlivesEnvironment::from_normalized_bounds(
+            param_env,
+            bounds,
+            infcx.implied_bounds_tys_with_compat(
+                body_id,
+                param_env,
+                assumed_wf_tys,
+                implied_bounds_compat,
+            ),
+        )
+    }
+}
 
 #[extension(pub trait InferCtxtRegionExt<'tcx>)]
 impl<'tcx> InferCtxt<'tcx> {
@@ -16,9 +79,24 @@ impl<'tcx> InferCtxt<'tcx> {
     /// doing something specific for normalization.
     fn resolve_regions(
         &self,
+        body_id: LocalDefId,
+        param_env: ty::ParamEnv<'tcx>,
+        assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>,
+    ) -> Vec<RegionResolutionError<'tcx>> {
+        self.resolve_regions_with_outlives_env(&OutlivesEnvironment::new(
+            self,
+            body_id,
+            param_env,
+            assumed_wf_tys,
+        ))
+    }
+
+    /// Don't call this directly unless you know what you're doing.
+    fn resolve_regions_with_outlives_env(
+        &self,
         outlives_env: &OutlivesEnvironment<'tcx>,
     ) -> Vec<RegionResolutionError<'tcx>> {
-        self.resolve_regions_with_normalize(outlives_env, |ty, origin| {
+        self.resolve_regions_with_normalize(&outlives_env, |ty, origin| {
             let ty = self.resolve_vars_if_possible(ty);
 
             if self.next_trait_solver() {
diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs
index acd00d9f74f..58d8a3a6254 100644
--- a/compiler/rustc_trait_selection/src/solve/delegate.rs
+++ b/compiler/rustc_trait_selection/src/solve/delegate.rs
@@ -1,7 +1,7 @@
 use std::ops::Deref;
 
 use rustc_data_structures::fx::FxHashSet;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
 use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
 use rustc_infer::infer::canonical::{
     Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarInfo, CanonicalVarValues,
@@ -43,8 +43,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
         self.0.tcx
     }
 
-    type Span = Span;
-
     fn build_with_canonical<V>(
         interner: TyCtxt<'tcx>,
         canonical: &CanonicalQueryInput<'tcx, V>,
@@ -98,9 +96,10 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
         param_env: ty::ParamEnv<'tcx>,
         arg: ty::GenericArg<'tcx>,
     ) -> Option<Vec<Goal<'tcx, ty::Predicate<'tcx>>>> {
-        crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg).map(|obligations| {
-            obligations.into_iter().map(|obligation| obligation.into()).collect()
-        })
+        crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg, DUMMY_SP, CRATE_DEF_ID)
+            .map(|obligations| {
+                obligations.into_iter().map(|obligation| obligation.into()).collect()
+            })
     }
 
     fn clone_opaque_types_for_query_response(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> {
@@ -146,9 +145,10 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
     fn instantiate_canonical_var_with_infer(
         &self,
         cv_info: CanonicalVarInfo<'tcx>,
+        span: Span,
         universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
     ) -> ty::GenericArg<'tcx> {
-        self.0.instantiate_canonical_var(DUMMY_SP, cv_info, universe_map)
+        self.0.instantiate_canonical_var(span, cv_info, universe_map)
     }
 
     fn insert_hidden_type(
@@ -174,11 +174,13 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
         self.0.add_item_bounds_for_hidden_type(def_id, args, param_env, hidden_ty, goals);
     }
 
-    fn inject_new_hidden_type_unchecked(&self, key: ty::OpaqueTypeKey<'tcx>, hidden_ty: Ty<'tcx>) {
-        self.0.inject_new_hidden_type_unchecked(key, ty::OpaqueHiddenType {
-            ty: hidden_ty,
-            span: DUMMY_SP,
-        })
+    fn inject_new_hidden_type_unchecked(
+        &self,
+        key: ty::OpaqueTypeKey<'tcx>,
+        hidden_ty: Ty<'tcx>,
+        span: Span,
+    ) {
+        self.0.inject_new_hidden_type_unchecked(key, ty::OpaqueHiddenType { ty: hidden_ty, span })
     }
 
     fn reset_opaque_types(&self) {
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 2b7da4bc5ff..c238e708ab8 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -1,25 +1,21 @@
 use std::marker::PhantomData;
 use std::mem;
-use std::ops::ControlFlow;
 
 use rustc_data_structures::thinvec::ExtractIf;
 use rustc_infer::infer::InferCtxt;
 use rustc_infer::traits::query::NoSolution;
-use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause};
 use rustc_infer::traits::{
-    self, FromSolverError, MismatchedProjectionTypes, Obligation, ObligationCause,
-    ObligationCauseCode, PredicateObligation, PredicateObligations, SelectionError, TraitEngine,
+    FromSolverError, PredicateObligation, PredicateObligations, TraitEngine,
 };
-use rustc_middle::ty::error::{ExpectedFound, TypeError};
-use rustc_middle::ty::{self, TyCtxt};
-use rustc_middle::{bug, span_bug};
 use rustc_next_trait_solver::solve::{GenerateProofTree, HasChanged, SolverDelegateEvalExt as _};
-use tracing::{instrument, trace};
+use tracing::instrument;
 
+use self::derive_errors::*;
 use super::Certainty;
 use super::delegate::SolverDelegate;
-use super::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor};
-use crate::traits::{FulfillmentError, FulfillmentErrorCode, ScrubbedTraitError};
+use crate::traits::{FulfillmentError, ScrubbedTraitError};
+
+mod derive_errors;
 
 /// A trait engine using the new trait solver.
 ///
@@ -86,7 +82,7 @@ impl<'tcx> ObligationStorage<'tcx> {
             self.overflowed.extend(ExtractIf::new(&mut self.pending, |o| {
                 let goal = o.clone().into();
                 let result = <&SolverDelegate<'tcx>>::from(infcx)
-                    .evaluate_root_goal(goal, GenerateProofTree::No)
+                    .evaluate_root_goal(goal, GenerateProofTree::No, o.cause.span)
                     .0;
                 matches!(result, Ok((HasChanged::Yes, _)))
             }));
@@ -167,7 +163,7 @@ where
             for obligation in self.obligations.unstalled_for_select() {
                 let goal = obligation.clone().into();
                 let result = <&SolverDelegate<'tcx>>::from(infcx)
-                    .evaluate_root_goal(goal, GenerateProofTree::No)
+                    .evaluate_root_goal(goal, GenerateProofTree::No, obligation.cause.span)
                     .0;
                 self.inspect_evaluated_obligation(infcx, &obligation, &result);
                 let (changed, certainty) = match result {
@@ -244,483 +240,3 @@ impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<'
         }
     }
 }
-
-fn fulfillment_error_for_no_solution<'tcx>(
-    infcx: &InferCtxt<'tcx>,
-    root_obligation: PredicateObligation<'tcx>,
-) -> FulfillmentError<'tcx> {
-    let obligation = find_best_leaf_obligation(infcx, &root_obligation, false);
-
-    let code = match obligation.predicate.kind().skip_binder() {
-        ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => {
-            FulfillmentErrorCode::Project(
-                // FIXME: This could be a `Sorts` if the term is a type
-                MismatchedProjectionTypes { err: TypeError::Mismatch },
-            )
-        }
-        ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, expected_ty)) => {
-            let ct_ty = match ct.kind() {
-                ty::ConstKind::Unevaluated(uv) => {
-                    infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args)
-                }
-                ty::ConstKind::Param(param_ct) => param_ct.find_ty_from_env(obligation.param_env),
-                ty::ConstKind::Value(ty, _) => ty,
-                kind => span_bug!(
-                    obligation.cause.span,
-                    "ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}"
-                ),
-            };
-            FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType {
-                ct,
-                ct_ty,
-                expected_ty,
-            })
-        }
-        ty::PredicateKind::NormalizesTo(..) => {
-            FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
-        }
-        ty::PredicateKind::AliasRelate(_, _, _) => {
-            FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
-        }
-        ty::PredicateKind::Subtype(pred) => {
-            let (a, b) = infcx.enter_forall_and_leak_universe(
-                obligation.predicate.kind().rebind((pred.a, pred.b)),
-            );
-            let expected_found = ExpectedFound::new(a, b);
-            FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
-        }
-        ty::PredicateKind::Coerce(pred) => {
-            let (a, b) = infcx.enter_forall_and_leak_universe(
-                obligation.predicate.kind().rebind((pred.a, pred.b)),
-            );
-            let expected_found = ExpectedFound::new(b, a);
-            FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
-        }
-        ty::PredicateKind::Clause(_)
-        | ty::PredicateKind::DynCompatible(_)
-        | ty::PredicateKind::Ambiguous => {
-            FulfillmentErrorCode::Select(SelectionError::Unimplemented)
-        }
-        ty::PredicateKind::ConstEquate(..) => {
-            bug!("unexpected goal: {obligation:?}")
-        }
-    };
-
-    FulfillmentError { obligation, code, root_obligation }
-}
-
-fn fulfillment_error_for_stalled<'tcx>(
-    infcx: &InferCtxt<'tcx>,
-    root_obligation: PredicateObligation<'tcx>,
-) -> FulfillmentError<'tcx> {
-    let (code, refine_obligation) = infcx.probe(|_| {
-        match <&SolverDelegate<'tcx>>::from(infcx)
-            .evaluate_root_goal(root_obligation.clone().into(), GenerateProofTree::No)
-            .0
-        {
-            Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => {
-                (FulfillmentErrorCode::Ambiguity { overflow: None }, true)
-            }
-            Ok((_, Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }))) => (
-                FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) },
-                // Don't look into overflows because we treat overflows weirdly anyways.
-                // We discard the inference constraints from overflowing goals, so
-                // recomputing the goal again during `find_best_leaf_obligation` may apply
-                // inference guidance that makes other goals go from ambig -> pass, for example.
-                //
-                // FIXME: We should probably just look into overflows here.
-                false,
-            ),
-            Ok((_, Certainty::Yes)) => {
-                bug!("did not expect successful goal when collecting ambiguity errors")
-            }
-            Err(_) => {
-                bug!("did not expect selection error when collecting ambiguity errors")
-            }
-        }
-    });
-
-    FulfillmentError {
-        obligation: if refine_obligation {
-            find_best_leaf_obligation(infcx, &root_obligation, true)
-        } else {
-            root_obligation.clone()
-        },
-        code,
-        root_obligation,
-    }
-}
-
-fn fulfillment_error_for_overflow<'tcx>(
-    infcx: &InferCtxt<'tcx>,
-    root_obligation: PredicateObligation<'tcx>,
-) -> FulfillmentError<'tcx> {
-    FulfillmentError {
-        obligation: find_best_leaf_obligation(infcx, &root_obligation, true),
-        code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) },
-        root_obligation,
-    }
-}
-
-fn find_best_leaf_obligation<'tcx>(
-    infcx: &InferCtxt<'tcx>,
-    obligation: &PredicateObligation<'tcx>,
-    consider_ambiguities: bool,
-) -> PredicateObligation<'tcx> {
-    let obligation = infcx.resolve_vars_if_possible(obligation.clone());
-    // FIXME: we use a probe here as the `BestObligation` visitor does not
-    // check whether it uses candidates which get shadowed by where-bounds.
-    //
-    // We should probably fix the visitor to not do so instead, as this also
-    // means the leaf obligation may be incorrect.
-    infcx
-        .fudge_inference_if_ok(|| {
-            infcx
-                .visit_proof_tree(obligation.clone().into(), &mut BestObligation {
-                    obligation: obligation.clone(),
-                    consider_ambiguities,
-                })
-                .break_value()
-                .ok_or(())
-        })
-        .unwrap_or(obligation)
-}
-
-struct BestObligation<'tcx> {
-    obligation: PredicateObligation<'tcx>,
-    consider_ambiguities: bool,
-}
-
-impl<'tcx> BestObligation<'tcx> {
-    fn with_derived_obligation(
-        &mut self,
-        derived_obligation: PredicateObligation<'tcx>,
-        and_then: impl FnOnce(&mut Self) -> <Self as ProofTreeVisitor<'tcx>>::Result,
-    ) -> <Self as ProofTreeVisitor<'tcx>>::Result {
-        let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation);
-        let res = and_then(self);
-        self.obligation = old_obligation;
-        res
-    }
-
-    /// Filter out the candidates that aren't interesting to visit for the
-    /// purposes of reporting errors. For ambiguities, we only consider
-    /// candidates that may hold. For errors, we only consider candidates that
-    /// *don't* hold and which have impl-where clauses that also don't hold.
-    fn non_trivial_candidates<'a>(
-        &self,
-        goal: &'a inspect::InspectGoal<'a, 'tcx>,
-    ) -> Vec<inspect::InspectCandidate<'a, 'tcx>> {
-        let mut candidates = goal.candidates();
-        match self.consider_ambiguities {
-            true => {
-                // If we have an ambiguous obligation, we must consider *all* candidates
-                // that hold, or else we may guide inference causing other goals to go
-                // from ambig -> pass/fail.
-                candidates.retain(|candidate| candidate.result().is_ok());
-            }
-            false => {
-                // If we have >1 candidate, one may still be due to "boring" reasons, like
-                // an alias-relate that failed to hold when deeply evaluated. We really
-                // don't care about reasons like this.
-                if candidates.len() > 1 {
-                    candidates.retain(|candidate| {
-                        goal.infcx().probe(|_| {
-                            candidate.instantiate_nested_goals(self.span()).iter().any(
-                                |nested_goal| {
-                                    matches!(
-                                        nested_goal.source(),
-                                        GoalSource::ImplWhereBound
-                                            | GoalSource::AliasBoundConstCondition
-                                            | GoalSource::InstantiateHigherRanked
-                                            | GoalSource::AliasWellFormed
-                                    ) && match self.consider_ambiguities {
-                                        true => {
-                                            matches!(
-                                                nested_goal.result(),
-                                                Ok(Certainty::Maybe(MaybeCause::Ambiguity))
-                                            )
-                                        }
-                                        false => matches!(nested_goal.result(), Err(_)),
-                                    }
-                                },
-                            )
-                        })
-                    });
-                }
-
-                // Prefer a non-rigid candidate if there is one.
-                if candidates.len() > 1 {
-                    candidates.retain(|candidate| {
-                        !matches!(candidate.kind(), inspect::ProbeKind::RigidAlias { .. })
-                    });
-                }
-            }
-        }
-
-        candidates
-    }
-}
-
-impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
-    type Result = ControlFlow<PredicateObligation<'tcx>>;
-
-    fn span(&self) -> rustc_span::Span {
-        self.obligation.cause.span
-    }
-
-    #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))]
-    fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
-        let candidates = self.non_trivial_candidates(goal);
-        trace!(candidates = ?candidates.iter().map(|c| c.kind()).collect::<Vec<_>>());
-
-        let [candidate] = candidates.as_slice() else {
-            return ControlFlow::Break(self.obligation.clone());
-        };
-
-        // Don't walk into impls that have `do_not_recommend`.
-        if let inspect::ProbeKind::TraitCandidate {
-            source: CandidateSource::Impl(impl_def_id),
-            result: _,
-        } = candidate.kind()
-            && goal.infcx().tcx.do_not_recommend_impl(impl_def_id)
-        {
-            return ControlFlow::Break(self.obligation.clone());
-        }
-
-        let tcx = goal.infcx().tcx;
-        // FIXME: Also, what about considering >1 layer up the stack? May be necessary
-        // for normalizes-to.
-        let pred_kind = goal.goal().predicate.kind();
-        let child_mode = match pred_kind.skip_binder() {
-            ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
-                ChildMode::Trait(pred_kind.rebind(pred))
-            }
-            ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(pred)) => {
-                ChildMode::Host(pred_kind.rebind(pred))
-            }
-            ty::PredicateKind::NormalizesTo(normalizes_to)
-                if matches!(
-                    normalizes_to.alias.kind(tcx),
-                    ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst
-                ) =>
-            {
-                ChildMode::Trait(pred_kind.rebind(ty::TraitPredicate {
-                    trait_ref: normalizes_to.alias.trait_ref(tcx),
-                    polarity: ty::PredicatePolarity::Positive,
-                }))
-            }
-            _ => ChildMode::PassThrough,
-        };
-
-        let nested_goals = candidate.instantiate_nested_goals(self.span());
-
-        // If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as
-        // an actual candidate, instead we should treat them as if the impl was never considered to
-        // have potentially applied. As if `impl<A, R> Trait for for<..> fn(..A) -> R` was written
-        // instead of `impl<T: FnPtr> Trait for T`.
-        //
-        // We do this as a separate loop so that we do not choose to tell the user about some nested
-        // goal before we encounter a `T: FnPtr` nested goal.
-        for nested_goal in &nested_goals {
-            if let Some(fn_ptr_trait) = tcx.lang_items().fn_ptr_trait()
-                && let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause()
-                && poly_trait_pred.def_id() == fn_ptr_trait
-                && let Err(NoSolution) = nested_goal.result()
-            {
-                return ControlFlow::Break(self.obligation.clone());
-            }
-        }
-
-        let mut impl_where_bound_count = 0;
-        for nested_goal in nested_goals {
-            trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result()));
-
-            let make_obligation = |cause| Obligation {
-                cause,
-                param_env: nested_goal.goal().param_env,
-                predicate: nested_goal.goal().predicate,
-                recursion_depth: self.obligation.recursion_depth + 1,
-            };
-
-            let obligation;
-            match (child_mode, nested_goal.source()) {
-                (ChildMode::Trait(_) | ChildMode::Host(_), GoalSource::Misc) => {
-                    continue;
-                }
-                (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => {
-                    obligation = make_obligation(derive_cause(
-                        tcx,
-                        candidate.kind(),
-                        self.obligation.cause.clone(),
-                        impl_where_bound_count,
-                        parent_trait_pred,
-                    ));
-                    impl_where_bound_count += 1;
-                }
-                (
-                    ChildMode::Host(parent_host_pred),
-                    GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition,
-                ) => {
-                    obligation = make_obligation(derive_host_cause(
-                        tcx,
-                        candidate.kind(),
-                        self.obligation.cause.clone(),
-                        impl_where_bound_count,
-                        parent_host_pred,
-                    ));
-                    impl_where_bound_count += 1;
-                }
-                // Skip over a higher-ranked predicate.
-                (_, GoalSource::InstantiateHigherRanked) => {
-                    obligation = self.obligation.clone();
-                }
-                (ChildMode::PassThrough, _)
-                | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => {
-                    obligation = make_obligation(self.obligation.cause.clone());
-                }
-            }
-
-            // Skip nested goals that aren't the *reason* for our goal's failure.
-            match self.consider_ambiguities {
-                true if matches!(
-                    nested_goal.result(),
-                    Ok(Certainty::Maybe(MaybeCause::Ambiguity))
-                ) => {}
-                false if matches!(nested_goal.result(), Err(_)) => {}
-                _ => continue,
-            }
-
-            self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
-        }
-
-        // alias-relate may fail because the lhs or rhs can't be normalized,
-        // and therefore is treated as rigid.
-        if let Some(ty::PredicateKind::AliasRelate(lhs, rhs, _)) = pred_kind.no_bound_vars() {
-            if let Some(obligation) = goal
-                .infcx()
-                .visit_proof_tree_at_depth(
-                    goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(lhs.into())),
-                    goal.depth() + 1,
-                    self,
-                )
-                .break_value()
-            {
-                return ControlFlow::Break(obligation);
-            } else if let Some(obligation) = goal
-                .infcx()
-                .visit_proof_tree_at_depth(
-                    goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(rhs.into())),
-                    goal.depth() + 1,
-                    self,
-                )
-                .break_value()
-            {
-                return ControlFlow::Break(obligation);
-            }
-        }
-
-        ControlFlow::Break(self.obligation.clone())
-    }
-}
-
-#[derive(Debug, Copy, Clone)]
-enum ChildMode<'tcx> {
-    // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
-    // and skip all `GoalSource::Misc`, which represent useless obligations
-    // such as alias-eq which may not hold.
-    Trait(ty::PolyTraitPredicate<'tcx>),
-    // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
-    // and skip all `GoalSource::Misc`, which represent useless obligations
-    // such as alias-eq which may not hold.
-    Host(ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>),
-    // Skip trying to derive an `ObligationCause` from this obligation, and
-    // report *all* sub-obligations as if they came directly from the parent
-    // obligation.
-    PassThrough,
-}
-
-fn derive_cause<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    candidate_kind: inspect::ProbeKind<TyCtxt<'tcx>>,
-    mut cause: ObligationCause<'tcx>,
-    idx: usize,
-    parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
-) -> ObligationCause<'tcx> {
-    match candidate_kind {
-        inspect::ProbeKind::TraitCandidate {
-            source: CandidateSource::Impl(impl_def_id),
-            result: _,
-        } => {
-            if let Some((_, span)) =
-                tcx.predicates_of(impl_def_id).instantiate_identity(tcx).iter().nth(idx)
-            {
-                cause = cause.derived_cause(parent_trait_pred, |derived| {
-                    ObligationCauseCode::ImplDerived(Box::new(traits::ImplDerivedCause {
-                        derived,
-                        impl_or_alias_def_id: impl_def_id,
-                        impl_def_predicate_index: Some(idx),
-                        span,
-                    }))
-                })
-            }
-        }
-        inspect::ProbeKind::TraitCandidate {
-            source: CandidateSource::BuiltinImpl(..),
-            result: _,
-        } => {
-            cause = cause.derived_cause(parent_trait_pred, ObligationCauseCode::BuiltinDerived);
-        }
-        _ => {}
-    };
-    cause
-}
-
-fn derive_host_cause<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    candidate_kind: inspect::ProbeKind<TyCtxt<'tcx>>,
-    mut cause: ObligationCause<'tcx>,
-    idx: usize,
-    parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
-) -> ObligationCause<'tcx> {
-    match candidate_kind {
-        inspect::ProbeKind::TraitCandidate {
-            source: CandidateSource::Impl(impl_def_id),
-            result: _,
-        } => {
-            if let Some((_, span)) = tcx
-                .predicates_of(impl_def_id)
-                .instantiate_identity(tcx)
-                .into_iter()
-                .chain(tcx.const_conditions(impl_def_id).instantiate_identity(tcx).into_iter().map(
-                    |(trait_ref, span)| {
-                        (
-                            trait_ref.to_host_effect_clause(
-                                tcx,
-                                parent_host_pred.skip_binder().constness,
-                            ),
-                            span,
-                        )
-                    },
-                ))
-                .nth(idx)
-            {
-                cause =
-                    cause.derived_host_cause(parent_host_pred, |derived| {
-                        ObligationCauseCode::ImplDerivedHost(Box::new(
-                            traits::ImplDerivedHostCause { derived, impl_def_id, span },
-                        ))
-                    })
-            }
-        }
-        inspect::ProbeKind::TraitCandidate {
-            source: CandidateSource::BuiltinImpl(..),
-            result: _,
-        } => {
-            cause =
-                cause.derived_host_cause(parent_host_pred, ObligationCauseCode::BuiltinDerivedHost);
-        }
-        _ => {}
-    };
-    cause
-}
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
new file mode 100644
index 00000000000..7364b4aa343
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
@@ -0,0 +1,531 @@
+use std::ops::ControlFlow;
+
+use rustc_infer::infer::InferCtxt;
+use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause};
+use rustc_infer::traits::{
+    self, MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode,
+    PredicateObligation, SelectionError,
+};
+use rustc_middle::ty::error::{ExpectedFound, TypeError};
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::{bug, span_bug};
+use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as _};
+use rustc_type_ir::solve::{Goal, NoSolution};
+use tracing::{instrument, trace};
+
+use crate::solve::Certainty;
+use crate::solve::delegate::SolverDelegate;
+use crate::solve::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor};
+use crate::traits::{FulfillmentError, FulfillmentErrorCode, wf};
+
+pub(super) fn fulfillment_error_for_no_solution<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    root_obligation: PredicateObligation<'tcx>,
+) -> FulfillmentError<'tcx> {
+    let obligation = find_best_leaf_obligation(infcx, &root_obligation, false);
+
+    let code = match obligation.predicate.kind().skip_binder() {
+        ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => {
+            FulfillmentErrorCode::Project(
+                // FIXME: This could be a `Sorts` if the term is a type
+                MismatchedProjectionTypes { err: TypeError::Mismatch },
+            )
+        }
+        ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, expected_ty)) => {
+            let ct_ty = match ct.kind() {
+                ty::ConstKind::Unevaluated(uv) => {
+                    infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args)
+                }
+                ty::ConstKind::Param(param_ct) => param_ct.find_ty_from_env(obligation.param_env),
+                ty::ConstKind::Value(cv) => cv.ty,
+                kind => span_bug!(
+                    obligation.cause.span,
+                    "ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}"
+                ),
+            };
+            FulfillmentErrorCode::Select(SelectionError::ConstArgHasWrongType {
+                ct,
+                ct_ty,
+                expected_ty,
+            })
+        }
+        ty::PredicateKind::NormalizesTo(..) => {
+            FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
+        }
+        ty::PredicateKind::AliasRelate(_, _, _) => {
+            FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
+        }
+        ty::PredicateKind::Subtype(pred) => {
+            let (a, b) = infcx.enter_forall_and_leak_universe(
+                obligation.predicate.kind().rebind((pred.a, pred.b)),
+            );
+            let expected_found = ExpectedFound::new(a, b);
+            FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
+        }
+        ty::PredicateKind::Coerce(pred) => {
+            let (a, b) = infcx.enter_forall_and_leak_universe(
+                obligation.predicate.kind().rebind((pred.a, pred.b)),
+            );
+            let expected_found = ExpectedFound::new(b, a);
+            FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
+        }
+        ty::PredicateKind::Clause(_)
+        | ty::PredicateKind::DynCompatible(_)
+        | ty::PredicateKind::Ambiguous => {
+            FulfillmentErrorCode::Select(SelectionError::Unimplemented)
+        }
+        ty::PredicateKind::ConstEquate(..) => {
+            bug!("unexpected goal: {obligation:?}")
+        }
+    };
+
+    FulfillmentError { obligation, code, root_obligation }
+}
+
+pub(super) fn fulfillment_error_for_stalled<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    root_obligation: PredicateObligation<'tcx>,
+) -> FulfillmentError<'tcx> {
+    let (code, refine_obligation) = infcx.probe(|_| {
+        match <&SolverDelegate<'tcx>>::from(infcx)
+            .evaluate_root_goal(
+                root_obligation.clone().into(),
+                GenerateProofTree::No,
+                root_obligation.cause.span,
+            )
+            .0
+        {
+            Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => {
+                (FulfillmentErrorCode::Ambiguity { overflow: None }, true)
+            }
+            Ok((_, Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }))) => (
+                FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) },
+                // Don't look into overflows because we treat overflows weirdly anyways.
+                // We discard the inference constraints from overflowing goals, so
+                // recomputing the goal again during `find_best_leaf_obligation` may apply
+                // inference guidance that makes other goals go from ambig -> pass, for example.
+                //
+                // FIXME: We should probably just look into overflows here.
+                false,
+            ),
+            Ok((_, Certainty::Yes)) => {
+                bug!("did not expect successful goal when collecting ambiguity errors")
+            }
+            Err(_) => {
+                bug!("did not expect selection error when collecting ambiguity errors")
+            }
+        }
+    });
+
+    FulfillmentError {
+        obligation: if refine_obligation {
+            find_best_leaf_obligation(infcx, &root_obligation, true)
+        } else {
+            root_obligation.clone()
+        },
+        code,
+        root_obligation,
+    }
+}
+
+pub(super) fn fulfillment_error_for_overflow<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    root_obligation: PredicateObligation<'tcx>,
+) -> FulfillmentError<'tcx> {
+    FulfillmentError {
+        obligation: find_best_leaf_obligation(infcx, &root_obligation, true),
+        code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) },
+        root_obligation,
+    }
+}
+
+fn find_best_leaf_obligation<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    obligation: &PredicateObligation<'tcx>,
+    consider_ambiguities: bool,
+) -> PredicateObligation<'tcx> {
+    let obligation = infcx.resolve_vars_if_possible(obligation.clone());
+    // FIXME: we use a probe here as the `BestObligation` visitor does not
+    // check whether it uses candidates which get shadowed by where-bounds.
+    //
+    // We should probably fix the visitor to not do so instead, as this also
+    // means the leaf obligation may be incorrect.
+    infcx
+        .fudge_inference_if_ok(|| {
+            infcx
+                .visit_proof_tree(
+                    obligation.clone().into(),
+                    &mut BestObligation { obligation: obligation.clone(), consider_ambiguities },
+                )
+                .break_value()
+                .ok_or(())
+        })
+        .unwrap_or(obligation)
+}
+
+struct BestObligation<'tcx> {
+    obligation: PredicateObligation<'tcx>,
+    consider_ambiguities: bool,
+}
+
+impl<'tcx> BestObligation<'tcx> {
+    fn with_derived_obligation(
+        &mut self,
+        derived_obligation: PredicateObligation<'tcx>,
+        and_then: impl FnOnce(&mut Self) -> <Self as ProofTreeVisitor<'tcx>>::Result,
+    ) -> <Self as ProofTreeVisitor<'tcx>>::Result {
+        let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation);
+        let res = and_then(self);
+        self.obligation = old_obligation;
+        res
+    }
+
+    /// Filter out the candidates that aren't interesting to visit for the
+    /// purposes of reporting errors. For ambiguities, we only consider
+    /// candidates that may hold. For errors, we only consider candidates that
+    /// *don't* hold and which have impl-where clauses that also don't hold.
+    fn non_trivial_candidates<'a>(
+        &self,
+        goal: &'a inspect::InspectGoal<'a, 'tcx>,
+    ) -> Vec<inspect::InspectCandidate<'a, 'tcx>> {
+        let mut candidates = goal.candidates();
+        match self.consider_ambiguities {
+            true => {
+                // If we have an ambiguous obligation, we must consider *all* candidates
+                // that hold, or else we may guide inference causing other goals to go
+                // from ambig -> pass/fail.
+                candidates.retain(|candidate| candidate.result().is_ok());
+            }
+            false => {
+                // If we have >1 candidate, one may still be due to "boring" reasons, like
+                // an alias-relate that failed to hold when deeply evaluated. We really
+                // don't care about reasons like this.
+                if candidates.len() > 1 {
+                    candidates.retain(|candidate| {
+                        goal.infcx().probe(|_| {
+                            candidate.instantiate_nested_goals(self.span()).iter().any(
+                                |nested_goal| {
+                                    matches!(
+                                        nested_goal.source(),
+                                        GoalSource::ImplWhereBound
+                                            | GoalSource::AliasBoundConstCondition
+                                            | GoalSource::InstantiateHigherRanked
+                                            | GoalSource::AliasWellFormed
+                                    ) && match (self.consider_ambiguities, nested_goal.result()) {
+                                        (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity)))
+                                        | (false, Err(_)) => true,
+                                        _ => false,
+                                    }
+                                },
+                            )
+                        })
+                    });
+                }
+
+                // Prefer a non-rigid candidate if there is one.
+                if candidates.len() > 1 {
+                    candidates.retain(|candidate| {
+                        !matches!(candidate.kind(), inspect::ProbeKind::RigidAlias { .. })
+                    });
+                }
+            }
+        }
+
+        candidates
+    }
+
+    /// HACK: We walk the nested obligations for a well-formed arg manually,
+    /// since there's nontrivial logic in `wf.rs` to set up an obligation cause.
+    /// Ideally we'd be able to track this better.
+    fn visit_well_formed_goal(
+        &mut self,
+        candidate: &inspect::InspectCandidate<'_, 'tcx>,
+        arg: ty::GenericArg<'tcx>,
+    ) -> ControlFlow<PredicateObligation<'tcx>> {
+        let infcx = candidate.goal().infcx();
+        let param_env = candidate.goal().goal().param_env;
+        let body_id = self.obligation.cause.body_id;
+
+        for obligation in wf::unnormalized_obligations(infcx, param_env, arg, self.span(), body_id)
+            .into_iter()
+            .flatten()
+        {
+            let nested_goal = candidate.instantiate_proof_tree_for_nested_goal(
+                GoalSource::Misc,
+                Goal::new(infcx.tcx, obligation.param_env, obligation.predicate),
+                self.span(),
+            );
+            // Skip nested goals that aren't the *reason* for our goal's failure.
+            match (self.consider_ambiguities, nested_goal.result()) {
+                (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {}
+                _ => continue,
+            }
+
+            self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
+        }
+
+        ControlFlow::Break(self.obligation.clone())
+    }
+}
+
+impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
+    type Result = ControlFlow<PredicateObligation<'tcx>>;
+
+    fn span(&self) -> rustc_span::Span {
+        self.obligation.cause.span
+    }
+
+    #[instrument(level = "trace", skip(self, goal), fields(goal = ?goal.goal()))]
+    fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
+        let candidates = self.non_trivial_candidates(goal);
+        trace!(candidates = ?candidates.iter().map(|c| c.kind()).collect::<Vec<_>>());
+
+        let [candidate] = candidates.as_slice() else {
+            return ControlFlow::Break(self.obligation.clone());
+        };
+
+        // Don't walk into impls that have `do_not_recommend`.
+        if let inspect::ProbeKind::TraitCandidate {
+            source: CandidateSource::Impl(impl_def_id),
+            result: _,
+        } = candidate.kind()
+            && goal.infcx().tcx.do_not_recommend_impl(impl_def_id)
+        {
+            return ControlFlow::Break(self.obligation.clone());
+        }
+
+        let tcx = goal.infcx().tcx;
+        // FIXME: Also, what about considering >1 layer up the stack? May be necessary
+        // for normalizes-to.
+        let pred_kind = goal.goal().predicate.kind();
+        let child_mode = match pred_kind.skip_binder() {
+            ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
+                ChildMode::Trait(pred_kind.rebind(pred))
+            }
+            ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(pred)) => {
+                ChildMode::Host(pred_kind.rebind(pred))
+            }
+            ty::PredicateKind::NormalizesTo(normalizes_to)
+                if matches!(
+                    normalizes_to.alias.kind(tcx),
+                    ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst
+                ) =>
+            {
+                ChildMode::Trait(pred_kind.rebind(ty::TraitPredicate {
+                    trait_ref: normalizes_to.alias.trait_ref(tcx),
+                    polarity: ty::PredicatePolarity::Positive,
+                }))
+            }
+            ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
+                return self.visit_well_formed_goal(candidate, arg);
+            }
+            _ => ChildMode::PassThrough,
+        };
+
+        let nested_goals = candidate.instantiate_nested_goals(self.span());
+
+        // If the candidate requires some `T: FnPtr` bound which does not hold should not be treated as
+        // an actual candidate, instead we should treat them as if the impl was never considered to
+        // have potentially applied. As if `impl<A, R> Trait for for<..> fn(..A) -> R` was written
+        // instead of `impl<T: FnPtr> Trait for T`.
+        //
+        // We do this as a separate loop so that we do not choose to tell the user about some nested
+        // goal before we encounter a `T: FnPtr` nested goal.
+        for nested_goal in &nested_goals {
+            if let Some(fn_ptr_trait) = tcx.lang_items().fn_ptr_trait()
+                && let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause()
+                && poly_trait_pred.def_id() == fn_ptr_trait
+                && let Err(NoSolution) = nested_goal.result()
+            {
+                return ControlFlow::Break(self.obligation.clone());
+            }
+        }
+
+        let mut impl_where_bound_count = 0;
+        for nested_goal in nested_goals {
+            trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result()));
+
+            let make_obligation = |cause| Obligation {
+                cause,
+                param_env: nested_goal.goal().param_env,
+                predicate: nested_goal.goal().predicate,
+                recursion_depth: self.obligation.recursion_depth + 1,
+            };
+
+            let obligation;
+            match (child_mode, nested_goal.source()) {
+                (ChildMode::Trait(_) | ChildMode::Host(_), GoalSource::Misc) => {
+                    continue;
+                }
+                (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => {
+                    obligation = make_obligation(derive_cause(
+                        tcx,
+                        candidate.kind(),
+                        self.obligation.cause.clone(),
+                        impl_where_bound_count,
+                        parent_trait_pred,
+                    ));
+                    impl_where_bound_count += 1;
+                }
+                (
+                    ChildMode::Host(parent_host_pred),
+                    GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition,
+                ) => {
+                    obligation = make_obligation(derive_host_cause(
+                        tcx,
+                        candidate.kind(),
+                        self.obligation.cause.clone(),
+                        impl_where_bound_count,
+                        parent_host_pred,
+                    ));
+                    impl_where_bound_count += 1;
+                }
+                // Skip over a higher-ranked predicate.
+                (_, GoalSource::InstantiateHigherRanked) => {
+                    obligation = self.obligation.clone();
+                }
+                (ChildMode::PassThrough, _)
+                | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => {
+                    obligation = make_obligation(self.obligation.cause.clone());
+                }
+            }
+
+            // Skip nested goals that aren't the *reason* for our goal's failure.
+            match (self.consider_ambiguities, nested_goal.result()) {
+                (true, Ok(Certainty::Maybe(MaybeCause::Ambiguity))) | (false, Err(_)) => {}
+                _ => continue,
+            }
+
+            self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
+        }
+
+        // alias-relate may fail because the lhs or rhs can't be normalized,
+        // and therefore is treated as rigid.
+        if let Some(ty::PredicateKind::AliasRelate(lhs, rhs, _)) = pred_kind.no_bound_vars() {
+            if let Some(obligation) = goal
+                .infcx()
+                .visit_proof_tree_at_depth(
+                    goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(lhs.into())),
+                    goal.depth() + 1,
+                    self,
+                )
+                .break_value()
+            {
+                return ControlFlow::Break(obligation);
+            } else if let Some(obligation) = goal
+                .infcx()
+                .visit_proof_tree_at_depth(
+                    goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(rhs.into())),
+                    goal.depth() + 1,
+                    self,
+                )
+                .break_value()
+            {
+                return ControlFlow::Break(obligation);
+            }
+        }
+
+        ControlFlow::Break(self.obligation.clone())
+    }
+}
+
+#[derive(Debug, Copy, Clone)]
+enum ChildMode<'tcx> {
+    // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
+    // and skip all `GoalSource::Misc`, which represent useless obligations
+    // such as alias-eq which may not hold.
+    Trait(ty::PolyTraitPredicate<'tcx>),
+    // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
+    // and skip all `GoalSource::Misc`, which represent useless obligations
+    // such as alias-eq which may not hold.
+    Host(ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>),
+    // Skip trying to derive an `ObligationCause` from this obligation, and
+    // report *all* sub-obligations as if they came directly from the parent
+    // obligation.
+    PassThrough,
+}
+
+fn derive_cause<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    candidate_kind: inspect::ProbeKind<TyCtxt<'tcx>>,
+    mut cause: ObligationCause<'tcx>,
+    idx: usize,
+    parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
+) -> ObligationCause<'tcx> {
+    match candidate_kind {
+        inspect::ProbeKind::TraitCandidate {
+            source: CandidateSource::Impl(impl_def_id),
+            result: _,
+        } => {
+            if let Some((_, span)) =
+                tcx.predicates_of(impl_def_id).instantiate_identity(tcx).iter().nth(idx)
+            {
+                cause = cause.derived_cause(parent_trait_pred, |derived| {
+                    ObligationCauseCode::ImplDerived(Box::new(traits::ImplDerivedCause {
+                        derived,
+                        impl_or_alias_def_id: impl_def_id,
+                        impl_def_predicate_index: Some(idx),
+                        span,
+                    }))
+                })
+            }
+        }
+        inspect::ProbeKind::TraitCandidate {
+            source: CandidateSource::BuiltinImpl(..),
+            result: _,
+        } => {
+            cause = cause.derived_cause(parent_trait_pred, ObligationCauseCode::BuiltinDerived);
+        }
+        _ => {}
+    };
+    cause
+}
+
+fn derive_host_cause<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    candidate_kind: inspect::ProbeKind<TyCtxt<'tcx>>,
+    mut cause: ObligationCause<'tcx>,
+    idx: usize,
+    parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
+) -> ObligationCause<'tcx> {
+    match candidate_kind {
+        inspect::ProbeKind::TraitCandidate {
+            source: CandidateSource::Impl(impl_def_id),
+            result: _,
+        } => {
+            if let Some((_, span)) = tcx
+                .predicates_of(impl_def_id)
+                .instantiate_identity(tcx)
+                .into_iter()
+                .chain(tcx.const_conditions(impl_def_id).instantiate_identity(tcx).into_iter().map(
+                    |(trait_ref, span)| {
+                        (
+                            trait_ref.to_host_effect_clause(
+                                tcx,
+                                parent_host_pred.skip_binder().constness,
+                            ),
+                            span,
+                        )
+                    },
+                ))
+                .nth(idx)
+            {
+                cause =
+                    cause.derived_host_cause(parent_host_pred, |derived| {
+                        ObligationCauseCode::ImplDerivedHost(Box::new(
+                            traits::ImplDerivedHostCause { derived, impl_def_id, span },
+                        ))
+                    })
+            }
+        }
+        inspect::ProbeKind::TraitCandidate {
+            source: CandidateSource::BuiltinImpl(..),
+            result: _,
+        } => {
+            cause =
+                cause.derived_host_cause(parent_host_pred, ObligationCauseCode::BuiltinDerivedHost);
+        }
+        _ => {}
+    };
+    cause
+}
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
index e735020a63e..9f4ee54bd4c 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
@@ -194,47 +194,57 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
 
         let goals = instantiated_goals
             .into_iter()
-            .map(|(source, goal)| match goal.predicate.kind().no_bound_vars() {
-                Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => {
-                    let unconstrained_term = match term.unpack() {
-                        ty::TermKind::Ty(_) => infcx.next_ty_var(span).into(),
-                        ty::TermKind::Const(_) => infcx.next_const_var(span).into(),
-                    };
-                    let goal =
-                        goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term });
-                    // We have to use a `probe` here as evaluating a `NormalizesTo` can constrain the
-                    // expected term. This means that candidates which only fail due to nested goals
-                    // and which normalize to a different term then the final result could ICE: when
-                    // building their proof tree, the expected term was unconstrained, but when
-                    // instantiating the candidate it is already constrained to the result of another
-                    // candidate.
-                    let proof_tree = infcx
-                        .probe(|_| infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes).1);
-                    InspectGoal::new(
-                        infcx,
-                        self.goal.depth + 1,
-                        proof_tree.unwrap(),
-                        Some(NormalizesToTermHack { term, unconstrained_term }),
-                        source,
-                    )
-                }
-                _ => {
-                    // We're using a probe here as evaluating a goal could constrain
-                    // inference variables by choosing one candidate. If we then recurse
-                    // into another candidate who ends up with different inference
-                    // constraints, we get an ICE if we already applied the constraints
-                    // from the chosen candidate.
-                    let proof_tree = infcx
-                        .probe(|_| infcx.evaluate_root_goal(goal, GenerateProofTree::Yes).1)
-                        .unwrap();
-                    InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source)
-                }
-            })
+            .map(|(source, goal)| self.instantiate_proof_tree_for_nested_goal(source, goal, span))
             .collect();
 
         (goals, opt_impl_args)
     }
 
+    pub fn instantiate_proof_tree_for_nested_goal(
+        &self,
+        source: GoalSource,
+        goal: Goal<'tcx, ty::Predicate<'tcx>>,
+        span: Span,
+    ) -> InspectGoal<'a, 'tcx> {
+        let infcx = self.goal.infcx;
+        match goal.predicate.kind().no_bound_vars() {
+            Some(ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })) => {
+                let unconstrained_term = match term.unpack() {
+                    ty::TermKind::Ty(_) => infcx.next_ty_var(span).into(),
+                    ty::TermKind::Const(_) => infcx.next_const_var(span).into(),
+                };
+                let goal =
+                    goal.with(infcx.tcx, ty::NormalizesTo { alias, term: unconstrained_term });
+                // We have to use a `probe` here as evaluating a `NormalizesTo` can constrain the
+                // expected term. This means that candidates which only fail due to nested goals
+                // and which normalize to a different term then the final result could ICE: when
+                // building their proof tree, the expected term was unconstrained, but when
+                // instantiating the candidate it is already constrained to the result of another
+                // candidate.
+                let proof_tree =
+                    infcx.probe(|_| infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes).1);
+                InspectGoal::new(
+                    infcx,
+                    self.goal.depth + 1,
+                    proof_tree.unwrap(),
+                    Some(NormalizesToTermHack { term, unconstrained_term }),
+                    source,
+                )
+            }
+            _ => {
+                // We're using a probe here as evaluating a goal could constrain
+                // inference variables by choosing one candidate. If we then recurse
+                // into another candidate who ends up with different inference
+                // constraints, we get an ICE if we already applied the constraints
+                // from the chosen candidate.
+                let proof_tree = infcx
+                    .probe(|_| infcx.evaluate_root_goal(goal, GenerateProofTree::Yes, span).1)
+                    .unwrap();
+                InspectGoal::new(infcx, self.goal.depth + 1, proof_tree, None, source)
+            }
+        }
+    }
+
     /// Visit all nested goals of this candidate, rolling back
     /// all inference constraints.
     pub fn visit_nested_in_probe<V: ProofTreeVisitor<'tcx>>(&self, visitor: &mut V) -> V::Result {
@@ -430,8 +440,11 @@ impl<'tcx> InferCtxt<'tcx> {
         depth: usize,
         visitor: &mut V,
     ) -> V::Result {
-        let (_, proof_tree) =
-            <&SolverDelegate<'tcx>>::from(self).evaluate_root_goal(goal, GenerateProofTree::Yes);
+        let (_, proof_tree) = <&SolverDelegate<'tcx>>::from(self).evaluate_root_goal(
+            goal,
+            GenerateProofTree::Yes,
+            visitor.span(),
+        );
         let proof_tree = proof_tree.unwrap();
         visitor.visit_goal(&InspectGoal::new(self, depth, proof_tree, None, GoalSource::Misc))
     }
diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs
index a724705ffe9..232357dc71a 100644
--- a/compiler/rustc_trait_selection/src/solve/normalize.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalize.rs
@@ -130,11 +130,12 @@ where
         self.depth += 1;
 
         let new_infer_ct = infcx.next_const_var(self.at.cause.span);
-        let obligation =
-            Obligation::new(tcx, self.at.cause.clone(), self.at.param_env, ty::NormalizesTo {
-                alias: uv.into(),
-                term: new_infer_ct.into(),
-            });
+        let obligation = Obligation::new(
+            tcx,
+            self.at.cause.clone(),
+            self.at.param_env,
+            ty::NormalizesTo { alias: uv.into(), term: new_infer_ct.into() },
+        );
 
         let result = if infcx.predicate_may_hold(&obligation) {
             self.fulfill_cx.register_predicate_obligation(infcx, obligation);
@@ -252,20 +253,20 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for DeeplyNormalizeForDiagnosticsFolder<'_,
     }
 
     fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        deeply_normalize_with_skipped_universes(self.at, ty, vec![
-            None;
-            ty.outer_exclusive_binder()
-                .as_usize()
-        ])
+        deeply_normalize_with_skipped_universes(
+            self.at,
+            ty,
+            vec![None; ty.outer_exclusive_binder().as_usize()],
+        )
         .unwrap_or_else(|_: Vec<ScrubbedTraitError<'tcx>>| ty.super_fold_with(self))
     }
 
     fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
-        deeply_normalize_with_skipped_universes(self.at, ct, vec![
-            None;
-            ct.outer_exclusive_binder()
-                .as_usize()
-        ])
+        deeply_normalize_with_skipped_universes(
+            self.at,
+            ct,
+            vec![None; ct.outer_exclusive_binder().as_usize()],
+        )
         .unwrap_or_else(|_: Vec<ScrubbedTraitError<'tcx>>| ct.super_fold_with(self))
     }
 }
diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
index 9a53e8a5d51..1fca2f4da7e 100644
--- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs
+++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -6,6 +6,7 @@ use std::iter;
 
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry};
 use rustc_data_structures::unord::UnordSet;
+use rustc_hir::def_id::CRATE_DEF_ID;
 use rustc_infer::infer::DefineOpaqueTypes;
 use rustc_middle::ty::{Region, RegionVid};
 use tracing::debug;
@@ -13,6 +14,7 @@ use tracing::debug;
 use super::*;
 use crate::errors::UnableToConstructConstantValue;
 use crate::infer::region_constraints::{Constraint, RegionConstraintData};
+use crate::regions::OutlivesEnvironmentBuildExt;
 use crate::traits::project::ProjectAndUnifyResult;
 
 // FIXME(twk): this is obviously not nice to duplicate like that
@@ -158,7 +160,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
             panic!("Unable to fulfill trait {trait_did:?} for '{ty:?}': {errors:?}");
         }
 
-        let outlives_env = OutlivesEnvironment::new(full_env);
+        let outlives_env = OutlivesEnvironment::new(&infcx, CRATE_DEF_ID, full_env, []);
         let _ = infcx.process_registered_region_obligations(&outlives_env, |ty, _| Ok(ty));
 
         let region_data = infcx.inner.borrow_mut().unwrap_region_constraints().data().clone();
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 50d47d20e1a..3b40882ef57 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -9,7 +9,7 @@ use std::fmt::Debug;
 use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
 use rustc_errors::{Diag, EmissionGuarantee};
 use rustc_hir::def::DefKind;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
 use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
 use rustc_infer::traits::PredicateObligations;
 use rustc_middle::bug;
@@ -27,7 +27,6 @@ use tracing::{debug, instrument, warn};
 use super::ObligationCtxt;
 use crate::error_reporting::traits::suggest_new_overflow_limit;
 use crate::infer::InferOk;
-use crate::infer::outlives::env::OutlivesEnvironment;
 use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor};
 use crate::solve::{SolverDelegate, deeply_normalize_for_diagnostics, inspect};
 use crate::traits::query::evaluate_obligation::InferCtxtExt;
@@ -369,9 +368,10 @@ fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>(
                 overflowing_predicates: ambiguities
                     .into_iter()
                     .filter(|error| {
-                        matches!(error.code, FulfillmentErrorCode::Ambiguity {
-                            overflow: Some(true)
-                        })
+                        matches!(
+                            error.code,
+                            FulfillmentErrorCode::Ambiguity { overflow: Some(true) }
+                        )
                     })
                     .map(|e| infcx.resolve_vars_if_possible(e.obligation.predicate))
                     .collect(),
@@ -492,13 +492,16 @@ fn plug_infer_with_placeholders<'tcx>(
                         // Comparing against a type variable never registers hidden types anyway
                         DefineOpaqueTypes::Yes,
                         ty,
-                        Ty::new_placeholder(self.infcx.tcx, ty::Placeholder {
-                            universe: self.universe,
-                            bound: ty::BoundTy {
-                                var: self.next_var(),
-                                kind: ty::BoundTyKind::Anon,
+                        Ty::new_placeholder(
+                            self.infcx.tcx,
+                            ty::Placeholder {
+                                universe: self.universe,
+                                bound: ty::BoundTy {
+                                    var: self.next_var(),
+                                    kind: ty::BoundTyKind::Anon,
+                                },
                             },
-                        }),
+                        ),
                     )
                 else {
                     bug!("we always expect to be able to plug an infer var with placeholder")
@@ -518,10 +521,10 @@ fn plug_infer_with_placeholders<'tcx>(
                         // registration happening anyway.
                         DefineOpaqueTypes::Yes,
                         ct,
-                        ty::Const::new_placeholder(self.infcx.tcx, ty::Placeholder {
-                            universe: self.universe,
-                            bound: self.next_var(),
-                        }),
+                        ty::Const::new_placeholder(
+                            self.infcx.tcx,
+                            ty::Placeholder { universe: self.universe, bound: self.next_var() },
+                        ),
                     )
                 else {
                     bug!("we always expect to be able to plug an infer var with placeholder")
@@ -546,13 +549,16 @@ fn plug_infer_with_placeholders<'tcx>(
                             // Lifetimes don't contain opaque types (or any types for that matter).
                             DefineOpaqueTypes::Yes,
                             r,
-                            ty::Region::new_placeholder(self.infcx.tcx, ty::Placeholder {
-                                universe: self.universe,
-                                bound: ty::BoundRegion {
-                                    var: self.next_var(),
-                                    kind: ty::BoundRegionKind::Anon,
+                            ty::Region::new_placeholder(
+                                self.infcx.tcx,
+                                ty::Placeholder {
+                                    universe: self.universe,
+                                    bound: ty::BoundRegion {
+                                        var: self.next_var(),
+                                        kind: ty::BoundRegionKind::Anon,
+                                    },
                                 },
-                            }),
+                            ),
                         )
                     else {
                         bug!("we always expect to be able to plug an infer var with placeholder")
@@ -596,8 +602,7 @@ fn try_prove_negated_where_clause<'tcx>(
     // FIXME: We could use the assumed_wf_types from both impls, I think,
     // if that wasn't implemented just for LocalDefId, and we'd need to do
     // the normalization ourselves since this is totally fallible...
-    let outlives_env = OutlivesEnvironment::new(param_env);
-    let errors = ocx.resolve_regions(&outlives_env);
+    let errors = ocx.resolve_regions(CRATE_DEF_ID, param_env, []);
     if !errors.is_empty() {
         return false;
     }
@@ -765,9 +770,9 @@ fn search_ambiguity_causes<'tcx>(
     causes: &mut FxIndexSet<IntercrateAmbiguityCause<'tcx>>,
 ) {
     infcx.probe(|_| {
-        infcx.visit_proof_tree(goal, &mut AmbiguityCausesVisitor {
-            cache: Default::default(),
-            causes,
-        })
+        infcx.visit_proof_tree(
+            goal,
+            &mut AmbiguityCausesVisitor { cache: Default::default(), causes },
+        )
     });
 }
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index 446f9eaa348..bdee6ca2afe 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -35,7 +35,7 @@ pub fn is_const_evaluatable<'tcx>(
         ty::ConstKind::Param(_)
         | ty::ConstKind::Bound(_, _)
         | ty::ConstKind::Placeholder(_)
-        | ty::ConstKind::Value(_, _)
+        | ty::ConstKind::Value(_)
         | ty::ConstKind::Error(_) => return Ok(()),
         ty::ConstKind::Infer(_) => return Err(NotConstEvaluatable::MentionsInfer),
     };
diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
index 66491d9abe1..617bc87a9d2 100644
--- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
+++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
@@ -4,19 +4,16 @@
 //!
 //! [^1]: Formerly known as "object safety".
 
-use std::iter;
 use std::ops::ControlFlow;
 
-use rustc_abi::BackendRepr;
 use rustc_errors::FatalError;
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_middle::bug;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{
-    self, EarlyBinder, ExistentialPredicateStableCmpExt as _, GenericArgs, Ty, TyCtxt,
-    TypeFoldable, TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitable,
-    TypeVisitableExt, TypeVisitor, TypingMode, Upcast,
+    self, EarlyBinder, GenericArgs, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
+    TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Upcast,
 };
 use rustc_span::Span;
 use rustc_type_ir::elaborate;
@@ -64,7 +61,7 @@ fn is_dyn_compatible(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
 }
 
 /// We say a method is *vtable safe* if it can be invoked on a trait
-/// object. Note that object-safe traits can have some
+/// object. Note that dyn-compatible traits can have some
 /// non-vtable-safe methods, so long as they require `Self: Sized` or
 /// otherwise ensure that they cannot be used when `Self = Trait`.
 pub fn is_vtable_safe_method(tcx: TyCtxt<'_>, trait_def_id: DefId, method: ty::AssocItem) -> bool {
@@ -109,14 +106,6 @@ fn dyn_compatibility_violations_for_trait(
         violations.push(DynCompatibilityViolation::SupertraitNonLifetimeBinder(spans));
     }
 
-    if violations.is_empty() {
-        for item in tcx.associated_items(trait_def_id).in_definition_order() {
-            if let ty::AssocKind::Fn = item.kind {
-                check_receiver_correct(tcx, trait_def_id, *item);
-            }
-        }
-    }
-
     violations
 }
 
@@ -421,7 +410,7 @@ fn virtual_call_violations_for_method<'tcx>(
     let receiver_ty = tcx.liberate_late_bound_regions(method.def_id, sig.input(0));
 
     // Until `unsized_locals` is fully implemented, `self: Self` can't be dispatched on.
-    // However, this is already considered object-safe. We allow it as a special case here.
+    // However, this is already considered dyn compatible. We allow it as a special case here.
     // FIXME(mikeyhew) get rid of this `if` statement once `receiver_is_dispatchable` allows
     // `Receiver: Unsize<Receiver[Self => dyn Trait]>`.
     if receiver_ty != tcx.types.self_param {
@@ -499,55 +488,6 @@ fn virtual_call_violations_for_method<'tcx>(
     errors
 }
 
-/// This code checks that `receiver_is_dispatchable` is correctly implemented.
-///
-/// This check is outlined from the dyn-compatibility check to avoid cycles with
-/// layout computation, which relies on knowing whether methods are dyn-compatible.
-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;
-    }
-
-    let method_def_id = method.def_id;
-    let sig = tcx.fn_sig(method_def_id).instantiate_identity();
-    let typing_env = ty::TypingEnv::non_body_analysis(tcx, method_def_id);
-    let receiver_ty = tcx.liberate_late_bound_regions(method_def_id, sig.input(0));
-
-    if receiver_ty == tcx.types.self_param {
-        // Assumed OK, may change later if unsized_locals permits `self: Self` as dispatchable.
-        return;
-    }
-
-    // e.g., `Rc<()>`
-    let unit_receiver_ty = receiver_for_self_ty(tcx, receiver_ty, tcx.types.unit, method_def_id);
-    match tcx.layout_of(typing_env.as_query_input(unit_receiver_ty)).map(|l| l.backend_repr) {
-        Ok(BackendRepr::Scalar(..)) => (),
-        abi => {
-            tcx.dcx().span_delayed_bug(
-                tcx.def_span(method_def_id),
-                format!("receiver {unit_receiver_ty:?} when `Self = ()` should have a Scalar ABI; found {abi:?}"),
-            );
-        }
-    }
-
-    let trait_object_ty = object_ty_for_trait(tcx, trait_def_id, tcx.lifetimes.re_static);
-
-    // e.g., `Rc<dyn Trait>`
-    let trait_object_receiver =
-        receiver_for_self_ty(tcx, receiver_ty, trait_object_ty, method_def_id);
-    match tcx.layout_of(typing_env.as_query_input(trait_object_receiver)).map(|l| l.backend_repr) {
-        Ok(BackendRepr::ScalarPair(..)) => (),
-        abi => {
-            tcx.dcx().span_delayed_bug(
-                tcx.def_span(method_def_id),
-                format!(
-                    "receiver {trait_object_receiver:?} when `Self = {trait_object_ty}` should have a ScalarPair ABI; found {abi:?}"
-                ),
-            );
-        }
-    }
-}
-
 /// Performs a type instantiation to produce the version of `receiver_ty` when `Self = self_ty`.
 /// For example, for `receiver_ty = Rc<Self>` and `self_ty = Foo`, returns `Rc<Foo>`.
 fn receiver_for_self_ty<'tcx>(
@@ -569,49 +509,6 @@ fn receiver_for_self_ty<'tcx>(
     result
 }
 
-/// Creates the object type for the current trait. For example,
-/// if the current trait is `Deref`, then this will be
-/// `dyn Deref<Target = Self::Target> + 'static`.
-#[instrument(level = "trace", skip(tcx), ret)]
-fn object_ty_for_trait<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    trait_def_id: DefId,
-    lifetime: ty::Region<'tcx>,
-) -> Ty<'tcx> {
-    let trait_ref = ty::TraitRef::identity(tcx, trait_def_id);
-    debug!(?trait_ref);
-
-    let trait_predicate = ty::Binder::dummy(ty::ExistentialPredicate::Trait(
-        ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref),
-    ));
-    debug!(?trait_predicate);
-
-    let pred: ty::Predicate<'tcx> = trait_ref.upcast(tcx);
-    let mut elaborated_predicates: Vec<_> = elaborate(tcx, [pred])
-        .filter_map(|pred| {
-            debug!(?pred);
-            let pred = pred.as_projection_clause()?;
-            Some(pred.map_bound(|p| {
-                ty::ExistentialPredicate::Projection(ty::ExistentialProjection::erase_self_ty(
-                    tcx, p,
-                ))
-            }))
-        })
-        .collect();
-    // NOTE: Since #37965, the existential predicates list has depended on the
-    // list of predicates to be sorted. This is mostly to enforce that the primary
-    // predicate comes first.
-    elaborated_predicates.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
-    elaborated_predicates.dedup();
-
-    let existential_predicates = tcx.mk_poly_existential_predicates_from_iter(
-        iter::once(trait_predicate).chain(elaborated_predicates),
-    );
-    debug!(?existential_predicates);
-
-    Ty::new_dynamic(tcx, existential_predicates, lifetime, ty::Dyn)
-}
-
 /// Checks the method's receiver (the `self` argument) can be dispatched on when `Self` is a
 /// trait object. We require that `DispatchableFromDyn` be implemented for the receiver type
 /// in the following way:
@@ -631,7 +528,7 @@ fn object_ty_for_trait<'tcx>(
 /// - `self: Pin<Box<Self>>` requires `Pin<Box<Self>>: DispatchFromDyn<Pin<Box<dyn Trait>>>`.
 ///
 /// The only case where the receiver is not dispatchable, but is still a valid receiver
-/// type (just not object-safe), is when there is more than one level of pointer indirection.
+/// type (just not dyn compatible), is when there is more than one level of pointer indirection.
 /// E.g., `self: &&Self`, `self: &Rc<Self>`, `self: Box<Box<Self>>`. In these cases, there
 /// is no way, or at least no inexpensive way, to coerce the receiver from the version where
 /// `Self = dyn Trait` to the version where `Self = T`, where `T` is the unknown erased type
diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs
index 4a3983fca31..9f3178f8879 100644
--- a/compiler/rustc_trait_selection/src/traits/engine.rs
+++ b/compiler/rustc_trait_selection/src/traits/engine.rs
@@ -8,7 +8,6 @@ use rustc_infer::infer::at::ToTrace;
 use rustc_infer::infer::canonical::{
     Canonical, CanonicalQueryResponse, CanonicalVarValues, QueryResponse,
 };
-use rustc_infer::infer::outlives::env::OutlivesEnvironment;
 use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk, RegionResolutionError, TypeTrace};
 use rustc_infer::traits::PredicateObligations;
 use rustc_macros::extension;
@@ -217,14 +216,15 @@ where
     /// will result in region constraints getting ignored.
     pub fn resolve_regions_and_report_errors(
         self,
-        generic_param_scope: LocalDefId,
-        outlives_env: &OutlivesEnvironment<'tcx>,
+        body_id: LocalDefId,
+        param_env: ty::ParamEnv<'tcx>,
+        assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>,
     ) -> Result<(), ErrorGuaranteed> {
-        let errors = self.infcx.resolve_regions(outlives_env);
+        let errors = self.infcx.resolve_regions(body_id, param_env, assumed_wf_tys);
         if errors.is_empty() {
             Ok(())
         } else {
-            Err(self.infcx.err_ctxt().report_region_errors(generic_param_scope, &errors))
+            Err(self.infcx.err_ctxt().report_region_errors(body_id, &errors))
         }
     }
 
@@ -235,9 +235,11 @@ where
     #[must_use]
     pub fn resolve_regions(
         self,
-        outlives_env: &OutlivesEnvironment<'tcx>,
+        body_id: LocalDefId,
+        param_env: ty::ParamEnv<'tcx>,
+        assumed_wf_tys: impl IntoIterator<Item = Ty<'tcx>>,
     ) -> Vec<RegionResolutionError<'tcx>> {
-        self.infcx.resolve_regions(outlives_env)
+        self.infcx.resolve_regions(body_id, param_env, assumed_wf_tys)
     }
 }
 
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 7529ee128f5..f2d2dd2f3ce 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -479,7 +479,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                         ty::ConstKind::Error(_) => {
                             return ProcessResult::Changed(PendingPredicateObligations::new());
                         }
-                        ty::ConstKind::Value(ty, _) => ty,
+                        ty::ConstKind::Value(cv) => cv.ty,
                         ty::ConstKind::Unevaluated(uv) => {
                             infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args)
                         }
diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs
index 7a67b943e94..79e178150de 100644
--- a/compiler/rustc_trait_selection/src/traits/misc.rs
+++ b/compiler/rustc_trait_selection/src/traits/misc.rs
@@ -4,13 +4,10 @@ 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;
 use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
 use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeVisitableExt, TypingMode};
 
-use super::outlives_bounds::InferCtxtExt;
 use crate::regions::InferCtxtRegionExt;
 use crate::traits::{self, FulfillmentError, ObligationCause};
 
@@ -170,15 +167,7 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>(
         }
 
         // 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);
+        let errors = infcx.resolve_regions(parent_cause.body_id, param_env, [self_type]);
         if !errors.is_empty() {
             infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Regions(errors)));
             continue;
@@ -261,15 +250,7 @@ pub fn all_fields_implement_trait<'tcx>(
             }
 
             // 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);
+            let errors = infcx.resolve_regions(parent_cause.body_id, param_env, [self_type]);
             if !errors.is_empty() {
                 infringing.push((field, ty, InfringingFieldsReason::Regions(errors)));
             }
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index fe5ad003a7e..105cb917571 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -76,6 +76,7 @@ use crate::infer::{InferCtxt, TyCtxtInferExt};
 use crate::regions::InferCtxtRegionExt;
 use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
 
+#[derive(Debug)]
 pub struct FulfillmentError<'tcx> {
     pub obligation: PredicateObligation<'tcx>,
     pub code: FulfillmentErrorCode<'tcx>,
@@ -107,12 +108,6 @@ impl<'tcx> FulfillmentError<'tcx> {
     }
 }
 
-impl<'tcx> Debug for FulfillmentError<'tcx> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "FulfillmentError({:?},{:?})", self.obligation, self.code)
-    }
-}
-
 #[derive(Clone)]
 pub enum FulfillmentErrorCode<'tcx> {
     /// Inherently impossible to fulfill; this trait is implemented if and only
@@ -290,12 +285,10 @@ fn do_normalize_predicates<'tcx>(
 
     // We can use the `elaborated_env` here; the region code only
     // cares about declarations like `'a: 'b`.
-    let outlives_env = OutlivesEnvironment::new(elaborated_env);
-
     // FIXME: It's very weird that we ignore region obligations but apparently
     // still need to use `resolve_regions` as we need the resolved regions in
     // the normalized predicates.
-    let errors = infcx.resolve_regions(&outlives_env);
+    let errors = infcx.resolve_regions(cause.body_id, elaborated_env, []);
     if !errors.is_empty() {
         tcx.dcx().span_delayed_bug(
             span,
@@ -667,13 +660,16 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>(
                     self.idx += 1;
                     idx
                 };
-                Ty::new_placeholder(self.tcx, ty::PlaceholderType {
-                    universe: ty::UniverseIndex::ROOT,
-                    bound: ty::BoundTy {
-                        var: ty::BoundVar::from_u32(idx),
-                        kind: ty::BoundTyKind::Anon,
+                Ty::new_placeholder(
+                    self.tcx,
+                    ty::PlaceholderType {
+                        universe: ty::UniverseIndex::ROOT,
+                        bound: ty::BoundTy {
+                            var: ty::BoundVar::from_u32(idx),
+                            kind: ty::BoundTyKind::Anon,
+                        },
                     },
-                })
+                )
             } else {
                 t.super_fold_with(self)
             }
@@ -681,14 +677,17 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>(
 
         fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
             if let ty::ConstKind::Infer(_) = c.kind() {
-                ty::Const::new_placeholder(self.tcx, ty::PlaceholderConst {
-                    universe: ty::UniverseIndex::ROOT,
-                    bound: ty::BoundVar::from_u32({
-                        let idx = self.idx;
-                        self.idx += 1;
-                        idx
-                    }),
-                })
+                ty::Const::new_placeholder(
+                    self.tcx,
+                    ty::PlaceholderConst {
+                        universe: ty::UniverseIndex::ROOT,
+                        bound: ty::BoundVar::from_u32({
+                            let idx = self.idx;
+                            self.idx += 1;
+                            idx
+                        }),
+                    },
+                )
             } else {
                 c.super_fold_with(self)
             }
diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
index 23dabe32ff2..18932695807 100644
--- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
@@ -1,4 +1,3 @@
-use rustc_data_structures::fx::FxIndexSet;
 use rustc_infer::infer::InferOk;
 use rustc_infer::infer::resolve::OpportunisticRegionResolver;
 use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds;
@@ -12,9 +11,6 @@ use tracing::instrument;
 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;
-
 /// Implied bounds are region relationships that we deduce
 /// automatically. The idea is that (e.g.) a caller must check that a
 /// function's argument types are well-formed immediately before
@@ -110,36 +106,18 @@ fn implied_outlives_bounds<'a, 'tcx>(
     bounds
 }
 
-#[extension(pub trait InferCtxtExt<'a, 'tcx>)]
-impl<'a, 'tcx: 'a> InferCtxt<'tcx> {
-    /// Do *NOT* call this directly.
-    fn implied_bounds_tys_compat(
-        &'a self,
-        param_env: ParamEnv<'tcx>,
+#[extension(pub trait InferCtxtExt<'tcx>)]
+impl<'tcx> InferCtxt<'tcx> {
+    /// Do *NOT* call this directly. You probably want to construct a `OutlivesEnvironment`
+    /// instead if you're interested in the implied bounds for a given signature.
+    fn implied_bounds_tys_with_compat<Tys: IntoIterator<Item = Ty<'tcx>>>(
+        &self,
         body_id: LocalDefId,
-        tys: &'a FxIndexSet<Ty<'tcx>>,
-        compat: bool,
-    ) -> BoundsCompat<'a, 'tcx> {
-        tys.iter()
-            .flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, *ty, compat))
-    }
-
-    /// If `-Z no-implied-bounds-compat` is set, calls `implied_bounds_tys_compat`
-    /// with `compat` set to `true`, otherwise `false`.
-    fn implied_bounds_tys(
-        &'a self,
         param_env: ParamEnv<'tcx>,
-        body_id: LocalDefId,
-        tys: &'a FxIndexSet<Ty<'tcx>>,
-    ) -> Bounds<'a, 'tcx> {
-        tys.iter().flat_map(move |ty| {
-            implied_outlives_bounds(
-                self,
-                param_env,
-                body_id,
-                *ty,
-                !self.tcx.sess.opts.unstable_opts.no_implied_bounds_compat,
-            )
-        })
+        tys: Tys,
+        compat: bool,
+    ) -> impl Iterator<Item = OutlivesBound<'tcx>> {
+        tys.into_iter()
+            .flat_map(move |ty| implied_outlives_bounds(self, param_env, body_id, ty, compat))
     }
 }
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 537b042bde5..de5aaa5bd97 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -1670,14 +1670,18 @@ fn confirm_closure_candidate<'cx, 'tcx>(
                 } else {
                     let upvars_projection_def_id =
                         tcx.require_lang_item(LangItem::AsyncFnKindUpvars, None);
-                    let tupled_upvars_ty = Ty::new_projection(tcx, upvars_projection_def_id, [
-                        ty::GenericArg::from(kind_ty),
-                        Ty::from_closure_kind(tcx, ty::ClosureKind::FnOnce).into(),
-                        tcx.lifetimes.re_static.into(),
-                        sig.tupled_inputs_ty.into(),
-                        args.tupled_upvars_ty().into(),
-                        args.coroutine_captures_by_ref_ty().into(),
-                    ]);
+                    let tupled_upvars_ty = Ty::new_projection(
+                        tcx,
+                        upvars_projection_def_id,
+                        [
+                            ty::GenericArg::from(kind_ty),
+                            Ty::from_closure_kind(tcx, ty::ClosureKind::FnOnce).into(),
+                            tcx.lifetimes.re_static.into(),
+                            sig.tupled_inputs_ty.into(),
+                            args.tupled_upvars_ty().into(),
+                            args.coroutine_captures_by_ref_ty().into(),
+                        ],
+                    );
                     sig.to_coroutine(
                         tcx,
                         args.parent_args(),
@@ -1795,14 +1799,18 @@ fn confirm_async_closure_candidate<'cx, 'tcx>(
                         // will project to the right upvars for the generator, appending the inputs and
                         // coroutine upvars respecting the closure kind.
                         // N.B. No need to register a `AsyncFnKindHelper` goal here, it's already in `nested`.
-                        let tupled_upvars_ty = Ty::new_projection(tcx, upvars_projection_def_id, [
-                            ty::GenericArg::from(kind_ty),
-                            Ty::from_closure_kind(tcx, goal_kind).into(),
-                            env_region.into(),
-                            sig.tupled_inputs_ty.into(),
-                            args.tupled_upvars_ty().into(),
-                            args.coroutine_captures_by_ref_ty().into(),
-                        ]);
+                        let tupled_upvars_ty = Ty::new_projection(
+                            tcx,
+                            upvars_projection_def_id,
+                            [
+                                ty::GenericArg::from(kind_ty),
+                                Ty::from_closure_kind(tcx, goal_kind).into(),
+                                env_region.into(),
+                                sig.tupled_inputs_ty.into(),
+                                args.tupled_upvars_ty().into(),
+                                args.coroutine_captures_by_ref_ty().into(),
+                            ],
+                        );
                         sig.to_coroutine(
                             tcx,
                             args.parent_args(),
@@ -1816,17 +1824,16 @@ fn confirm_async_closure_candidate<'cx, 'tcx>(
                 name => bug!("no such associated type: {name}"),
             };
             let projection_term = match item_name {
-                sym::CallOnceFuture | sym::Output => {
-                    ty::AliasTerm::new(tcx, obligation.predicate.def_id, [
-                        self_ty,
-                        sig.tupled_inputs_ty,
-                    ])
-                }
-                sym::CallRefFuture => ty::AliasTerm::new(tcx, obligation.predicate.def_id, [
-                    ty::GenericArg::from(self_ty),
-                    sig.tupled_inputs_ty.into(),
-                    env_region.into(),
-                ]),
+                sym::CallOnceFuture | sym::Output => ty::AliasTerm::new(
+                    tcx,
+                    obligation.predicate.def_id,
+                    [self_ty, sig.tupled_inputs_ty],
+                ),
+                sym::CallRefFuture => ty::AliasTerm::new(
+                    tcx,
+                    obligation.predicate.def_id,
+                    [ty::GenericArg::from(self_ty), sig.tupled_inputs_ty.into(), env_region.into()],
+                ),
                 name => bug!("no such associated type: {name}"),
             };
 
@@ -1846,17 +1853,20 @@ fn confirm_async_closure_candidate<'cx, 'tcx>(
                 name => bug!("no such associated type: {name}"),
             };
             let projection_term = match item_name {
-                sym::CallOnceFuture | sym::Output => {
-                    ty::AliasTerm::new(tcx, obligation.predicate.def_id, [
-                        self_ty,
-                        Ty::new_tup(tcx, sig.inputs()),
-                    ])
-                }
-                sym::CallRefFuture => ty::AliasTerm::new(tcx, obligation.predicate.def_id, [
-                    ty::GenericArg::from(self_ty),
-                    Ty::new_tup(tcx, sig.inputs()).into(),
-                    env_region.into(),
-                ]),
+                sym::CallOnceFuture | sym::Output => ty::AliasTerm::new(
+                    tcx,
+                    obligation.predicate.def_id,
+                    [self_ty, Ty::new_tup(tcx, sig.inputs())],
+                ),
+                sym::CallRefFuture => ty::AliasTerm::new(
+                    tcx,
+                    obligation.predicate.def_id,
+                    [
+                        ty::GenericArg::from(self_ty),
+                        Ty::new_tup(tcx, sig.inputs()).into(),
+                        env_region.into(),
+                    ],
+                ),
                 name => bug!("no such associated type: {name}"),
             };
 
@@ -1879,11 +1889,11 @@ fn confirm_async_closure_candidate<'cx, 'tcx>(
                 sym::CallOnceFuture | sym::Output => {
                     ty::AliasTerm::new(tcx, obligation.predicate.def_id, [self_ty, sig.inputs()[0]])
                 }
-                sym::CallRefFuture => ty::AliasTerm::new(tcx, obligation.predicate.def_id, [
-                    ty::GenericArg::from(self_ty),
-                    sig.inputs()[0].into(),
-                    env_region.into(),
-                ]),
+                sym::CallRefFuture => ty::AliasTerm::new(
+                    tcx,
+                    obligation.predicate.def_id,
+                    [ty::GenericArg::from(self_ty), sig.inputs()[0].into(), env_region.into()],
+                ),
                 name => bug!("no such associated type: {name}"),
             };
 
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 26ba1511b54..92098e20448 100644
--- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
@@ -6,7 +6,8 @@ use rustc_span::{DUMMY_SP, Span};
 use tracing::{debug, instrument};
 
 use crate::traits::query::NoSolution;
-use crate::traits::{ObligationCause, ObligationCtxt};
+use crate::traits::query::normalize::QueryNormalizeExt;
+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
@@ -90,6 +91,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
 pub fn compute_dropck_outlives_inner<'tcx>(
     ocx: &ObligationCtxt<'_, 'tcx>,
     goal: ParamEnvAnd<'tcx, DropckOutlives<'tcx>>,
+    span: Span,
 ) -> Result<DropckOutlivesResult<'tcx>, NoSolution> {
     let tcx = ocx.infcx.tcx;
     let ParamEnvAnd { param_env, value: DropckOutlives { dropped_ty } } = goal;
@@ -135,7 +137,7 @@ pub fn compute_dropck_outlives_inner<'tcx>(
     // Set used to detect infinite recursion.
     let mut ty_set = FxHashSet::default();
 
-    let cause = ObligationCause::dummy();
+    let cause = ObligationCause::dummy_with_span(span);
     let mut constraints = DropckConstraint::empty();
     while let Some((ty, depth)) = ty_stack.pop() {
         debug!(
@@ -171,18 +173,13 @@ pub fn compute_dropck_outlives_inner<'tcx>(
         // do not themselves define a destructor", more or less. We have
         // to push them onto the stack to be expanded.
         for ty in constraints.dtorck_types.drain(..) {
-            let normalized_ty = ocx.normalize(&cause, param_env, ty);
-
-            let errors = ocx.select_where_possible();
-            if !errors.is_empty() {
-                debug!("failed to normalize dtorck type: {ty} ~> {errors:#?}");
-                return Err(NoSolution);
-            }
+            let Normalized { value: ty, obligations } =
+                ocx.infcx.at(&cause, param_env).query_normalize(ty)?;
+            ocx.register_obligations(obligations);
 
-            let normalized_ty = ocx.infcx.resolve_vars_if_possible(normalized_ty);
-            debug!("dropck_outlives: ty from dtorck_types = {:?}", normalized_ty);
+            debug!("dropck_outlives: ty from dtorck_types = {:?}", ty);
 
-            match normalized_ty.kind() {
+            match ty.kind() {
                 // All parameters live for the duration of the
                 // function.
                 ty::Param(..) => {}
@@ -190,12 +187,12 @@ pub fn compute_dropck_outlives_inner<'tcx>(
                 // A projection that we couldn't resolve - it
                 // might have a destructor.
                 ty::Alias(..) => {
-                    result.kinds.push(normalized_ty.into());
+                    result.kinds.push(ty.into());
                 }
 
                 _ => {
-                    if ty_set.insert(normalized_ty) {
-                        ty_stack.push((normalized_ty, depth + 1));
+                    if ty_set.insert(ty) {
+                        ty_stack.push((ty, depth + 1));
                     }
                 }
             }
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 254dee794f1..4eecde00eaa 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
@@ -30,8 +30,9 @@ impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> {
     fn perform_locally_with_next_solver(
         ocx: &ObligationCtxt<'_, 'tcx>,
         key: ParamEnvAnd<'tcx, Self>,
+        span: Span,
     ) -> Result<Self::QueryResponse, NoSolution> {
-        type_op_ascribe_user_type_with_span(ocx, key, None)
+        type_op_ascribe_user_type_with_span(ocx, key, span)
     }
 }
 
@@ -41,11 +42,10 @@ impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> {
 pub fn type_op_ascribe_user_type_with_span<'tcx>(
     ocx: &ObligationCtxt<'_, 'tcx>,
     key: ParamEnvAnd<'tcx, AscribeUserType<'tcx>>,
-    span: Option<Span>,
+    span: Span,
 ) -> Result<(), NoSolution> {
     let (param_env, AscribeUserType { mir_ty, user_ty }) = key.into_parts();
     debug!("type_op_ascribe_user_type: mir_ty={:?} user_ty={:?}", mir_ty, user_ty);
-    let span = span.unwrap_or(DUMMY_SP);
     match user_ty.kind {
         UserTypeKind::Ty(user_ty) => relate_mir_and_user_ty(ocx, param_env, span, mir_ty, user_ty)?,
         UserTypeKind::TypeOf(def_id, user_args) => {
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 fe47e837dfb..ec0b7903396 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
@@ -5,8 +5,8 @@ use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds;
 use rustc_middle::infer::canonical::CanonicalQueryResponse;
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt};
-use rustc_span::DUMMY_SP;
 use rustc_span::def_id::CRATE_DEF_ID;
+use rustc_span::{DUMMY_SP, Span};
 use rustc_type_ir::outlives::{Component, push_outlives_components};
 use smallvec::{SmallVec, smallvec};
 use tracing::debug;
@@ -45,11 +45,12 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> {
     fn perform_locally_with_next_solver(
         ocx: &ObligationCtxt<'_, 'tcx>,
         key: ParamEnvAnd<'tcx, Self>,
+        span: Span,
     ) -> Result<Self::QueryResponse, NoSolution> {
         if ocx.infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat {
-            compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty)
+            compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty, span)
         } else {
-            compute_implied_outlives_bounds_compat_inner(ocx, key.param_env, key.value.ty)
+            compute_implied_outlives_bounds_compat_inner(ocx, key.param_env, key.value.ty, span)
         }
     }
 }
@@ -58,13 +59,14 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
     ocx: &ObligationCtxt<'_, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     ty: Ty<'tcx>,
+    span: Span,
 ) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> {
     let normalize_op = |ty| -> Result<_, NoSolution> {
         // We must normalize the type so we can compute the right outlives components.
         // for example, if we have some constrained param type like `T: Trait<Out = U>`,
         // and we know that `&'a T::Out` is WF, then we want to imply `U: 'a`.
         let ty = ocx
-            .deeply_normalize(&ObligationCause::dummy(), param_env, ty)
+            .deeply_normalize(&ObligationCause::dummy_with_span(span), param_env, ty)
             .map_err(|_| NoSolution)?;
         if !ocx.select_all_or_error().is_empty() {
             return Err(NoSolution);
@@ -90,7 +92,9 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>(
 
         // From the full set of obligations, just filter down to the region relationships.
         for obligation in
-            wf::unnormalized_obligations(ocx.infcx, param_env, arg).into_iter().flatten()
+            wf::unnormalized_obligations(ocx.infcx, param_env, arg, DUMMY_SP, CRATE_DEF_ID)
+                .into_iter()
+                .flatten()
         {
             assert!(!obligation.has_escaping_bound_vars());
             let Some(pred) = obligation.predicate.kind().no_bound_vars() else {
@@ -142,6 +146,7 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>(
     ocx: &ObligationCtxt<'_, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     ty: Ty<'tcx>,
+    span: Span,
 ) -> Result<Vec<OutlivesBound<'tcx>>, NoSolution> {
     let tcx = ocx.infcx.tcx;
 
@@ -171,8 +176,8 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>(
         // FIXME(@lcnr): It's not really "always fine", having fewer implied
         // bounds can be backward incompatible, e.g. #101951 was caused by
         // us not dealing with inference vars in `TypeOutlives` predicates.
-        let obligations = wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, DUMMY_SP)
-            .unwrap_or_default();
+        let obligations =
+            wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, span).unwrap_or_default();
 
         for obligation in obligations {
             debug!(?obligation);
@@ -255,7 +260,7 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>(
                 // Need to manually normalize in the new solver as `wf::obligations` does not.
                 if ocx.infcx.next_trait_solver() {
                     ty_a = ocx
-                        .deeply_normalize(&ObligationCause::dummy(), param_env, ty_a)
+                        .deeply_normalize(&ObligationCause::dummy_with_span(span), param_env, ty_a)
                         .map_err(|_| NoSolution)?;
                 }
                 let mut components = smallvec![];
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 54fce914bb6..68feb19c55b 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
@@ -92,6 +92,7 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<TyCtxt<'tcx>> + 't
     fn perform_locally_with_next_solver(
         ocx: &ObligationCtxt<'_, 'tcx>,
         key: ParamEnvAnd<'tcx, Self>,
+        span: Span,
     ) -> Result<Self::QueryResponse, NoSolution>;
 
     fn fully_perform_into(
@@ -152,7 +153,7 @@ where
         if infcx.next_trait_solver() {
             return Ok(scrape_region_constraints(
                 infcx,
-                |ocx| QueryTypeOp::perform_locally_with_next_solver(ocx, self),
+                |ocx| QueryTypeOp::perform_locally_with_next_solver(ocx, self, span),
                 "query type op",
                 span,
             )?
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 94df222932e..2f6bbd7f4cf 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
@@ -2,9 +2,10 @@ use std::fmt;
 
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::traits::query::NoSolution;
-pub use rustc_middle::traits::query::type_op::Normalize;
+pub use rustc_middle::traits::query::type_op::{DeeplyNormalize, Normalize};
 use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
+use rustc_span::Span;
 
 use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse};
 use crate::traits::ObligationCtxt;
@@ -27,11 +28,53 @@ where
     }
 
     fn perform_locally_with_next_solver(
+        _ocx: &ObligationCtxt<'_, 'tcx>,
+        key: ParamEnvAnd<'tcx, Self>,
+        _span: Span,
+    ) -> Result<Self::QueryResponse, NoSolution> {
+        Ok(key.value.value)
+    }
+}
+
+impl<'tcx, T> super::QueryTypeOp<'tcx> for DeeplyNormalize<T>
+where
+    T: Normalizable<'tcx> + 'tcx,
+{
+    type QueryResponse = T;
+
+    fn try_fast_path(_tcx: TyCtxt<'tcx>, key: &ParamEnvAnd<'tcx, Self>) -> Option<T> {
+        if !key.value.value.has_aliases() { Some(key.value.value) } else { None }
+    }
+
+    fn perform_query(
+        tcx: TyCtxt<'tcx>,
+        canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Self>>,
+    ) -> Result<CanonicalQueryResponse<'tcx, Self::QueryResponse>, NoSolution> {
+        T::type_op_method(
+            tcx,
+            CanonicalQueryInput {
+                typing_mode: canonicalized.typing_mode,
+                canonical: canonicalized.canonical.unchecked_map(
+                    |ty::ParamEnvAnd { param_env, value }| ty::ParamEnvAnd {
+                        param_env,
+                        value: Normalize { value: value.value },
+                    },
+                ),
+            },
+        )
+    }
+
+    fn perform_locally_with_next_solver(
         ocx: &ObligationCtxt<'_, 'tcx>,
         key: ParamEnvAnd<'tcx, Self>,
+        span: Span,
     ) -> Result<Self::QueryResponse, NoSolution> {
-        // FIXME(-Znext-solver): shouldn't be using old normalizer
-        Ok(ocx.normalize(&ObligationCause::dummy(), key.param_env, key.value.value))
+        ocx.deeply_normalize(
+            &ObligationCause::dummy_with_span(span),
+            key.param_env,
+            key.value.value,
+        )
+        .map_err(|_| NoSolution)
     }
 }
 
@@ -79,3 +122,14 @@ impl<'tcx> Normalizable<'tcx> for ty::FnSig<'tcx> {
         tcx.type_op_normalize_fn_sig(canonicalized)
     }
 }
+
+/// This impl is not needed, since we never normalize type outlives predicates
+/// in the old solver, but is required by trait bounds to be happy.
+impl<'tcx> Normalizable<'tcx> for ty::PolyTypeOutlivesPredicate<'tcx> {
+    fn type_op_method(
+        _tcx: TyCtxt<'tcx>,
+        _canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, Normalize<Self>>>,
+    ) -> Result<CanonicalQueryResponse<'tcx, Self>, NoSolution> {
+        unreachable!("we never normalize PolyTypeOutlivesPredicate")
+    }
+}
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 fa05f901f66..99a2779aa82 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,5 +1,6 @@
 use rustc_middle::traits::query::{DropckOutlivesResult, NoSolution};
 use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
+use rustc_span::Span;
 
 use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse};
 use crate::traits::ObligationCtxt;
@@ -28,7 +29,8 @@ impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> {
     fn perform_locally_with_next_solver(
         ocx: &ObligationCtxt<'_, 'tcx>,
         key: ParamEnvAnd<'tcx, Self>,
+        span: Span,
     ) -> Result<Self::QueryResponse, NoSolution> {
-        compute_dropck_outlives_inner(ocx, key.param_env.and(key.value))
+        compute_dropck_outlives_inner(ocx, key.param_env.and(key.value), span)
     }
 }
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 b2dab379262..4f9e2e79d62 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
@@ -4,6 +4,7 @@ use rustc_middle::traits::ObligationCause;
 use rustc_middle::traits::query::NoSolution;
 pub use rustc_middle::traits::query::type_op::ProvePredicate;
 use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt};
+use rustc_span::Span;
 
 use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse};
 use crate::traits::ObligationCtxt;
@@ -57,10 +58,11 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> {
     fn perform_locally_with_next_solver(
         ocx: &ObligationCtxt<'_, 'tcx>,
         key: ParamEnvAnd<'tcx, Self>,
+        span: Span,
     ) -> Result<Self::QueryResponse, NoSolution> {
         ocx.register_obligation(Obligation::new(
             ocx.infcx.tcx,
-            ObligationCause::dummy(),
+            ObligationCause::dummy_with_span(span),
             key.param_env,
             key.value.predicate,
         ));
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 13a6744c2e9..5b362b2356e 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -12,9 +12,7 @@ use hir::LangItem;
 use hir::def_id::DefId;
 use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
 use rustc_hir as hir;
-use rustc_infer::traits::{
-    Obligation, ObligationCause, PolyTraitObligation, PredicateObligations, SelectionError,
-};
+use rustc_infer::traits::{Obligation, PolyTraitObligation, SelectionError};
 use rustc_middle::ty::fast_reject::DeepRejectCtxt;
 use rustc_middle::ty::{self, Ty, TypeVisitableExt, TypingMode};
 use rustc_middle::{bug, span_bug};
@@ -23,8 +21,6 @@ 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;
 
 impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
@@ -106,6 +102,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 self.assemble_candidate_for_tuple(obligation, &mut candidates);
             } else if tcx.is_lang_item(def_id, LangItem::FnPtrTrait) {
                 self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates);
+            } else if tcx.is_lang_item(def_id, LangItem::BikeshedGuaranteedNoDrop) {
+                self.assemble_candidates_for_bikeshed_guaranteed_no_drop_trait(
+                    obligation,
+                    &mut candidates,
+                );
             } else {
                 if tcx.is_lang_item(def_id, LangItem::Clone) {
                     // Same builtin conditions as `Copy`, i.e., every type which has builtin support
@@ -904,54 +905,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         })
     }
 
-    /// Temporary migration for #89190
-    fn need_migrate_deref_output_trait_object(
-        &mut self,
-        ty: Ty<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        cause: &ObligationCause<'tcx>,
-    ) -> Option<ty::PolyExistentialTraitRef<'tcx>> {
-        // Don't drop any candidates in intercrate mode, as it's incomplete.
-        // (Not that it matters, since `Unsize` is not a stable trait.)
-        //
-        // FIXME(@lcnr): This should probably only trigger during analysis,
-        // disabling candidates during codegen is also questionable.
-        if let TypingMode::Coherence = self.infcx.typing_mode() {
-            return None;
-        }
-
-        let tcx = self.tcx();
-        if tcx.features().trait_upcasting() {
-            return None;
-        }
-
-        // <ty as Deref>
-        let trait_ref = ty::TraitRef::new(tcx, tcx.lang_items().deref_trait()?, [ty]);
-
-        let obligation =
-            traits::Obligation::new(tcx, cause.clone(), param_env, ty::Binder::dummy(trait_ref));
-        if !self.infcx.predicate_may_hold(&obligation) {
-            return None;
-        }
-
-        self.infcx.probe(|_| {
-            let ty = traits::normalize_projection_ty(
-                self,
-                param_env,
-                ty::AliasTy::new_from_args(tcx, tcx.lang_items().deref_target()?, trait_ref.args),
-                cause.clone(),
-                0,
-                // We're *intentionally* throwing these away,
-                // since we don't actually use them.
-                &mut PredicateObligations::new(),
-            )
-            .as_type()
-            .unwrap();
-
-            if let ty::Dynamic(data, ..) = ty.kind() { data.principal() } else { None }
-        })
-    }
-
     /// Searches for unsizing that might apply to `obligation`.
     fn assemble_candidates_for_unsizing(
         &mut self,
@@ -972,11 +925,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         //     T: Trait
         // so it seems ok if we (conservatively) fail to accept that `Unsize`
         // obligation above. Should be possible to extend this in the future.
-        let Some(source) = obligation.self_ty().no_bound_vars() else {
+        let Some(trait_pred) = obligation.predicate.no_bound_vars() else {
             // Don't add any candidates if there are bound regions.
             return;
         };
-        let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1);
+        let source = trait_pred.self_ty();
+        let target = trait_pred.trait_ref.args.type_at(1);
 
         debug!(?source, ?target, "assemble_candidates_for_unsizing");
 
@@ -1019,15 +973,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     let principal_a = a_data.principal().unwrap();
                     let target_trait_did = principal_def_id_b.unwrap();
                     let source_trait_ref = principal_a.with_self_ty(self.tcx(), source);
-                    if let Some(deref_trait_ref) = self.need_migrate_deref_output_trait_object(
-                        source,
-                        obligation.param_env,
-                        &obligation.cause,
-                    ) {
-                        if deref_trait_ref.def_id() == target_trait_did {
-                            return;
-                        }
-                    }
 
                     for (idx, upcast_trait_ref) in
                         util::supertraits(self.tcx(), source_trait_ref).enumerate()
@@ -1244,4 +1189,48 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             }
         }
     }
+
+    fn assemble_candidates_for_bikeshed_guaranteed_no_drop_trait(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        match obligation.predicate.self_ty().skip_binder().kind() {
+            ty::Ref(..)
+            | ty::Adt(..)
+            | ty::Tuple(_)
+            | ty::Array(..)
+            | ty::FnDef(..)
+            | ty::FnPtr(..)
+            | ty::Error(_)
+            | ty::Uint(_)
+            | ty::Int(_)
+            | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
+            | ty::Bool
+            | ty::Float(_)
+            | ty::Char
+            | ty::RawPtr(..)
+            | ty::Never
+            | ty::Pat(..)
+            | ty::Dynamic(..)
+            | ty::Str
+            | ty::Slice(_)
+            | ty::Foreign(..)
+            | ty::Alias(..)
+            | ty::Param(_)
+            | ty::Placeholder(..)
+            | ty::Closure(..)
+            | ty::CoroutineClosure(..)
+            | ty::Coroutine(..)
+            | ty::UnsafeBinder(_)
+            | ty::CoroutineWitness(..)
+            | ty::Bound(..) => {
+                candidates.vec.push(BikeshedGuaranteedNoDropCandidate);
+            }
+
+            ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
+                candidates.ambiguous = true;
+            }
+        }
+    }
 }
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 729ae3f2c2a..32cbb97e314 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -20,6 +20,7 @@ use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, Upcast};
 use rustc_middle::{bug, span_bug};
 use rustc_span::def_id::DefId;
 use rustc_type_ir::elaborate;
+use thin_vec::thin_vec;
 use tracing::{debug, instrument};
 
 use super::SelectionCandidate::{self, *};
@@ -130,6 +131,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             TraitUpcastingUnsizeCandidate(idx) => {
                 self.confirm_trait_upcasting_unsize_candidate(obligation, idx)?
             }
+
+            BikeshedGuaranteedNoDropCandidate => {
+                self.confirm_bikeshed_guaranteed_no_drop_candidate(obligation)
+            }
         };
 
         // The obligations returned by confirmation are recursively evaluated
@@ -301,11 +306,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, '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),
-                ]);
+                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(),
@@ -316,10 +325,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             };
 
             let make_freeze_obl = |ty| {
-                let trait_ref =
-                    ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Freeze, None), [
-                        ty::GenericArg::from(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(),
@@ -863,10 +873,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             }
             ty::CoroutineClosure(_, args) => {
                 args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| {
-                    ty::TraitRef::new(self.tcx(), obligation.predicate.def_id(), [
-                        self_ty,
-                        sig.tupled_inputs_ty,
-                    ])
+                    ty::TraitRef::new(
+                        self.tcx(),
+                        obligation.predicate.def_id(),
+                        [self_ty, sig.tupled_inputs_ty],
+                    )
                 })
             }
             _ => {
@@ -892,10 +903,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             ty::CoroutineClosure(_, args) => {
                 let args = args.as_coroutine_closure();
                 let trait_ref = args.coroutine_closure_sig().map_bound(|sig| {
-                    ty::TraitRef::new(self.tcx(), obligation.predicate.def_id(), [
-                        self_ty,
-                        sig.tupled_inputs_ty,
-                    ])
+                    ty::TraitRef::new(
+                        self.tcx(),
+                        obligation.predicate.def_id(),
+                        [self_ty, sig.tupled_inputs_ty],
+                    )
                 });
 
                 // Note that unlike below, we don't need to check `Future + Sized` for
@@ -906,10 +918,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             ty::FnDef(..) | ty::FnPtr(..) => {
                 let sig = self_ty.fn_sig(tcx);
                 let trait_ref = sig.map_bound(|sig| {
-                    ty::TraitRef::new(self.tcx(), obligation.predicate.def_id(), [
-                        self_ty,
-                        Ty::new_tup(tcx, sig.inputs()),
-                    ])
+                    ty::TraitRef::new(
+                        self.tcx(),
+                        obligation.predicate.def_id(),
+                        [self_ty, Ty::new_tup(tcx, sig.inputs())],
+                    )
                 });
 
                 // We must additionally check that the return type impls `Future + Sized`.
@@ -934,10 +947,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 let args = args.as_closure();
                 let sig = args.sig();
                 let trait_ref = sig.map_bound(|sig| {
-                    ty::TraitRef::new(self.tcx(), obligation.predicate.def_id(), [
-                        self_ty,
-                        sig.inputs()[0],
-                    ])
+                    ty::TraitRef::new(
+                        self.tcx(),
+                        obligation.predicate.def_id(),
+                        [self_ty, sig.inputs()[0]],
+                    )
                 });
 
                 // We must additionally check that the return type impls `Future + Sized`.
@@ -1295,10 +1309,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 // Construct the nested `TailField<T>: Unsize<TailField<U>>` predicate.
                 let tail_unsize_obligation = obligation.with(
                     tcx,
-                    ty::TraitRef::new(tcx, obligation.predicate.def_id(), [
-                        source_tail,
-                        target_tail,
-                    ]),
+                    ty::TraitRef::new(
+                        tcx,
+                        obligation.predicate.def_id(),
+                        [source_tail, target_tail],
+                    ),
                 );
                 nested.push(tail_unsize_obligation);
 
@@ -1336,6 +1351,93 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             _ => bug!("source: {source}, target: {target}"),
         })
     }
+
+    fn confirm_bikeshed_guaranteed_no_drop_candidate(
+        &mut self,
+        obligation: &PolyTraitObligation<'tcx>,
+    ) -> ImplSource<'tcx, PredicateObligation<'tcx>> {
+        let mut obligations = thin_vec![];
+
+        let tcx = self.tcx();
+        let self_ty = obligation.predicate.self_ty();
+        match *self_ty.skip_binder().kind() {
+            // `&mut T` and `&T` always implement `BikeshedGuaranteedNoDrop`.
+            ty::Ref(..) => {}
+            // `ManuallyDrop<T>` always implements `BikeshedGuaranteedNoDrop`.
+            ty::Adt(def, _) if def.is_manually_drop() => {}
+            // Arrays and tuples implement `BikeshedGuaranteedNoDrop` only if
+            // their constituent types implement `BikeshedGuaranteedNoDrop`.
+            ty::Tuple(tys) => {
+                obligations.extend(tys.iter().map(|elem_ty| {
+                    obligation.with(
+                        tcx,
+                        self_ty.rebind(ty::TraitRef::new(
+                            tcx,
+                            obligation.predicate.def_id(),
+                            [elem_ty],
+                        )),
+                    )
+                }));
+            }
+            ty::Array(elem_ty, _) => {
+                obligations.push(obligation.with(
+                    tcx,
+                    self_ty.rebind(ty::TraitRef::new(
+                        tcx,
+                        obligation.predicate.def_id(),
+                        [elem_ty],
+                    )),
+                ));
+            }
+
+            // All other types implement `BikeshedGuaranteedNoDrop` only if
+            // they implement `Copy`. We could be smart here and short-circuit
+            // some trivially `Copy`/`!Copy` types, but there's no benefit.
+            ty::FnDef(..)
+            | ty::FnPtr(..)
+            | ty::Error(_)
+            | ty::Uint(_)
+            | ty::Int(_)
+            | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
+            | ty::Bool
+            | ty::Float(_)
+            | ty::Char
+            | ty::RawPtr(..)
+            | ty::Never
+            | ty::Pat(..)
+            | ty::Dynamic(..)
+            | ty::Str
+            | ty::Slice(_)
+            | ty::Foreign(..)
+            | ty::Adt(..)
+            | ty::Alias(..)
+            | ty::Param(_)
+            | ty::Placeholder(..)
+            | ty::Closure(..)
+            | ty::CoroutineClosure(..)
+            | ty::Coroutine(..)
+            | ty::UnsafeBinder(_)
+            | ty::CoroutineWitness(..)
+            | ty::Bound(..) => {
+                obligations.push(obligation.with(
+                    tcx,
+                    self_ty.map_bound(|ty| {
+                        ty::TraitRef::new(
+                            tcx,
+                            tcx.require_lang_item(LangItem::Copy, Some(obligation.cause.span)),
+                            [ty],
+                        )
+                    }),
+                ));
+            }
+
+            ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
+                panic!("unexpected type `{self_ty:?}`")
+            }
+        }
+
+        ImplSource::Builtin(BuiltinImplSource::Misc, obligations)
+    }
 }
 
 /// Compute a goal that some RPITIT (right now, only RPITITs corresponding to Futures)
@@ -1359,10 +1461,11 @@ fn pointer_like_goal_for_rpitit<'tcx>(
         ty::GenericParamDefKind::Lifetime => {
             let kind = ty::BoundRegionKind::Named(arg.def_id, tcx.item_name(arg.def_id));
             bound_vars.push(ty::BoundVariableKind::Region(kind));
-            ty::Region::new_bound(tcx, ty::INNERMOST, ty::BoundRegion {
-                var: ty::BoundVar::from_usize(bound_vars.len() - 1),
-                kind,
-            })
+            ty::Region::new_bound(
+                tcx,
+                ty::INNERMOST,
+                ty::BoundRegion { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
+            )
             .into()
         }
         ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Const { .. } => {
@@ -1371,9 +1474,11 @@ fn pointer_like_goal_for_rpitit<'tcx>(
     });
 
     ty::Binder::bind_with_vars(
-        ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::PointerLike, Some(cause.span)), [
-            Ty::new_projection_from_args(tcx, rpitit_item, args),
-        ]),
+        ty::TraitRef::new(
+            tcx,
+            tcx.require_lang_item(LangItem::PointerLike, Some(cause.span)),
+            [Ty::new_projection_from_args(tcx, rpitit_item, args)],
+        ),
         tcx.mk_bound_variable_kinds(&bound_vars),
     )
 }
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 0cc0d7f786b..436ce3dddd9 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -962,7 +962,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                             return Ok(EvaluatedToAmbig);
                         }
                         ty::ConstKind::Error(_) => return Ok(EvaluatedToOk),
-                        ty::ConstKind::Value(ty, _) => ty,
+                        ty::ConstKind::Value(cv) => cv.ty,
                         ty::ConstKind::Unevaluated(uv) => {
                             self.tcx().type_of(uv.def).instantiate(self.tcx(), uv.args)
                         }
@@ -1620,9 +1620,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             // projections, we will never be able to equate, e.g. `<T as Tr>::A`
             // with `<<T as Tr>::A as Tr>::A`.
             let relevant_bounds = if in_parent_alias_type {
-                self.tcx().item_non_self_assumptions(alias_ty.def_id)
+                self.tcx().item_non_self_bounds(alias_ty.def_id)
             } else {
-                self.tcx().item_super_predicates(alias_ty.def_id)
+                self.tcx().item_self_bounds(alias_ty.def_id)
             };
 
             for bound in relevant_bounds.instantiate(self.tcx(), alias_ty.args) {
@@ -1949,7 +1949,8 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 | TraitAliasCandidate
                 | TraitUpcastingUnsizeCandidate(_)
                 | BuiltinObjectCandidate
-                | BuiltinUnsizeCandidate => false,
+                | BuiltinUnsizeCandidate
+                | BikeshedGuaranteedNoDropCandidate => false,
                 // Non-global param candidates have already been handled, global
                 // where-bounds get ignored.
                 ParamCandidate(_) | ImplCandidate(_) => true,
@@ -2422,9 +2423,11 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 } else {
                     // If this is an ill-formed auto/built-in trait, then synthesize
                     // new error args for the missing generics.
-                    let err_args = ty::GenericArgs::extend_with_error(tcx, trait_def_id, &[
-                        normalized_ty.into(),
-                    ]);
+                    let err_args = ty::GenericArgs::extend_with_error(
+                        tcx,
+                        trait_def_id,
+                        &[normalized_ty.into()],
+                    );
                     ty::TraitRef::new_from_args(tcx, trait_def_id, err_args)
                 };
 
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index 401b41c796d..cb3e81f5477 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -575,7 +575,7 @@ fn report_conflicting_impls<'tcx>(
     match used_to_be_allowed {
         None => {
             let reported = if overlap.with_impl.is_local()
-                || tcx.ensure().orphan_check_impl(impl_def_id).is_ok()
+                || tcx.ensure_ok().orphan_check_impl(impl_def_id).is_ok()
             {
                 let mut err = tcx.dcx().struct_span_err(impl_span, msg());
                 err.code(E0119);
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 c351cf5aaac..f39c611d19f 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs
@@ -379,7 +379,7 @@ pub(crate) fn assoc_def(
         // Ensure that the impl is constrained, otherwise projection may give us
         // bad unconstrained infer vars.
         if let Some(impl_def_id) = impl_def_id.as_local() {
-            tcx.ensure().enforce_impl_non_lifetime_params_are_constrained(impl_def_id)?;
+            tcx.ensure_ok().enforce_impl_non_lifetime_params_are_constrained(impl_def_id)?;
         }
 
         let item = tcx.associated_item(impl_item_id);
@@ -402,7 +402,7 @@ pub(crate) fn assoc_def(
         if assoc_item.item.container == ty::AssocItemContainer::Impl
             && let Some(impl_def_id) = assoc_item.item.container_id(tcx).as_local()
         {
-            tcx.ensure().enforce_impl_non_lifetime_params_are_constrained(impl_def_id)?;
+            tcx.ensure_ok().enforce_impl_non_lifetime_params_are_constrained(impl_def_id)?;
         }
 
         Ok(assoc_item)
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index c9fb2a757e1..d30363ec158 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -25,7 +25,7 @@ use tracing::debug;
 /// trait Bar {}
 /// trait Foo = Bar + Bar;
 ///
-/// let not_object_safe: dyn Foo; // bad, two `Bar` principals.
+/// let dyn_incompatible: dyn Foo; // bad, two `Bar` principals.
 /// ```
 pub fn expand_trait_aliases<'tcx>(
     tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs
index b5bc8364c7b..0024316b877 100644
--- a/compiler/rustc_trait_selection/src/traits/vtable.rs
+++ b/compiler/rustc_trait_selection/src/traits/vtable.rs
@@ -2,26 +2,22 @@ use std::fmt::Debug;
 use std::ops::ControlFlow;
 
 use rustc_hir::def_id::DefId;
-use rustc_infer::infer::at::ToTrace;
-use rustc_infer::infer::{BoundRegionConversionTime, TyCtxtInferExt};
-use rustc_infer::traits::ObligationCause;
 use rustc_infer::traits::util::PredicateSet;
 use rustc_middle::bug;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{
     self, GenericArgs, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt, Upcast, VtblEntry,
 };
-use rustc_span::{DUMMY_SP, Span, sym};
+use rustc_span::DUMMY_SP;
 use smallvec::{SmallVec, smallvec};
 use tracing::debug;
 
-use crate::errors::DumpVTableEntries;
-use crate::traits::{ObligationCtxt, impossible_predicates, is_vtable_safe_method};
+use crate::traits::{impossible_predicates, is_vtable_safe_method};
 
 #[derive(Clone, Debug)]
 pub enum VtblSegment<'tcx> {
     MetadataDSA,
-    TraitOwnEntries { trait_ref: ty::PolyTraitRef<'tcx>, emit_vptr: bool },
+    TraitOwnEntries { trait_ref: ty::TraitRef<'tcx>, emit_vptr: bool },
 }
 
 /// Prepare the segments for a vtable
@@ -29,7 +25,7 @@ pub enum VtblSegment<'tcx> {
 // about our `Self` type here.
 pub fn prepare_vtable_segments<'tcx, T>(
     tcx: TyCtxt<'tcx>,
-    trait_ref: ty::PolyTraitRef<'tcx>,
+    trait_ref: ty::TraitRef<'tcx>,
     segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
 ) -> Option<T> {
     prepare_vtable_segments_inner(tcx, trait_ref, segment_visitor).break_value()
@@ -39,7 +35,7 @@ pub fn prepare_vtable_segments<'tcx, T>(
 /// such that we can use `?` in the body.
 fn prepare_vtable_segments_inner<'tcx, T>(
     tcx: TyCtxt<'tcx>,
-    trait_ref: ty::PolyTraitRef<'tcx>,
+    trait_ref: ty::TraitRef<'tcx>,
     mut segment_visitor: impl FnMut(VtblSegment<'tcx>) -> ControlFlow<T>,
 ) -> ControlFlow<T> {
     // The following constraints holds for the final arrangement.
@@ -92,7 +88,7 @@ fn prepare_vtable_segments_inner<'tcx, T>(
     let mut emit_vptr_on_new_entry = false;
     let mut visited = PredicateSet::new(tcx);
     let predicate = trait_ref.upcast(tcx);
-    let mut stack: SmallVec<[(ty::PolyTraitRef<'tcx>, _, _); 5]> =
+    let mut stack: SmallVec<[(ty::TraitRef<'tcx>, _, _); 5]> =
         smallvec![(trait_ref, emit_vptr_on_new_entry, maybe_iter(None))];
     visited.insert(predicate);
 
@@ -125,10 +121,18 @@ fn prepare_vtable_segments_inner<'tcx, T>(
             let &(inner_most_trait_ref, _, _) = stack.last().unwrap();
 
             let mut direct_super_traits_iter = tcx
-                .explicit_super_predicates_of(inner_most_trait_ref.def_id())
+                .explicit_super_predicates_of(inner_most_trait_ref.def_id)
                 .iter_identity_copied()
                 .filter_map(move |(pred, _)| {
-                    pred.instantiate_supertrait(tcx, inner_most_trait_ref).as_trait_clause()
+                    pred.instantiate_supertrait(tcx, ty::Binder::dummy(inner_most_trait_ref))
+                        .as_trait_clause()
+                })
+                .map(move |pred| {
+                    tcx.normalize_erasing_late_bound_regions(
+                        ty::TypingEnv::fully_monomorphized(),
+                        pred,
+                    )
+                    .trait_ref
                 });
 
             // Find an unvisited supertrait
@@ -136,16 +140,11 @@ fn prepare_vtable_segments_inner<'tcx, T>(
                 .find(|&super_trait| visited.insert(super_trait.upcast(tcx)))
             {
                 // Push it to the stack for the next iteration of 'diving_in to pick up
-                Some(unvisited_super_trait) => {
-                    // We're throwing away potential constness of super traits here.
-                    // FIXME: handle ~const super traits
-                    let next_super_trait = unvisited_super_trait.map_bound(|t| t.trait_ref);
-                    stack.push((
-                        next_super_trait,
-                        emit_vptr_on_new_entry,
-                        maybe_iter(Some(direct_super_traits_iter)),
-                    ))
-                }
+                Some(next_super_trait) => stack.push((
+                    next_super_trait,
+                    emit_vptr_on_new_entry,
+                    maybe_iter(Some(direct_super_traits_iter)),
+                )),
 
                 // There are no more unvisited direct super traits, dive-in finished
                 None => break 'diving_in,
@@ -154,8 +153,7 @@ fn prepare_vtable_segments_inner<'tcx, T>(
 
         // emit innermost item, move to next sibling and stop there if possible, otherwise jump to outer level.
         while let Some((inner_most_trait_ref, emit_vptr, mut siblings)) = stack.pop() {
-            let has_entries =
-                has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id());
+            let has_entries = has_own_existential_vtable_entries(tcx, inner_most_trait_ref.def_id);
 
             segment_visitor(VtblSegment::TraitOwnEntries {
                 trait_ref: inner_most_trait_ref,
@@ -169,11 +167,6 @@ fn prepare_vtable_segments_inner<'tcx, T>(
             if let Some(next_inner_most_trait_ref) =
                 siblings.find(|&sibling| visited.insert(sibling.upcast(tcx)))
             {
-                // We're throwing away potential constness of super traits here.
-                // FIXME: handle ~const super traits
-                let next_inner_most_trait_ref =
-                    next_inner_most_trait_ref.map_bound(|t| t.trait_ref);
-
                 stack.push((next_inner_most_trait_ref, emit_vptr_on_new_entry, siblings));
 
                 // just pushed a new trait onto the stack, so we need to go through its super traits
@@ -192,15 +185,6 @@ fn maybe_iter<I: Iterator>(i: Option<I>) -> impl Iterator<Item = I::Item> {
     i.into_iter().flatten()
 }
 
-fn dump_vtable_entries<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    sp: Span,
-    trait_ref: ty::PolyTraitRef<'tcx>,
-    entries: &[VtblEntry<'tcx>],
-) {
-    tcx.dcx().emit_err(DumpVTableEntries { span: sp, trait_ref, entries: format!("{entries:#?}") });
-}
-
 fn has_own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
     own_existential_vtable_entries_iter(tcx, trait_def_id).next().is_some()
 }
@@ -239,8 +223,15 @@ fn own_existential_vtable_entries_iter(
 /// that come from `trait_ref`, including its supertraits.
 fn vtable_entries<'tcx>(
     tcx: TyCtxt<'tcx>,
-    trait_ref: ty::PolyTraitRef<'tcx>,
+    trait_ref: ty::TraitRef<'tcx>,
 ) -> &'tcx [VtblEntry<'tcx>] {
+    debug_assert!(!trait_ref.has_non_region_infer() && !trait_ref.has_non_region_param());
+    debug_assert_eq!(
+        tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref),
+        trait_ref,
+        "vtable trait ref should be normalized"
+    );
+
     debug!("vtable_entries({:?})", trait_ref);
 
     let mut entries = vec![];
@@ -251,33 +242,26 @@ fn vtable_entries<'tcx>(
                 entries.extend(TyCtxt::COMMON_VTABLE_ENTRIES);
             }
             VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
-                let existential_trait_ref = trait_ref
-                    .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
+                let existential_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref);
 
                 // Lookup the shape of vtable for the trait.
                 let own_existential_entries =
-                    tcx.own_existential_vtable_entries(existential_trait_ref.def_id());
+                    tcx.own_existential_vtable_entries(existential_trait_ref.def_id);
 
                 let own_entries = own_existential_entries.iter().copied().map(|def_id| {
                     debug!("vtable_entries: trait_method={:?}", def_id);
 
                     // The method may have some early-bound lifetimes; add regions for those.
-                    let args = trait_ref.map_bound(|trait_ref| {
+                    // FIXME: Is this normalize needed?
+                    let args = tcx.normalize_erasing_regions(
+                        ty::TypingEnv::fully_monomorphized(),
                         GenericArgs::for_item(tcx, def_id, |param, _| match param.kind {
                             GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
                             GenericParamDefKind::Type { .. }
                             | GenericParamDefKind::Const { .. } => {
                                 trait_ref.args[param.index as usize]
                             }
-                        })
-                    });
-
-                    // The trait type may have higher-ranked lifetimes in it;
-                    // erase them if they appear, so that we get the type
-                    // at some particular call site.
-                    let args = tcx.normalize_erasing_late_bound_regions(
-                        ty::TypingEnv::fully_monomorphized(),
-                        args,
+                        }),
                     );
 
                     // It's possible that the method relies on where-clauses that
@@ -317,11 +301,6 @@ fn vtable_entries<'tcx>(
 
     let _ = prepare_vtable_segments(tcx, trait_ref, vtable_segment_callback);
 
-    if tcx.has_attr(trait_ref.def_id(), sym::rustc_dump_vtable) {
-        let sp = tcx.def_span(trait_ref.def_id());
-        dump_vtable_entries(tcx, sp, trait_ref, &entries);
-    }
-
     tcx.arena.alloc_from_iter(entries)
 }
 
@@ -329,14 +308,28 @@ fn vtable_entries<'tcx>(
 // for `Supertrait`'s methods in the vtable of `Subtrait`.
 pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRef<'tcx>) -> usize {
     debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param());
+    debug_assert_eq!(
+        tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), key),
+        key,
+        "vtable trait ref should be normalized"
+    );
 
     let ty::Dynamic(source, _, _) = *key.self_ty().kind() else {
         bug!();
     };
-    let source_principal =
-        source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self);
+    let source_principal = tcx.instantiate_bound_regions_with_erased(
+        source.principal().unwrap().with_self_ty(tcx, key.self_ty()),
+    );
+
+    // We're monomorphizing a call to a dyn trait object that can never be constructed.
+    if tcx.instantiate_and_check_impossible_predicates((
+        source_principal.def_id,
+        source_principal.args,
+    )) {
+        return 0;
+    }
 
-    let target_principal = ty::Binder::dummy(ty::ExistentialTraitRef::erase_self_ty(tcx, key));
+    let target_principal = ty::ExistentialTraitRef::erase_self_ty(tcx, key);
 
     let vtable_segment_callback = {
         let mut vptr_offset = 0;
@@ -346,17 +339,14 @@ pub(crate) fn first_method_vtable_slot<'tcx>(tcx: TyCtxt<'tcx>, key: ty::TraitRe
                     vptr_offset += TyCtxt::COMMON_VTABLE_ENTRIES.len();
                 }
                 VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
-                    if trait_refs_are_compatible(
-                        tcx,
-                        vtable_principal
-                            .map_bound(|t| ty::ExistentialTraitRef::erase_self_ty(tcx, t)),
-                        target_principal,
-                    ) {
+                    if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal)
+                        == target_principal
+                    {
                         return ControlFlow::Break(vptr_offset);
                     }
 
                     vptr_offset +=
-                        tcx.own_existential_vtable_entries(vtable_principal.def_id()).len();
+                        tcx.own_existential_vtable_entries(vtable_principal.def_id).len();
 
                     if emit_vptr {
                         vptr_offset += 1;
@@ -382,20 +372,35 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
     ),
 ) -> Option<usize> {
     debug_assert!(!key.has_non_region_infer() && !key.has_non_region_param());
+    debug_assert_eq!(
+        tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), key),
+        key,
+        "upcasting trait refs should be normalized"
+    );
+
     let (source, target) = key;
 
     // If the target principal is `None`, we can just return `None`.
-    let ty::Dynamic(target, _, _) = *target.kind() else {
+    let ty::Dynamic(target_data, _, _) = *target.kind() else {
         bug!();
     };
-    let target_principal = target.principal()?;
+    let target_principal = tcx.instantiate_bound_regions_with_erased(target_data.principal()?);
 
     // 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 {
+    let ty::Dynamic(source_data, _, _) = *source.kind() else {
         bug!();
     };
-    let source_principal =
-        source.principal().unwrap().with_self_ty(tcx, tcx.types.trait_object_dummy_self);
+    let source_principal = tcx.instantiate_bound_regions_with_erased(
+        source_data.principal().unwrap().with_self_ty(tcx, source),
+    );
+
+    // We're monomorphizing a dyn trait object upcast that can never be constructed.
+    if tcx.instantiate_and_check_impossible_predicates((
+        source_principal.def_id,
+        source_principal.args,
+    )) {
+        return None;
+    }
 
     let vtable_segment_callback = {
         let mut vptr_offset = 0;
@@ -406,13 +411,10 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
                 }
                 VtblSegment::TraitOwnEntries { trait_ref: vtable_principal, emit_vptr } => {
                     vptr_offset +=
-                        tcx.own_existential_vtable_entries(vtable_principal.def_id()).len();
-                    if trait_refs_are_compatible(
-                        tcx,
-                        vtable_principal
-                            .map_bound(|t| ty::ExistentialTraitRef::erase_self_ty(tcx, t)),
-                        target_principal,
-                    ) {
+                        tcx.own_existential_vtable_entries(vtable_principal.def_id).len();
+                    if ty::ExistentialTraitRef::erase_self_ty(tcx, vtable_principal)
+                        == target_principal
+                    {
                         if emit_vptr {
                             return ControlFlow::Break(Some(vptr_offset));
                         } else {
@@ -432,41 +434,6 @@ pub(crate) fn supertrait_vtable_slot<'tcx>(
     prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap()
 }
 
-fn trait_refs_are_compatible<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    hr_vtable_principal: ty::PolyExistentialTraitRef<'tcx>,
-    hr_target_principal: ty::PolyExistentialTraitRef<'tcx>,
-) -> bool {
-    if hr_vtable_principal.def_id() != hr_target_principal.def_id() {
-        return false;
-    }
-
-    let (infcx, param_env) =
-        tcx.infer_ctxt().build_with_typing_env(ty::TypingEnv::fully_monomorphized());
-    let ocx = ObligationCtxt::new(&infcx);
-    let hr_source_principal =
-        ocx.normalize(&ObligationCause::dummy(), param_env, hr_vtable_principal);
-    let hr_target_principal =
-        ocx.normalize(&ObligationCause::dummy(), param_env, hr_target_principal);
-    infcx.enter_forall(hr_target_principal, |target_principal| {
-        let source_principal = infcx.instantiate_binder_with_fresh_vars(
-            DUMMY_SP,
-            BoundRegionConversionTime::HigherRankedType,
-            hr_source_principal,
-        );
-        let Ok(()) = ocx.eq_trace(
-            &ObligationCause::dummy(),
-            param_env,
-            ToTrace::to_trace(&ObligationCause::dummy(), hr_target_principal, hr_source_principal),
-            target_principal,
-            source_principal,
-        ) else {
-            return false;
-        };
-        ocx.select_all_or_error().is_empty()
-    })
-}
-
 pub(super) fn provide(providers: &mut Providers) {
     *providers = Providers {
         own_existential_vtable_entries,
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index 9d32eb05386..2f72b44f6b6 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -8,8 +8,9 @@ use rustc_middle::ty::{
     self, GenericArg, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable,
     TypeVisitable, TypeVisitableExt, TypeVisitor,
 };
-use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
-use rustc_span::{DUMMY_SP, Span};
+use rustc_session::parse::feature_err;
+use rustc_span::def_id::{DefId, LocalDefId};
+use rustc_span::{Span, sym};
 use tracing::{debug, instrument, trace};
 
 use crate::infer::InferCtxt;
@@ -89,6 +90,8 @@ pub fn unnormalized_obligations<'tcx>(
     infcx: &InferCtxt<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     arg: GenericArg<'tcx>,
+    span: Span,
+    body_id: LocalDefId,
 ) -> Option<PredicateObligations<'tcx>> {
     debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg));
 
@@ -106,8 +109,8 @@ pub fn unnormalized_obligations<'tcx>(
     let mut wf = WfPredicates {
         infcx,
         param_env,
-        body_id: CRATE_DEF_ID,
-        span: DUMMY_SP,
+        body_id,
+        span,
         out: PredicateObligations::new(),
         recursion_depth: 0,
         item: None,
@@ -689,7 +692,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
                 self.require_sized(subty, ObligationCauseCode::SliceOrArrayElem);
                 // 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);
+                let cause = self.cause(ObligationCauseCode::ArrayLen(t));
                 self.out.push(traits::Obligation::with_depth(
                     tcx,
                     cause,
@@ -702,8 +705,47 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
                 ));
             }
 
-            ty::Pat(subty, _) => {
+            ty::Pat(subty, pat) => {
                 self.require_sized(subty, ObligationCauseCode::Misc);
+                match *pat {
+                    ty::PatternKind::Range { start, end, include_end: _ } => {
+                        let mut check = |c| {
+                            let cause = self.cause(ObligationCauseCode::Misc);
+                            self.out.push(traits::Obligation::with_depth(
+                                tcx,
+                                cause.clone(),
+                                self.recursion_depth,
+                                self.param_env,
+                                ty::Binder::dummy(ty::PredicateKind::Clause(
+                                    ty::ClauseKind::ConstArgHasType(c, subty),
+                                )),
+                            ));
+                            if !tcx.features().generic_pattern_types() {
+                                if c.has_param() {
+                                    if self.span.is_dummy() {
+                                        self.tcx().dcx().delayed_bug(
+                                            "feature error should be reported elsewhere, too",
+                                        );
+                                    } else {
+                                        feature_err(
+                                            &self.tcx().sess,
+                                            sym::generic_pattern_types,
+                                            self.span,
+                                            "wraparound pattern type ranges cause monomorphization time errors",
+                                        )
+                                        .emit();
+                                    }
+                                }
+                            }
+                        };
+                        if let Some(start) = start {
+                            check(start)
+                        }
+                        if let Some(end) = end {
+                            check(end)
+                        }
+                    }
+                }
             }
 
             ty::Tuple(tys) => {
@@ -828,8 +870,28 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
                 // Let the visitor iterate into the argument/return
                 // types appearing in the fn signature.
             }
-            ty::UnsafeBinder(_) => {
-                // FIXME(unsafe_binders): We should also recurse into the binder here.
+            ty::UnsafeBinder(ty) => {
+                // FIXME(unsafe_binders): For now, we have no way to express
+                // that a type must be `ManuallyDrop` OR `Copy` (or a pointer).
+                if !ty.has_escaping_bound_vars() {
+                    self.out.push(traits::Obligation::new(
+                        self.tcx(),
+                        self.cause(ObligationCauseCode::Misc),
+                        self.param_env,
+                        ty.map_bound(|ty| {
+                            ty::TraitRef::new(
+                                self.tcx(),
+                                self.tcx().require_lang_item(
+                                    LangItem::BikeshedGuaranteedNoDrop,
+                                    Some(self.span),
+                                ),
+                                [ty],
+                            )
+                        }),
+                    ));
+                }
+
+                // We recurse into the binder below.
             }
 
             ty::Dynamic(data, r, _) => {