about summary refs log tree commit diff
path: root/compiler/rustc_hir_analysis/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_hir_analysis/src')
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/generics.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/check/_match.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/callee.rs142
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs135
-rw-r--r--compiler/rustc_hir_analysis/src/check/coercion.rs13
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_method.rs1048
-rw-r--r--compiler/rustc_hir_analysis/src/check/demand.rs52
-rw-r--r--compiler/rustc_hir_analysis/src/check/expr.rs29
-rw-r--r--compiler/rustc_hir_analysis/src/check/fn_ctxt/_impl.rs13
-rw-r--r--compiler/rustc_hir_analysis/src/check/fn_ctxt/checks.rs14
-rw-r--r--compiler/rustc_hir_analysis/src/check/fn_ctxt/mod.rs16
-rw-r--r--compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs127
-rw-r--r--compiler/rustc_hir_analysis/src/check/inherited.rs36
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsicck.rs20
-rw-r--r--compiler/rustc_hir_analysis/src/check/method/probe.rs116
-rw-r--r--compiler/rustc_hir_analysis/src/check/method/suggest.rs7
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/op.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs77
-rw-r--r--compiler/rustc_hir_analysis/src/check/writeback.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/builtin.rs646
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs13
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/collect/lifetimes.rs1888
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/constrained_generic_params.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/expr_use_visitor.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/hir_wf_check.rs58
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs87
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs66
-rw-r--r--compiler/rustc_hir_analysis/src/mem_categorization.rs6
32 files changed, 3376 insertions, 1277 deletions
diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs
index b66e59d8ac6..47915b4bd4e 100644
--- a/compiler/rustc_hir_analysis/src/astconv/generics.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs
@@ -83,9 +83,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 Res::Def(DefKind::TyParam, src_def_id) => {
                     if let Some(param_local_id) = param.def_id.as_local() {
                         let param_name = tcx.hir().ty_param_name(param_local_id);
-                        let param_type = tcx.infer_ctxt().enter(|infcx| {
-                            infcx.resolve_numeric_literals_with_default(tcx.type_of(param.def_id))
-                        });
+                        let infcx = tcx.infer_ctxt().build();
+                        let param_type =
+                            infcx.resolve_numeric_literals_with_default(tcx.type_of(param.def_id));
                         if param_type.is_suggestable(tcx, false) {
                             err.span_suggestion(
                                 tcx.def_span(src_def_id),
diff --git a/compiler/rustc_hir_analysis/src/check/_match.rs b/compiler/rustc_hir_analysis/src/check/_match.rs
index 201927091a6..143508b785f 100644
--- a/compiler/rustc_hir_analysis/src/check/_match.rs
+++ b/compiler/rustc_hir_analysis/src/check/_match.rs
@@ -259,7 +259,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 err.help("consider adding an `else` block that evaluates to the expected type");
                 error = true;
             },
-            ret_reason.is_none(),
+            false,
         );
         error
     }
diff --git a/compiler/rustc_hir_analysis/src/check/callee.rs b/compiler/rustc_hir_analysis/src/check/callee.rs
index 080771844a4..f0a7c910906 100644
--- a/compiler/rustc_hir_analysis/src/check/callee.rs
+++ b/compiler/rustc_hir_analysis/src/check/callee.rs
@@ -1,8 +1,10 @@
+use super::method::probe::{IsSuggestion, Mode, ProbeScope};
 use super::method::MethodCallee;
 use super::{DefIdOrName, Expectation, FnCtxt, TupleArgumentsFlag};
 use crate::type_error_struct;
 
-use rustc_errors::{struct_span_err, Applicability, Diagnostic};
+use rustc_ast::util::parser::PREC_POSTFIX;
+use rustc_errors::{struct_span_err, Applicability, Diagnostic, StashKey};
 use rustc_hir as hir;
 use rustc_hir::def::{self, Namespace, Res};
 use rustc_hir::def_id::DefId;
@@ -60,6 +62,7 @@ pub fn check_legal_trait_for_method_call(
     }
 }
 
+#[derive(Debug)]
 enum CallStep<'tcx> {
     Builtin(Ty<'tcx>),
     DeferredClosure(LocalDefId, ty::FnSig<'tcx>),
@@ -188,6 +191,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 return None;
             }
 
+            ty::Error(_) => {
+                return None;
+            }
+
             _ => {}
         }
 
@@ -394,6 +401,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
             ty::FnPtr(sig) => (sig, None),
             _ => {
+                if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = &callee_expr.kind
+                    && let [segment] = path.segments
+                    && let Some(mut diag) = self
+                        .tcx
+                        .sess
+                        .diagnostic()
+                        .steal_diagnostic(segment.ident.span, StashKey::CallIntoMethod)
+                {
+                    // Try suggesting `foo(a)` -> `a.foo()` if possible.
+                    if let Some(ty) =
+                        self.suggest_call_as_method(
+                            &mut diag,
+                            segment,
+                            arg_exprs,
+                            call_expr,
+                            expected
+                        )
+                    {
+                        diag.emit();
+                        return ty;
+                    } else {
+                        diag.emit();
+                    }
+                }
+
                 self.report_invalid_callee(call_expr, callee_expr, callee_ty, arg_exprs);
 
                 // This is the "default" function signature, used in case of error.
@@ -441,6 +473,105 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         fn_sig.output()
     }
 
+    /// Attempts to reinterpret `method(rcvr, args...)` as `rcvr.method(args...)`
+    /// and suggesting the fix if the method probe is successful.
+    fn suggest_call_as_method(
+        &self,
+        diag: &mut Diagnostic,
+        segment: &'tcx hir::PathSegment<'tcx>,
+        arg_exprs: &'tcx [hir::Expr<'tcx>],
+        call_expr: &'tcx hir::Expr<'tcx>,
+        expected: Expectation<'tcx>,
+    ) -> Option<Ty<'tcx>> {
+        if let [callee_expr, rest @ ..] = arg_exprs {
+            let callee_ty = self.check_expr(callee_expr);
+            // First, do a probe with `IsSuggestion(true)` to avoid emitting
+            // any strange errors. If it's successful, then we'll do a true
+            // method lookup.
+            let Ok(pick) = self
+            .probe_for_name(
+                call_expr.span,
+                Mode::MethodCall,
+                segment.ident,
+                IsSuggestion(true),
+                callee_ty,
+                call_expr.hir_id,
+                // We didn't record the in scope traits during late resolution
+                // so we need to probe AllTraits unfortunately
+                ProbeScope::AllTraits,
+            ) else {
+                return None;
+            };
+
+            let pick = self.confirm_method(
+                call_expr.span,
+                callee_expr,
+                call_expr,
+                callee_ty,
+                pick,
+                segment,
+            );
+            if pick.illegal_sized_bound.is_some() {
+                return None;
+            }
+
+            let up_to_rcvr_span = segment.ident.span.until(callee_expr.span);
+            let rest_span = callee_expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi());
+            let rest_snippet = if let Some(first) = rest.first() {
+                self.tcx
+                    .sess
+                    .source_map()
+                    .span_to_snippet(first.span.to(call_expr.span.shrink_to_hi()))
+            } else {
+                Ok(")".to_string())
+            };
+
+            if let Ok(rest_snippet) = rest_snippet {
+                let sugg = if callee_expr.precedence().order() >= PREC_POSTFIX {
+                    vec![
+                        (up_to_rcvr_span, "".to_string()),
+                        (rest_span, format!(".{}({rest_snippet}", segment.ident)),
+                    ]
+                } else {
+                    vec![
+                        (up_to_rcvr_span, "(".to_string()),
+                        (rest_span, format!(").{}({rest_snippet}", segment.ident)),
+                    ]
+                };
+                let self_ty = self.resolve_vars_if_possible(pick.callee.sig.inputs()[0]);
+                diag.multipart_suggestion(
+                    format!(
+                        "use the `.` operator to call the method `{}{}` on `{self_ty}`",
+                        self.tcx
+                            .associated_item(pick.callee.def_id)
+                            .trait_container(self.tcx)
+                            .map_or_else(
+                                || String::new(),
+                                |trait_def_id| self.tcx.def_path_str(trait_def_id) + "::"
+                            ),
+                        segment.ident
+                    ),
+                    sugg,
+                    Applicability::MaybeIncorrect,
+                );
+
+                // Let's check the method fully now
+                let return_ty = self.check_method_argument_types(
+                    segment.ident.span,
+                    call_expr,
+                    Ok(pick.callee),
+                    rest,
+                    TupleArgumentsFlag::DontTupleArguments,
+                    expected,
+                );
+
+                return Some(return_ty);
+            }
+        }
+
+        None
+    }
+
     fn report_invalid_callee(
         &self,
         call_expr: &'tcx hir::Expr<'tcx>,
@@ -459,10 +590,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 def::CtorOf::Struct => "struct",
                 def::CtorOf::Variant => "enum variant",
             };
-            let removal_span =
-                callee_expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi());
-            unit_variant =
-                Some((removal_span, descr, rustc_hir_pretty::qpath_to_string(qpath)));
+            let removal_span = callee_expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi());
+            unit_variant = Some((removal_span, descr, rustc_hir_pretty::qpath_to_string(qpath)));
         }
 
         let callee_ty = self.resolve_vars_if_possible(callee_ty);
@@ -525,7 +654,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
 
         if !self.maybe_suggest_bad_array_definition(&mut err, call_expr, callee_expr) {
-            if let Some((maybe_def, output_ty, _)) = self.extract_callable_info(callee_expr, callee_ty)
+            if let Some((maybe_def, output_ty, _)) =
+                self.extract_callable_info(callee_expr, callee_ty)
                 && !self.type_is_sized_modulo_regions(self.param_env, output_ty, callee_expr.span)
             {
                 let descr = match maybe_def {
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index bf022286b82..7cee9779c5f 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -2,7 +2,7 @@ use crate::check::intrinsicck::InlineAsmCtxt;
 
 use super::coercion::CoerceMany;
 use super::compare_method::check_type_bounds;
-use super::compare_method::{compare_const_impl, compare_impl_method, compare_ty_impl};
+use super::compare_method::{compare_impl_method, compare_ty_impl};
 use super::*;
 use rustc_attr as attr;
 use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan};
@@ -29,9 +29,8 @@ use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVE
 use rustc_span::symbol::sym;
 use rustc_span::{self, Span};
 use rustc_target::spec::abi::Abi;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::{self, ObligationCtxt};
-use rustc_ty_utils::representability::{self, Representability};
 
 use std::ops::ControlFlow;
 
@@ -78,7 +77,7 @@ pub(super) fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Ab
 /// * inherited: other fields inherited from the enclosing fn (if any)
 #[instrument(skip(inherited, body), level = "debug")]
 pub(super) fn check_fn<'a, 'tcx>(
-    inherited: &'a Inherited<'a, 'tcx>,
+    inherited: &'a Inherited<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     fn_sig: ty::FnSig<'tcx>,
     decl: &'tcx hir::FnDecl<'tcx>,
@@ -381,7 +380,6 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     let def = tcx.adt_def(def_id);
     let span = tcx.def_span(def_id);
     def.destructor(tcx); // force the destructor to be evaluated
-    check_representable(tcx, span, def_id);
 
     if def.repr().simd() {
         check_simd(tcx, span, def_id);
@@ -395,7 +393,6 @@ fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     let def = tcx.adt_def(def_id);
     let span = tcx.def_span(def_id);
     def.destructor(tcx); // force the destructor to be evaluated
-    check_representable(tcx, span, def_id);
     check_transparent(tcx, span, def);
     check_union_fields(tcx, span, def_id);
     check_packed(tcx, span, def);
@@ -718,10 +715,12 @@ pub(super) fn check_opaque_for_cycles<'tcx>(
 /// check those cases in the `param_env` of that function, which may have
 /// bounds not on this opaque type:
 ///
-/// type X<T> = impl Clone
+/// ```ignore (illustrative)
+/// type X<T> = impl Clone;
 /// fn f<T: Clone>(t: T) -> X<T> {
 ///     t
 /// }
+/// ```
 ///
 /// Without this check the above code is incorrectly accepted: we would ICE if
 /// some tried, for example, to clone an `Option<X<&mut ()>>`.
@@ -742,52 +741,52 @@ fn check_opaque_meets_bounds<'tcx>(
     };
     let param_env = tcx.param_env(defining_use_anchor);
 
-    tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(defining_use_anchor)).enter(
-        move |infcx| {
-            let ocx = ObligationCtxt::new(&infcx);
-            let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs);
+    let infcx = tcx
+        .infer_ctxt()
+        .with_opaque_type_inference(DefiningAnchor::Bind(defining_use_anchor))
+        .build();
+    let ocx = ObligationCtxt::new(&infcx);
+    let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs);
 
-            let misc_cause = traits::ObligationCause::misc(span, hir_id);
+    let misc_cause = traits::ObligationCause::misc(span, hir_id);
 
-            match infcx.at(&misc_cause, param_env).eq(opaque_ty, hidden_type) {
-                Ok(infer_ok) => ocx.register_infer_ok_obligations(infer_ok),
-                Err(ty_err) => {
-                    tcx.sess.delay_span_bug(
-                        span,
-                        &format!("could not unify `{hidden_type}` with revealed type:\n{ty_err}"),
-                    );
-                }
-            }
+    match infcx.at(&misc_cause, param_env).eq(opaque_ty, hidden_type) {
+        Ok(infer_ok) => ocx.register_infer_ok_obligations(infer_ok),
+        Err(ty_err) => {
+            tcx.sess.delay_span_bug(
+                span,
+                &format!("could not unify `{hidden_type}` with revealed type:\n{ty_err}"),
+            );
+        }
+    }
 
-            // Additionally require the hidden type to be well-formed with only the generics of the opaque type.
-            // Defining use functions may have more bounds than the opaque type, which is ok, as long as the
-            // hidden type is well formed even without those bounds.
-            let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(hidden_type.into()))
-                .to_predicate(tcx);
-            ocx.register_obligation(Obligation::new(misc_cause, param_env, predicate));
-
-            // Check that all obligations are satisfied by the implementation's
-            // version.
-            let errors = ocx.select_all_or_error();
-            if !errors.is_empty() {
-                infcx.report_fulfillment_errors(&errors, None, false);
-            }
-            match origin {
-                // Checked when type checking the function containing them.
-                hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {}
-                // Can have different predicates to their defining use
-                hir::OpaqueTyOrigin::TyAlias => {
-                    let outlives_environment = OutlivesEnvironment::new(param_env);
-                    infcx.check_region_obligations_and_report_errors(
-                        defining_use_anchor,
-                        &outlives_environment,
-                    );
-                }
-            }
-            // Clean up after ourselves
-            let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
-        },
-    );
+    // Additionally require the hidden type to be well-formed with only the generics of the opaque type.
+    // Defining use functions may have more bounds than the opaque type, which is ok, as long as the
+    // hidden type is well formed even without those bounds.
+    let predicate =
+        ty::Binder::dummy(ty::PredicateKind::WellFormed(hidden_type.into())).to_predicate(tcx);
+    ocx.register_obligation(Obligation::new(misc_cause, param_env, predicate));
+
+    // Check that all obligations are satisfied by the implementation's
+    // version.
+    let errors = ocx.select_all_or_error();
+    if !errors.is_empty() {
+        infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+    }
+    match origin {
+        // Checked when type checking the function containing them.
+        hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {}
+        // Can have different predicates to their defining use
+        hir::OpaqueTyOrigin::TyAlias => {
+            let outlives_environment = OutlivesEnvironment::new(param_env);
+            infcx.check_region_obligations_and_report_errors(
+                defining_use_anchor,
+                &outlives_environment,
+            );
+        }
+    }
+    // Clean up after ourselves
+    let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
 }
 
 fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
@@ -1058,14 +1057,10 @@ fn check_impl_items_against_trait<'tcx>(
         let impl_item_full = tcx.hir().impl_item(impl_item.id);
         match impl_item_full.kind {
             hir::ImplItemKind::Const(..) => {
-                // Find associated const definition.
-                compare_const_impl(
-                    tcx,
-                    &ty_impl_item,
-                    impl_item.span,
-                    &ty_trait_item,
-                    impl_trait_ref,
-                );
+                let _ = tcx.compare_assoc_const_impl_item_with_trait_item((
+                    impl_item.id.def_id.def_id,
+                    ty_impl_item.trait_item_def_id.unwrap(),
+                ));
             }
             hir::ImplItemKind::Fn(..) => {
                 let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id);
@@ -1077,7 +1072,7 @@ fn check_impl_items_against_trait<'tcx>(
                     opt_trait_span,
                 );
             }
-            hir::ImplItemKind::TyAlias(impl_ty) => {
+            hir::ImplItemKind::Type(impl_ty) => {
                 let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id);
                 compare_ty_impl(
                     tcx,
@@ -1165,27 +1160,6 @@ fn check_impl_items_against_trait<'tcx>(
     }
 }
 
-/// Checks whether a type can be represented in memory. In particular, it
-/// identifies types that contain themselves without indirection through a
-/// pointer, which would mean their size is unbounded.
-pub(super) fn check_representable(tcx: TyCtxt<'_>, sp: Span, item_def_id: LocalDefId) -> bool {
-    let rty = tcx.type_of(item_def_id);
-
-    // Check that it is possible to represent this type. This call identifies
-    // (1) types that contain themselves and (2) types that contain a different
-    // recursive type. It is only necessary to throw an error on those that
-    // contain themselves. For case 2, there must be an inner type that will be
-    // caught by case 1.
-    match representability::ty_is_representable(tcx, rty, sp, None) {
-        Representability::SelfRecursive(spans) => {
-            recursive_type_with_infinite_size_error(tcx, item_def_id.to_def_id(), spans);
-            return false;
-        }
-        Representability::Representable | Representability::ContainsRecursive => (),
-    }
-    true
-}
-
 pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
     let t = tcx.type_of(def_id);
     if let ty::Adt(def, substs) = t.kind()
@@ -1523,7 +1497,6 @@ fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: L
 
     detect_discriminant_duplicate(tcx, def.discriminants(tcx).collect(), vs, sp);
 
-    check_representable(tcx, sp, def_id);
     check_transparent(tcx, sp, def);
 }
 
diff --git a/compiler/rustc_hir_analysis/src/check/coercion.rs b/compiler/rustc_hir_analysis/src/check/coercion.rs
index d738e563256..cf87fe3c510 100644
--- a/compiler/rustc_hir_analysis/src/check/coercion.rs
+++ b/compiler/rustc_hir_analysis/src/check/coercion.rs
@@ -61,7 +61,7 @@ use rustc_span::symbol::sym;
 use rustc_span::{self, BytePos, DesugaringKind, Span};
 use rustc_target::spec::abi::Abi;
 use rustc_trait_selection::infer::InferCtxtExt as _;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
 
 use smallvec::{smallvec, SmallVec};
@@ -702,7 +702,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
 
                 // Object safety violations or miscellaneous.
                 Err(err) => {
-                    self.report_selection_error(obligation.clone(), &obligation, &err, false);
+                    self.err_ctxt().report_selection_error(
+                        obligation.clone(),
+                        &obligation,
+                        &err,
+                        false,
+                    );
                     // Treat this like an obligation and follow through
                     // with the unsizing - the lack of a coercion should
                     // be silent, as it causes a type mismatch later.
@@ -1549,7 +1554,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
                         }
                     }
                     _ => {
-                        err = fcx.report_mismatched_types(
+                        err = fcx.err_ctxt().report_mismatched_types(
                             cause,
                             expected,
                             found,
@@ -1629,7 +1634,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
         expression: Option<&'tcx hir::Expr<'tcx>>,
         blk_id: Option<hir::HirId>,
     ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
-        let mut err = fcx.report_mismatched_types(cause, expected, found, ty_err);
+        let mut err = fcx.err_ctxt().report_mismatched_types(cause, expected, found, ty_err);
 
         let mut pointing_at_return_type = false;
         let mut fn_output = None;
diff --git a/compiler/rustc_hir_analysis/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs
index ae98a8f6209..5e5dbedb4bd 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_method.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs
@@ -1,6 +1,6 @@
 use super::potentially_plural_count;
 use crate::errors::LifetimesOrBoundsMismatchOnTrait;
-use hir::def_id::DefId;
+use hir::def_id::{DefId, LocalDefId};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed};
 use rustc_hir as hir;
@@ -19,7 +19,7 @@ use rustc_middle::ty::{
 };
 use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt};
 use rustc_span::Span;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
 use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
 use rustc_trait_selection::traits::{
     self, ObligationCause, ObligationCauseCode, ObligationCtxt, Reveal,
@@ -215,224 +215,220 @@ fn compare_predicate_entailment<'tcx>(
     );
     let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
 
-    tcx.infer_ctxt().enter(|ref infcx| {
-        let ocx = ObligationCtxt::new(infcx);
+    let infcx = &tcx.infer_ctxt().build();
+    let ocx = ObligationCtxt::new(infcx);
 
-        debug!("compare_impl_method: caller_bounds={:?}", param_env.caller_bounds());
+    debug!("compare_impl_method: caller_bounds={:?}", param_env.caller_bounds());
 
-        let mut selcx = traits::SelectionContext::new(&infcx);
-        let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_substs);
-        for (predicate, span) in iter::zip(impl_m_own_bounds.predicates, impl_m_own_bounds.spans) {
-            let normalize_cause = traits::ObligationCause::misc(span, impl_m_hir_id);
-            let traits::Normalized { value: predicate, obligations } =
-                traits::normalize(&mut selcx, param_env, normalize_cause, predicate);
+    let mut selcx = traits::SelectionContext::new(&infcx);
+    let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_substs);
+    for (predicate, span) in iter::zip(impl_m_own_bounds.predicates, impl_m_own_bounds.spans) {
+        let normalize_cause = traits::ObligationCause::misc(span, impl_m_hir_id);
+        let traits::Normalized { value: predicate, obligations } =
+            traits::normalize(&mut selcx, param_env, normalize_cause, predicate);
 
-            ocx.register_obligations(obligations);
-            let cause = ObligationCause::new(
-                span,
-                impl_m_hir_id,
-                ObligationCauseCode::CompareImplItemObligation {
-                    impl_item_def_id: impl_m.def_id.expect_local(),
-                    trait_item_def_id: trait_m.def_id,
-                    kind: impl_m.kind,
-                },
-            );
-            ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate));
-        }
-
-        // We now need to check that the signature of the impl method is
-        // compatible with that of the trait method. We do this by
-        // checking that `impl_fty <: trait_fty`.
-        //
-        // FIXME. Unfortunately, this doesn't quite work right now because
-        // associated type normalization is not integrated into subtype
-        // checks. For the comparison to be valid, we need to
-        // normalize the associated types in the impl/trait methods
-        // first. However, because function types bind regions, just
-        // calling `normalize_associated_types_in` would have no effect on
-        // any associated types appearing in the fn arguments or return
-        // type.
-
-        // Compute placeholder form of impl and trait method tys.
-        let tcx = infcx.tcx;
-
-        let mut wf_tys = FxHashSet::default();
-
-        let impl_sig = infcx.replace_bound_vars_with_fresh_vars(
-            impl_m_span,
-            infer::HigherRankedType,
-            tcx.fn_sig(impl_m.def_id),
+        ocx.register_obligations(obligations);
+        let cause = ObligationCause::new(
+            span,
+            impl_m_hir_id,
+            ObligationCauseCode::CompareImplItemObligation {
+                impl_item_def_id: impl_m.def_id.expect_local(),
+                trait_item_def_id: trait_m.def_id,
+                kind: impl_m.kind,
+            },
         );
+        ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate));
+    }
 
-        let norm_cause = ObligationCause::misc(impl_m_span, impl_m_hir_id);
-        let impl_sig = ocx.normalize(norm_cause.clone(), param_env, impl_sig);
-        let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig));
-        debug!("compare_impl_method: impl_fty={:?}", impl_fty);
-
-        let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs);
-        let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig);
-
-        // Next, add all inputs and output as well-formed tys. Importantly,
-        // we have to do this before normalization, since the normalized ty may
-        // not contain the input parameters. See issue #87748.
-        wf_tys.extend(trait_sig.inputs_and_output.iter());
-        let trait_sig = ocx.normalize(norm_cause, param_env, trait_sig);
-        // We also have to add the normalized trait signature
-        // as we don't normalize during implied bounds computation.
-        wf_tys.extend(trait_sig.inputs_and_output.iter());
-        let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig));
-
-        debug!("compare_impl_method: trait_fty={:?}", trait_fty);
-
-        // FIXME: We'd want to keep more accurate spans than "the method signature" when
-        // processing the comparison between the trait and impl fn, but we sadly lose them
-        // and point at the whole signature when a trait bound or specific input or output
-        // type would be more appropriate. In other places we have a `Vec<Span>`
-        // corresponding to their `Vec<Predicate>`, but we don't have that here.
-        // Fixing this would improve the output of test `issue-83765.rs`.
-        let mut result = infcx
-            .at(&cause, param_env)
-            .sup(trait_fty, impl_fty)
-            .map(|infer_ok| ocx.register_infer_ok_obligations(infer_ok));
-
-        // HACK(RPITIT): #101614. When we are trying to infer the hidden types for
-        // RPITITs, we need to equate the output tys instead of just subtyping. If
-        // we just use `sup` above, we'll end up `&'static str <: _#1t`, which causes
-        // us to infer `_#1t = #'_#2r str`, where `'_#2r` is unconstrained, which gets
-        // fixed up to `ReEmpty`, and which is certainly not what we want.
-        if trait_fty.has_infer_types() {
-            result = result.and_then(|()| {
-                infcx
-                    .at(&cause, param_env)
-                    .eq(trait_sig.output(), impl_sig.output())
-                    .map(|infer_ok| ocx.register_infer_ok_obligations(infer_ok))
-            });
-        }
+    // We now need to check that the signature of the impl method is
+    // compatible with that of the trait method. We do this by
+    // checking that `impl_fty <: trait_fty`.
+    //
+    // FIXME. Unfortunately, this doesn't quite work right now because
+    // associated type normalization is not integrated into subtype
+    // checks. For the comparison to be valid, we need to
+    // normalize the associated types in the impl/trait methods
+    // first. However, because function types bind regions, just
+    // calling `normalize_associated_types_in` would have no effect on
+    // any associated types appearing in the fn arguments or return
+    // type.
+
+    // Compute placeholder form of impl and trait method tys.
+    let tcx = infcx.tcx;
 
-        if let Err(terr) = result {
-            debug!("sub_types failed: impl ty {:?}, trait ty {:?}", impl_fty, trait_fty);
+    let mut wf_tys = FxHashSet::default();
 
-            let (impl_err_span, trait_err_span) =
-                extract_spans_for_error_reporting(&infcx, terr, &cause, impl_m, trait_m);
+    let impl_sig = infcx.replace_bound_vars_with_fresh_vars(
+        impl_m_span,
+        infer::HigherRankedType,
+        tcx.fn_sig(impl_m.def_id),
+    );
 
-            cause.span = impl_err_span;
+    let norm_cause = ObligationCause::misc(impl_m_span, impl_m_hir_id);
+    let impl_sig = ocx.normalize(norm_cause.clone(), param_env, impl_sig);
+    let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig));
+    debug!("compare_impl_method: impl_fty={:?}", impl_fty);
+
+    let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs);
+    let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig);
+
+    // Next, add all inputs and output as well-formed tys. Importantly,
+    // we have to do this before normalization, since the normalized ty may
+    // not contain the input parameters. See issue #87748.
+    wf_tys.extend(trait_sig.inputs_and_output.iter());
+    let trait_sig = ocx.normalize(norm_cause, param_env, trait_sig);
+    // We also have to add the normalized trait signature
+    // as we don't normalize during implied bounds computation.
+    wf_tys.extend(trait_sig.inputs_and_output.iter());
+    let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig));
+
+    debug!("compare_impl_method: trait_fty={:?}", trait_fty);
+
+    // FIXME: We'd want to keep more accurate spans than "the method signature" when
+    // processing the comparison between the trait and impl fn, but we sadly lose them
+    // and point at the whole signature when a trait bound or specific input or output
+    // type would be more appropriate. In other places we have a `Vec<Span>`
+    // corresponding to their `Vec<Predicate>`, but we don't have that here.
+    // Fixing this would improve the output of test `issue-83765.rs`.
+    let mut result = infcx
+        .at(&cause, param_env)
+        .sup(trait_fty, impl_fty)
+        .map(|infer_ok| ocx.register_infer_ok_obligations(infer_ok));
+
+    // HACK(RPITIT): #101614. When we are trying to infer the hidden types for
+    // RPITITs, we need to equate the output tys instead of just subtyping. If
+    // we just use `sup` above, we'll end up `&'static str <: _#1t`, which causes
+    // us to infer `_#1t = #'_#2r str`, where `'_#2r` is unconstrained, which gets
+    // fixed up to `ReEmpty`, and which is certainly not what we want.
+    if trait_fty.has_infer_types() {
+        result = result.and_then(|()| {
+            infcx
+                .at(&cause, param_env)
+                .eq(trait_sig.output(), impl_sig.output())
+                .map(|infer_ok| ocx.register_infer_ok_obligations(infer_ok))
+        });
+    }
 
-            let mut diag = struct_span_err!(
-                tcx.sess,
-                cause.span(),
-                E0053,
-                "method `{}` has an incompatible type for trait",
-                trait_m.name
-            );
-            match &terr {
-                TypeError::ArgumentMutability(0) | TypeError::ArgumentSorts(_, 0)
-                    if trait_m.fn_has_self_parameter =>
-                {
-                    let ty = trait_sig.inputs()[0];
-                    let sugg = match ExplicitSelf::determine(ty, |_| ty == impl_trait_ref.self_ty())
-                    {
-                        ExplicitSelf::ByValue => "self".to_owned(),
-                        ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(),
-                        ExplicitSelf::ByReference(_, hir::Mutability::Mut) => {
-                            "&mut self".to_owned()
-                        }
-                        _ => format!("self: {ty}"),
-                    };
+    if let Err(terr) = result {
+        debug!("sub_types failed: impl ty {:?}, trait ty {:?}", impl_fty, trait_fty);
 
-                    // When the `impl` receiver is an arbitrary self type, like `self: Box<Self>`, the
-                    // span points only at the type `Box<Self`>, but we want to cover the whole
-                    // argument pattern and type.
-                    let span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
-                        ImplItemKind::Fn(ref sig, body) => tcx
-                            .hir()
-                            .body_param_names(body)
-                            .zip(sig.decl.inputs.iter())
-                            .map(|(param, ty)| param.span.to(ty.span))
-                            .next()
-                            .unwrap_or(impl_err_span),
-                        _ => bug!("{:?} is not a method", impl_m),
-                    };
+        let (impl_err_span, trait_err_span) =
+            extract_spans_for_error_reporting(&infcx, terr, &cause, impl_m, trait_m);
+
+        cause.span = impl_err_span;
 
+        let mut diag = struct_span_err!(
+            tcx.sess,
+            cause.span(),
+            E0053,
+            "method `{}` has an incompatible type for trait",
+            trait_m.name
+        );
+        match &terr {
+            TypeError::ArgumentMutability(0) | TypeError::ArgumentSorts(_, 0)
+                if trait_m.fn_has_self_parameter =>
+            {
+                let ty = trait_sig.inputs()[0];
+                let sugg = match ExplicitSelf::determine(ty, |_| ty == impl_trait_ref.self_ty()) {
+                    ExplicitSelf::ByValue => "self".to_owned(),
+                    ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(),
+                    ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(),
+                    _ => format!("self: {ty}"),
+                };
+
+                // When the `impl` receiver is an arbitrary self type, like `self: Box<Self>`, the
+                // span points only at the type `Box<Self`>, but we want to cover the whole
+                // argument pattern and type.
+                let span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
+                    ImplItemKind::Fn(ref sig, body) => tcx
+                        .hir()
+                        .body_param_names(body)
+                        .zip(sig.decl.inputs.iter())
+                        .map(|(param, ty)| param.span.to(ty.span))
+                        .next()
+                        .unwrap_or(impl_err_span),
+                    _ => bug!("{:?} is not a method", impl_m),
+                };
+
+                diag.span_suggestion(
+                    span,
+                    "change the self-receiver type to match the trait",
+                    sugg,
+                    Applicability::MachineApplicable,
+                );
+            }
+            TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(_, i) => {
+                if trait_sig.inputs().len() == *i {
+                    // Suggestion to change output type. We do not suggest in `async` functions
+                    // to avoid complex logic or incorrect output.
+                    match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
+                        ImplItemKind::Fn(ref sig, _)
+                            if sig.header.asyncness == hir::IsAsync::NotAsync =>
+                        {
+                            let msg = "change the output type to match the trait";
+                            let ap = Applicability::MachineApplicable;
+                            match sig.decl.output {
+                                hir::FnRetTy::DefaultReturn(sp) => {
+                                    let sugg = format!("-> {} ", trait_sig.output());
+                                    diag.span_suggestion_verbose(sp, msg, sugg, ap);
+                                }
+                                hir::FnRetTy::Return(hir_ty) => {
+                                    let sugg = trait_sig.output();
+                                    diag.span_suggestion(hir_ty.span, msg, sugg, ap);
+                                }
+                            };
+                        }
+                        _ => {}
+                    };
+                } else if let Some(trait_ty) = trait_sig.inputs().get(*i) {
                     diag.span_suggestion(
-                        span,
-                        "change the self-receiver type to match the trait",
-                        sugg,
+                        impl_err_span,
+                        "change the parameter type to match the trait",
+                        trait_ty,
                         Applicability::MachineApplicable,
                     );
                 }
-                TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(_, i) => {
-                    if trait_sig.inputs().len() == *i {
-                        // Suggestion to change output type. We do not suggest in `async` functions
-                        // to avoid complex logic or incorrect output.
-                        match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
-                            ImplItemKind::Fn(ref sig, _)
-                                if sig.header.asyncness == hir::IsAsync::NotAsync =>
-                            {
-                                let msg = "change the output type to match the trait";
-                                let ap = Applicability::MachineApplicable;
-                                match sig.decl.output {
-                                    hir::FnRetTy::DefaultReturn(sp) => {
-                                        let sugg = format!("-> {} ", trait_sig.output());
-                                        diag.span_suggestion_verbose(sp, msg, sugg, ap);
-                                    }
-                                    hir::FnRetTy::Return(hir_ty) => {
-                                        let sugg = trait_sig.output();
-                                        diag.span_suggestion(hir_ty.span, msg, sugg, ap);
-                                    }
-                                };
-                            }
-                            _ => {}
-                        };
-                    } else if let Some(trait_ty) = trait_sig.inputs().get(*i) {
-                        diag.span_suggestion(
-                            impl_err_span,
-                            "change the parameter type to match the trait",
-                            trait_ty,
-                            Applicability::MachineApplicable,
-                        );
-                    }
-                }
-                _ => {}
             }
+            _ => {}
+        }
 
-            infcx.note_type_err(
-                &mut diag,
-                &cause,
-                trait_err_span.map(|sp| (sp, "type in trait".to_owned())),
-                Some(infer::ValuePairs::Terms(ExpectedFound {
-                    expected: trait_fty.into(),
-                    found: impl_fty.into(),
-                })),
-                terr,
-                false,
-                false,
-            );
+        infcx.err_ctxt().note_type_err(
+            &mut diag,
+            &cause,
+            trait_err_span.map(|sp| (sp, "type in trait".to_owned())),
+            Some(infer::ValuePairs::Terms(ExpectedFound {
+                expected: trait_fty.into(),
+                found: impl_fty.into(),
+            })),
+            terr,
+            false,
+            false,
+        );
 
-            return Err(diag.emit());
-        }
+        return Err(diag.emit());
+    }
 
-        // Check that all obligations are satisfied by the implementation's
-        // version.
-        let errors = ocx.select_all_or_error();
-        if !errors.is_empty() {
-            let reported = infcx.report_fulfillment_errors(&errors, None, false);
-            return Err(reported);
-        }
+    // Check that all obligations are satisfied by the implementation's
+    // version.
+    let errors = ocx.select_all_or_error();
+    if !errors.is_empty() {
+        let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+        return Err(reported);
+    }
 
-        // Finally, resolve all regions. This catches wily misuses of
-        // lifetime parameters.
-        let outlives_environment = OutlivesEnvironment::with_bounds(
-            param_env,
-            Some(infcx),
-            infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys),
-        );
-        infcx.check_region_obligations_and_report_errors(
-            impl_m.def_id.expect_local(),
-            &outlives_environment,
-        );
+    // Finally, resolve all regions. This catches wily misuses of
+    // lifetime parameters.
+    let outlives_environment = OutlivesEnvironment::with_bounds(
+        param_env,
+        Some(infcx),
+        infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys),
+    );
+    infcx.check_region_obligations_and_report_errors(
+        impl_m.def_id.expect_local(),
+        &outlives_environment,
+    );
 
-        Ok(())
-    })
+    Ok(())
 }
 
 pub fn collect_trait_impl_trait_tys<'tcx>(
@@ -465,125 +461,120 @@ pub fn collect_trait_impl_trait_tys<'tcx>(
     let trait_to_placeholder_substs =
         impl_to_placeholder_substs.rebase_onto(tcx, impl_m.container_id(tcx), trait_to_impl_substs);
 
-    tcx.infer_ctxt().enter(|ref infcx| {
-        let ocx = ObligationCtxt::new(infcx);
-
-        let norm_cause = ObligationCause::misc(return_span, impl_m_hir_id);
-        let impl_return_ty = ocx.normalize(
-            norm_cause.clone(),
-            param_env,
-            infcx
-                .replace_bound_vars_with_fresh_vars(
-                    return_span,
-                    infer::HigherRankedType,
-                    tcx.fn_sig(impl_m.def_id),
-                )
-                .output(),
-        );
-
-        let mut collector =
-            ImplTraitInTraitCollector::new(&ocx, return_span, param_env, impl_m_hir_id);
-        let unnormalized_trait_return_ty = tcx
-            .liberate_late_bound_regions(
-                impl_m.def_id,
-                tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs),
+    let infcx = &tcx.infer_ctxt().build();
+    let ocx = ObligationCtxt::new(infcx);
+
+    let norm_cause = ObligationCause::misc(return_span, impl_m_hir_id);
+    let impl_return_ty = ocx.normalize(
+        norm_cause.clone(),
+        param_env,
+        infcx
+            .replace_bound_vars_with_fresh_vars(
+                return_span,
+                infer::HigherRankedType,
+                tcx.fn_sig(impl_m.def_id),
             )
-            .output()
-            .fold_with(&mut collector);
-        let trait_return_ty =
-            ocx.normalize(norm_cause.clone(), param_env, unnormalized_trait_return_ty);
+            .output(),
+    );
 
-        let wf_tys = FxHashSet::from_iter([unnormalized_trait_return_ty, trait_return_ty]);
+    let mut collector = ImplTraitInTraitCollector::new(&ocx, return_span, param_env, impl_m_hir_id);
+    let unnormalized_trait_return_ty = tcx
+        .liberate_late_bound_regions(
+            impl_m.def_id,
+            tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs),
+        )
+        .output()
+        .fold_with(&mut collector);
+    let trait_return_ty =
+        ocx.normalize(norm_cause.clone(), param_env, unnormalized_trait_return_ty);
 
-        match infcx.at(&cause, param_env).eq(trait_return_ty, impl_return_ty) {
-            Ok(infer::InferOk { value: (), obligations }) => {
-                ocx.register_obligations(obligations);
-            }
-            Err(terr) => {
-                let mut diag = struct_span_err!(
-                    tcx.sess,
-                    cause.span(),
-                    E0053,
-                    "method `{}` has an incompatible return type for trait",
-                    trait_m.name
-                );
-                let hir = tcx.hir();
-                infcx.note_type_err(
-                    &mut diag,
-                    &cause,
-                    hir.get_if_local(impl_m.def_id)
-                        .and_then(|node| node.fn_decl())
-                        .map(|decl| (decl.output.span(), "return type in trait".to_owned())),
-                    Some(infer::ValuePairs::Terms(ExpectedFound {
-                        expected: trait_return_ty.into(),
-                        found: impl_return_ty.into(),
-                    })),
-                    terr,
-                    false,
-                    false,
-                );
-                return Err(diag.emit());
-            }
-        }
+    let wf_tys = FxHashSet::from_iter([unnormalized_trait_return_ty, trait_return_ty]);
 
-        // Check that all obligations are satisfied by the implementation's
-        // RPITs.
-        let errors = ocx.select_all_or_error();
-        if !errors.is_empty() {
-            let reported = infcx.report_fulfillment_errors(&errors, None, false);
-            return Err(reported);
+    match infcx.at(&cause, param_env).eq(trait_return_ty, impl_return_ty) {
+        Ok(infer::InferOk { value: (), obligations }) => {
+            ocx.register_obligations(obligations);
         }
+        Err(terr) => {
+            let mut diag = struct_span_err!(
+                tcx.sess,
+                cause.span(),
+                E0053,
+                "method `{}` has an incompatible return type for trait",
+                trait_m.name
+            );
+            let hir = tcx.hir();
+            infcx.err_ctxt().note_type_err(
+                &mut diag,
+                &cause,
+                hir.get_if_local(impl_m.def_id)
+                    .and_then(|node| node.fn_decl())
+                    .map(|decl| (decl.output.span(), "return type in trait".to_owned())),
+                Some(infer::ValuePairs::Terms(ExpectedFound {
+                    expected: trait_return_ty.into(),
+                    found: impl_return_ty.into(),
+                })),
+                terr,
+                false,
+                false,
+            );
+            return Err(diag.emit());
+        }
+    }
 
-        // Finally, resolve all regions. This catches wily misuses of
-        // lifetime parameters.
-        let outlives_environment = OutlivesEnvironment::with_bounds(
-            param_env,
-            Some(infcx),
-            infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys),
-        );
-        infcx.check_region_obligations_and_report_errors(
-            impl_m.def_id.expect_local(),
-            &outlives_environment,
-        );
+    // Check that all obligations are satisfied by the implementation's
+    // RPITs.
+    let errors = ocx.select_all_or_error();
+    if !errors.is_empty() {
+        let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+        return Err(reported);
+    }
 
-        let mut collected_tys = FxHashMap::default();
-        for (def_id, (ty, substs)) in collector.types {
-            match infcx.fully_resolve(ty) {
-                Ok(ty) => {
-                    // `ty` contains free regions that we created earlier while liberating the
-                    // trait fn signature.  However, projection normalization expects `ty` to
-                    // contains `def_id`'s early-bound regions.
-                    let id_substs = InternalSubsts::identity_for_item(tcx, def_id);
-                    debug!(?id_substs, ?substs);
-                    let map: FxHashMap<ty::GenericArg<'tcx>, ty::GenericArg<'tcx>> = substs
-                        .iter()
-                        .enumerate()
-                        .map(|(index, arg)| (arg, id_substs[index]))
-                        .collect();
-                    debug!(?map);
-
-                    let ty = tcx.fold_regions(ty, |region, _| {
-                        if let ty::ReFree(_) = region.kind() {
-                            map[&region.into()].expect_region()
-                        } else {
-                            region
-                        }
-                    });
-                    debug!(%ty);
-                    collected_tys.insert(def_id, ty);
-                }
-                Err(err) => {
-                    tcx.sess.delay_span_bug(
-                        return_span,
-                        format!("could not fully resolve: {ty} => {err:?}"),
-                    );
-                    collected_tys.insert(def_id, tcx.ty_error());
-                }
+    // Finally, resolve all regions. This catches wily misuses of
+    // lifetime parameters.
+    let outlives_environment = OutlivesEnvironment::with_bounds(
+        param_env,
+        Some(infcx),
+        infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys),
+    );
+    infcx.check_region_obligations_and_report_errors(
+        impl_m.def_id.expect_local(),
+        &outlives_environment,
+    );
+
+    let mut collected_tys = FxHashMap::default();
+    for (def_id, (ty, substs)) in collector.types {
+        match infcx.fully_resolve(ty) {
+            Ok(ty) => {
+                // `ty` contains free regions that we created earlier while liberating the
+                // trait fn signature.  However, projection normalization expects `ty` to
+                // contains `def_id`'s early-bound regions.
+                let id_substs = InternalSubsts::identity_for_item(tcx, def_id);
+                debug!(?id_substs, ?substs);
+                let map: FxHashMap<ty::GenericArg<'tcx>, ty::GenericArg<'tcx>> =
+                    substs.iter().enumerate().map(|(index, arg)| (arg, id_substs[index])).collect();
+                debug!(?map);
+
+                let ty = tcx.fold_regions(ty, |region, _| {
+                    if let ty::ReFree(_) = region.kind() {
+                        map[&region.into()].expect_region()
+                    } else {
+                        region
+                    }
+                });
+                debug!(%ty);
+                collected_tys.insert(def_id, ty);
+            }
+            Err(err) => {
+                tcx.sess.delay_span_bug(
+                    return_span,
+                    format!("could not fully resolve: {ty} => {err:?}"),
+                );
+                collected_tys.insert(def_id, tcx.ty_error());
             }
         }
+    }
 
-        Ok(&*tcx.arena.alloc(collected_tys))
-    })
+    Ok(&*tcx.arena.alloc(collected_tys))
 }
 
 struct ImplTraitInTraitCollector<'a, 'tcx> {
@@ -712,8 +703,8 @@ fn check_region_bounds_on_impl_item<'tcx>(
 }
 
 #[instrument(level = "debug", skip(infcx))]
-fn extract_spans_for_error_reporting<'a, 'tcx>(
-    infcx: &infer::InferCtxt<'a, 'tcx>,
+fn extract_spans_for_error_reporting<'tcx>(
+    infcx: &infer::InferCtxt<'tcx>,
     terr: TypeError<'_>,
     cause: &ObligationCause<'tcx>,
     impl_m: &ty::AssocItem,
@@ -768,16 +759,15 @@ fn compare_self_type<'tcx>(
         let self_arg_ty = tcx.fn_sig(method.def_id).input(0);
         let param_env = ty::ParamEnv::reveal_all();
 
-        tcx.infer_ctxt().enter(|infcx| {
-            let self_arg_ty = tcx.liberate_late_bound_regions(method.def_id, self_arg_ty);
-            let can_eq_self = |ty| infcx.can_eq(param_env, untransformed_self_ty, ty).is_ok();
-            match ExplicitSelf::determine(self_arg_ty, can_eq_self) {
-                ExplicitSelf::ByValue => "self".to_owned(),
-                ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(),
-                ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(),
-                _ => format!("self: {self_arg_ty}"),
-            }
-        })
+        let infcx = tcx.infer_ctxt().build();
+        let self_arg_ty = tcx.liberate_late_bound_regions(method.def_id, self_arg_ty);
+        let can_eq_self = |ty| infcx.can_eq(param_env, untransformed_self_ty, ty).is_ok();
+        match ExplicitSelf::determine(self_arg_ty, can_eq_self) {
+            ExplicitSelf::ByValue => "self".to_owned(),
+            ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(),
+            ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(),
+            _ => format!("self: {self_arg_ty}"),
+        }
     };
 
     match (trait_m.fn_has_self_parameter, impl_m.fn_has_self_parameter) {
@@ -1300,114 +1290,114 @@ fn compare_generic_param_kinds<'tcx>(
     Ok(())
 }
 
-pub(crate) fn compare_const_impl<'tcx>(
+/// Use `tcx.compare_assoc_const_impl_item_with_trait_item` instead
+pub(crate) fn raw_compare_const_impl<'tcx>(
     tcx: TyCtxt<'tcx>,
-    impl_c: &ty::AssocItem,
-    impl_c_span: Span,
-    trait_c: &ty::AssocItem,
-    impl_trait_ref: ty::TraitRef<'tcx>,
-) {
+    (impl_const_item_def, trait_const_item_def): (LocalDefId, DefId),
+) -> Result<(), ErrorGuaranteed> {
+    let impl_const_item = tcx.associated_item(impl_const_item_def);
+    let trait_const_item = tcx.associated_item(trait_const_item_def);
+    let impl_trait_ref = tcx.impl_trait_ref(impl_const_item.container_id(tcx)).unwrap();
     debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref);
 
-    tcx.infer_ctxt().enter(|infcx| {
-        let param_env = tcx.param_env(impl_c.def_id);
-        let ocx = ObligationCtxt::new(&infcx);
-
-        // The below is for the most part highly similar to the procedure
-        // for methods above. It is simpler in many respects, especially
-        // because we shouldn't really have to deal with lifetimes or
-        // predicates. In fact some of this should probably be put into
-        // shared functions because of DRY violations...
-        let trait_to_impl_substs = impl_trait_ref.substs;
-
-        // Create a parameter environment that represents the implementation's
-        // method.
-        let impl_c_hir_id = tcx.hir().local_def_id_to_hir_id(impl_c.def_id.expect_local());
-
-        // Compute placeholder form of impl and trait const tys.
-        let impl_ty = tcx.type_of(impl_c.def_id);
-        let trait_ty = tcx.bound_type_of(trait_c.def_id).subst(tcx, trait_to_impl_substs);
-        let mut cause = ObligationCause::new(
-            impl_c_span,
-            impl_c_hir_id,
-            ObligationCauseCode::CompareImplItemObligation {
-                impl_item_def_id: impl_c.def_id.expect_local(),
-                trait_item_def_id: trait_c.def_id,
-                kind: impl_c.kind,
-            },
-        );
+    let impl_c_span = tcx.def_span(impl_const_item_def.to_def_id());
 
-        // There is no "body" here, so just pass dummy id.
-        let impl_ty = ocx.normalize(cause.clone(), param_env, impl_ty);
+    let infcx = tcx.infer_ctxt().build();
+    let param_env = tcx.param_env(impl_const_item_def.to_def_id());
+    let ocx = ObligationCtxt::new(&infcx);
 
-        debug!("compare_const_impl: impl_ty={:?}", impl_ty);
+    // The below is for the most part highly similar to the procedure
+    // for methods above. It is simpler in many respects, especially
+    // because we shouldn't really have to deal with lifetimes or
+    // predicates. In fact some of this should probably be put into
+    // shared functions because of DRY violations...
+    let trait_to_impl_substs = impl_trait_ref.substs;
 
-        let trait_ty = ocx.normalize(cause.clone(), param_env, trait_ty);
+    // Create a parameter environment that represents the implementation's
+    // method.
+    let impl_c_hir_id = tcx.hir().local_def_id_to_hir_id(impl_const_item_def);
 
-        debug!("compare_const_impl: trait_ty={:?}", trait_ty);
+    // Compute placeholder form of impl and trait const tys.
+    let impl_ty = tcx.type_of(impl_const_item_def.to_def_id());
+    let trait_ty = tcx.bound_type_of(trait_const_item_def).subst(tcx, trait_to_impl_substs);
+    let mut cause = ObligationCause::new(
+        impl_c_span,
+        impl_c_hir_id,
+        ObligationCauseCode::CompareImplItemObligation {
+            impl_item_def_id: impl_const_item_def,
+            trait_item_def_id: trait_const_item_def,
+            kind: impl_const_item.kind,
+        },
+    );
 
-        let err = infcx
-            .at(&cause, param_env)
-            .sup(trait_ty, impl_ty)
-            .map(|ok| ocx.register_infer_ok_obligations(ok));
+    // There is no "body" here, so just pass dummy id.
+    let impl_ty = ocx.normalize(cause.clone(), param_env, impl_ty);
 
-        if let Err(terr) = err {
-            debug!(
-                "checking associated const for compatibility: impl ty {:?}, trait ty {:?}",
-                impl_ty, trait_ty
-            );
+    debug!("compare_const_impl: impl_ty={:?}", impl_ty);
 
-            // Locate the Span containing just the type of the offending impl
-            match tcx.hir().expect_impl_item(impl_c.def_id.expect_local()).kind {
-                ImplItemKind::Const(ref ty, _) => cause.span = ty.span,
-                _ => bug!("{:?} is not a impl const", impl_c),
-            }
+    let trait_ty = ocx.normalize(cause.clone(), param_env, trait_ty);
 
-            let mut diag = struct_span_err!(
-                tcx.sess,
-                cause.span,
-                E0326,
-                "implemented const `{}` has an incompatible type for trait",
-                trait_c.name
-            );
+    debug!("compare_const_impl: trait_ty={:?}", trait_ty);
 
-            let trait_c_span = trait_c.def_id.as_local().map(|trait_c_def_id| {
-                // Add a label to the Span containing just the type of the const
-                match tcx.hir().expect_trait_item(trait_c_def_id).kind {
-                    TraitItemKind::Const(ref ty, _) => ty.span,
-                    _ => bug!("{:?} is not a trait const", trait_c),
-                }
-            });
+    let err = infcx
+        .at(&cause, param_env)
+        .sup(trait_ty, impl_ty)
+        .map(|ok| ocx.register_infer_ok_obligations(ok));
 
-            infcx.note_type_err(
-                &mut diag,
-                &cause,
-                trait_c_span.map(|span| (span, "type in trait".to_owned())),
-                Some(infer::ValuePairs::Terms(ExpectedFound {
-                    expected: trait_ty.into(),
-                    found: impl_ty.into(),
-                })),
-                terr,
-                false,
-                false,
-            );
-            diag.emit();
-        }
+    if let Err(terr) = err {
+        debug!(
+            "checking associated const for compatibility: impl ty {:?}, trait ty {:?}",
+            impl_ty, trait_ty
+        );
 
-        // Check that all obligations are satisfied by the implementation's
-        // version.
-        let errors = ocx.select_all_or_error();
-        if !errors.is_empty() {
-            infcx.report_fulfillment_errors(&errors, None, false);
-            return;
+        // Locate the Span containing just the type of the offending impl
+        match tcx.hir().expect_impl_item(impl_const_item_def).kind {
+            ImplItemKind::Const(ref ty, _) => cause.span = ty.span,
+            _ => bug!("{:?} is not a impl const", impl_const_item),
         }
 
-        let outlives_environment = OutlivesEnvironment::new(param_env);
-        infcx.check_region_obligations_and_report_errors(
-            impl_c.def_id.expect_local(),
-            &outlives_environment,
+        let mut diag = struct_span_err!(
+            tcx.sess,
+            cause.span,
+            E0326,
+            "implemented const `{}` has an incompatible type for trait",
+            trait_const_item.name
         );
-    });
+
+        let trait_c_span = trait_const_item_def.as_local().map(|trait_c_def_id| {
+            // Add a label to the Span containing just the type of the const
+            match tcx.hir().expect_trait_item(trait_c_def_id).kind {
+                TraitItemKind::Const(ref ty, _) => ty.span,
+                _ => bug!("{:?} is not a trait const", trait_const_item),
+            }
+        });
+
+        infcx.err_ctxt().note_type_err(
+            &mut diag,
+            &cause,
+            trait_c_span.map(|span| (span, "type in trait".to_owned())),
+            Some(infer::ValuePairs::Terms(ExpectedFound {
+                expected: trait_ty.into(),
+                found: impl_ty.into(),
+            })),
+            terr,
+            false,
+            false,
+        );
+        return Err(diag.emit());
+    };
+
+    // Check that all obligations are satisfied by the implementation's
+    // version.
+    let errors = ocx.select_all_or_error();
+    if !errors.is_empty() {
+        return Err(infcx.err_ctxt().report_fulfillment_errors(&errors, None, false));
+    }
+
+    // FIXME return `ErrorReported` if region obligations error?
+    let outlives_environment = OutlivesEnvironment::new(param_env);
+    infcx.check_region_obligations_and_report_errors(impl_const_item_def, &outlives_environment);
+    Ok(())
 }
 
 pub(crate) fn compare_ty_impl<'tcx>(
@@ -1488,52 +1478,50 @@ fn compare_type_predicate_entailment<'tcx>(
         hir::Constness::NotConst,
     );
     let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
-    tcx.infer_ctxt().enter(|infcx| {
-        let ocx = ObligationCtxt::new(&infcx);
+    let infcx = tcx.infer_ctxt().build();
+    let ocx = ObligationCtxt::new(&infcx);
 
-        debug!("compare_type_predicate_entailment: caller_bounds={:?}", param_env.caller_bounds());
+    debug!("compare_type_predicate_entailment: caller_bounds={:?}", param_env.caller_bounds());
 
-        let mut selcx = traits::SelectionContext::new(&infcx);
+    let mut selcx = traits::SelectionContext::new(&infcx);
 
-        assert_eq!(impl_ty_own_bounds.predicates.len(), impl_ty_own_bounds.spans.len());
-        for (span, predicate) in
-            std::iter::zip(impl_ty_own_bounds.spans, impl_ty_own_bounds.predicates)
-        {
-            let cause = ObligationCause::misc(span, impl_ty_hir_id);
-            let traits::Normalized { value: predicate, obligations } =
-                traits::normalize(&mut selcx, param_env, cause, predicate);
-
-            let cause = ObligationCause::new(
-                span,
-                impl_ty_hir_id,
-                ObligationCauseCode::CompareImplItemObligation {
-                    impl_item_def_id: impl_ty.def_id.expect_local(),
-                    trait_item_def_id: trait_ty.def_id,
-                    kind: impl_ty.kind,
-                },
-            );
-            ocx.register_obligations(obligations);
-            ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate));
-        }
-
-        // Check that all obligations are satisfied by the implementation's
-        // version.
-        let errors = ocx.select_all_or_error();
-        if !errors.is_empty() {
-            let reported = infcx.report_fulfillment_errors(&errors, None, false);
-            return Err(reported);
-        }
+    assert_eq!(impl_ty_own_bounds.predicates.len(), impl_ty_own_bounds.spans.len());
+    for (span, predicate) in std::iter::zip(impl_ty_own_bounds.spans, impl_ty_own_bounds.predicates)
+    {
+        let cause = ObligationCause::misc(span, impl_ty_hir_id);
+        let traits::Normalized { value: predicate, obligations } =
+            traits::normalize(&mut selcx, param_env, cause, predicate);
 
-        // Finally, resolve all regions. This catches wily misuses of
-        // lifetime parameters.
-        let outlives_environment = OutlivesEnvironment::new(param_env);
-        infcx.check_region_obligations_and_report_errors(
-            impl_ty.def_id.expect_local(),
-            &outlives_environment,
+        let cause = ObligationCause::new(
+            span,
+            impl_ty_hir_id,
+            ObligationCauseCode::CompareImplItemObligation {
+                impl_item_def_id: impl_ty.def_id.expect_local(),
+                trait_item_def_id: trait_ty.def_id,
+                kind: impl_ty.kind,
+            },
         );
+        ocx.register_obligations(obligations);
+        ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate));
+    }
+
+    // Check that all obligations are satisfied by the implementation's
+    // version.
+    let errors = ocx.select_all_or_error();
+    if !errors.is_empty() {
+        let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+        return Err(reported);
+    }
 
-        Ok(())
-    })
+    // Finally, resolve all regions. This catches wily misuses of
+    // lifetime parameters.
+    let outlives_environment = OutlivesEnvironment::new(param_env);
+    infcx.check_region_obligations_and_report_errors(
+        impl_ty.def_id.expect_local(),
+        &outlives_environment,
+    );
+
+    Ok(())
 }
 
 /// Validate that `ProjectionCandidate`s created for this associated type will
@@ -1693,94 +1681,94 @@ pub fn check_type_bounds<'tcx>(
     let impl_ty_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id);
     let rebased_substs = impl_ty_substs.rebase_onto(tcx, container_id, impl_trait_ref.substs);
 
-    tcx.infer_ctxt().enter(move |infcx| {
-        let ocx = ObligationCtxt::new(&infcx);
+    let infcx = tcx.infer_ctxt().build();
+    let ocx = ObligationCtxt::new(&infcx);
 
-        let assumed_wf_types =
-            ocx.assumed_wf_types(param_env, impl_ty_span, impl_ty.def_id.expect_local());
+    let assumed_wf_types =
+        ocx.assumed_wf_types(param_env, impl_ty_span, impl_ty.def_id.expect_local());
 
-        let mut selcx = traits::SelectionContext::new(&infcx);
-        let normalize_cause = ObligationCause::new(
-            impl_ty_span,
-            impl_ty_hir_id,
-            ObligationCauseCode::CheckAssociatedTypeBounds {
-                impl_item_def_id: impl_ty.def_id.expect_local(),
-                trait_item_def_id: trait_ty.def_id,
-            },
-        );
-        let mk_cause = |span: Span| {
-            let code = if span.is_dummy() {
-                traits::ItemObligation(trait_ty.def_id)
-            } else {
-                traits::BindingObligation(trait_ty.def_id, span)
-            };
-            ObligationCause::new(impl_ty_span, impl_ty_hir_id, code)
+    let mut selcx = traits::SelectionContext::new(&infcx);
+    let normalize_cause = ObligationCause::new(
+        impl_ty_span,
+        impl_ty_hir_id,
+        ObligationCauseCode::CheckAssociatedTypeBounds {
+            impl_item_def_id: impl_ty.def_id.expect_local(),
+            trait_item_def_id: trait_ty.def_id,
+        },
+    );
+    let mk_cause = |span: Span| {
+        let code = if span.is_dummy() {
+            traits::ItemObligation(trait_ty.def_id)
+        } else {
+            traits::BindingObligation(trait_ty.def_id, span)
         };
+        ObligationCause::new(impl_ty_span, impl_ty_hir_id, code)
+    };
 
-        let obligations = tcx
-            .bound_explicit_item_bounds(trait_ty.def_id)
-            .transpose_iter()
-            .map(|e| e.map_bound(|e| *e).transpose_tuple2())
-            .map(|(bound, span)| {
-                debug!(?bound);
-                // this is where opaque type is found
-                let concrete_ty_bound = bound.subst(tcx, rebased_substs);
-                debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound);
-
-                traits::Obligation::new(mk_cause(span.0), param_env, concrete_ty_bound)
-            })
-            .collect();
-        debug!("check_type_bounds: item_bounds={:?}", obligations);
-
-        for mut obligation in util::elaborate_obligations(tcx, obligations) {
-            let traits::Normalized { value: normalized_predicate, obligations } = traits::normalize(
-                &mut selcx,
-                normalize_param_env,
-                normalize_cause.clone(),
-                obligation.predicate,
-            );
-            debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate);
-            obligation.predicate = normalized_predicate;
+    let obligations = tcx
+        .bound_explicit_item_bounds(trait_ty.def_id)
+        .transpose_iter()
+        .map(|e| e.map_bound(|e| *e).transpose_tuple2())
+        .map(|(bound, span)| {
+            debug!(?bound);
+            // this is where opaque type is found
+            let concrete_ty_bound = bound.subst(tcx, rebased_substs);
+            debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound);
+
+            traits::Obligation::new(mk_cause(span.0), param_env, concrete_ty_bound)
+        })
+        .collect();
+    debug!("check_type_bounds: item_bounds={:?}", obligations);
+
+    for mut obligation in util::elaborate_obligations(tcx, obligations) {
+        let traits::Normalized { value: normalized_predicate, obligations } = traits::normalize(
+            &mut selcx,
+            normalize_param_env,
+            normalize_cause.clone(),
+            obligation.predicate,
+        );
+        debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate);
+        obligation.predicate = normalized_predicate;
 
-            ocx.register_obligations(obligations);
-            ocx.register_obligation(obligation);
-        }
-        // Check that all obligations are satisfied by the implementation's
-        // version.
-        let errors = ocx.select_all_or_error();
-        if !errors.is_empty() {
-            let reported = infcx.report_fulfillment_errors(&errors, None, false);
-            return Err(reported);
-        }
+        ocx.register_obligations(obligations);
+        ocx.register_obligation(obligation);
+    }
+    // Check that all obligations are satisfied by the implementation's
+    // version.
+    let errors = ocx.select_all_or_error();
+    if !errors.is_empty() {
+        let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+        return Err(reported);
+    }
 
-        // Finally, resolve all regions. This catches wily misuses of
-        // lifetime parameters.
-        let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_hir_id, assumed_wf_types);
-        let outlives_environment =
-            OutlivesEnvironment::with_bounds(param_env, Some(&infcx), implied_bounds);
+    // Finally, resolve all regions. This catches wily misuses of
+    // lifetime parameters.
+    let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_hir_id, assumed_wf_types);
+    let outlives_environment =
+        OutlivesEnvironment::with_bounds(param_env, Some(&infcx), implied_bounds);
 
-        infcx.check_region_obligations_and_report_errors(
-            impl_ty.def_id.expect_local(),
-            &outlives_environment,
-        );
+    infcx.check_region_obligations_and_report_errors(
+        impl_ty.def_id.expect_local(),
+        &outlives_environment,
+    );
 
-        let constraints = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
-        for (key, value) in constraints {
-            infcx
-                .report_mismatched_types(
-                    &ObligationCause::misc(
-                        value.hidden_type.span,
-                        tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()),
-                    ),
-                    tcx.mk_opaque(key.def_id.to_def_id(), key.substs),
-                    value.hidden_type.ty,
-                    TypeError::Mismatch,
-                )
-                .emit();
-        }
+    let constraints = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
+    for (key, value) in constraints {
+        infcx
+            .err_ctxt()
+            .report_mismatched_types(
+                &ObligationCause::misc(
+                    value.hidden_type.span,
+                    tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()),
+                ),
+                tcx.mk_opaque(key.def_id.to_def_id(), key.substs),
+                value.hidden_type.ty,
+                TypeError::Mismatch,
+            )
+            .emit();
+    }
 
-        Ok(())
-    })
+    Ok(())
 }
 
 fn assoc_item_kind_str(impl_item: &ty::AssocItem) -> &'static str {
diff --git a/compiler/rustc_hir_analysis/src/check/demand.rs b/compiler/rustc_hir_analysis/src/check/demand.rs
index 264df8b914b..a5222c92331 100644
--- a/compiler/rustc_hir_analysis/src/check/demand.rs
+++ b/compiler/rustc_hir_analysis/src/check/demand.rs
@@ -32,17 +32,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         error: Option<TypeError<'tcx>>,
     ) {
         self.annotate_expected_due_to_let_ty(err, expr, error);
-        self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr);
-        self.suggest_compatible_variants(err, expr, expected, expr_ty);
-        self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty);
-        if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) {
-            return;
-        }
-        self.suggest_no_capture_closure(err, expected, expr_ty);
-        self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
-        self.suggest_missing_parentheses(err, expr);
-        self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected);
-        self.suggest_copied_or_cloned(err, expr, expr_ty, expected);
+
+        // Use `||` to give these suggestions a precedence
+        let _ = self.suggest_missing_parentheses(err, expr)
+            || self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr)
+            || self.suggest_compatible_variants(err, expr, expected, expr_ty)
+            || self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty)
+            || self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty)
+            || self.suggest_no_capture_closure(err, expected, expr_ty)
+            || self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty)
+            || self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected)
+            || self.suggest_copied_or_cloned(err, expr, expr_ty, expected)
+            || self.suggest_into(err, expr, expr_ty, expected);
+
         self.note_type_is_not_clone(err, expected, expr_ty, expr);
         self.note_need_for_fn_pointer(err, expected, expr_ty);
         self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
@@ -77,7 +79,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 self.register_predicates(obligations);
                 None
             }
-            Err(e) => Some(self.report_mismatched_types(&cause, expected, actual, e)),
+            Err(e) => Some(self.err_ctxt().report_mismatched_types(&cause, expected, actual, e)),
         }
     }
 
@@ -107,7 +109,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 self.register_predicates(obligations);
                 None
             }
-            Err(e) => Some(self.report_mismatched_types(cause, expected, actual, e)),
+            Err(e) => Some(self.err_ctxt().report_mismatched_types(cause, expected, actual, e)),
         }
     }
 
@@ -151,7 +153,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let expr = expr.peel_drop_temps();
         let cause = self.misc(expr.span);
         let expr_ty = self.resolve_vars_with_obligations(checked_ty);
-        let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e.clone());
+        let mut err = self.err_ctxt().report_mismatched_types(&cause, expected, expr_ty, e.clone());
 
         let is_insufficiently_polymorphic =
             matches!(e, TypeError::RegionsInsufficientlyPolymorphic(..));
@@ -286,7 +288,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expr: &hir::Expr<'_>,
         expected: Ty<'tcx>,
         expr_ty: Ty<'tcx>,
-    ) {
+    ) -> bool {
         if let ty::Adt(expected_adt, substs) = expected.kind() {
             if let hir::ExprKind::Field(base, ident) = expr.kind {
                 let base_ty = self.typeck_results.borrow().expr_ty(base);
@@ -299,7 +301,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         "",
                         Applicability::MaybeIncorrect,
                     );
-                    return
+                    return true;
                 }
             }
 
@@ -338,7 +340,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             } else if self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) {
                                 vec!["None", "Some(())"]
                             } else {
-                                return;
+                                return false;
                             };
                             if let Some(indent) =
                                 self.tcx.sess.source_map().indentation_before(span.shrink_to_lo())
@@ -358,7 +360,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                     Applicability::MaybeIncorrect,
                                 );
                             }
-                            return;
+                            return true;
                         }
                     }
                 }
@@ -445,6 +447,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         suggestions_for(&**variant, *ctor_kind, *field_name),
                         Applicability::MaybeIncorrect,
                     );
+                    return true;
                 }
                 _ => {
                     // More than one matching variant.
@@ -460,9 +463,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         ),
                         Applicability::MaybeIncorrect,
                     );
+                    return true;
                 }
             }
         }
+
+        false
     }
 
     fn suggest_non_zero_new_unwrap(
@@ -471,19 +477,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expr: &hir::Expr<'_>,
         expected: Ty<'tcx>,
         expr_ty: Ty<'tcx>,
-    ) {
+    ) -> bool {
         let tcx = self.tcx;
         let (adt, unwrap) = match expected.kind() {
             // In case Option<NonZero*> is wanted, but * is provided, suggest calling new
             ty::Adt(adt, substs) if tcx.is_diagnostic_item(sym::Option, adt.did()) => {
                 // Unwrap option
-                let ty::Adt(adt, _) = substs.type_at(0).kind() else { return };
+                let ty::Adt(adt, _) = substs.type_at(0).kind() else { return false; };
 
                 (adt, "")
             }
             // In case NonZero* is wanted, but * is provided also add `.unwrap()` to satisfy types
             ty::Adt(adt, _) => (adt, ".unwrap()"),
-            _ => return,
+            _ => return false,
         };
 
         let map = [
@@ -502,7 +508,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let Some((s, _)) = map
             .iter()
             .find(|&&(s, t)| self.tcx.is_diagnostic_item(s, adt.did()) && self.can_coerce(expr_ty, t))
-            else { return };
+            else { return false; };
 
         let path = self.tcx.def_path_str(adt.non_enum_variant().def_id);
 
@@ -514,6 +520,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ],
             Applicability::MaybeIncorrect,
         );
+
+        true
     }
 
     pub fn get_conversion_methods(
diff --git a/compiler/rustc_hir_analysis/src/check/expr.rs b/compiler/rustc_hir_analysis/src/check/expr.rs
index b9459887c46..375c13d922b 100644
--- a/compiler/rustc_hir_analysis/src/check/expr.rs
+++ b/compiler/rustc_hir_analysis/src/check/expr.rs
@@ -1045,6 +1045,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let rhs_ty = self.check_expr(&rhs);
             let (applicability, eq) = if self.can_coerce(rhs_ty, lhs_ty) {
                 (Applicability::MachineApplicable, true)
+            } else if let ExprKind::Binary(
+                Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. },
+                _,
+                rhs_expr,
+            ) = lhs.kind
+            {
+                let actual_lhs_ty = self.check_expr(&rhs_expr);
+                (Applicability::MaybeIncorrect, self.can_coerce(rhs_ty, actual_lhs_ty))
             } else {
                 (Applicability::MaybeIncorrect, false)
             };
@@ -1067,9 +1075,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
             if eq {
                 err.span_suggestion_verbose(
-                    span,
+                    span.shrink_to_hi(),
                     "you might have meant to compare for equality",
-                    "==",
+                    '=',
                     applicability,
                 );
             }
@@ -1641,13 +1649,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                     Err(_) => {
                                         // This should never happen, since we're just subtyping the
                                         // remaining_fields, but it's fine to emit this, I guess.
-                                        self.report_mismatched_types(
-                                            &cause,
-                                            target_ty,
-                                            fru_ty,
-                                            FieldMisMatch(variant.name, ident.name),
-                                        )
-                                        .emit();
+                                        self.err_ctxt()
+                                            .report_mismatched_types(
+                                                &cause,
+                                                target_ty,
+                                                fru_ty,
+                                                FieldMisMatch(variant.name, ident.name),
+                                            )
+                                            .emit();
                                     }
                                 }
                             }
@@ -1934,7 +1943,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             self.set_tainted_by_errors();
             return;
         }
-        let mut err = self.type_error_struct_with_diag(
+        let mut err = self.err_ctxt().type_error_struct_with_diag(
             field.ident.span,
             |actual| match ty.kind() {
                 ty::Adt(adt, ..) if adt.is_enum() => struct_span_err!(
diff --git a/compiler/rustc_hir_analysis/src/check/fn_ctxt/_impl.rs b/compiler/rustc_hir_analysis/src/check/fn_ctxt/_impl.rs
index e51e5280620..d140c3a0989 100644
--- a/compiler/rustc_hir_analysis/src/check/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_analysis/src/check/fn_ctxt/_impl.rs
@@ -32,7 +32,7 @@ use rustc_span::hygiene::DesugaringKind;
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::{Span, DUMMY_SP};
 use rustc_trait_selection::infer::InferCtxtExt as _;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::{
     self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt,
 };
@@ -615,7 +615,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         if !errors.is_empty() {
             self.adjust_fulfillment_errors_for_expr_obligation(&mut errors);
-            self.report_fulfillment_errors(&errors, self.inh.body_id, false);
+            self.err_ctxt().report_fulfillment_errors(&errors, self.inh.body_id, false);
         }
     }
 
@@ -629,7 +629,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if !result.is_empty() {
             mutate_fulfillment_errors(&mut result);
             self.adjust_fulfillment_errors_for_expr_obligation(&mut result);
-            self.report_fulfillment_errors(&result, self.inh.body_id, fallback_has_occurred);
+            self.err_ctxt().report_fulfillment_errors(
+                &result,
+                self.inh.body_id,
+                fallback_has_occurred,
+            );
         }
     }
 
@@ -1466,7 +1470,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ty
         } else {
             if !self.is_tainted_by_errors() {
-                self.emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true)
+                self.err_ctxt()
+                    .emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true)
                     .emit();
             }
             let err = self.tcx.ty_error();
diff --git a/compiler/rustc_hir_analysis/src/check/fn_ctxt/checks.rs b/compiler/rustc_hir_analysis/src/check/fn_ctxt/checks.rs
index 13e74021b9e..285db90a9df 100644
--- a/compiler/rustc_hir_analysis/src/check/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_analysis/src/check/fn_ctxt/checks.rs
@@ -650,7 +650,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     if tys.len() == 1 {
                         // A tuple wrap suggestion actually occurs within,
                         // so don't do anything special here.
-                        err = self.report_and_explain_type_error(
+                        err = self.err_ctxt().report_and_explain_type_error(
                             TypeTrace::types(
                                 &self.misc(*lo),
                                 true,
@@ -742,7 +742,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let cause = &self.misc(provided_span);
                 let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
                 if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308(_)) {
-                    self.report_and_explain_type_error(trace, *e).emit();
+                    self.err_ctxt().report_and_explain_type_error(trace, *e).emit();
                     return true;
                 }
                 false
@@ -766,7 +766,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let (provided_ty, provided_arg_span) = provided_arg_tys[*provided_idx];
             let cause = &self.misc(provided_arg_span);
             let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
-            let mut err = self.report_and_explain_type_error(trace, *err);
+            let mut err = self.err_ctxt().report_and_explain_type_error(trace, *err);
             self.emit_coerce_suggestions(
                 &mut err,
                 &provided_args[*provided_idx],
@@ -840,7 +840,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         let cause = &self.misc(provided_span);
                         let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
                         if let Some(e) = error {
-                            self.note_type_err(
+                            self.err_ctxt().note_type_err(
                                 &mut err,
                                 &trace.cause,
                                 None,
@@ -1474,7 +1474,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         &mut |err| {
                             if let Some(expected_ty) = expected.only_has_type(self) {
                                 if !self.consider_removing_semicolon(blk, expected_ty, err) {
-                                    self.consider_returning_binding(blk, expected_ty, err);
+                                    self.err_ctxt().consider_returning_binding(
+                                        blk,
+                                        expected_ty,
+                                        err,
+                                    );
                                 }
                                 if expected_ty == self.tcx.types.bool {
                                     // If this is caused by a missing `let` in a `while let`,
diff --git a/compiler/rustc_hir_analysis/src/check/fn_ctxt/mod.rs b/compiler/rustc_hir_analysis/src/check/fn_ctxt/mod.rs
index d929a3e6548..51f4cb7e0eb 100644
--- a/compiler/rustc_hir_analysis/src/check/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/fn_ctxt/mod.rs
@@ -13,6 +13,7 @@ use crate::check::{Diverges, EnclosingBreakables, Inherited, UnsafetyState};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer;
+use rustc_infer::infer::error_reporting::TypeErrCtxt;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
 use rustc_middle::ty::subst::GenericArgKind;
@@ -117,7 +118,7 @@ pub struct FnCtxt<'a, 'tcx> {
 
     pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>,
 
-    pub(super) inh: &'a Inherited<'a, 'tcx>,
+    pub(super) inh: &'a Inherited<'tcx>,
 
     /// True if the function or closure's return type is known before
     /// entering the function/closure, i.e. if the return type is
@@ -131,7 +132,7 @@ pub struct FnCtxt<'a, 'tcx> {
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn new(
-        inh: &'a Inherited<'a, 'tcx>,
+        inh: &'a Inherited<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
         body_id: hir::HirId,
     ) -> FnCtxt<'a, 'tcx> {
@@ -168,13 +169,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self.tcx.sess
     }
 
+    /// Creates an `TypeErrCtxt` with a reference to the in-progress
+    /// `TypeckResults` which is used for diagnostics.
+    /// Use [`InferCtxt::err_ctxt`] to start one without a `TypeckResults`.
+    ///
+    /// [`InferCtxt::err_ctxt`]: infer::InferCtxt::err_ctxt
+    pub fn err_ctxt(&'a self) -> TypeErrCtxt<'a, 'tcx> {
+        TypeErrCtxt { infcx: &self.infcx, typeck_results: Some(self.typeck_results.borrow()) }
+    }
+
     pub fn errors_reported_since_creation(&self) -> bool {
         self.tcx.sess.err_count() > self.err_count_on_creation
     }
 }
 
 impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> {
-    type Target = Inherited<'a, 'tcx>;
+    type Target = Inherited<'tcx>;
     fn deref(&self) -> &Self::Target {
         &self.inh
     }
diff --git a/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs
index 7b1a2ad35cd..7a40def177a 100644
--- a/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs
@@ -3,7 +3,7 @@ use crate::astconv::AstConv;
 use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
 
 use hir::def_id::DefId;
-use rustc_ast::util::parser::ExprPrecedence;
+use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
 use rustc_errors::{Applicability, Diagnostic, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind};
@@ -327,7 +327,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
         expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
-    ) {
+    ) -> bool {
         let expr = expr.peel_blocks();
         if let Some((sp, msg, suggestion, applicability, verbose)) =
             self.check_ref(expr, found, expected)
@@ -337,14 +337,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             } else {
                 err.span_suggestion(sp, &msg, suggestion, applicability);
             }
+            return true;
         } else if self.suggest_else_fn_with_closure(err, expr, found, expected)
         {
+            return true;
         } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
             && let ty::FnDef(def_id, ..) = &found.kind()
             && let Some(sp) = self.tcx.hir().span_if_local(*def_id)
         {
             err.span_label(sp, format!("{found} defined here"));
-        } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
+            return true;
+        } else if self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
+            return true;
+        } else {
             let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
             if !methods.is_empty() {
                 let mut suggestions = methods.iter()
@@ -395,6 +400,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         suggestions,
                         Applicability::MaybeIncorrect,
                     );
+                    return true;
                 }
             } else if let ty::Adt(found_adt, found_substs) = found.kind()
                 && self.tcx.is_diagnostic_item(sym::Option, found_adt.did())
@@ -419,9 +425,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         format!(".map(|x| &*{}x)", "*".repeat(ref_cnt)),
                         Applicability::MaybeIncorrect,
                     );
+                    return true;
                 }
             }
         }
+
+        false
     }
 
     /// When encountering the expected boxed value allocated in the stack, suggest allocating it
@@ -432,13 +441,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expr: &hir::Expr<'_>,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
-    ) {
+    ) -> bool {
         if self.tcx.hir().is_inside_const_context(expr.hir_id) {
             // Do not suggest `Box::new` in const context.
-            return;
+            return false;
         }
         if !expected.is_box() || found.is_box() {
-            return;
+            return false;
         }
         let boxed_found = self.tcx.mk_box(found);
         if self.can_coerce(boxed_found, expected) {
@@ -456,6 +465,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                  https://doc.rust-lang.org/rust-by-example/std/box.html, and \
                  https://doc.rust-lang.org/std/boxed/index.html",
             );
+            true
+        } else {
+            false
         }
     }
 
@@ -466,7 +478,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         err: &mut Diagnostic,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
-    ) {
+    ) -> bool {
         if let (ty::FnPtr(_), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) {
             if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) {
                 // Report upto four upvars being captured to reduce the amount error messages
@@ -490,8 +502,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     multi_span,
                     "closures can only be coerced to `fn` types if they do not capture any variables"
                 );
+                return true;
             }
         }
+        false
     }
 
     /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
@@ -862,18 +876,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars));
             let ty = self.normalize_associated_types_in(expr.span, ty);
             let ty = match self.tcx.asyncness(fn_id.owner) {
-                hir::IsAsync::Async => self
-                    .tcx
-                    .infer_ctxt()
-                    .enter(|infcx| {
-                        infcx.get_impl_future_output_ty(ty).unwrap_or_else(|| {
+                hir::IsAsync::Async => {
+                    let infcx = self.tcx.infer_ctxt().build();
+                    infcx
+                        .get_impl_future_output_ty(ty)
+                        .unwrap_or_else(|| {
                             span_bug!(
                                 fn_decl.output.span(),
                                 "failed to get output type of async function"
                             )
                         })
-                    })
-                    .skip_binder(),
+                        .skip_binder()
+                }
                 hir::IsAsync::NotAsync => ty,
             };
             if self.can_coerce(found, ty) {
@@ -893,11 +907,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         err: &mut Diagnostic,
         expr: &hir::Expr<'_>,
-    ) {
+    ) -> bool {
         let sp = self.tcx.sess.source_map().start_point(expr.span);
         if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) {
             // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
             err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
+            true
+        } else {
+            false
         }
     }
 
@@ -910,7 +927,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         mut expr: &hir::Expr<'_>,
         mut expr_ty: Ty<'tcx>,
         mut expected_ty: Ty<'tcx>,
-    ) {
+    ) -> bool {
         loop {
             match (&expr.kind, expr_ty.kind(), expected_ty.kind()) {
                 (
@@ -924,9 +941,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
                 (hir::ExprKind::Block(blk, _), _, _) => {
                     self.suggest_block_to_brackets(diag, *blk, expr_ty, expected_ty);
-                    break;
+                    break true;
                 }
-                _ => break,
+                _ => break false,
             }
         }
     }
@@ -937,11 +954,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expr: &hir::Expr<'_>,
         expr_ty: Ty<'tcx>,
         expected_ty: Ty<'tcx>,
-    ) {
-        let ty::Adt(adt_def, substs) = expr_ty.kind() else { return; };
-        let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return; };
+    ) -> bool {
+        let ty::Adt(adt_def, substs) = expr_ty.kind() else { return false; };
+        let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return false; };
         if adt_def != expected_adt_def {
-            return;
+            return false;
         }
 
         let mut suggest_copied_or_cloned = || {
@@ -960,6 +977,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         ".copied()",
                         Applicability::MachineApplicable,
                     );
+                    return true;
                 } else if let Some(clone_did) = self.tcx.lang_items().clone_trait()
                     && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
                         self,
@@ -977,8 +995,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         ".cloned()",
                         Applicability::MachineApplicable,
                     );
+                    return true;
                 }
             }
+            false
         };
 
         if let Some(result_did) = self.tcx.get_diagnostic_item(sym::Result)
@@ -986,12 +1006,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // Check that the error types are equal
             && self.can_eq(self.param_env, substs.type_at(1), expected_substs.type_at(1)).is_ok()
         {
-            suggest_copied_or_cloned();
+            return suggest_copied_or_cloned();
         } else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option)
             && adt_def.did() == option_did
         {
-            suggest_copied_or_cloned();
+            return suggest_copied_or_cloned();
+        }
+
+        false
+    }
+
+    pub(crate) fn suggest_into(
+        &self,
+        diag: &mut Diagnostic,
+        expr: &hir::Expr<'_>,
+        expr_ty: Ty<'tcx>,
+        expected_ty: Ty<'tcx>,
+    ) -> bool {
+        let expr = expr.peel_blocks();
+
+        // We have better suggestions for scalar interconversions...
+        if expr_ty.is_scalar() && expected_ty.is_scalar() {
+            return false;
+        }
+
+        // Don't suggest turning a block into another type (e.g. `{}.into()`)
+        if matches!(expr.kind, hir::ExprKind::Block(..)) {
+            return false;
+        }
+
+        // We'll later suggest `.as_ref` when noting the type error,
+        // so skip if we will suggest that instead.
+        if self.err_ctxt().should_suggest_as_ref(expected_ty, expr_ty).is_some() {
+            return false;
+        }
+
+        if let Some(into_def_id) = self.tcx.get_diagnostic_item(sym::Into)
+            && self.predicate_must_hold_modulo_regions(&traits::Obligation::new(
+                self.misc(expr.span),
+                self.param_env,
+                ty::Binder::dummy(ty::TraitRef {
+                    def_id: into_def_id,
+                    substs: self.tcx.mk_substs_trait(expr_ty, &[expected_ty.into()]),
+                })
+                .to_poly_trait_predicate()
+                .to_predicate(self.tcx),
+            ))
+        {
+            let sugg = if expr.precedence().order() >= PREC_POSTFIX {
+                vec![(expr.span.shrink_to_hi(), ".into()".to_owned())]
+            } else {
+                vec![(expr.span.shrink_to_lo(), "(".to_owned()), (expr.span.shrink_to_hi(), ").into()".to_owned())]
+            };
+            diag.multipart_suggestion(
+                format!("call `Into::into` on this expression to convert `{expr_ty}` into `{expected_ty}`"),
+                sugg,
+                Applicability::MaybeIncorrect
+            );
+            return true;
         }
+
+        false
     }
 
     /// Suggest wrapping the block in square brackets instead of curly braces
@@ -1112,7 +1187,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected_ty: Ty<'tcx>,
         err: &mut Diagnostic,
     ) -> bool {
-        if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) {
+        if let Some((span_semi, boxed)) = self.err_ctxt().could_remove_semicolon(blk, expected_ty) {
             if let StatementAsExpression::NeedsBoxing = boxed {
                 err.span_suggestion_verbose(
                     span_semi,
@@ -1123,7 +1198,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             } else {
                 err.span_suggestion_short(
                     span_semi,
-                    "remove this semicolon",
+                    "remove this semicolon to return this value",
                     "",
                     Applicability::MachineApplicable,
                 );
diff --git a/compiler/rustc_hir_analysis/src/check/inherited.rs b/compiler/rustc_hir_analysis/src/check/inherited.rs
index 2546227e138..0fb7651b3a1 100644
--- a/compiler/rustc_hir_analysis/src/check/inherited.rs
+++ b/compiler/rustc_hir_analysis/src/check/inherited.rs
@@ -6,7 +6,7 @@ use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::HirIdMap;
 use rustc_infer::infer;
-use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt};
+use rustc_infer::infer::{DefiningAnchor, InferCtxt, InferOk, TyCtxtInferExt};
 use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::visit::TypeVisitable;
 use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -29,10 +29,10 @@ use std::ops::Deref;
 /// Here, the function `foo()` and the closure passed to
 /// `bar()` will each have their own `FnCtxt`, but they will
 /// share the inherited fields.
-pub struct Inherited<'a, 'tcx> {
-    pub(super) infcx: InferCtxt<'a, 'tcx>,
+pub struct Inherited<'tcx> {
+    pub(super) infcx: InferCtxt<'tcx>,
 
-    pub(super) typeck_results: &'a RefCell<ty::TypeckResults<'tcx>>,
+    pub(super) typeck_results: RefCell<ty::TypeckResults<'tcx>>,
 
     pub(super) locals: RefCell<HirIdMap<super::LocalTy<'tcx>>>,
 
@@ -70,22 +70,23 @@ pub struct Inherited<'a, 'tcx> {
     pub(super) diverging_type_vars: RefCell<FxHashSet<Ty<'tcx>>>,
 }
 
-impl<'a, 'tcx> Deref for Inherited<'a, 'tcx> {
-    type Target = InferCtxt<'a, 'tcx>;
+impl<'tcx> Deref for Inherited<'tcx> {
+    type Target = InferCtxt<'tcx>;
     fn deref(&self) -> &Self::Target {
         &self.infcx
     }
 }
 
 /// A temporary returned by `Inherited::build(...)`. This is necessary
-/// for multiple `InferCtxt` to share the same `in_progress_typeck_results`
+/// for multiple `InferCtxt` to share the same `typeck_results`
 /// without using `Rc` or something similar.
 pub struct InheritedBuilder<'tcx> {
     infcx: infer::InferCtxtBuilder<'tcx>,
     def_id: LocalDefId,
+    typeck_results: RefCell<ty::TypeckResults<'tcx>>,
 }
 
-impl<'tcx> Inherited<'_, 'tcx> {
+impl<'tcx> Inherited<'tcx> {
     pub fn build(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> InheritedBuilder<'tcx> {
         let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner;
 
@@ -93,7 +94,7 @@ impl<'tcx> Inherited<'_, 'tcx> {
             infcx: tcx
                 .infer_ctxt()
                 .ignoring_regions()
-                .with_fresh_in_progress_typeck_results(hir_owner)
+                .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id))
                 .with_normalize_fn_sig_for_diagnostic(Lrc::new(move |infcx, fn_sig| {
                     if fn_sig.has_escaping_bound_vars() {
                         return fn_sig;
@@ -117,26 +118,29 @@ impl<'tcx> Inherited<'_, 'tcx> {
                     })
                 })),
             def_id,
+            typeck_results: RefCell::new(ty::TypeckResults::new(hir_owner)),
         }
     }
 }
 
 impl<'tcx> InheritedBuilder<'tcx> {
-    pub fn enter<F, R>(&mut self, f: F) -> R
+    pub fn enter<F, R>(mut self, f: F) -> R
     where
-        F: for<'a> FnOnce(Inherited<'a, 'tcx>) -> R,
+        F: FnOnce(&Inherited<'tcx>) -> R,
     {
         let def_id = self.def_id;
-        self.infcx.enter(|infcx| f(Inherited::new(infcx, def_id)))
+        f(&Inherited::new(self.infcx.build(), def_id, self.typeck_results))
     }
 }
 
-impl<'a, 'tcx> Inherited<'a, 'tcx> {
-    fn new(infcx: InferCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self {
+impl<'tcx> Inherited<'tcx> {
+    fn new(
+        infcx: InferCtxt<'tcx>,
+        def_id: LocalDefId,
+        typeck_results: RefCell<ty::TypeckResults<'tcx>>,
+    ) -> Self {
         let tcx = infcx.tcx;
         let body_id = tcx.hir().maybe_body_owned_by(def_id);
-        let typeck_results =
-            infcx.in_progress_typeck_results.expect("building `FnCtxt` without typeck results");
 
         Inherited {
             typeck_results,
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
index 4abc00cefb6..25228f424cd 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
@@ -44,13 +44,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn check_transmute(&self, from: Ty<'tcx>, to: Ty<'tcx>, hir_id: HirId) {
         let tcx = self.tcx;
         let span = tcx.hir().span(hir_id);
-        let convert = |ty: Ty<'tcx>| {
+        let normalize = |ty| {
             let ty = self.resolve_vars_if_possible(ty);
-            let ty = tcx.normalize_erasing_regions(self.param_env, ty);
-            (SizeSkeleton::compute(ty, tcx, self.param_env), ty)
+            self.tcx.normalize_erasing_regions(self.param_env, ty)
         };
-        let (sk_from, from) = convert(from);
-        let (sk_to, to) = convert(to);
+        let from = normalize(from);
+        let to = normalize(to);
+        trace!(?from, ?to);
+
+        // Transmutes that are only changing lifetimes are always ok.
+        if from == to {
+            return;
+        }
+
+        let skel = |ty| SizeSkeleton::compute(ty, tcx, self.param_env);
+        let sk_from = skel(from);
+        let sk_to = skel(to);
+        trace!(?sk_from, ?sk_to);
 
         // Check for same size using the skeletons.
         if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) {
diff --git a/compiler/rustc_hir_analysis/src/check/method/probe.rs b/compiler/rustc_hir_analysis/src/check/method/probe.rs
index a761a93dea4..ba078ad0abb 100644
--- a/compiler/rustc_hir_analysis/src/check/method/probe.rs
+++ b/compiler/rustc_hir_analysis/src/check/method/probe.rs
@@ -472,69 +472,65 @@ fn method_autoderef_steps<'tcx>(
 ) -> MethodAutoderefStepsResult<'tcx> {
     debug!("method_autoderef_steps({:?})", goal);
 
-    tcx.infer_ctxt().enter_with_canonical(DUMMY_SP, &goal, |ref infcx, goal, inference_vars| {
-        let ParamEnvAnd { param_env, value: self_ty } = goal;
-
-        let mut autoderef =
-            Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty, DUMMY_SP)
-                .include_raw_pointers()
-                .silence_errors();
-        let mut reached_raw_pointer = false;
-        let mut steps: Vec<_> = autoderef
-            .by_ref()
-            .map(|(ty, d)| {
-                let step = CandidateStep {
-                    self_ty: infcx.make_query_response_ignoring_pending_obligations(
-                        inference_vars.clone(),
-                        ty,
-                    ),
-                    autoderefs: d,
-                    from_unsafe_deref: reached_raw_pointer,
-                    unsize: false,
-                };
-                if let ty::RawPtr(_) = ty.kind() {
-                    // all the subsequent steps will be from_unsafe_deref
-                    reached_raw_pointer = true;
-                }
-                step
-            })
-            .collect();
-
-        let final_ty = autoderef.final_ty(true);
-        let opt_bad_ty = match final_ty.kind() {
-            ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy {
-                reached_raw_pointer,
-                ty: infcx
-                    .make_query_response_ignoring_pending_obligations(inference_vars, final_ty),
-            }),
-            ty::Array(elem_ty, _) => {
-                let dereferences = steps.len() - 1;
-
-                steps.push(CandidateStep {
-                    self_ty: infcx.make_query_response_ignoring_pending_obligations(
-                        inference_vars,
-                        infcx.tcx.mk_slice(*elem_ty),
-                    ),
-                    autoderefs: dereferences,
-                    // this could be from an unsafe deref if we had
-                    // a *mut/const [T; N]
-                    from_unsafe_deref: reached_raw_pointer,
-                    unsize: true,
-                });
-
-                None
+    let (ref infcx, goal, inference_vars) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal);
+    let ParamEnvAnd { param_env, value: self_ty } = goal;
+
+    let mut autoderef =
+        Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty, DUMMY_SP)
+            .include_raw_pointers()
+            .silence_errors();
+    let mut reached_raw_pointer = false;
+    let mut steps: Vec<_> = autoderef
+        .by_ref()
+        .map(|(ty, d)| {
+            let step = CandidateStep {
+                self_ty: infcx
+                    .make_query_response_ignoring_pending_obligations(inference_vars.clone(), ty),
+                autoderefs: d,
+                from_unsafe_deref: reached_raw_pointer,
+                unsize: false,
+            };
+            if let ty::RawPtr(_) = ty.kind() {
+                // all the subsequent steps will be from_unsafe_deref
+                reached_raw_pointer = true;
             }
-            _ => None,
-        };
-
-        debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty);
+            step
+        })
+        .collect();
+
+    let final_ty = autoderef.final_ty(true);
+    let opt_bad_ty = match final_ty.kind() {
+        ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy {
+            reached_raw_pointer,
+            ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty),
+        }),
+        ty::Array(elem_ty, _) => {
+            let dereferences = steps.len() - 1;
+
+            steps.push(CandidateStep {
+                self_ty: infcx.make_query_response_ignoring_pending_obligations(
+                    inference_vars,
+                    infcx.tcx.mk_slice(*elem_ty),
+                ),
+                autoderefs: dereferences,
+                // this could be from an unsafe deref if we had
+                // a *mut/const [T; N]
+                from_unsafe_deref: reached_raw_pointer,
+                unsize: true,
+            });
 
-        MethodAutoderefStepsResult {
-            steps: tcx.arena.alloc_from_iter(steps),
-            opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)),
-            reached_recursion_limit: autoderef.reached_recursion_limit(),
+            None
         }
-    })
+        _ => None,
+    };
+
+    debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty);
+
+    MethodAutoderefStepsResult {
+        steps: tcx.arena.alloc_from_iter(steps),
+        opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)),
+        reached_recursion_limit: autoderef.reached_recursion_limit(),
+    }
 }
 
 impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
diff --git a/compiler/rustc_hir_analysis/src/check/method/suggest.rs b/compiler/rustc_hir_analysis/src/check/method/suggest.rs
index ad1084bd1b1..e276c4f7d84 100644
--- a/compiler/rustc_hir_analysis/src/check/method/suggest.rs
+++ b/compiler/rustc_hir_analysis/src/check/method/suggest.rs
@@ -22,7 +22,7 @@ use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef};
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::Symbol;
 use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span};
-use rustc_trait_selection::traits::error_reporting::on_unimplemented::InferCtxtExt as _;
+use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
 use rustc_trait_selection::traits::{
     FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedNote,
@@ -855,8 +855,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                             // Avoid crashing.
                                             return (None, None);
                                         }
-                                        let OnUnimplementedNote { message, label, .. } =
-                                            self.on_unimplemented_note(trait_ref, &obligation);
+                                        let OnUnimplementedNote { message, label, .. } = self
+                                            .err_ctxt()
+                                            .on_unimplemented_note(trait_ref, &obligation);
                                         (message, label)
                                     })
                                     .unwrap_or((None, None))
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index 593a9776bde..331bd7e26c8 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -123,7 +123,6 @@ use rustc_span::{self, BytePos, Span, Symbol};
 use rustc_target::abi::VariantIdx;
 use rustc_target::spec::abi::Abi;
 use rustc_trait_selection::traits;
-use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error;
 use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor;
 use std::cell::RefCell;
 use std::num::NonZeroU32;
@@ -251,6 +250,7 @@ pub fn provide(providers: &mut Providers) {
         check_mod_item_types,
         region_scope_tree,
         collect_trait_impl_trait_tys,
+        compare_assoc_const_impl_item_with_trait_item: compare_method::raw_compare_const_impl,
         ..*providers
     };
 }
diff --git a/compiler/rustc_hir_analysis/src/check/op.rs b/compiler/rustc_hir_analysis/src/check/op.rs
index d876b1d20fe..5e498a92ec2 100644
--- a/compiler/rustc_hir_analysis/src/check/op.rs
+++ b/compiler/rustc_hir_analysis/src/check/op.rs
@@ -18,7 +18,7 @@ use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{sym, Ident};
 use rustc_span::Span;
 use rustc_trait_selection::infer::InferCtxtExt;
-use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt as _;
+use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::{FulfillmentError, TraitEngine, TraitEngineExt};
 use rustc_type_ir::sty::TyKind::*;
 
@@ -512,7 +512,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                         _ => None,
                                     };
 
-                                    self.suggest_restricting_param_bound(
+                                    self.err_ctxt().suggest_restricting_param_bound(
                                         &mut err,
                                         trait_pred,
                                         output_associated_item,
@@ -662,7 +662,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             error.obligation.predicate.to_opt_poly_trait_pred()
                         });
                         for pred in predicates {
-                            self.suggest_restricting_param_bound(
+                            self.err_ctxt().suggest_restricting_param_bound(
                                 &mut err,
                                 pred,
                                 None,
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index d607f901420..0a8a1bec9b8 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -22,7 +22,7 @@ use rustc_session::parse::feature_err;
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::{Span, DUMMY_SP};
 use rustc_trait_selection::autoderef::Autoderef;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
 use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
 use rustc_trait_selection::traits::{
@@ -91,29 +91,28 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>(
 {
     let param_env = tcx.param_env(body_def_id);
     let body_id = tcx.hir().local_def_id_to_hir_id(body_def_id);
-    tcx.infer_ctxt().enter(|ref infcx| {
-        let ocx = ObligationCtxt::new(infcx);
+    let infcx = &tcx.infer_ctxt().build();
+    let ocx = ObligationCtxt::new(infcx);
 
-        let assumed_wf_types = ocx.assumed_wf_types(param_env, span, body_def_id);
+    let assumed_wf_types = ocx.assumed_wf_types(param_env, span, body_def_id);
 
-        let mut wfcx = WfCheckingCtxt { ocx, span, body_id, param_env };
+    let mut wfcx = WfCheckingCtxt { ocx, span, body_id, param_env };
 
-        if !tcx.features().trivial_bounds {
-            wfcx.check_false_global_bounds()
-        }
-        f(&mut wfcx);
-        let errors = wfcx.select_all_or_error();
-        if !errors.is_empty() {
-            infcx.report_fulfillment_errors(&errors, None, false);
-            return;
-        }
+    if !tcx.features().trivial_bounds {
+        wfcx.check_false_global_bounds()
+    }
+    f(&mut wfcx);
+    let errors = wfcx.select_all_or_error();
+    if !errors.is_empty() {
+        infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+        return;
+    }
 
-        let implied_bounds = infcx.implied_bounds_tys(param_env, body_id, assumed_wf_types);
-        let outlives_environment =
-            OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds);
+    let implied_bounds = infcx.implied_bounds_tys(param_env, body_id, assumed_wf_types);
+    let outlives_environment =
+        OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds);
 
-        infcx.check_region_obligations_and_report_errors(body_def_id, &outlives_environment);
-    })
+    infcx.check_region_obligations_and_report_errors(body_def_id, &outlives_environment);
 }
 
 fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) {
@@ -699,29 +698,28 @@ fn resolve_regions_with_wf_tys<'tcx>(
     id: hir::HirId,
     param_env: ty::ParamEnv<'tcx>,
     wf_tys: &FxHashSet<Ty<'tcx>>,
-    add_constraints: impl for<'a> FnOnce(&'a InferCtxt<'a, 'tcx>, &'a RegionBoundPairs<'tcx>),
+    add_constraints: impl for<'a> FnOnce(&'a InferCtxt<'tcx>, &'a RegionBoundPairs<'tcx>),
 ) -> bool {
     // Unfortunately, we have to use a new `InferCtxt` each call, because
     // region constraints get added and solved there and we need to test each
     // call individually.
-    tcx.infer_ctxt().enter(|infcx| {
-        let outlives_environment = OutlivesEnvironment::with_bounds(
-            param_env,
-            Some(&infcx),
-            infcx.implied_bounds_tys(param_env, id, wf_tys.clone()),
-        );
-        let region_bound_pairs = outlives_environment.region_bound_pairs();
+    let infcx = tcx.infer_ctxt().build();
+    let outlives_environment = OutlivesEnvironment::with_bounds(
+        param_env,
+        Some(&infcx),
+        infcx.implied_bounds_tys(param_env, id, wf_tys.clone()),
+    );
+    let region_bound_pairs = outlives_environment.region_bound_pairs();
 
-        add_constraints(&infcx, region_bound_pairs);
+    add_constraints(&infcx, region_bound_pairs);
 
-        let errors = infcx.resolve_regions(&outlives_environment);
+    let errors = infcx.resolve_regions(&outlives_environment);
 
-        debug!(?errors, "errors");
+    debug!(?errors, "errors");
 
-        // If we were able to prove that the type outlives the region without
-        // an error, it must be because of the implied or explicit bounds...
-        errors.is_empty()
-    })
+    // If we were able to prove that the type outlives the region without
+    // an error, it must be because of the implied or explicit bounds...
+    errors.is_empty()
 }
 
 /// TypeVisitor that looks for uses of GATs like
@@ -839,7 +837,7 @@ fn check_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>) {
     let (method_sig, span) = match impl_item.kind {
         hir::ImplItemKind::Fn(ref sig, _) => (Some(sig), impl_item.span),
         // Constrain binding and overflow error spans to `<Ty>` in `type foo = <Ty>`.
-        hir::ImplItemKind::TyAlias(ty) if ty.span != DUMMY_SP => (None, ty.span),
+        hir::ImplItemKind::Type(ty) if ty.span != DUMMY_SP => (None, ty.span),
         _ => (None, impl_item.span),
     };
 
@@ -1043,6 +1041,8 @@ fn check_type_defn<'tcx, F>(
 ) where
     F: FnMut(&WfCheckingCtxt<'_, 'tcx>) -> Vec<AdtVariant<'tcx>>,
 {
+    let _ = tcx.representability(item.def_id.def_id);
+
     enter_wf_checking_ctxt(tcx, item.span, item.def_id.def_id, |wfcx| {
         let variants = lookup_fields(wfcx);
         let packed = tcx.adt_def(item.def_id).repr().packed();
@@ -1677,7 +1677,7 @@ fn receiver_is_valid<'tcx>(
     // `self: Self` is always valid.
     if can_eq_self(receiver_ty) {
         if let Err(err) = wfcx.equate_types(&cause, wfcx.param_env, self_ty, receiver_ty) {
-            infcx.report_mismatched_types(&cause, self_ty, receiver_ty, err).emit();
+            infcx.err_ctxt().report_mismatched_types(&cause, self_ty, receiver_ty, err).emit();
         }
         return true;
     }
@@ -1709,7 +1709,10 @@ fn receiver_is_valid<'tcx>(
                 if let Err(err) =
                     wfcx.equate_types(&cause, wfcx.param_env, self_ty, potential_self_ty)
                 {
-                    infcx.report_mismatched_types(&cause, self_ty, potential_self_ty, err).emit();
+                    infcx
+                        .err_ctxt()
+                        .report_mismatched_types(&cause, self_ty, potential_self_ty, err)
+                        .emit();
                 }
 
                 break;
diff --git a/compiler/rustc_hir_analysis/src/check/writeback.rs b/compiler/rustc_hir_analysis/src/check/writeback.rs
index 680dbf7037f..3583769b7cd 100644
--- a/compiler/rustc_hir_analysis/src/check/writeback.rs
+++ b/compiler/rustc_hir_analysis/src/check/writeback.rs
@@ -700,7 +700,7 @@ impl Locatable for hir::HirId {
 /// unresolved types and so forth.
 struct Resolver<'cx, 'tcx> {
     tcx: TyCtxt<'tcx>,
-    infcx: &'cx InferCtxt<'cx, 'tcx>,
+    infcx: &'cx InferCtxt<'tcx>,
     span: &'cx dyn Locatable,
     body: &'tcx hir::Body<'tcx>,
 
@@ -720,6 +720,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
     fn report_error(&self, p: impl Into<ty::GenericArg<'tcx>>) {
         if !self.tcx.sess.has_errors().is_some() {
             self.infcx
+                .err_ctxt()
                 .emit_inference_failure_err(
                     Some(self.body.id()),
                     self.span.to_span(self.tcx),
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index d4eb826f0b4..b6c91d425df 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -12,7 +12,7 @@ use rustc_infer::infer::outlives::env::OutlivesEnvironment;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
 use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitable};
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
 use rustc_trait_selection::traits::misc::{can_type_implement_copy, CopyImplementationError};
 use rustc_trait_selection::traits::predicate_for_trait_def;
 use rustc_trait_selection::traits::{self, ObligationCause};
@@ -108,43 +108,42 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
                 // why this field does not implement Copy. This is useful because sometimes
                 // it is not immediately clear why Copy is not implemented for a field, since
                 // all we point at is the field itself.
-                tcx.infer_ctxt().ignoring_regions().enter(|infcx| {
-                    for error in traits::fully_solve_bound(
-                        &infcx,
-                        traits::ObligationCause::dummy_with_span(field_ty_span),
-                        param_env,
-                        ty,
-                        tcx.lang_items().copy_trait().unwrap(),
-                    ) {
-                        let error_predicate = error.obligation.predicate;
-                        // Only note if it's not the root obligation, otherwise it's trivial and
-                        // should be self-explanatory (i.e. a field literally doesn't implement Copy).
-
-                        // FIXME: This error could be more descriptive, especially if the error_predicate
-                        // contains a foreign type or if it's a deeply nested type...
-                        if error_predicate != error.root_obligation.predicate {
-                            errors
-                                .entry((ty.to_string(), error_predicate.to_string()))
-                                .or_default()
-                                .push(error.obligation.cause.span);
-                        }
-                        if let ty::PredicateKind::Trait(ty::TraitPredicate {
-                            trait_ref,
-                            polarity: ty::ImplPolarity::Positive,
-                            ..
-                        }) = error_predicate.kind().skip_binder()
-                        {
-                            let ty = trait_ref.self_ty();
-                            if let ty::Param(_) = ty.kind() {
-                                bounds.push((
-                                    format!("{ty}"),
-                                    trait_ref.print_only_trait_path().to_string(),
-                                    Some(trait_ref.def_id),
-                                ));
-                            }
+                let infcx = tcx.infer_ctxt().ignoring_regions().build();
+                for error in traits::fully_solve_bound(
+                    &infcx,
+                    traits::ObligationCause::dummy_with_span(field_ty_span),
+                    param_env,
+                    ty,
+                    tcx.lang_items().copy_trait().unwrap(),
+                ) {
+                    let error_predicate = error.obligation.predicate;
+                    // Only note if it's not the root obligation, otherwise it's trivial and
+                    // should be self-explanatory (i.e. a field literally doesn't implement Copy).
+
+                    // FIXME: This error could be more descriptive, especially if the error_predicate
+                    // contains a foreign type or if it's a deeply nested type...
+                    if error_predicate != error.root_obligation.predicate {
+                        errors
+                            .entry((ty.to_string(), error_predicate.to_string()))
+                            .or_default()
+                            .push(error.obligation.cause.span);
+                    }
+                    if let ty::PredicateKind::Trait(ty::TraitPredicate {
+                        trait_ref,
+                        polarity: ty::ImplPolarity::Positive,
+                        ..
+                    }) = error_predicate.kind().skip_binder()
+                    {
+                        let ty = trait_ref.self_ty();
+                        if let ty::Param(_) = ty.kind() {
+                            bounds.push((
+                                format!("{ty}"),
+                                trait_ref.print_only_trait_path().to_string(),
+                                Some(trait_ref.def_id),
+                            ));
                         }
                     }
-                });
+                }
             }
             for ((ty, error_predicate), spans) in errors {
                 let span: MultiSpan = spans.into();
@@ -205,91 +204,89 @@ fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did:
 
     let create_err = |msg: &str| struct_span_err!(tcx.sess, span, E0378, "{}", msg);
 
-    tcx.infer_ctxt().enter(|infcx| {
-        let cause = ObligationCause::misc(span, impl_hir_id);
-
-        use rustc_type_ir::sty::TyKind::*;
-        match (source.kind(), target.kind()) {
-            (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b))
-                if infcx.at(&cause, param_env).eq(r_a, *r_b).is_ok() && mutbl_a == *mutbl_b => {}
-            (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => (),
-            (&Adt(def_a, substs_a), &Adt(def_b, substs_b))
-                if def_a.is_struct() && def_b.is_struct() =>
-            {
-                if def_a != def_b {
-                    let source_path = tcx.def_path_str(def_a.did());
-                    let target_path = tcx.def_path_str(def_b.did());
-
-                    create_err(&format!(
-                        "the trait `DispatchFromDyn` may only be implemented \
-                                for a coercion between structures with the same \
-                                definition; expected `{}`, found `{}`",
-                        source_path, target_path,
-                    ))
-                    .emit();
+    let infcx = tcx.infer_ctxt().build();
+    let cause = ObligationCause::misc(span, impl_hir_id);
+
+    use rustc_type_ir::sty::TyKind::*;
+    match (source.kind(), target.kind()) {
+        (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b))
+            if infcx.at(&cause, param_env).eq(r_a, *r_b).is_ok() && mutbl_a == *mutbl_b => {}
+        (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => (),
+        (&Adt(def_a, substs_a), &Adt(def_b, substs_b))
+            if def_a.is_struct() && def_b.is_struct() =>
+        {
+            if def_a != def_b {
+                let source_path = tcx.def_path_str(def_a.did());
+                let target_path = tcx.def_path_str(def_b.did());
+
+                create_err(&format!(
+                    "the trait `DispatchFromDyn` may only be implemented \
+                            for a coercion between structures with the same \
+                            definition; expected `{}`, found `{}`",
+                    source_path, target_path,
+                ))
+                .emit();
 
-                    return;
-                }
+                return;
+            }
 
-                if def_a.repr().c() || def_a.repr().packed() {
-                    create_err(
-                        "structs implementing `DispatchFromDyn` may not have \
-                             `#[repr(packed)]` or `#[repr(C)]`",
-                    )
-                    .emit();
-                }
+            if def_a.repr().c() || def_a.repr().packed() {
+                create_err(
+                    "structs implementing `DispatchFromDyn` may not have \
+                         `#[repr(packed)]` or `#[repr(C)]`",
+                )
+                .emit();
+            }
 
-                let fields = &def_a.non_enum_variant().fields;
+            let fields = &def_a.non_enum_variant().fields;
 
-                let coerced_fields = fields
-                    .iter()
-                    .filter(|field| {
-                        let ty_a = field.ty(tcx, substs_a);
-                        let ty_b = field.ty(tcx, substs_b);
+            let coerced_fields = fields
+                .iter()
+                .filter(|field| {
+                    let ty_a = field.ty(tcx, substs_a);
+                    let ty_b = field.ty(tcx, substs_b);
 
-                        if let Ok(layout) = tcx.layout_of(param_env.and(ty_a)) {
-                            if layout.is_zst() && layout.align.abi.bytes() == 1 {
-                                // ignore ZST fields with alignment of 1 byte
-                                return false;
-                            }
+                    if let Ok(layout) = tcx.layout_of(param_env.and(ty_a)) {
+                        if layout.is_zst() && layout.align.abi.bytes() == 1 {
+                            // ignore ZST fields with alignment of 1 byte
+                            return false;
                         }
+                    }
 
-                        if let Ok(ok) = infcx.at(&cause, param_env).eq(ty_a, ty_b) {
-                            if ok.obligations.is_empty() {
-                                create_err(
-                                    "the trait `DispatchFromDyn` may only be implemented \
-                                     for structs containing the field being coerced, \
-                                     ZST fields with 1 byte alignment, and nothing else",
-                                )
-                                .note(&format!(
-                                    "extra field `{}` of type `{}` is not allowed",
-                                    field.name, ty_a,
-                                ))
-                                .emit();
-
-                                return false;
-                            }
+                    if let Ok(ok) = infcx.at(&cause, param_env).eq(ty_a, ty_b) {
+                        if ok.obligations.is_empty() {
+                            create_err(
+                                "the trait `DispatchFromDyn` may only be implemented \
+                                 for structs containing the field being coerced, \
+                                 ZST fields with 1 byte alignment, and nothing else",
+                            )
+                            .note(&format!(
+                                "extra field `{}` of type `{}` is not allowed",
+                                field.name, ty_a,
+                            ))
+                            .emit();
+
+                            return false;
                         }
+                    }
 
-                        return true;
-                    })
-                    .collect::<Vec<_>>();
+                    return true;
+                })
+                .collect::<Vec<_>>();
 
-                if coerced_fields.is_empty() {
-                    create_err(
-                        "the trait `DispatchFromDyn` may only be implemented \
-                            for a coercion between structures with a single field \
-                            being coerced, none found",
-                    )
-                    .emit();
-                } else if coerced_fields.len() > 1 {
-                    create_err(
-                        "implementing the `DispatchFromDyn` trait requires multiple coercions",
-                    )
+            if coerced_fields.is_empty() {
+                create_err(
+                    "the trait `DispatchFromDyn` may only be implemented \
+                        for a coercion between structures with a single field \
+                        being coerced, none found",
+                )
+                .emit();
+            } else if coerced_fields.len() > 1 {
+                create_err("implementing the `DispatchFromDyn` trait requires multiple coercions")
                     .note(
                         "the trait `DispatchFromDyn` may only be implemented \
-                                for a coercion between structures with a single field \
-                                being coerced",
+                            for a coercion between structures with a single field \
+                            being coerced",
                     )
                     .note(&format!(
                         "currently, {} fields need coercions: {}",
@@ -308,39 +305,38 @@ fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did:
                             .join(", ")
                     ))
                     .emit();
-                } else {
-                    let errors = traits::fully_solve_obligations(
-                        &infcx,
-                        coerced_fields.into_iter().map(|field| {
-                            predicate_for_trait_def(
-                                tcx,
-                                param_env,
-                                cause.clone(),
-                                dispatch_from_dyn_trait,
-                                0,
-                                field.ty(tcx, substs_a),
-                                &[field.ty(tcx, substs_b).into()],
-                            )
-                        }),
-                    );
-                    if !errors.is_empty() {
-                        infcx.report_fulfillment_errors(&errors, None, false);
-                    }
-
-                    // Finally, resolve all regions.
-                    let outlives_env = OutlivesEnvironment::new(param_env);
-                    infcx.check_region_obligations_and_report_errors(impl_did, &outlives_env);
+            } else {
+                let errors = traits::fully_solve_obligations(
+                    &infcx,
+                    coerced_fields.into_iter().map(|field| {
+                        predicate_for_trait_def(
+                            tcx,
+                            param_env,
+                            cause.clone(),
+                            dispatch_from_dyn_trait,
+                            0,
+                            field.ty(tcx, substs_a),
+                            &[field.ty(tcx, substs_b).into()],
+                        )
+                    }),
+                );
+                if !errors.is_empty() {
+                    infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
                 }
-            }
-            _ => {
-                create_err(
-                    "the trait `DispatchFromDyn` may only be implemented \
-                        for a coercion between structures",
-                )
-                .emit();
+
+                // Finally, resolve all regions.
+                let outlives_env = OutlivesEnvironment::new(param_env);
+                infcx.check_region_obligations_and_report_errors(impl_did, &outlives_env);
             }
         }
-    })
+        _ => {
+            create_err(
+                "the trait `DispatchFromDyn` may only be implemented \
+                    for a coercion between structures",
+            )
+            .emit();
+        }
+    }
 }
 
 pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedInfo {
@@ -369,220 +365,208 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn
 
     debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source, target);
 
-    tcx.infer_ctxt().enter(|infcx| {
-        let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did);
-        let cause = ObligationCause::misc(span, impl_hir_id);
-        let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>,
-                           mt_b: ty::TypeAndMut<'tcx>,
-                           mk_ptr: &dyn Fn(Ty<'tcx>) -> Ty<'tcx>| {
-            if (mt_a.mutbl, mt_b.mutbl) == (hir::Mutability::Not, hir::Mutability::Mut) {
-                infcx
-                    .report_mismatched_types(
-                        &cause,
-                        mk_ptr(mt_b.ty),
-                        target,
-                        ty::error::TypeError::Mutability,
-                    )
-                    .emit();
-            }
-            (mt_a.ty, mt_b.ty, unsize_trait, None)
-        };
-        let (source, target, trait_def_id, kind) = match (source.kind(), target.kind()) {
-            (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => {
-                infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
-                let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
-                let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b };
-                check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty))
-            }
+    let infcx = tcx.infer_ctxt().build();
+    let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did);
+    let cause = ObligationCause::misc(span, impl_hir_id);
+    let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>,
+                       mt_b: ty::TypeAndMut<'tcx>,
+                       mk_ptr: &dyn Fn(Ty<'tcx>) -> Ty<'tcx>| {
+        if (mt_a.mutbl, mt_b.mutbl) == (hir::Mutability::Not, hir::Mutability::Mut) {
+            infcx
+                .err_ctxt()
+                .report_mismatched_types(
+                    &cause,
+                    mk_ptr(mt_b.ty),
+                    target,
+                    ty::error::TypeError::Mutability,
+                )
+                .emit();
+        }
+        (mt_a.ty, mt_b.ty, unsize_trait, None)
+    };
+    let (source, target, trait_def_id, kind) = match (source.kind(), target.kind()) {
+        (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => {
+            infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
+            let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
+            let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b };
+            check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty))
+        }
 
-            (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(mt_b)) => {
-                let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
-                check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty))
-            }
+        (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(mt_b)) => {
+            let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
+            check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty))
+        }
+
+        (&ty::RawPtr(mt_a), &ty::RawPtr(mt_b)) => check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty)),
 
-            (&ty::RawPtr(mt_a), &ty::RawPtr(mt_b)) => {
-                check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty))
+        (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b))
+            if def_a.is_struct() && def_b.is_struct() =>
+        {
+            if def_a != def_b {
+                let source_path = tcx.def_path_str(def_a.did());
+                let target_path = tcx.def_path_str(def_b.did());
+                struct_span_err!(
+                    tcx.sess,
+                    span,
+                    E0377,
+                    "the trait `CoerceUnsized` may only be implemented \
+                           for a coercion between structures with the same \
+                           definition; expected `{}`, found `{}`",
+                    source_path,
+                    target_path
+                )
+                .emit();
+                return err_info;
             }
 
-            (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b))
-                if def_a.is_struct() && def_b.is_struct() =>
-            {
-                if def_a != def_b {
-                    let source_path = tcx.def_path_str(def_a.did());
-                    let target_path = tcx.def_path_str(def_b.did());
-                    struct_span_err!(
-                        tcx.sess,
-                        span,
-                        E0377,
-                        "the trait `CoerceUnsized` may only be implemented \
-                               for a coercion between structures with the same \
-                               definition; expected `{}`, found `{}`",
-                        source_path,
-                        target_path
-                    )
-                    .emit();
-                    return err_info;
-                }
+            // Here we are considering a case of converting
+            // `S<P0...Pn>` to S<Q0...Qn>`. As an example, let's imagine a struct `Foo<T, U>`,
+            // which acts like a pointer to `U`, but carries along some extra data of type `T`:
+            //
+            //     struct Foo<T, U> {
+            //         extra: T,
+            //         ptr: *mut U,
+            //     }
+            //
+            // We might have an impl that allows (e.g.) `Foo<T, [i32; 3]>` to be unsized
+            // to `Foo<T, [i32]>`. That impl would look like:
+            //
+            //   impl<T, U: Unsize<V>, V> CoerceUnsized<Foo<T, V>> for Foo<T, U> {}
+            //
+            // Here `U = [i32; 3]` and `V = [i32]`. At runtime,
+            // when this coercion occurs, we would be changing the
+            // field `ptr` from a thin pointer of type `*mut [i32;
+            // 3]` to a fat pointer of type `*mut [i32]` (with
+            // extra data `3`).  **The purpose of this check is to
+            // make sure that we know how to do this conversion.**
+            //
+            // To check if this impl is legal, we would walk down
+            // the fields of `Foo` and consider their types with
+            // both substitutes. We are looking to find that
+            // exactly one (non-phantom) field has changed its
+            // type, which we will expect to be the pointer that
+            // is becoming fat (we could probably generalize this
+            // to multiple thin pointers of the same type becoming
+            // fat, but we don't). In this case:
+            //
+            // - `extra` has type `T` before and type `T` after
+            // - `ptr` has type `*mut U` before and type `*mut V` after
+            //
+            // Since just one field changed, we would then check
+            // that `*mut U: CoerceUnsized<*mut V>` is implemented
+            // (in other words, that we know how to do this
+            // conversion). This will work out because `U:
+            // Unsize<V>`, and we have a builtin rule that `*mut
+            // U` can be coerced to `*mut V` if `U: Unsize<V>`.
+            let fields = &def_a.non_enum_variant().fields;
+            let diff_fields = fields
+                .iter()
+                .enumerate()
+                .filter_map(|(i, f)| {
+                    let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b));
+
+                    if tcx.type_of(f.did).is_phantom_data() {
+                        // Ignore PhantomData fields
+                        return None;
+                    }
 
-                // Here we are considering a case of converting
-                // `S<P0...Pn>` to S<Q0...Qn>`. As an example, let's imagine a struct `Foo<T, U>`,
-                // which acts like a pointer to `U`, but carries along some extra data of type `T`:
-                //
-                //     struct Foo<T, U> {
-                //         extra: T,
-                //         ptr: *mut U,
-                //     }
-                //
-                // We might have an impl that allows (e.g.) `Foo<T, [i32; 3]>` to be unsized
-                // to `Foo<T, [i32]>`. That impl would look like:
-                //
-                //   impl<T, U: Unsize<V>, V> CoerceUnsized<Foo<T, V>> for Foo<T, U> {}
-                //
-                // Here `U = [i32; 3]` and `V = [i32]`. At runtime,
-                // when this coercion occurs, we would be changing the
-                // field `ptr` from a thin pointer of type `*mut [i32;
-                // 3]` to a fat pointer of type `*mut [i32]` (with
-                // extra data `3`).  **The purpose of this check is to
-                // make sure that we know how to do this conversion.**
-                //
-                // To check if this impl is legal, we would walk down
-                // the fields of `Foo` and consider their types with
-                // both substitutes. We are looking to find that
-                // exactly one (non-phantom) field has changed its
-                // type, which we will expect to be the pointer that
-                // is becoming fat (we could probably generalize this
-                // to multiple thin pointers of the same type becoming
-                // fat, but we don't). In this case:
-                //
-                // - `extra` has type `T` before and type `T` after
-                // - `ptr` has type `*mut U` before and type `*mut V` after
-                //
-                // Since just one field changed, we would then check
-                // that `*mut U: CoerceUnsized<*mut V>` is implemented
-                // (in other words, that we know how to do this
-                // conversion). This will work out because `U:
-                // Unsize<V>`, and we have a builtin rule that `*mut
-                // U` can be coerced to `*mut V` if `U: Unsize<V>`.
-                let fields = &def_a.non_enum_variant().fields;
-                let diff_fields = fields
-                    .iter()
-                    .enumerate()
-                    .filter_map(|(i, f)| {
-                        let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b));
-
-                        if tcx.type_of(f.did).is_phantom_data() {
-                            // Ignore PhantomData fields
+                    // Ignore fields that aren't changed; it may
+                    // be that we could get away with subtyping or
+                    // something more accepting, but we use
+                    // equality because we want to be able to
+                    // perform this check without computing
+                    // variance where possible. (This is because
+                    // we may have to evaluate constraint
+                    // expressions in the course of execution.)
+                    // See e.g., #41936.
+                    if let Ok(ok) = infcx.at(&cause, param_env).eq(a, b) {
+                        if ok.obligations.is_empty() {
                             return None;
                         }
+                    }
 
-                        // Ignore fields that aren't changed; it may
-                        // be that we could get away with subtyping or
-                        // something more accepting, but we use
-                        // equality because we want to be able to
-                        // perform this check without computing
-                        // variance where possible. (This is because
-                        // we may have to evaluate constraint
-                        // expressions in the course of execution.)
-                        // See e.g., #41936.
-                        if let Ok(ok) = infcx.at(&cause, param_env).eq(a, b) {
-                            if ok.obligations.is_empty() {
-                                return None;
-                            }
-                        }
+                    // Collect up all fields that were significantly changed
+                    // i.e., those that contain T in coerce_unsized T -> U
+                    Some((i, a, b))
+                })
+                .collect::<Vec<_>>();
 
-                        // Collect up all fields that were significantly changed
-                        // i.e., those that contain T in coerce_unsized T -> U
-                        Some((i, a, b))
-                    })
-                    .collect::<Vec<_>>();
-
-                if diff_fields.is_empty() {
-                    struct_span_err!(
-                        tcx.sess,
-                        span,
-                        E0374,
-                        "the trait `CoerceUnsized` may only be implemented \
-                               for a coercion between structures with one field \
-                               being coerced, none found"
-                    )
-                    .emit();
-                    return err_info;
-                } else if diff_fields.len() > 1 {
-                    let item = tcx.hir().expect_item(impl_did);
-                    let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(ref t), .. }) =
-                        item.kind
-                    {
+            if diff_fields.is_empty() {
+                struct_span_err!(
+                    tcx.sess,
+                    span,
+                    E0374,
+                    "the trait `CoerceUnsized` may only be implemented \
+                           for a coercion between structures with one field \
+                           being coerced, none found"
+                )
+                .emit();
+                return err_info;
+            } else if diff_fields.len() > 1 {
+                let item = tcx.hir().expect_item(impl_did);
+                let span =
+                    if let ItemKind::Impl(hir::Impl { of_trait: Some(ref t), .. }) = item.kind {
                         t.path.span
                     } else {
                         tcx.def_span(impl_did)
                     };
 
-                    struct_span_err!(
-                        tcx.sess,
-                        span,
-                        E0375,
-                        "implementing the trait \
-                                                    `CoerceUnsized` requires multiple \
-                                                    coercions"
-                    )
-                    .note(
-                        "`CoerceUnsized` may only be implemented for \
-                              a coercion between structures with one field being coerced",
-                    )
-                    .note(&format!(
-                        "currently, {} fields need coercions: {}",
-                        diff_fields.len(),
-                        diff_fields
-                            .iter()
-                            .map(|&(i, a, b)| {
-                                format!("`{}` (`{}` to `{}`)", fields[i].name, a, b)
-                            })
-                            .collect::<Vec<_>>()
-                            .join(", ")
-                    ))
-                    .span_label(span, "requires multiple coercions")
-                    .emit();
-                    return err_info;
-                }
-
-                let (i, a, b) = diff_fields[0];
-                let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
-                (a, b, coerce_unsized_trait, Some(kind))
-            }
-
-            _ => {
                 struct_span_err!(
                     tcx.sess,
                     span,
-                    E0376,
-                    "the trait `CoerceUnsized` may only be implemented \
-                           for a coercion between structures"
+                    E0375,
+                    "implementing the trait \
+                                                `CoerceUnsized` requires multiple \
+                                                coercions"
+                )
+                .note(
+                    "`CoerceUnsized` may only be implemented for \
+                          a coercion between structures with one field being coerced",
                 )
+                .note(&format!(
+                    "currently, {} fields need coercions: {}",
+                    diff_fields.len(),
+                    diff_fields
+                        .iter()
+                        .map(|&(i, a, b)| { format!("`{}` (`{}` to `{}`)", fields[i].name, a, b) })
+                        .collect::<Vec<_>>()
+                        .join(", ")
+                ))
+                .span_label(span, "requires multiple coercions")
                 .emit();
                 return err_info;
             }
-        };
-
-        // Register an obligation for `A: Trait<B>`.
-        let cause = traits::ObligationCause::misc(span, impl_hir_id);
-        let predicate = predicate_for_trait_def(
-            tcx,
-            param_env,
-            cause,
-            trait_def_id,
-            0,
-            source,
-            &[target.into()],
-        );
-        let errors = traits::fully_solve_obligation(&infcx, predicate);
-        if !errors.is_empty() {
-            infcx.report_fulfillment_errors(&errors, None, false);
+
+            let (i, a, b) = diff_fields[0];
+            let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
+            (a, b, coerce_unsized_trait, Some(kind))
         }
 
-        // Finally, resolve all regions.
-        let outlives_env = OutlivesEnvironment::new(param_env);
-        infcx.check_region_obligations_and_report_errors(impl_did, &outlives_env);
+        _ => {
+            struct_span_err!(
+                tcx.sess,
+                span,
+                E0376,
+                "the trait `CoerceUnsized` may only be implemented \
+                       for a coercion between structures"
+            )
+            .emit();
+            return err_info;
+        }
+    };
+
+    // Register an obligation for `A: Trait<B>`.
+    let cause = traits::ObligationCause::misc(span, impl_hir_id);
+    let predicate =
+        predicate_for_trait_def(tcx, param_env, cause, trait_def_id, 0, source, &[target.into()]);
+    let errors = traits::fully_solve_obligation(&infcx, predicate);
+    if !errors.is_empty() {
+        infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+    }
+
+    // Finally, resolve all regions.
+    let outlives_env = OutlivesEnvironment::new(param_env);
+    infcx.check_region_obligations_and_report_errors(impl_did, &outlives_env);
 
-        CoerceUnsizedInfo { custom_kind: kind }
-    })
+    CoerceUnsizedInfo { custom_kind: kind }
 }
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index ab4b861b6cb..5c76016c662 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -46,6 +46,7 @@ use std::iter;
 
 mod generics_of;
 mod item_bounds;
+mod lifetimes;
 mod predicates_of;
 mod type_of;
 
@@ -57,6 +58,7 @@ fn collect_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
 }
 
 pub fn provide(providers: &mut Providers) {
+    lifetimes::provide(providers);
     *providers = Providers {
         opt_const_param_of: type_of::opt_const_param_of,
         type_of: type_of::type_of,
@@ -738,7 +740,7 @@ fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) {
         hir::ImplItemKind::Fn(..) => {
             tcx.ensure().fn_sig(def_id);
         }
-        hir::ImplItemKind::TyAlias(_) => {
+        hir::ImplItemKind::Type(_) => {
             // Account for `type T = _;`
             let mut visitor = HirPlaceholderCollector::default();
             visitor.visit_impl_item(impl_item);
@@ -1580,13 +1582,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
         codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
     }
 
-    // The panic_no_unwind function called by TerminatorKind::Abort will never
-    // unwind. If the panic handler that it invokes unwind then it will simply
-    // call the panic handler again.
-    if Some(did.to_def_id()) == tcx.lang_items().panic_no_unwind() {
-        codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
-    }
-
     let supported_target_features = tcx.supported_target_features(LOCAL_CRATE);
 
     let mut inline_span = None;
@@ -1647,7 +1642,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
                 )
                 .emit();
             }
-        } else if attr.has_name(sym::rustc_allocator_nounwind) {
+        } else if attr.has_name(sym::rustc_nounwind) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
         } else if attr.has_name(sym::rustc_reallocator) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR;
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index 7ffacbecf5f..707fd6c7527 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -213,7 +213,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
         Node::TraitItem(item) if matches!(item.kind, TraitItemKind::Type(..)) => {
             (None, Defaults::Deny)
         }
-        Node::ImplItem(item) if matches!(item.kind, ImplItemKind::TyAlias(..)) => {
+        Node::ImplItem(item) if matches!(item.kind, ImplItemKind::Type(..)) => {
             (None, Defaults::Deny)
         }
 
diff --git a/compiler/rustc_hir_analysis/src/collect/lifetimes.rs b/compiler/rustc_hir_analysis/src/collect/lifetimes.rs
new file mode 100644
index 00000000000..c1214698cf7
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/collect/lifetimes.rs
@@ -0,0 +1,1888 @@
+//! Resolution of early vs late bound lifetimes.
+//!
+//! Name resolution for lifetimes is performed on the AST and embedded into HIR.  From this
+//! information, typechecking needs to transform the lifetime parameters into bound lifetimes.
+//! Lifetimes can be early-bound or late-bound.  Construction of typechecking terms needs to visit
+//! the types in HIR to identify late-bound lifetimes and assign their Debruijn indices.  This file
+//! is also responsible for assigning their semantics to implicit lifetimes in trait objects.
+
+use rustc_ast::walk_list;
+use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
+use rustc_errors::struct_span_err;
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::{GenericArg, GenericParam, GenericParamKind, HirIdMap, LifetimeName, Node};
+use rustc_middle::bug;
+use rustc_middle::hir::map::Map;
+use rustc_middle::hir::nested_filter;
+use rustc_middle::middle::resolve_lifetime::*;
+use rustc_middle::ty::{self, DefIdTree, TyCtxt};
+use rustc_span::def_id::DefId;
+use rustc_span::symbol::{sym, Ident};
+use rustc_span::Span;
+use std::fmt;
+
+trait RegionExt {
+    fn early(hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region);
+
+    fn late(index: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region);
+
+    fn id(&self) -> Option<DefId>;
+
+    fn shifted(self, amount: u32) -> Region;
+}
+
+impl RegionExt for Region {
+    fn early(hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region) {
+        let def_id = hir_map.local_def_id(param.hir_id);
+        debug!("Region::early: def_id={:?}", def_id);
+        (def_id, Region::EarlyBound(def_id.to_def_id()))
+    }
+
+    fn late(idx: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region) {
+        let depth = ty::INNERMOST;
+        let def_id = hir_map.local_def_id(param.hir_id);
+        debug!(
+            "Region::late: idx={:?}, param={:?} depth={:?} def_id={:?}",
+            idx, param, depth, def_id,
+        );
+        (def_id, Region::LateBound(depth, idx, def_id.to_def_id()))
+    }
+
+    fn id(&self) -> Option<DefId> {
+        match *self {
+            Region::Static => None,
+
+            Region::EarlyBound(id) | Region::LateBound(_, _, id) | Region::Free(_, id) => Some(id),
+        }
+    }
+
+    fn shifted(self, amount: u32) -> Region {
+        match self {
+            Region::LateBound(debruijn, idx, id) => {
+                Region::LateBound(debruijn.shifted_in(amount), idx, id)
+            }
+            _ => self,
+        }
+    }
+}
+
+/// Maps the id of each lifetime reference to the lifetime decl
+/// that it corresponds to.
+///
+/// FIXME. This struct gets converted to a `ResolveLifetimes` for
+/// actual use. It has the same data, but indexed by `LocalDefId`.  This
+/// is silly.
+#[derive(Debug, Default)]
+struct NamedRegionMap {
+    // maps from every use of a named (not anonymous) lifetime to a
+    // `Region` describing how that region is bound
+    defs: HirIdMap<Region>,
+
+    // Maps relevant hir items to the bound vars on them. These include:
+    // - function defs
+    // - function pointers
+    // - closures
+    // - trait refs
+    // - bound types (like `T` in `for<'a> T<'a>: Foo`)
+    late_bound_vars: HirIdMap<Vec<ty::BoundVariableKind>>,
+}
+
+struct LifetimeContext<'a, 'tcx> {
+    tcx: TyCtxt<'tcx>,
+    map: &'a mut NamedRegionMap,
+    scope: ScopeRef<'a>,
+
+    /// Indicates that we only care about the definition of a trait. This should
+    /// be false if the `Item` we are resolving lifetimes for is not a trait or
+    /// we eventually need lifetimes resolve for trait items.
+    trait_definition_only: bool,
+}
+
+#[derive(Debug)]
+enum Scope<'a> {
+    /// Declares lifetimes, and each can be early-bound or late-bound.
+    /// The `DebruijnIndex` of late-bound lifetimes starts at `1` and
+    /// it should be shifted by the number of `Binder`s in between the
+    /// declaration `Binder` and the location it's referenced from.
+    Binder {
+        /// We use an IndexMap here because we want these lifetimes in order
+        /// for diagnostics.
+        lifetimes: FxIndexMap<LocalDefId, Region>,
+
+        scope_type: BinderScopeType,
+
+        /// The late bound vars for a given item are stored by `HirId` to be
+        /// queried later. However, if we enter an elision scope, we have to
+        /// later append the elided bound vars to the list and need to know what
+        /// to append to.
+        hir_id: hir::HirId,
+
+        s: ScopeRef<'a>,
+
+        /// If this binder comes from a where clause, specify how it was created.
+        /// This is used to diagnose inaccessible lifetimes in APIT:
+        /// ```ignore (illustrative)
+        /// fn foo(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
+        /// ```
+        where_bound_origin: Option<hir::PredicateOrigin>,
+    },
+
+    /// Lifetimes introduced by a fn are scoped to the call-site for that fn,
+    /// if this is a fn body, otherwise the original definitions are used.
+    /// Unspecified lifetimes are inferred, unless an elision scope is nested,
+    /// e.g., `(&T, fn(&T) -> &T);` becomes `(&'_ T, for<'a> fn(&'a T) -> &'a T)`.
+    Body {
+        id: hir::BodyId,
+        s: ScopeRef<'a>,
+    },
+
+    /// A scope which either determines unspecified lifetimes or errors
+    /// on them (e.g., due to ambiguity).
+    Elision {
+        s: ScopeRef<'a>,
+    },
+
+    /// Use a specific lifetime (if `Some`) or leave it unset (to be
+    /// inferred in a function body or potentially error outside one),
+    /// for the default choice of lifetime in a trait object type.
+    ObjectLifetimeDefault {
+        lifetime: Option<Region>,
+        s: ScopeRef<'a>,
+    },
+
+    /// When we have nested trait refs, we concatenate late bound vars for inner
+    /// trait refs from outer ones. But we also need to include any HRTB
+    /// lifetimes encountered when identifying the trait that an associated type
+    /// is declared on.
+    Supertrait {
+        lifetimes: Vec<ty::BoundVariableKind>,
+        s: ScopeRef<'a>,
+    },
+
+    TraitRefBoundary {
+        s: ScopeRef<'a>,
+    },
+
+    Root,
+}
+
+#[derive(Copy, Clone, Debug)]
+enum BinderScopeType {
+    /// Any non-concatenating binder scopes.
+    Normal,
+    /// Within a syntactic trait ref, there may be multiple poly trait refs that
+    /// are nested (under the `associated_type_bounds` feature). The binders of
+    /// the inner poly trait refs are extended from the outer poly trait refs
+    /// and don't increase the late bound depth. If you had
+    /// `T: for<'a>  Foo<Bar: for<'b> Baz<'a, 'b>>`, then the `for<'b>` scope
+    /// would be `Concatenating`. This also used in trait refs in where clauses
+    /// where we have two binders `for<> T: for<> Foo` (I've intentionally left
+    /// out any lifetimes because they aren't needed to show the two scopes).
+    /// The inner `for<>` has a scope of `Concatenating`.
+    Concatenating,
+}
+
+// A helper struct for debugging scopes without printing parent scopes
+struct TruncatedScopeDebug<'a>(&'a Scope<'a>);
+
+impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self.0 {
+            Scope::Binder { lifetimes, scope_type, hir_id, where_bound_origin, s: _ } => f
+                .debug_struct("Binder")
+                .field("lifetimes", lifetimes)
+                .field("scope_type", scope_type)
+                .field("hir_id", hir_id)
+                .field("where_bound_origin", where_bound_origin)
+                .field("s", &"..")
+                .finish(),
+            Scope::Body { id, s: _ } => {
+                f.debug_struct("Body").field("id", id).field("s", &"..").finish()
+            }
+            Scope::Elision { s: _ } => f.debug_struct("Elision").field("s", &"..").finish(),
+            Scope::ObjectLifetimeDefault { lifetime, s: _ } => f
+                .debug_struct("ObjectLifetimeDefault")
+                .field("lifetime", lifetime)
+                .field("s", &"..")
+                .finish(),
+            Scope::Supertrait { lifetimes, s: _ } => f
+                .debug_struct("Supertrait")
+                .field("lifetimes", lifetimes)
+                .field("s", &"..")
+                .finish(),
+            Scope::TraitRefBoundary { s: _ } => f.debug_struct("TraitRefBoundary").finish(),
+            Scope::Root => f.debug_struct("Root").finish(),
+        }
+    }
+}
+
+type ScopeRef<'a> = &'a Scope<'a>;
+
+const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root;
+
+pub(crate) fn provide(providers: &mut ty::query::Providers) {
+    *providers = ty::query::Providers {
+        resolve_lifetimes_trait_definition,
+        resolve_lifetimes,
+
+        named_region_map: |tcx, id| resolve_lifetimes_for(tcx, id).defs.get(&id),
+        is_late_bound_map,
+        object_lifetime_default,
+        late_bound_vars_map: |tcx, id| resolve_lifetimes_for(tcx, id).late_bound_vars.get(&id),
+
+        ..*providers
+    };
+}
+
+/// Like `resolve_lifetimes`, but does not resolve lifetimes for trait items.
+/// Also does not generate any diagnostics.
+///
+/// This is ultimately a subset of the `resolve_lifetimes` work. It effectively
+/// resolves lifetimes only within the trait "header" -- that is, the trait
+/// and supertrait list. In contrast, `resolve_lifetimes` resolves all the
+/// lifetimes within the trait and its items. There is room to refactor this,
+/// for example to resolve lifetimes for each trait item in separate queries,
+/// but it's convenient to do the entire trait at once because the lifetimes
+/// from the trait definition are in scope within the trait items as well.
+///
+/// The reason for this separate call is to resolve what would otherwise
+/// be a cycle. Consider this example:
+///
+/// ```ignore UNSOLVED (maybe @jackh726 knows what lifetime parameter to give Sub)
+/// trait Base<'a> {
+///     type BaseItem;
+/// }
+/// trait Sub<'b>: for<'a> Base<'a> {
+///    type SubItem: Sub<BaseItem = &'b u32>;
+/// }
+/// ```
+///
+/// When we resolve `Sub` and all its items, we also have to resolve `Sub<BaseItem = &'b u32>`.
+/// To figure out the index of `'b`, we have to know about the supertraits
+/// of `Sub` so that we can determine that the `for<'a>` will be in scope.
+/// (This is because we -- currently at least -- flatten all the late-bound
+/// lifetimes into a single binder.) This requires us to resolve the
+/// *trait definition* of `Sub`; basically just enough lifetime information
+/// to look at the supertraits.
+#[instrument(level = "debug", skip(tcx))]
+fn resolve_lifetimes_trait_definition(
+    tcx: TyCtxt<'_>,
+    local_def_id: LocalDefId,
+) -> ResolveLifetimes {
+    convert_named_region_map(do_resolve(tcx, local_def_id, true))
+}
+
+/// Computes the `ResolveLifetimes` map that contains data for an entire `Item`.
+/// You should not read the result of this query directly, but rather use
+/// `named_region_map`, `is_late_bound_map`, etc.
+#[instrument(level = "debug", skip(tcx))]
+fn resolve_lifetimes(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> ResolveLifetimes {
+    convert_named_region_map(do_resolve(tcx, local_def_id, false))
+}
+
+fn do_resolve(
+    tcx: TyCtxt<'_>,
+    local_def_id: LocalDefId,
+    trait_definition_only: bool,
+) -> NamedRegionMap {
+    let item = tcx.hir().expect_item(local_def_id);
+    let mut named_region_map =
+        NamedRegionMap { defs: Default::default(), late_bound_vars: Default::default() };
+    let mut visitor = LifetimeContext {
+        tcx,
+        map: &mut named_region_map,
+        scope: ROOT_SCOPE,
+        trait_definition_only,
+    };
+    visitor.visit_item(item);
+
+    named_region_map
+}
+
+fn convert_named_region_map(named_region_map: NamedRegionMap) -> ResolveLifetimes {
+    let mut rl = ResolveLifetimes::default();
+
+    for (hir_id, v) in named_region_map.defs {
+        let map = rl.defs.entry(hir_id.owner).or_default();
+        map.insert(hir_id.local_id, v);
+    }
+    for (hir_id, v) in named_region_map.late_bound_vars {
+        let map = rl.late_bound_vars.entry(hir_id.owner).or_default();
+        map.insert(hir_id.local_id, v);
+    }
+
+    debug!(?rl.defs);
+    debug!(?rl.late_bound_vars);
+    rl
+}
+
+/// Given `any` owner (structs, traits, trait methods, etc.), does lifetime resolution.
+/// There are two important things this does.
+/// First, we have to resolve lifetimes for
+/// the entire *`Item`* that contains this owner, because that's the largest "scope"
+/// where we can have relevant lifetimes.
+/// Second, if we are asking for lifetimes in a trait *definition*, we use `resolve_lifetimes_trait_definition`
+/// instead of `resolve_lifetimes`, which does not descend into the trait items and does not emit diagnostics.
+/// This allows us to avoid cycles. Importantly, if we ask for lifetimes for lifetimes that have an owner
+/// other than the trait itself (like the trait methods or associated types), then we just use the regular
+/// `resolve_lifetimes`.
+fn resolve_lifetimes_for<'tcx>(tcx: TyCtxt<'tcx>, def_id: hir::OwnerId) -> &'tcx ResolveLifetimes {
+    let item_id = item_for(tcx, def_id.def_id);
+    let local_def_id = item_id.def_id.def_id;
+    if item_id.def_id == def_id {
+        let item = tcx.hir().item(item_id);
+        match item.kind {
+            hir::ItemKind::Trait(..) => tcx.resolve_lifetimes_trait_definition(local_def_id),
+            _ => tcx.resolve_lifetimes(local_def_id),
+        }
+    } else {
+        tcx.resolve_lifetimes(local_def_id)
+    }
+}
+
+/// Finds the `Item` that contains the given `LocalDefId`
+fn item_for(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> hir::ItemId {
+    match tcx.hir().find_by_def_id(local_def_id) {
+        Some(Node::Item(item)) => {
+            return item.item_id();
+        }
+        _ => {}
+    }
+    let item = {
+        let hir_id = tcx.hir().local_def_id_to_hir_id(local_def_id);
+        let mut parent_iter = tcx.hir().parent_iter(hir_id);
+        loop {
+            let node = parent_iter.next().map(|n| n.1);
+            match node {
+                Some(hir::Node::Item(item)) => break item.item_id(),
+                Some(hir::Node::Crate(_)) | None => bug!("Called `item_for` on an Item."),
+                _ => {}
+            }
+        }
+    };
+    item
+}
+
+fn late_region_as_bound_region<'tcx>(tcx: TyCtxt<'tcx>, region: &Region) -> ty::BoundVariableKind {
+    match region {
+        Region::LateBound(_, _, def_id) => {
+            let name = tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id.expect_local()));
+            ty::BoundVariableKind::Region(ty::BrNamed(*def_id, name))
+        }
+        _ => bug!("{:?} is not a late region", region),
+    }
+}
+
+impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
+    /// Returns the binders in scope and the type of `Binder` that should be created for a poly trait ref.
+    fn poly_trait_ref_binder_info(&mut self) -> (Vec<ty::BoundVariableKind>, BinderScopeType) {
+        let mut scope = self.scope;
+        let mut supertrait_lifetimes = vec![];
+        loop {
+            match scope {
+                Scope::Body { .. } | Scope::Root => {
+                    break (vec![], BinderScopeType::Normal);
+                }
+
+                Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } => {
+                    scope = s;
+                }
+
+                Scope::Supertrait { s, lifetimes } => {
+                    supertrait_lifetimes = lifetimes.clone();
+                    scope = s;
+                }
+
+                Scope::TraitRefBoundary { .. } => {
+                    // We should only see super trait lifetimes if there is a `Binder` above
+                    assert!(supertrait_lifetimes.is_empty());
+                    break (vec![], BinderScopeType::Normal);
+                }
+
+                Scope::Binder { hir_id, .. } => {
+                    // Nested poly trait refs have the binders concatenated
+                    let mut full_binders =
+                        self.map.late_bound_vars.entry(*hir_id).or_default().clone();
+                    full_binders.extend(supertrait_lifetimes.into_iter());
+                    break (full_binders, BinderScopeType::Concatenating);
+                }
+            }
+        }
+    }
+}
+impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
+    type NestedFilter = nested_filter::All;
+
+    fn nested_visit_map(&mut self) -> Self::Map {
+        self.tcx.hir()
+    }
+
+    // We want to nest trait/impl items in their parent, but nothing else.
+    fn visit_nested_item(&mut self, _: hir::ItemId) {}
+
+    fn visit_trait_item_ref(&mut self, ii: &'tcx hir::TraitItemRef) {
+        if !self.trait_definition_only {
+            intravisit::walk_trait_item_ref(self, ii)
+        }
+    }
+
+    fn visit_nested_body(&mut self, body: hir::BodyId) {
+        let body = self.tcx.hir().body(body);
+        self.with(Scope::Body { id: body.id(), s: self.scope }, |this| {
+            this.visit_body(body);
+        });
+    }
+
+    fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
+        if let hir::ExprKind::Closure(hir::Closure {
+            binder, bound_generic_params, fn_decl, ..
+        }) = e.kind
+        {
+            if let &hir::ClosureBinder::For { span: for_sp, .. } = binder {
+                fn span_of_infer(ty: &hir::Ty<'_>) -> Option<Span> {
+                    struct V(Option<Span>);
+
+                    impl<'v> Visitor<'v> for V {
+                        fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
+                            match t.kind {
+                                _ if self.0.is_some() => (),
+                                hir::TyKind::Infer => {
+                                    self.0 = Some(t.span);
+                                }
+                                _ => intravisit::walk_ty(self, t),
+                            }
+                        }
+                    }
+
+                    let mut v = V(None);
+                    v.visit_ty(ty);
+                    v.0
+                }
+
+                let infer_in_rt_sp = match fn_decl.output {
+                    hir::FnRetTy::DefaultReturn(sp) => Some(sp),
+                    hir::FnRetTy::Return(ty) => span_of_infer(ty),
+                };
+
+                let infer_spans = fn_decl
+                    .inputs
+                    .into_iter()
+                    .filter_map(span_of_infer)
+                    .chain(infer_in_rt_sp)
+                    .collect::<Vec<_>>();
+
+                if !infer_spans.is_empty() {
+                    self.tcx.sess
+                        .struct_span_err(
+                            infer_spans,
+                            "implicit types in closure signatures are forbidden when `for<...>` is present",
+                        )
+                        .span_label(for_sp, "`for<...>` is here")
+                        .emit();
+                }
+            }
+
+            let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) =
+                bound_generic_params
+                    .iter()
+                    .filter(|param| matches!(param.kind, GenericParamKind::Lifetime { .. }))
+                    .enumerate()
+                    .map(|(late_bound_idx, param)| {
+                        let pair = Region::late(late_bound_idx as u32, self.tcx.hir(), param);
+                        let r = late_region_as_bound_region(self.tcx, &pair.1);
+                        (pair, r)
+                    })
+                    .unzip();
+
+            self.record_late_bound_vars(e.hir_id, binders);
+            let scope = Scope::Binder {
+                hir_id: e.hir_id,
+                lifetimes,
+                s: self.scope,
+                scope_type: BinderScopeType::Normal,
+                where_bound_origin: None,
+            };
+
+            self.with(scope, |this| {
+                // a closure has no bounds, so everything
+                // contained within is scoped within its binder.
+                intravisit::walk_expr(this, e)
+            });
+        } else {
+            intravisit::walk_expr(self, e)
+        }
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
+        match &item.kind {
+            hir::ItemKind::Impl(hir::Impl { of_trait, .. }) => {
+                if let Some(of_trait) = of_trait {
+                    self.record_late_bound_vars(of_trait.hir_ref_id, Vec::default());
+                }
+            }
+            _ => {}
+        }
+        match item.kind {
+            hir::ItemKind::Fn(_, ref generics, _) => {
+                self.visit_early_late(item.hir_id(), generics, |this| {
+                    intravisit::walk_item(this, item);
+                });
+            }
+
+            hir::ItemKind::ExternCrate(_)
+            | hir::ItemKind::Use(..)
+            | hir::ItemKind::Macro(..)
+            | hir::ItemKind::Mod(..)
+            | hir::ItemKind::ForeignMod { .. }
+            | hir::ItemKind::GlobalAsm(..) => {
+                // These sorts of items have no lifetime parameters at all.
+                intravisit::walk_item(self, item);
+            }
+            hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => {
+                // No lifetime parameters, but implied 'static.
+                self.with(Scope::Elision { s: self.scope }, |this| {
+                    intravisit::walk_item(this, item)
+                });
+            }
+            hir::ItemKind::OpaqueTy(hir::OpaqueTy { .. }) => {
+                // Opaque types are visited when we visit the
+                // `TyKind::OpaqueDef`, so that they have the lifetimes from
+                // their parent opaque_ty in scope.
+                //
+                // The core idea here is that since OpaqueTys are generated with the impl Trait as
+                // their owner, we can keep going until we find the Item that owns that. We then
+                // conservatively add all resolved lifetimes. Otherwise we run into problems in
+                // cases like `type Foo<'a> = impl Bar<As = impl Baz + 'a>`.
+                for (_hir_id, node) in self.tcx.hir().parent_iter(item.def_id.into()) {
+                    match node {
+                        hir::Node::Item(parent_item) => {
+                            let resolved_lifetimes: &ResolveLifetimes = self.tcx.resolve_lifetimes(
+                                item_for(self.tcx, parent_item.def_id.def_id).def_id.def_id,
+                            );
+                            // We need to add *all* deps, since opaque tys may want them from *us*
+                            for (&owner, defs) in resolved_lifetimes.defs.iter() {
+                                defs.iter().for_each(|(&local_id, region)| {
+                                    self.map.defs.insert(hir::HirId { owner, local_id }, *region);
+                                });
+                            }
+                            for (&owner, late_bound_vars) in
+                                resolved_lifetimes.late_bound_vars.iter()
+                            {
+                                late_bound_vars.iter().for_each(|(&local_id, late_bound_vars)| {
+                                    self.record_late_bound_vars(
+                                        hir::HirId { owner, local_id },
+                                        late_bound_vars.clone(),
+                                    );
+                                });
+                            }
+                            break;
+                        }
+                        hir::Node::Crate(_) => bug!("No Item about an OpaqueTy"),
+                        _ => {}
+                    }
+                }
+            }
+            hir::ItemKind::TyAlias(_, ref generics)
+            | hir::ItemKind::Enum(_, ref generics)
+            | hir::ItemKind::Struct(_, ref generics)
+            | hir::ItemKind::Union(_, ref generics)
+            | hir::ItemKind::Trait(_, _, ref generics, ..)
+            | hir::ItemKind::TraitAlias(ref generics, ..)
+            | hir::ItemKind::Impl(hir::Impl { ref generics, .. }) => {
+                // These kinds of items have only early-bound lifetime parameters.
+                let lifetimes = generics
+                    .params
+                    .iter()
+                    .filter_map(|param| match param.kind {
+                        GenericParamKind::Lifetime { .. } => {
+                            Some(Region::early(self.tcx.hir(), param))
+                        }
+                        GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None,
+                    })
+                    .collect();
+                self.record_late_bound_vars(item.hir_id(), vec![]);
+                let scope = Scope::Binder {
+                    hir_id: item.hir_id(),
+                    lifetimes,
+                    scope_type: BinderScopeType::Normal,
+                    s: ROOT_SCOPE,
+                    where_bound_origin: None,
+                };
+                self.with(scope, |this| {
+                    let scope = Scope::TraitRefBoundary { s: this.scope };
+                    this.with(scope, |this| {
+                        intravisit::walk_item(this, item);
+                    });
+                });
+            }
+        }
+    }
+
+    fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
+        match item.kind {
+            hir::ForeignItemKind::Fn(_, _, ref generics) => {
+                self.visit_early_late(item.hir_id(), generics, |this| {
+                    intravisit::walk_foreign_item(this, item);
+                })
+            }
+            hir::ForeignItemKind::Static(..) => {
+                intravisit::walk_foreign_item(self, item);
+            }
+            hir::ForeignItemKind::Type => {
+                intravisit::walk_foreign_item(self, item);
+            }
+        }
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
+        match ty.kind {
+            hir::TyKind::BareFn(ref c) => {
+                let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) = c
+                    .generic_params
+                    .iter()
+                    .filter(|param| matches!(param.kind, GenericParamKind::Lifetime { .. }))
+                    .enumerate()
+                    .map(|(late_bound_idx, param)| {
+                        let pair = Region::late(late_bound_idx as u32, self.tcx.hir(), param);
+                        let r = late_region_as_bound_region(self.tcx, &pair.1);
+                        (pair, r)
+                    })
+                    .unzip();
+                self.record_late_bound_vars(ty.hir_id, binders);
+                let scope = Scope::Binder {
+                    hir_id: ty.hir_id,
+                    lifetimes,
+                    s: self.scope,
+                    scope_type: BinderScopeType::Normal,
+                    where_bound_origin: None,
+                };
+                self.with(scope, |this| {
+                    // a bare fn has no bounds, so everything
+                    // contained within is scoped within its binder.
+                    intravisit::walk_ty(this, ty);
+                });
+            }
+            hir::TyKind::TraitObject(bounds, ref lifetime, _) => {
+                debug!(?bounds, ?lifetime, "TraitObject");
+                let scope = Scope::TraitRefBoundary { s: self.scope };
+                self.with(scope, |this| {
+                    for bound in bounds {
+                        this.visit_poly_trait_ref(bound);
+                    }
+                });
+                match lifetime.name {
+                    LifetimeName::ImplicitObjectLifetimeDefault => {
+                        // If the user does not write *anything*, we
+                        // use the object lifetime defaulting
+                        // rules. So e.g., `Box<dyn Debug>` becomes
+                        // `Box<dyn Debug + 'static>`.
+                        self.resolve_object_lifetime_default(lifetime)
+                    }
+                    LifetimeName::Infer => {
+                        // If the user writes `'_`, we use the *ordinary* elision
+                        // rules. So the `'_` in e.g., `Box<dyn Debug + '_>` will be
+                        // resolved the same as the `'_` in `&'_ Foo`.
+                        //
+                        // cc #48468
+                    }
+                    LifetimeName::Param(..) | LifetimeName::Static => {
+                        // If the user wrote an explicit name, use that.
+                        self.visit_lifetime(lifetime);
+                    }
+                    LifetimeName::Error => {}
+                }
+            }
+            hir::TyKind::Rptr(ref lifetime_ref, ref mt) => {
+                self.visit_lifetime(lifetime_ref);
+                let scope = Scope::ObjectLifetimeDefault {
+                    lifetime: self.map.defs.get(&lifetime_ref.hir_id).cloned(),
+                    s: self.scope,
+                };
+                self.with(scope, |this| this.visit_ty(&mt.ty));
+            }
+            hir::TyKind::OpaqueDef(item_id, lifetimes, _in_trait) => {
+                // Resolve the lifetimes in the bounds to the lifetime defs in the generics.
+                // `fn foo<'a>() -> impl MyTrait<'a> { ... }` desugars to
+                // `type MyAnonTy<'b> = impl MyTrait<'b>;`
+                //                 ^                  ^ this gets resolved in the scope of
+                //                                      the opaque_ty generics
+                let opaque_ty = self.tcx.hir().item(item_id);
+                let (generics, bounds) = match opaque_ty.kind {
+                    hir::ItemKind::OpaqueTy(hir::OpaqueTy {
+                        origin: hir::OpaqueTyOrigin::TyAlias,
+                        ..
+                    }) => {
+                        intravisit::walk_ty(self, ty);
+
+                        // Elided lifetimes are not allowed in non-return
+                        // position impl Trait
+                        let scope = Scope::TraitRefBoundary { s: self.scope };
+                        self.with(scope, |this| {
+                            let scope = Scope::Elision { s: this.scope };
+                            this.with(scope, |this| {
+                                intravisit::walk_item(this, opaque_ty);
+                            })
+                        });
+
+                        return;
+                    }
+                    hir::ItemKind::OpaqueTy(hir::OpaqueTy {
+                        origin: hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..),
+                        ref generics,
+                        bounds,
+                        ..
+                    }) => (generics, bounds),
+                    ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i),
+                };
+
+                // Resolve the lifetimes that are applied to the opaque type.
+                // These are resolved in the current scope.
+                // `fn foo<'a>() -> impl MyTrait<'a> { ... }` desugars to
+                // `fn foo<'a>() -> MyAnonTy<'a> { ... }`
+                //          ^                 ^this gets resolved in the current scope
+                for lifetime in lifetimes {
+                    let hir::GenericArg::Lifetime(lifetime) = lifetime else {
+                        continue
+                    };
+                    self.visit_lifetime(lifetime);
+
+                    // Check for predicates like `impl for<'a> Trait<impl OtherTrait<'a>>`
+                    // and ban them. Type variables instantiated inside binders aren't
+                    // well-supported at the moment, so this doesn't work.
+                    // In the future, this should be fixed and this error should be removed.
+                    let def = self.map.defs.get(&lifetime.hir_id).cloned();
+                    let Some(Region::LateBound(_, _, def_id)) = def else {
+                        continue
+                    };
+                    let Some(def_id) = def_id.as_local() else {
+                        continue
+                    };
+                    let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
+                    // Ensure that the parent of the def is an item, not HRTB
+                    let parent_id = self.tcx.hir().get_parent_node(hir_id);
+                    if !parent_id.is_owner() {
+                        if !self.trait_definition_only {
+                            struct_span_err!(
+                                self.tcx.sess,
+                                lifetime.span,
+                                E0657,
+                                "`impl Trait` can only capture lifetimes \
+                                    bound at the fn or impl level"
+                            )
+                            .emit();
+                        }
+                        self.uninsert_lifetime_on_error(lifetime, def.unwrap());
+                    }
+                    if let hir::Node::Item(hir::Item {
+                        kind: hir::ItemKind::OpaqueTy { .. }, ..
+                    }) = self.tcx.hir().get(parent_id)
+                    {
+                        if !self.trait_definition_only {
+                            let mut err = self.tcx.sess.struct_span_err(
+                                lifetime.span,
+                                "higher kinded lifetime bounds on nested opaque types are not supported yet",
+                            );
+                            err.span_note(self.tcx.def_span(def_id), "lifetime declared here");
+                            err.emit();
+                        }
+                        self.uninsert_lifetime_on_error(lifetime, def.unwrap());
+                    }
+                }
+
+                // We want to start our early-bound indices at the end of the parent scope,
+                // not including any parent `impl Trait`s.
+                let mut lifetimes = FxIndexMap::default();
+                debug!(?generics.params);
+                for param in generics.params {
+                    match param.kind {
+                        GenericParamKind::Lifetime { .. } => {
+                            let (def_id, reg) = Region::early(self.tcx.hir(), &param);
+                            lifetimes.insert(def_id, reg);
+                        }
+                        GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {}
+                    }
+                }
+                self.record_late_bound_vars(ty.hir_id, vec![]);
+
+                let scope = Scope::Binder {
+                    hir_id: ty.hir_id,
+                    lifetimes,
+                    s: self.scope,
+                    scope_type: BinderScopeType::Normal,
+                    where_bound_origin: None,
+                };
+                self.with(scope, |this| {
+                    let scope = Scope::TraitRefBoundary { s: this.scope };
+                    this.with(scope, |this| {
+                        this.visit_generics(generics);
+                        for bound in bounds {
+                            this.visit_param_bound(bound);
+                        }
+                    })
+                });
+            }
+            _ => intravisit::walk_ty(self, ty),
+        }
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
+        use self::hir::TraitItemKind::*;
+        match trait_item.kind {
+            Fn(_, _) => {
+                self.visit_early_late(trait_item.hir_id(), &trait_item.generics, |this| {
+                    intravisit::walk_trait_item(this, trait_item)
+                });
+            }
+            Type(bounds, ref ty) => {
+                let generics = &trait_item.generics;
+                let lifetimes = generics
+                    .params
+                    .iter()
+                    .filter_map(|param| match param.kind {
+                        GenericParamKind::Lifetime { .. } => {
+                            Some(Region::early(self.tcx.hir(), param))
+                        }
+                        GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None,
+                    })
+                    .collect();
+                self.record_late_bound_vars(trait_item.hir_id(), vec![]);
+                let scope = Scope::Binder {
+                    hir_id: trait_item.hir_id(),
+                    lifetimes,
+                    s: self.scope,
+                    scope_type: BinderScopeType::Normal,
+                    where_bound_origin: None,
+                };
+                self.with(scope, |this| {
+                    let scope = Scope::TraitRefBoundary { s: this.scope };
+                    this.with(scope, |this| {
+                        this.visit_generics(generics);
+                        for bound in bounds {
+                            this.visit_param_bound(bound);
+                        }
+                        if let Some(ty) = ty {
+                            this.visit_ty(ty);
+                        }
+                    })
+                });
+            }
+            Const(_, _) => {
+                // Only methods and types support generics.
+                assert!(trait_item.generics.params.is_empty());
+                intravisit::walk_trait_item(self, trait_item);
+            }
+        }
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
+        use self::hir::ImplItemKind::*;
+        match impl_item.kind {
+            Fn(..) => self.visit_early_late(impl_item.hir_id(), &impl_item.generics, |this| {
+                intravisit::walk_impl_item(this, impl_item)
+            }),
+            Type(ref ty) => {
+                let generics = &impl_item.generics;
+                let lifetimes: FxIndexMap<LocalDefId, Region> = generics
+                    .params
+                    .iter()
+                    .filter_map(|param| match param.kind {
+                        GenericParamKind::Lifetime { .. } => {
+                            Some(Region::early(self.tcx.hir(), param))
+                        }
+                        GenericParamKind::Const { .. } | GenericParamKind::Type { .. } => None,
+                    })
+                    .collect();
+                self.record_late_bound_vars(impl_item.hir_id(), vec![]);
+                let scope = Scope::Binder {
+                    hir_id: impl_item.hir_id(),
+                    lifetimes,
+                    s: self.scope,
+                    scope_type: BinderScopeType::Normal,
+                    where_bound_origin: None,
+                };
+                self.with(scope, |this| {
+                    let scope = Scope::TraitRefBoundary { s: this.scope };
+                    this.with(scope, |this| {
+                        this.visit_generics(generics);
+                        this.visit_ty(ty);
+                    })
+                });
+            }
+            Const(_, _) => {
+                // Only methods and types support generics.
+                assert!(impl_item.generics.params.is_empty());
+                intravisit::walk_impl_item(self, impl_item);
+            }
+        }
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
+        match lifetime_ref.name {
+            hir::LifetimeName::Static => self.insert_lifetime(lifetime_ref, Region::Static),
+            hir::LifetimeName::Param(param_def_id, _) => {
+                self.resolve_lifetime_ref(param_def_id, lifetime_ref)
+            }
+            // If we've already reported an error, just ignore `lifetime_ref`.
+            hir::LifetimeName::Error => {}
+            // Those will be resolved by typechecking.
+            hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Infer => {}
+        }
+    }
+
+    fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
+        for (i, segment) in path.segments.iter().enumerate() {
+            let depth = path.segments.len() - i - 1;
+            if let Some(ref args) = segment.args {
+                self.visit_segment_args(path.res, depth, args);
+            }
+        }
+    }
+
+    fn visit_fn(
+        &mut self,
+        fk: intravisit::FnKind<'tcx>,
+        fd: &'tcx hir::FnDecl<'tcx>,
+        body_id: hir::BodyId,
+        _: Span,
+        _: hir::HirId,
+    ) {
+        let output = match fd.output {
+            hir::FnRetTy::DefaultReturn(_) => None,
+            hir::FnRetTy::Return(ref ty) => Some(&**ty),
+        };
+        self.visit_fn_like_elision(&fd.inputs, output, matches!(fk, intravisit::FnKind::Closure));
+        intravisit::walk_fn_kind(self, fk);
+        self.visit_nested_body(body_id)
+    }
+
+    fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) {
+        let scope = Scope::TraitRefBoundary { s: self.scope };
+        self.with(scope, |this| {
+            for param in generics.params {
+                match param.kind {
+                    GenericParamKind::Lifetime { .. } => {}
+                    GenericParamKind::Type { ref default, .. } => {
+                        if let Some(ref ty) = default {
+                            this.visit_ty(&ty);
+                        }
+                    }
+                    GenericParamKind::Const { ref ty, default } => {
+                        this.visit_ty(&ty);
+                        if let Some(default) = default {
+                            this.visit_body(this.tcx.hir().body(default.body));
+                        }
+                    }
+                }
+            }
+            for predicate in generics.predicates {
+                match predicate {
+                    &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
+                        hir_id,
+                        ref bounded_ty,
+                        bounds,
+                        ref bound_generic_params,
+                        origin,
+                        ..
+                    }) => {
+                        let lifetimes: FxIndexMap<LocalDefId, Region> =
+                            bound_generic_params
+                                .iter()
+                                .filter(|param| {
+                                    matches!(param.kind, GenericParamKind::Lifetime { .. })
+                                })
+                                .enumerate()
+                                .map(|(late_bound_idx, param)| {
+                                        Region::late(late_bound_idx as u32, this.tcx.hir(), param)
+                                })
+                                .collect();
+                        let binders: Vec<_> =
+                            lifetimes
+                                .iter()
+                                .map(|(_, region)| {
+                                     late_region_as_bound_region(this.tcx, region)
+                                })
+                                .collect();
+                        this.record_late_bound_vars(hir_id, binders.clone());
+                        // Even if there are no lifetimes defined here, we still wrap it in a binder
+                        // scope. If there happens to be a nested poly trait ref (an error), that
+                        // will be `Concatenating` anyways, so we don't have to worry about the depth
+                        // being wrong.
+                        let scope = Scope::Binder {
+                            hir_id,
+                            lifetimes,
+                            s: this.scope,
+                            scope_type: BinderScopeType::Normal,
+                            where_bound_origin: Some(origin),
+                        };
+                        this.with(scope, |this| {
+                            this.visit_ty(&bounded_ty);
+                            walk_list!(this, visit_param_bound, bounds);
+                        })
+                    }
+                    &hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate {
+                        ref lifetime,
+                        bounds,
+                        ..
+                    }) => {
+                        this.visit_lifetime(lifetime);
+                        walk_list!(this, visit_param_bound, bounds);
+
+                        if lifetime.name != hir::LifetimeName::Static {
+                            for bound in bounds {
+                                let hir::GenericBound::Outlives(ref lt) = bound else {
+                                    continue;
+                                };
+                                if lt.name != hir::LifetimeName::Static {
+                                    continue;
+                                }
+                                this.insert_lifetime(lt, Region::Static);
+                                this.tcx
+                                    .sess
+                                    .struct_span_warn(
+                                        lifetime.span,
+                                        &format!(
+                                            "unnecessary lifetime parameter `{}`",
+                                            lifetime.name.ident(),
+                                        ),
+                                    )
+                                    .help(&format!(
+                                        "you can use the `'static` lifetime directly, in place of `{}`",
+                                        lifetime.name.ident(),
+                                    ))
+                                    .emit();
+                            }
+                        }
+                    }
+                    &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate {
+                        ref lhs_ty,
+                        ref rhs_ty,
+                        ..
+                    }) => {
+                        this.visit_ty(lhs_ty);
+                        this.visit_ty(rhs_ty);
+                    }
+                }
+            }
+        })
+    }
+
+    fn visit_param_bound(&mut self, bound: &'tcx hir::GenericBound<'tcx>) {
+        match bound {
+            hir::GenericBound::LangItemTrait(_, _, hir_id, _) => {
+                // FIXME(jackh726): This is pretty weird. `LangItemTrait` doesn't go
+                // through the regular poly trait ref code, so we don't get another
+                // chance to introduce a binder. For now, I'm keeping the existing logic
+                // of "if there isn't a Binder scope above us, add one", but I
+                // imagine there's a better way to go about this.
+                let (binders, scope_type) = self.poly_trait_ref_binder_info();
+
+                self.record_late_bound_vars(*hir_id, binders);
+                let scope = Scope::Binder {
+                    hir_id: *hir_id,
+                    lifetimes: FxIndexMap::default(),
+                    s: self.scope,
+                    scope_type,
+                    where_bound_origin: None,
+                };
+                self.with(scope, |this| {
+                    intravisit::walk_param_bound(this, bound);
+                });
+            }
+            _ => intravisit::walk_param_bound(self, bound),
+        }
+    }
+
+    fn visit_poly_trait_ref(&mut self, trait_ref: &'tcx hir::PolyTraitRef<'tcx>) {
+        debug!("visit_poly_trait_ref(trait_ref={:?})", trait_ref);
+
+        let (mut binders, scope_type) = self.poly_trait_ref_binder_info();
+
+        let initial_bound_vars = binders.len() as u32;
+        let mut lifetimes: FxIndexMap<LocalDefId, Region> = FxIndexMap::default();
+        let binders_iter = trait_ref
+            .bound_generic_params
+            .iter()
+            .filter(|param| matches!(param.kind, GenericParamKind::Lifetime { .. }))
+            .enumerate()
+            .map(|(late_bound_idx, param)| {
+                let pair =
+                    Region::late(initial_bound_vars + late_bound_idx as u32, self.tcx.hir(), param);
+                let r = late_region_as_bound_region(self.tcx, &pair.1);
+                lifetimes.insert(pair.0, pair.1);
+                r
+            });
+        binders.extend(binders_iter);
+
+        debug!(?binders);
+        self.record_late_bound_vars(trait_ref.trait_ref.hir_ref_id, binders);
+
+        // Always introduce a scope here, even if this is in a where clause and
+        // we introduced the binders around the bounded Ty. In that case, we
+        // just reuse the concatenation functionality also present in nested trait
+        // refs.
+        let scope = Scope::Binder {
+            hir_id: trait_ref.trait_ref.hir_ref_id,
+            lifetimes,
+            s: self.scope,
+            scope_type,
+            where_bound_origin: None,
+        };
+        self.with(scope, |this| {
+            walk_list!(this, visit_generic_param, trait_ref.bound_generic_params);
+            this.visit_trait_ref(&trait_ref.trait_ref);
+        });
+    }
+}
+
+fn object_lifetime_default<'tcx>(tcx: TyCtxt<'tcx>, param_def_id: DefId) -> ObjectLifetimeDefault {
+    debug_assert_eq!(tcx.def_kind(param_def_id), DefKind::TyParam);
+    let param_def_id = param_def_id.expect_local();
+    let parent_def_id = tcx.local_parent(param_def_id);
+    let generics = tcx.hir().get_generics(parent_def_id).unwrap();
+    let param_hir_id = tcx.local_def_id_to_hir_id(param_def_id);
+    let param = generics.params.iter().find(|p| p.hir_id == param_hir_id).unwrap();
+
+    // Scan the bounds and where-clauses on parameters to extract bounds
+    // of the form `T:'a` so as to determine the `ObjectLifetimeDefault`
+    // for each type parameter.
+    match param.kind {
+        GenericParamKind::Type { .. } => {
+            let mut set = Set1::Empty;
+
+            // Look for `type: ...` where clauses.
+            for bound in generics.bounds_for_param(param_def_id) {
+                // Ignore `for<'a> type: ...` as they can change what
+                // lifetimes mean (although we could "just" handle it).
+                if !bound.bound_generic_params.is_empty() {
+                    continue;
+                }
+
+                for bound in bound.bounds {
+                    if let hir::GenericBound::Outlives(ref lifetime) = *bound {
+                        set.insert(lifetime.name.normalize_to_macros_2_0());
+                    }
+                }
+            }
+
+            match set {
+                Set1::Empty => ObjectLifetimeDefault::Empty,
+                Set1::One(hir::LifetimeName::Static) => ObjectLifetimeDefault::Static,
+                Set1::One(hir::LifetimeName::Param(param_def_id, _)) => {
+                    ObjectLifetimeDefault::Param(param_def_id.to_def_id())
+                }
+                _ => ObjectLifetimeDefault::Ambiguous,
+            }
+        }
+        _ => {
+            bug!("object_lifetime_default_raw must only be called on a type parameter")
+        }
+    }
+}
+
+impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
+    fn with<F>(&mut self, wrap_scope: Scope<'_>, f: F)
+    where
+        F: for<'b> FnOnce(&mut LifetimeContext<'b, 'tcx>),
+    {
+        let LifetimeContext { tcx, map, .. } = self;
+        let mut this = LifetimeContext {
+            tcx: *tcx,
+            map,
+            scope: &wrap_scope,
+            trait_definition_only: self.trait_definition_only,
+        };
+        let span = debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope));
+        {
+            let _enter = span.enter();
+            f(&mut this);
+        }
+    }
+
+    fn record_late_bound_vars(&mut self, hir_id: hir::HirId, binder: Vec<ty::BoundVariableKind>) {
+        if let Some(old) = self.map.late_bound_vars.insert(hir_id, binder) {
+            bug!(
+                "overwrote bound vars for {hir_id:?}:\nold={old:?}\nnew={:?}",
+                self.map.late_bound_vars[&hir_id]
+            )
+        }
+    }
+
+    /// Visits self by adding a scope and handling recursive walk over the contents with `walk`.
+    ///
+    /// Handles visiting fns and methods. These are a bit complicated because we must distinguish
+    /// early- vs late-bound lifetime parameters. We do this by checking which lifetimes appear
+    /// within type bounds; those are early bound lifetimes, and the rest are late bound.
+    ///
+    /// For example:
+    ///
+    ///    fn foo<'a,'b,'c,T:Trait<'b>>(...)
+    ///
+    /// Here `'a` and `'c` are late bound but `'b` is early bound. Note that early- and late-bound
+    /// lifetimes may be interspersed together.
+    ///
+    /// If early bound lifetimes are present, we separate them into their own list (and likewise
+    /// for late bound). They will be numbered sequentially, starting from the lowest index that is
+    /// already in scope (for a fn item, that will be 0, but for a method it might not be). Late
+    /// bound lifetimes are resolved by name and associated with a binder ID (`binder_id`), so the
+    /// ordering is not important there.
+    fn visit_early_late<F>(
+        &mut self,
+        hir_id: hir::HirId,
+        generics: &'tcx hir::Generics<'tcx>,
+        walk: F,
+    ) where
+        F: for<'b, 'c> FnOnce(&'b mut LifetimeContext<'c, 'tcx>),
+    {
+        let mut named_late_bound_vars = 0;
+        let lifetimes: FxIndexMap<LocalDefId, Region> = generics
+            .params
+            .iter()
+            .filter_map(|param| match param.kind {
+                GenericParamKind::Lifetime { .. } => {
+                    if self.tcx.is_late_bound(param.hir_id) {
+                        let late_bound_idx = named_late_bound_vars;
+                        named_late_bound_vars += 1;
+                        Some(Region::late(late_bound_idx, self.tcx.hir(), param))
+                    } else {
+                        Some(Region::early(self.tcx.hir(), param))
+                    }
+                }
+                GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None,
+            })
+            .collect();
+
+        let binders: Vec<_> = generics
+            .params
+            .iter()
+            .filter(|param| {
+                matches!(param.kind, GenericParamKind::Lifetime { .. })
+                    && self.tcx.is_late_bound(param.hir_id)
+            })
+            .enumerate()
+            .map(|(late_bound_idx, param)| {
+                let pair = Region::late(late_bound_idx as u32, self.tcx.hir(), param);
+                late_region_as_bound_region(self.tcx, &pair.1)
+            })
+            .collect();
+        self.record_late_bound_vars(hir_id, binders);
+        let scope = Scope::Binder {
+            hir_id,
+            lifetimes,
+            s: self.scope,
+            scope_type: BinderScopeType::Normal,
+            where_bound_origin: None,
+        };
+        self.with(scope, walk);
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn resolve_lifetime_ref(
+        &mut self,
+        region_def_id: LocalDefId,
+        lifetime_ref: &'tcx hir::Lifetime,
+    ) {
+        // Walk up the scope chain, tracking the number of fn scopes
+        // that we pass through, until we find a lifetime with the
+        // given name or we run out of scopes.
+        // search.
+        let mut late_depth = 0;
+        let mut scope = self.scope;
+        let mut outermost_body = None;
+        let result = loop {
+            match *scope {
+                Scope::Body { id, s } => {
+                    outermost_body = Some(id);
+                    scope = s;
+                }
+
+                Scope::Root => {
+                    break None;
+                }
+
+                Scope::Binder { ref lifetimes, scope_type, s, where_bound_origin, .. } => {
+                    if let Some(&def) = lifetimes.get(&region_def_id) {
+                        break Some(def.shifted(late_depth));
+                    }
+                    match scope_type {
+                        BinderScopeType::Normal => late_depth += 1,
+                        BinderScopeType::Concatenating => {}
+                    }
+                    // Fresh lifetimes in APIT used to be allowed in async fns and forbidden in
+                    // regular fns.
+                    if let Some(hir::PredicateOrigin::ImplTrait) = where_bound_origin
+                        && let hir::LifetimeName::Param(_, hir::ParamName::Fresh) = lifetime_ref.name
+                        && let hir::IsAsync::NotAsync = self.tcx.asyncness(lifetime_ref.hir_id.owner.def_id)
+                        && !self.tcx.features().anonymous_lifetime_in_impl_trait
+                    {
+                        let mut diag =  rustc_session::parse::feature_err(
+                            &self.tcx.sess.parse_sess,
+                            sym::anonymous_lifetime_in_impl_trait,
+                            lifetime_ref.span,
+                            "anonymous lifetimes in `impl Trait` are unstable",
+                        );
+
+                        match self.tcx.hir().get_generics(lifetime_ref.hir_id.owner.def_id) {
+                            Some(generics) => {
+
+                                let new_param_sugg_tuple;
+
+                                new_param_sugg_tuple = match generics.span_for_param_suggestion() {
+                                    Some(_) => {
+                                        Some((self.tcx.sess.source_map().span_through_char(generics.span, '<').shrink_to_hi(), "'a, ".to_owned()))
+                                    },
+                                    None => Some((generics.span, "<'a>".to_owned()))
+                                };
+
+                                let mut multi_sugg_vec = vec![(lifetime_ref.span.shrink_to_hi(), "'a ".to_owned())];
+
+                                if let Some(new_tuple) =  new_param_sugg_tuple{
+                                    multi_sugg_vec.push(new_tuple);
+                                }
+
+                                diag.span_label(lifetime_ref.span, "expected named lifetime parameter");
+                                diag.multipart_suggestion("consider introducing a named lifetime parameter",
+                                multi_sugg_vec,
+                                rustc_errors::Applicability::MaybeIncorrect);
+
+                            },
+                            None => { }
+                        }
+
+                        diag.emit();
+                        return;
+                    }
+                    scope = s;
+                }
+
+                Scope::Elision { s, .. }
+                | Scope::ObjectLifetimeDefault { s, .. }
+                | Scope::Supertrait { s, .. }
+                | Scope::TraitRefBoundary { s, .. } => {
+                    scope = s;
+                }
+            }
+        };
+
+        if let Some(mut def) = result {
+            if let Region::EarlyBound(..) = def {
+                // Do not free early-bound regions, only late-bound ones.
+            } else if let Some(body_id) = outermost_body {
+                let fn_id = self.tcx.hir().body_owner(body_id);
+                match self.tcx.hir().get(fn_id) {
+                    Node::Item(&hir::Item { kind: hir::ItemKind::Fn(..), .. })
+                    | Node::TraitItem(&hir::TraitItem {
+                        kind: hir::TraitItemKind::Fn(..), ..
+                    })
+                    | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) => {
+                        let scope = self.tcx.hir().local_def_id(fn_id);
+                        def = Region::Free(scope.to_def_id(), def.id().unwrap());
+                    }
+                    _ => {}
+                }
+            }
+
+            self.insert_lifetime(lifetime_ref, def);
+            return;
+        }
+
+        // We may fail to resolve higher-ranked lifetimes that are mentioned by APIT.
+        // AST-based resolution does not care for impl-trait desugaring, which are the
+        // responibility of lowering.  This may create a mismatch between the resolution
+        // AST found (`region_def_id`) which points to HRTB, and what HIR allows.
+        // ```
+        // fn foo(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
+        // ```
+        //
+        // In such case, walk back the binders to diagnose it properly.
+        let mut scope = self.scope;
+        loop {
+            match *scope {
+                Scope::Binder {
+                    where_bound_origin: Some(hir::PredicateOrigin::ImplTrait), ..
+                } => {
+                    let mut err = self.tcx.sess.struct_span_err(
+                        lifetime_ref.span,
+                        "`impl Trait` can only mention lifetimes bound at the fn or impl level",
+                    );
+                    err.span_note(self.tcx.def_span(region_def_id), "lifetime declared here");
+                    err.emit();
+                    return;
+                }
+                Scope::Root => break,
+                Scope::Binder { s, .. }
+                | Scope::Body { s, .. }
+                | Scope::Elision { s, .. }
+                | Scope::ObjectLifetimeDefault { s, .. }
+                | Scope::Supertrait { s, .. }
+                | Scope::TraitRefBoundary { s, .. } => {
+                    scope = s;
+                }
+            }
+        }
+
+        self.tcx.sess.delay_span_bug(
+            lifetime_ref.span,
+            &format!("Could not resolve {:?} in scope {:#?}", lifetime_ref, self.scope,),
+        );
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn visit_segment_args(
+        &mut self,
+        res: Res,
+        depth: usize,
+        generic_args: &'tcx hir::GenericArgs<'tcx>,
+    ) {
+        if generic_args.parenthesized {
+            self.visit_fn_like_elision(
+                generic_args.inputs(),
+                Some(generic_args.bindings[0].ty()),
+                false,
+            );
+            return;
+        }
+
+        for arg in generic_args.args {
+            if let hir::GenericArg::Lifetime(lt) = arg {
+                self.visit_lifetime(lt);
+            }
+        }
+
+        // Figure out if this is a type/trait segment,
+        // which requires object lifetime defaults.
+        let type_def_id = match res {
+            Res::Def(DefKind::AssocTy, def_id) if depth == 1 => Some(self.tcx.parent(def_id)),
+            Res::Def(DefKind::Variant, def_id) if depth == 0 => Some(self.tcx.parent(def_id)),
+            Res::Def(
+                DefKind::Struct
+                | DefKind::Union
+                | DefKind::Enum
+                | DefKind::TyAlias
+                | DefKind::Trait,
+                def_id,
+            ) if depth == 0 => Some(def_id),
+            _ => None,
+        };
+
+        debug!(?type_def_id);
+
+        // Compute a vector of defaults, one for each type parameter,
+        // per the rules given in RFCs 599 and 1156. Example:
+        //
+        // ```rust
+        // struct Foo<'a, T: 'a, U> { }
+        // ```
+        //
+        // If you have `Foo<'x, dyn Bar, dyn Baz>`, we want to default
+        // `dyn Bar` to `dyn Bar + 'x` (because of the `T: 'a` bound)
+        // and `dyn Baz` to `dyn Baz + 'static` (because there is no
+        // such bound).
+        //
+        // Therefore, we would compute `object_lifetime_defaults` to a
+        // vector like `['x, 'static]`. Note that the vector only
+        // includes type parameters.
+        let object_lifetime_defaults = type_def_id.map_or_else(Vec::new, |def_id| {
+            let in_body = {
+                let mut scope = self.scope;
+                loop {
+                    match *scope {
+                        Scope::Root => break false,
+
+                        Scope::Body { .. } => break true,
+
+                        Scope::Binder { s, .. }
+                        | Scope::Elision { s, .. }
+                        | Scope::ObjectLifetimeDefault { s, .. }
+                        | Scope::Supertrait { s, .. }
+                        | Scope::TraitRefBoundary { s, .. } => {
+                            scope = s;
+                        }
+                    }
+                }
+            };
+
+            let map = &self.map;
+            let generics = self.tcx.generics_of(def_id);
+
+            // `type_def_id` points to an item, so there is nothing to inherit generics from.
+            debug_assert_eq!(generics.parent_count, 0);
+
+            let set_to_region = |set: ObjectLifetimeDefault| match set {
+                ObjectLifetimeDefault::Empty => {
+                    if in_body {
+                        None
+                    } else {
+                        Some(Region::Static)
+                    }
+                }
+                ObjectLifetimeDefault::Static => Some(Region::Static),
+                ObjectLifetimeDefault::Param(param_def_id) => {
+                    // This index can be used with `generic_args` since `parent_count == 0`.
+                    let index = generics.param_def_id_to_index[&param_def_id] as usize;
+                    generic_args.args.get(index).and_then(|arg| match arg {
+                        GenericArg::Lifetime(lt) => map.defs.get(&lt.hir_id).copied(),
+                        _ => None,
+                    })
+                }
+                ObjectLifetimeDefault::Ambiguous => None,
+            };
+            generics
+                .params
+                .iter()
+                .filter_map(|param| {
+                    match self.tcx.def_kind(param.def_id) {
+                        // Generic consts don't impose any constraints.
+                        //
+                        // We still store a dummy value here to allow generic parameters
+                        // in an arbitrary order.
+                        DefKind::ConstParam => Some(ObjectLifetimeDefault::Empty),
+                        DefKind::TyParam => Some(self.tcx.object_lifetime_default(param.def_id)),
+                        // We may also get a `Trait` or `TraitAlias` because of how generics `Self` parameter
+                        // works.  Ignore it because it can't have a meaningful lifetime default.
+                        DefKind::LifetimeParam | DefKind::Trait | DefKind::TraitAlias => None,
+                        dk => bug!("unexpected def_kind {:?}", dk),
+                    }
+                })
+                .map(set_to_region)
+                .collect()
+        });
+
+        debug!(?object_lifetime_defaults);
+
+        let mut i = 0;
+        for arg in generic_args.args {
+            match arg {
+                GenericArg::Lifetime(_) => {}
+                GenericArg::Type(ty) => {
+                    if let Some(&lt) = object_lifetime_defaults.get(i) {
+                        let scope = Scope::ObjectLifetimeDefault { lifetime: lt, s: self.scope };
+                        self.with(scope, |this| this.visit_ty(ty));
+                    } else {
+                        self.visit_ty(ty);
+                    }
+                    i += 1;
+                }
+                GenericArg::Const(ct) => {
+                    self.visit_anon_const(&ct.value);
+                    i += 1;
+                }
+                GenericArg::Infer(inf) => {
+                    self.visit_id(inf.hir_id);
+                    i += 1;
+                }
+            }
+        }
+
+        // Hack: when resolving the type `XX` in binding like `dyn
+        // Foo<'b, Item = XX>`, the current object-lifetime default
+        // would be to examine the trait `Foo` to check whether it has
+        // a lifetime bound declared on `Item`. e.g., if `Foo` is
+        // declared like so, then the default object lifetime bound in
+        // `XX` should be `'b`:
+        //
+        // ```rust
+        // trait Foo<'a> {
+        //   type Item: 'a;
+        // }
+        // ```
+        //
+        // but if we just have `type Item;`, then it would be
+        // `'static`. However, we don't get all of this logic correct.
+        //
+        // Instead, we do something hacky: if there are no lifetime parameters
+        // to the trait, then we simply use a default object lifetime
+        // bound of `'static`, because there is no other possibility. On the other hand,
+        // if there ARE lifetime parameters, then we require the user to give an
+        // explicit bound for now.
+        //
+        // This is intended to leave room for us to implement the
+        // correct behavior in the future.
+        let has_lifetime_parameter =
+            generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)));
+
+        // Resolve lifetimes found in the bindings, so either in the type `XX` in `Item = XX` or
+        // in the trait ref `YY<...>` in `Item: YY<...>`.
+        for binding in generic_args.bindings {
+            let scope = Scope::ObjectLifetimeDefault {
+                lifetime: if has_lifetime_parameter { None } else { Some(Region::Static) },
+                s: self.scope,
+            };
+            if let Some(type_def_id) = type_def_id {
+                let lifetimes = LifetimeContext::supertrait_hrtb_lifetimes(
+                    self.tcx,
+                    type_def_id,
+                    binding.ident,
+                );
+                self.with(scope, |this| {
+                    let scope = Scope::Supertrait {
+                        lifetimes: lifetimes.unwrap_or_default(),
+                        s: this.scope,
+                    };
+                    this.with(scope, |this| this.visit_assoc_type_binding(binding));
+                });
+            } else {
+                self.with(scope, |this| this.visit_assoc_type_binding(binding));
+            }
+        }
+    }
+
+    /// Returns all the late-bound vars that come into scope from supertrait HRTBs, based on the
+    /// associated type name and starting trait.
+    /// For example, imagine we have
+    /// ```ignore (illustrative)
+    /// trait Foo<'a, 'b> {
+    ///   type As;
+    /// }
+    /// trait Bar<'b>: for<'a> Foo<'a, 'b> {}
+    /// trait Bar: for<'b> Bar<'b> {}
+    /// ```
+    /// In this case, if we wanted to the supertrait HRTB lifetimes for `As` on
+    /// the starting trait `Bar`, we would return `Some(['b, 'a])`.
+    fn supertrait_hrtb_lifetimes(
+        tcx: TyCtxt<'tcx>,
+        def_id: DefId,
+        assoc_name: Ident,
+    ) -> Option<Vec<ty::BoundVariableKind>> {
+        let trait_defines_associated_type_named = |trait_def_id: DefId| {
+            tcx.associated_items(trait_def_id)
+                .find_by_name_and_kind(tcx, assoc_name, ty::AssocKind::Type, trait_def_id)
+                .is_some()
+        };
+
+        use smallvec::{smallvec, SmallVec};
+        let mut stack: SmallVec<[(DefId, SmallVec<[ty::BoundVariableKind; 8]>); 8]> =
+            smallvec![(def_id, smallvec![])];
+        let mut visited: FxHashSet<DefId> = FxHashSet::default();
+        loop {
+            let Some((def_id, bound_vars)) = stack.pop() else {
+                break None;
+            };
+            // See issue #83753. If someone writes an associated type on a non-trait, just treat it as
+            // there being no supertrait HRTBs.
+            match tcx.def_kind(def_id) {
+                DefKind::Trait | DefKind::TraitAlias | DefKind::Impl => {}
+                _ => break None,
+            }
+
+            if trait_defines_associated_type_named(def_id) {
+                break Some(bound_vars.into_iter().collect());
+            }
+            let predicates =
+                tcx.super_predicates_that_define_assoc_type((def_id, Some(assoc_name)));
+            let obligations = predicates.predicates.iter().filter_map(|&(pred, _)| {
+                let bound_predicate = pred.kind();
+                match bound_predicate.skip_binder() {
+                    ty::PredicateKind::Trait(data) => {
+                        // The order here needs to match what we would get from `subst_supertrait`
+                        let pred_bound_vars = bound_predicate.bound_vars();
+                        let mut all_bound_vars = bound_vars.clone();
+                        all_bound_vars.extend(pred_bound_vars.iter());
+                        let super_def_id = data.trait_ref.def_id;
+                        Some((super_def_id, all_bound_vars))
+                    }
+                    _ => None,
+                }
+            });
+
+            let obligations = obligations.filter(|o| visited.insert(o.0));
+            stack.extend(obligations);
+        }
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn visit_fn_like_elision(
+        &mut self,
+        inputs: &'tcx [hir::Ty<'tcx>],
+        output: Option<&'tcx hir::Ty<'tcx>>,
+        in_closure: bool,
+    ) {
+        self.with(Scope::Elision { s: self.scope }, |this| {
+            for input in inputs {
+                this.visit_ty(input);
+            }
+            if !in_closure && let Some(output) = output {
+                this.visit_ty(output);
+            }
+        });
+        if in_closure && let Some(output) = output {
+            self.visit_ty(output);
+        }
+    }
+
+    fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
+        debug!("resolve_object_lifetime_default(lifetime_ref={:?})", lifetime_ref);
+        let mut late_depth = 0;
+        let mut scope = self.scope;
+        let lifetime = loop {
+            match *scope {
+                Scope::Binder { s, scope_type, .. } => {
+                    match scope_type {
+                        BinderScopeType::Normal => late_depth += 1,
+                        BinderScopeType::Concatenating => {}
+                    }
+                    scope = s;
+                }
+
+                Scope::Root | Scope::Elision { .. } => break Region::Static,
+
+                Scope::Body { .. } | Scope::ObjectLifetimeDefault { lifetime: None, .. } => return,
+
+                Scope::ObjectLifetimeDefault { lifetime: Some(l), .. } => break l,
+
+                Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } => {
+                    scope = s;
+                }
+            }
+        };
+        self.insert_lifetime(lifetime_ref, lifetime.shifted(late_depth));
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: Region) {
+        debug!(
+            node = ?self.tcx.hir().node_to_string(lifetime_ref.hir_id),
+            span = ?self.tcx.sess.source_map().span_to_diagnostic_string(lifetime_ref.span)
+        );
+        self.map.defs.insert(lifetime_ref.hir_id, def);
+    }
+
+    /// Sometimes we resolve a lifetime, but later find that it is an
+    /// error (esp. around impl trait). In that case, we remove the
+    /// entry into `map.defs` so as not to confuse later code.
+    fn uninsert_lifetime_on_error(&mut self, lifetime_ref: &'tcx hir::Lifetime, bad_def: Region) {
+        let old_value = self.map.defs.remove(&lifetime_ref.hir_id);
+        assert_eq!(old_value, Some(bad_def));
+    }
+}
+
+/// Detects late-bound lifetimes and inserts them into
+/// `late_bound`.
+///
+/// A region declared on a fn is **late-bound** if:
+/// - it is constrained by an argument type;
+/// - it does not appear in a where-clause.
+///
+/// "Constrained" basically means that it appears in any type but
+/// not amongst the inputs to a projection. In other words, `<&'a
+/// T as Trait<''b>>::Foo` does not constrain `'a` or `'b`.
+fn is_late_bound_map(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<&FxIndexSet<LocalDefId>> {
+    let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+    let decl = tcx.hir().fn_decl_by_hir_id(hir_id)?;
+    let generics = tcx.hir().get_generics(def_id)?;
+
+    let mut late_bound = FxIndexSet::default();
+
+    let mut constrained_by_input = ConstrainedCollector::default();
+    for arg_ty in decl.inputs {
+        constrained_by_input.visit_ty(arg_ty);
+    }
+
+    let mut appears_in_output = AllCollector::default();
+    intravisit::walk_fn_ret_ty(&mut appears_in_output, &decl.output);
+
+    debug!(?constrained_by_input.regions);
+
+    // Walk the lifetimes that appear in where clauses.
+    //
+    // Subtle point: because we disallow nested bindings, we can just
+    // ignore binders here and scrape up all names we see.
+    let mut appears_in_where_clause = AllCollector::default();
+    appears_in_where_clause.visit_generics(generics);
+    debug!(?appears_in_where_clause.regions);
+
+    // Late bound regions are those that:
+    // - appear in the inputs
+    // - do not appear in the where-clauses
+    // - are not implicitly captured by `impl Trait`
+    for param in generics.params {
+        match param.kind {
+            hir::GenericParamKind::Lifetime { .. } => { /* fall through */ }
+
+            // Neither types nor consts are late-bound.
+            hir::GenericParamKind::Type { .. } | hir::GenericParamKind::Const { .. } => continue,
+        }
+
+        let param_def_id = tcx.hir().local_def_id(param.hir_id);
+
+        // appears in the where clauses? early-bound.
+        if appears_in_where_clause.regions.contains(&param_def_id) {
+            continue;
+        }
+
+        // does not appear in the inputs, but appears in the return type? early-bound.
+        if !constrained_by_input.regions.contains(&param_def_id)
+            && appears_in_output.regions.contains(&param_def_id)
+        {
+            continue;
+        }
+
+        debug!("lifetime {:?} with id {:?} is late-bound", param.name.ident(), param.hir_id);
+
+        let inserted = late_bound.insert(param_def_id);
+        assert!(inserted, "visited lifetime {:?} twice", param.hir_id);
+    }
+
+    debug!(?late_bound);
+    return Some(tcx.arena.alloc(late_bound));
+
+    #[derive(Default)]
+    struct ConstrainedCollector {
+        regions: FxHashSet<LocalDefId>,
+    }
+
+    impl<'v> Visitor<'v> for ConstrainedCollector {
+        fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
+            match ty.kind {
+                hir::TyKind::Path(
+                    hir::QPath::Resolved(Some(_), _) | hir::QPath::TypeRelative(..),
+                ) => {
+                    // ignore lifetimes appearing in associated type
+                    // projections, as they are not *constrained*
+                    // (defined above)
+                }
+
+                hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => {
+                    // consider only the lifetimes on the final
+                    // segment; I am not sure it's even currently
+                    // valid to have them elsewhere, but even if it
+                    // is, those would be potentially inputs to
+                    // projections
+                    if let Some(last_segment) = path.segments.last() {
+                        self.visit_path_segment(last_segment);
+                    }
+                }
+
+                _ => {
+                    intravisit::walk_ty(self, ty);
+                }
+            }
+        }
+
+        fn visit_lifetime(&mut self, lifetime_ref: &'v hir::Lifetime) {
+            if let hir::LifetimeName::Param(def_id, _) = lifetime_ref.name {
+                self.regions.insert(def_id);
+            }
+        }
+    }
+
+    #[derive(Default)]
+    struct AllCollector {
+        regions: FxHashSet<LocalDefId>,
+    }
+
+    impl<'v> Visitor<'v> for AllCollector {
+        fn visit_lifetime(&mut self, lifetime_ref: &'v hir::Lifetime) {
+            if let hir::LifetimeName::Param(def_id, _) = lifetime_ref.name {
+                self.regions.insert(def_id);
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index 08a58f4fed8..0aa44431c79 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -284,7 +284,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
                     icx.to_ty(ty)
                 }
             }
-            ImplItemKind::TyAlias(ty) => {
+            ImplItemKind::Type(ty) => {
                 if tcx.impl_trait_ref(tcx.hir().get_parent_item(hir_id)).is_none() {
                     check_feature_inherent_assoc_ty(tcx, item.span);
                 }
@@ -492,8 +492,10 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
                         },
                         def_id.to_def_id(),
                     );
-                    if let Some(assoc_item) = assoc_item {
-                        tcx.type_of(tcx.generics_of(assoc_item.def_id).params[idx].def_id)
+                    if let Some(param)
+                        = assoc_item.map(|item| &tcx.generics_of(item.def_id).params[idx]).filter(|param| param.kind.is_ty_or_const())
+                    {
+                        tcx.type_of(param.def_id)
                     } else {
                         // FIXME(associated_const_equality): add a useful error message here.
                         tcx.ty_error_with_message(
diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
index 8428e466406..213b89fc784 100644
--- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
+++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
@@ -114,9 +114,9 @@ pub fn identify_constrained_generic_params<'tcx>(
 /// ```
 /// The impl's predicates are collected from left to right. Ignoring
 /// the implicit `Sized` bounds, these are
-///   * T: Debug
-///   * U: Iterator
-///   * <U as Iterator>::Item = T -- a desugared ProjectionPredicate
+///   * `T: Debug`
+///   * `U: Iterator`
+///   * `<U as Iterator>::Item = T` -- a desugared ProjectionPredicate
 ///
 /// When we, for example, try to go over the trait-reference
 /// `IntoIter<u32> as Trait`, we substitute the impl parameters with fresh
@@ -132,12 +132,16 @@ pub fn identify_constrained_generic_params<'tcx>(
 ///
 /// We *do* have to be somewhat careful when projection targets contain
 /// projections themselves, for example in
+///
+/// ```ignore (illustrative)
 ///     impl<S,U,V,W> Trait for U where
 /// /* 0 */   S: Iterator<Item = U>,
 /// /* - */   U: Iterator,
 /// /* 1 */   <U as Iterator>::Item: ToOwned<Owned=(W,<V as Iterator>::Item)>
 /// /* 2 */   W: Iterator<Item = V>
 /// /* 3 */   V: Debug
+/// ```
+///
 /// we have to evaluate the projections in the order I wrote them:
 /// `V: Debug` requires `V` to be evaluated. The only projection that
 /// *determines* `V` is 2 (1 contains it, but *does not determine it*,
diff --git a/compiler/rustc_hir_analysis/src/expr_use_visitor.rs b/compiler/rustc_hir_analysis/src/expr_use_visitor.rs
index f483342b445..cbc3769901d 100644
--- a/compiler/rustc_hir_analysis/src/expr_use_visitor.rs
+++ b/compiler/rustc_hir_analysis/src/expr_use_visitor.rs
@@ -134,7 +134,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
     /// - `typeck_results` --- typeck results for the code being analyzed
     pub fn new(
         delegate: &'a mut (dyn Delegate<'tcx> + 'a),
-        infcx: &'a InferCtxt<'a, 'tcx>,
+        infcx: &'a InferCtxt<'tcx>,
         body_owner: LocalDefId,
         param_env: ty::ParamEnv<'tcx>,
         typeck_results: &'a ty::TypeckResults<'tcx>,
diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
index 7b080dc2942..b0fdfcf38a6 100644
--- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
@@ -64,38 +64,36 @@ fn diagnostic_hir_wf_check<'tcx>(
 
     impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> {
         fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
-            self.tcx.infer_ctxt().enter(|infcx| {
-                let tcx_ty =
-                    self.icx.to_ty(ty).fold_with(&mut EraseAllBoundRegions { tcx: self.tcx });
-                let cause = traits::ObligationCause::new(
-                    ty.span,
-                    self.hir_id,
-                    traits::ObligationCauseCode::WellFormed(None),
-                );
-                let errors = traits::fully_solve_obligation(
-                    &infcx,
-                    traits::Obligation::new(
-                        cause,
-                        self.param_env,
-                        ty::Binder::dummy(ty::PredicateKind::WellFormed(tcx_ty.into()))
-                            .to_predicate(self.tcx),
-                    ),
-                );
-                if !errors.is_empty() {
-                    debug!("Wf-check got errors for {:?}: {:?}", ty, errors);
-                    for error in errors {
-                        if error.obligation.predicate == self.predicate {
-                            // Save the cause from the greatest depth - this corresponds
-                            // to picking more-specific types (e.g. `MyStruct<u8>`)
-                            // over less-specific types (e.g. `Option<MyStruct<u8>>`)
-                            if self.depth >= self.cause_depth {
-                                self.cause = Some(error.obligation.cause);
-                                self.cause_depth = self.depth
-                            }
+            let infcx = self.tcx.infer_ctxt().build();
+            let tcx_ty = self.icx.to_ty(ty).fold_with(&mut EraseAllBoundRegions { tcx: self.tcx });
+            let cause = traits::ObligationCause::new(
+                ty.span,
+                self.hir_id,
+                traits::ObligationCauseCode::WellFormed(None),
+            );
+            let errors = traits::fully_solve_obligation(
+                &infcx,
+                traits::Obligation::new(
+                    cause,
+                    self.param_env,
+                    ty::Binder::dummy(ty::PredicateKind::WellFormed(tcx_ty.into()))
+                        .to_predicate(self.tcx),
+                ),
+            );
+            if !errors.is_empty() {
+                debug!("Wf-check got errors for {:?}: {:?}", ty, errors);
+                for error in errors {
+                    if error.obligation.predicate == self.predicate {
+                        // Save the cause from the greatest depth - this corresponds
+                        // to picking more-specific types (e.g. `MyStruct<u8>`)
+                        // over less-specific types (e.g. `Option<MyStruct<u8>>`)
+                        if self.depth >= self.cause_depth {
+                            self.cause = Some(error.obligation.cause);
+                            self.cause_depth = self.depth
                         }
                     }
                 }
-            });
+            }
             self.depth += 1;
             intravisit::walk_ty(self, ty);
             self.depth -= 1;
@@ -119,7 +117,7 @@ fn diagnostic_hir_wf_check<'tcx>(
     let ty = match loc {
         WellFormedLoc::Ty(_) => match hir.get(hir_id) {
             hir::Node::ImplItem(item) => match item.kind {
-                hir::ImplItemKind::TyAlias(ty) => Some(ty),
+                hir::ImplItemKind::Type(ty) => Some(ty),
                 hir::ImplItemKind::Const(ty, _) => Some(ty),
                 ref item => bug!("Unexpected ImplItem {:?}", item),
             },
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs
index c499364056f..69155a422b0 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs
@@ -197,6 +197,9 @@ fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: Symbol
 
 /// Enforce that we do not have two items in an impl with the same name.
 fn enforce_impl_items_are_distinct(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) {
+    if tcx.impl_trait_ref(impl_def_id).is_some() {
+        return;
+    }
     let mut seen_type_items = FxHashMap::default();
     let mut seen_value_items = FxHashMap::default();
     for &impl_item_ref in tcx.associated_item_def_ids(impl_def_id) {
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
index 5bebd7dee09..e806e94879d 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
@@ -77,7 +77,7 @@ use rustc_middle::ty::subst::{GenericArg, InternalSubsts, SubstsRef};
 use rustc_middle::ty::trait_def::TraitSpecializationKind;
 use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
 use rustc_span::Span;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
 use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
 use rustc_trait_selection::traits::{self, translate_substs, wf, ObligationCtxt};
 
@@ -130,8 +130,10 @@ fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node
 ///
 /// Example
 ///
+/// ```ignore (illustrative)
 /// impl<A, B> Foo<A> for B { /* impl2 */ }
 /// impl<C> Foo<Vec<C>> for C { /* impl1 */ }
+/// ```
 ///
 /// Would return `S1 = [C]` and `S2 = [Vec<C>, C]`.
 fn get_impl_substs<'tcx>(
@@ -139,34 +141,33 @@ fn get_impl_substs<'tcx>(
     impl1_def_id: LocalDefId,
     impl2_node: Node,
 ) -> Option<(SubstsRef<'tcx>, SubstsRef<'tcx>)> {
-    tcx.infer_ctxt().enter(|ref infcx| {
-        let ocx = ObligationCtxt::new(infcx);
-        let param_env = tcx.param_env(impl1_def_id);
-        let impl1_hir_id = tcx.hir().local_def_id_to_hir_id(impl1_def_id);
+    let infcx = &tcx.infer_ctxt().build();
+    let ocx = ObligationCtxt::new(infcx);
+    let param_env = tcx.param_env(impl1_def_id);
+    let impl1_hir_id = tcx.hir().local_def_id_to_hir_id(impl1_def_id);
 
-        let assumed_wf_types =
-            ocx.assumed_wf_types(param_env, tcx.def_span(impl1_def_id), impl1_def_id);
+    let assumed_wf_types =
+        ocx.assumed_wf_types(param_env, tcx.def_span(impl1_def_id), impl1_def_id);
 
-        let impl1_substs = InternalSubsts::identity_for_item(tcx, impl1_def_id.to_def_id());
-        let impl2_substs =
-            translate_substs(infcx, param_env, impl1_def_id.to_def_id(), impl1_substs, impl2_node);
+    let impl1_substs = InternalSubsts::identity_for_item(tcx, impl1_def_id.to_def_id());
+    let impl2_substs =
+        translate_substs(infcx, param_env, impl1_def_id.to_def_id(), impl1_substs, impl2_node);
 
-        let errors = ocx.select_all_or_error();
-        if !errors.is_empty() {
-            ocx.infcx.report_fulfillment_errors(&errors, None, false);
-            return None;
-        }
+    let errors = ocx.select_all_or_error();
+    if !errors.is_empty() {
+        ocx.infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+        return None;
+    }
 
-        let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_hir_id, assumed_wf_types);
-        let outlives_env = OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds);
-        infcx.check_region_obligations_and_report_errors(impl1_def_id, &outlives_env);
-        let Ok(impl2_substs) = infcx.fully_resolve(impl2_substs) else {
-            let span = tcx.def_span(impl1_def_id);
-            tcx.sess.emit_err(SubstsOnOverriddenImpl { span });
-            return None;
-        };
-        Some((impl1_substs, impl2_substs))
-    })
+    let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_hir_id, assumed_wf_types);
+    let outlives_env = OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds);
+    infcx.check_region_obligations_and_report_errors(impl1_def_id, &outlives_env);
+    let Ok(impl2_substs) = infcx.fully_resolve(impl2_substs) else {
+        let span = tcx.def_span(impl1_def_id);
+        tcx.sess.emit_err(SubstsOnOverriddenImpl { span });
+        return None;
+    };
+    Some((impl1_substs, impl2_substs))
 }
 
 /// Returns a list of all of the unconstrained subst of the given impl.
@@ -226,13 +227,17 @@ fn unconstrained_parent_impl_substs<'tcx>(
 ///
 /// For example forbid the following:
 ///
+/// ```ignore (illustrative)
 /// impl<A> Tr for A { }
 /// impl<B> Tr for (B, B) { }
+/// ```
 ///
 /// Note that only consider the unconstrained parameters of the base impl:
 ///
+/// ```ignore (illustrative)
 /// impl<S, I: IntoIterator<Item = S>> Tr<S> for I { }
 /// impl<T> Tr<T> for Vec<T> { }
+/// ```
 ///
 /// The substs for the parent impl here are `[T, Vec<T>]`, which repeats `T`,
 /// but `S` is constrained in the parent impl, so `parent_substs` is only
@@ -257,8 +262,10 @@ fn check_duplicate_params<'tcx>(
 ///
 /// For example forbid the following:
 ///
+/// ```ignore (illustrative)
 /// impl<A> Tr for A { }
 /// impl Tr for &'static i32 { }
+/// ```
 fn check_static_lifetimes<'tcx>(
     tcx: TyCtxt<'tcx>,
     parent_substs: &Vec<GenericArg<'tcx>>,
@@ -344,23 +351,21 @@ fn check_predicates<'tcx>(
 
     // Include the well-formed predicates of the type parameters of the impl.
     for arg in tcx.impl_trait_ref(impl1_def_id).unwrap().substs {
-        tcx.infer_ctxt().enter(|ref infcx| {
-            let obligations = wf::obligations(
-                infcx,
-                tcx.param_env(impl1_def_id),
-                tcx.hir().local_def_id_to_hir_id(impl1_def_id),
-                0,
-                arg,
-                span,
-            )
-            .unwrap();
+        let infcx = &tcx.infer_ctxt().build();
+        let obligations = wf::obligations(
+            infcx,
+            tcx.param_env(impl1_def_id),
+            tcx.hir().local_def_id_to_hir_id(impl1_def_id),
+            0,
+            arg,
+            span,
+        )
+        .unwrap();
 
-            assert!(!obligations.needs_infer());
-            impl2_predicates.extend(
-                traits::elaborate_obligations(tcx, obligations)
-                    .map(|obligation| obligation.predicate),
-            )
-        })
+        assert!(!obligations.needs_infer());
+        impl2_predicates.extend(
+            traits::elaborate_obligations(tcx, obligations).map(|obligation| obligation.predicate),
+        )
     }
     impl2_predicates.extend(
         traits::elaborate_predicates_with_span(tcx, always_applicable_traits)
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index d31b9b7ae46..b7d9fc8a2fe 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -110,7 +110,7 @@ use rustc_middle::util;
 use rustc_session::config::EntryFnType;
 use rustc_span::{symbol::sym, Span, DUMMY_SP};
 use rustc_target::spec::abi::Abi;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
 
 use std::iter;
@@ -141,24 +141,23 @@ fn require_same_types<'tcx>(
     expected: Ty<'tcx>,
     actual: Ty<'tcx>,
 ) -> bool {
-    tcx.infer_ctxt().enter(|ref infcx| {
-        let param_env = ty::ParamEnv::empty();
-        let errors = match infcx.at(cause, param_env).eq(expected, actual) {
-            Ok(InferOk { obligations, .. }) => traits::fully_solve_obligations(infcx, obligations),
-            Err(err) => {
-                infcx.report_mismatched_types(cause, expected, actual, err).emit();
-                return false;
-            }
-        };
+    let infcx = &tcx.infer_ctxt().build();
+    let param_env = ty::ParamEnv::empty();
+    let errors = match infcx.at(cause, param_env).eq(expected, actual) {
+        Ok(InferOk { obligations, .. }) => traits::fully_solve_obligations(infcx, obligations),
+        Err(err) => {
+            infcx.err_ctxt().report_mismatched_types(cause, expected, actual, err).emit();
+            return false;
+        }
+    };
 
-        match &errors[..] {
-            [] => true,
-            errors => {
-                infcx.report_fulfillment_errors(errors, None, false);
-                false
-            }
+    match &errors[..] {
+        [] => true,
+        errors => {
+            infcx.err_ctxt().report_fulfillment_errors(errors, None, false);
+            false
         }
-    })
+    }
 }
 
 fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
@@ -305,23 +304,22 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
             error = true;
         }
         let return_ty = return_ty.skip_binder();
-        tcx.infer_ctxt().enter(|infcx| {
-            // Main should have no WC, so empty param env is OK here.
-            let param_env = ty::ParamEnv::empty();
-            let cause = traits::ObligationCause::new(
-                return_ty_span,
-                main_diagnostics_hir_id,
-                ObligationCauseCode::MainFunctionType,
-            );
-            let ocx = traits::ObligationCtxt::new(&infcx);
-            let norm_return_ty = ocx.normalize(cause.clone(), param_env, return_ty);
-            ocx.register_bound(cause, param_env, norm_return_ty, term_did);
-            let errors = ocx.select_all_or_error();
-            if !errors.is_empty() {
-                infcx.report_fulfillment_errors(&errors, None, false);
-                error = true;
-            }
-        });
+        let infcx = tcx.infer_ctxt().build();
+        // Main should have no WC, so empty param env is OK here.
+        let param_env = ty::ParamEnv::empty();
+        let cause = traits::ObligationCause::new(
+            return_ty_span,
+            main_diagnostics_hir_id,
+            ObligationCauseCode::MainFunctionType,
+        );
+        let ocx = traits::ObligationCtxt::new(&infcx);
+        let norm_return_ty = ocx.normalize(cause.clone(), param_env, return_ty);
+        ocx.register_bound(cause, param_env, norm_return_ty, term_did);
+        let errors = ocx.select_all_or_error();
+        if !errors.is_empty() {
+            infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+            error = true;
+        }
         // now we can take the return type of the given main function
         expected_return_type = main_fnsig.output();
     } else {
diff --git a/compiler/rustc_hir_analysis/src/mem_categorization.rs b/compiler/rustc_hir_analysis/src/mem_categorization.rs
index 46b49647836..b62c5b5e077 100644
--- a/compiler/rustc_hir_analysis/src/mem_categorization.rs
+++ b/compiler/rustc_hir_analysis/src/mem_categorization.rs
@@ -92,7 +92,7 @@ impl HirNode for hir::Pat<'_> {
 #[derive(Clone)]
 pub(crate) struct MemCategorizationContext<'a, 'tcx> {
     pub(crate) typeck_results: &'a ty::TypeckResults<'tcx>,
-    infcx: &'a InferCtxt<'a, 'tcx>,
+    infcx: &'a InferCtxt<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     body_owner: LocalDefId,
     upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>,
@@ -103,7 +103,7 @@ pub(crate) type McResult<T> = Result<T, ()>;
 impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
     /// Creates a `MemCategorizationContext`.
     pub(crate) fn new(
-        infcx: &'a InferCtxt<'a, 'tcx>,
+        infcx: &'a InferCtxt<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
         body_owner: LocalDefId,
         typeck_results: &'a ty::TypeckResults<'tcx>,
@@ -184,7 +184,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
     ///   modes #42640) may look like `Some(x)` but in fact have
     ///   implicit deref patterns attached (e.g., it is really
     ///   `&Some(x)`). In that case, we return the "outermost" type
-    ///   (e.g., `&Option<T>).
+    ///   (e.g., `&Option<T>`).
     pub(crate) fn pat_ty_adjusted(&self, pat: &hir::Pat<'_>) -> McResult<Ty<'tcx>> {
         // Check for implicit `&` types wrapping the pattern; note
         // that these are never attached to binding patterns, so