about summary refs log tree commit diff
diff options
context:
space:
mode:
authorCameron Steffen <cam.steffen94@gmail.com>2022-09-19 22:03:59 -0500
committerCameron Steffen <cam.steffen94@gmail.com>2022-10-07 07:10:40 -0500
commit283abbf0e7d20176f76006825b5c52e9a4234e4c (patch)
tree169a55f89da9def5accb58df926ef0efd1cdf46d
parent91269fa5b8a7272a2a45b0b5e8a6fa4be24fe96a (diff)
downloadrust-283abbf0e7d20176f76006825b5c52e9a4234e4c.tar.gz
rust-283abbf0e7d20176f76006825b5c52e9a4234e4c.zip
Change InferCtxtBuilder from enter to build
-rw-r--r--compiler/rustc_borrowck/src/consumers.rs9
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs88
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs74
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs5
-rw-r--r--compiler/rustc_borrowck/src/lib.rs13
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types.rs118
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs29
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/ops.rs7
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs42
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/generics.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs90
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_method.rs1027
-rw-r--r--compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs14
-rw-r--r--compiler/rustc_hir_analysis/src/check/inherited.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/method/probe.rs116
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs62
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/builtin.rs645
-rw-r--r--compiler/rustc_hir_analysis/src/hir_wf_check.rs56
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs77
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs64
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs19
-rw-r--r--compiler/rustc_lint/src/non_fmt_panic.rs28
-rw-r--r--compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs144
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs128
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs7
-rw-r--r--compiler/rustc_trait_selection/src/infer.rs18
-rw-r--r--compiler/rustc_trait_selection/src/traits/auto_trait.rs220
-rw-r--r--compiler/rustc_trait_selection/src/traits/codegen.rs93
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs69
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs15
-rw-r--r--compiler/rustc_trait_selection/src/traits/misc.rs109
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs133
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs15
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_match.rs7
-rw-r--r--compiler/rustc_traits/src/dropck_outlives.rs218
-rw-r--r--compiler/rustc_traits/src/evaluate_obligation.rs20
-rw-r--r--compiler/rustc_traits/src/normalize_erasing_regions.rs43
-rw-r--r--compiler/rustc_ty_utils/src/common_traits.rs11
-rw-r--r--compiler/rustc_ty_utils/src/instance.rs24
-rw-r--r--src/librustdoc/clean/blanket_impl.rs110
-rw-r--r--src/librustdoc/clean/mod.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/escape.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/future_not_send.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs38
-rw-r--r--src/tools/clippy/clippy_utils/src/sugg.rs7
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs48
-rw-r--r--src/tools/clippy/clippy_utils/src/usage.rs19
53 files changed, 1984 insertions, 2200 deletions
diff --git a/compiler/rustc_borrowck/src/consumers.rs b/compiler/rustc_borrowck/src/consumers.rs
index efc17a173f4..b162095f8a6 100644
--- a/compiler/rustc_borrowck/src/consumers.rs
+++ b/compiler/rustc_borrowck/src/consumers.rs
@@ -31,9 +31,8 @@ pub fn get_body_with_borrowck_facts<'tcx>(
     def: ty::WithOptConstParam<LocalDefId>,
 ) -> BodyWithBorrowckFacts<'tcx> {
     let (input_body, promoted) = tcx.mir_promoted(def);
-    tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(def.did)).enter(|infcx| {
-        let input_body: &Body<'_> = &input_body.borrow();
-        let promoted: &IndexVec<_, _> = &promoted.borrow();
-        *super::do_mir_borrowck(&infcx, input_body, promoted, true).1.unwrap()
-    })
+    let infcx = tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(def.did)).build();
+    let input_body: &Body<'_> = &input_body.borrow();
+    let promoted: &IndexVec<_, _> = &promoted.borrow();
+    *super::do_mir_borrowck(&infcx, input_body, promoted, true).1.unwrap()
 }
diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
index fc79953c2dd..02071ed6b36 100644
--- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
@@ -238,20 +238,11 @@ impl<'tcx> TypeOpInfo<'tcx> for PredicateQuery<'tcx> {
         placeholder_region: ty::Region<'tcx>,
         error_region: Option<ty::Region<'tcx>>,
     ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
-        mbcx.infcx.tcx.infer_ctxt().enter_with_canonical(
-            cause.span,
-            &self.canonical_query,
-            |ref infcx, key, _| {
-                let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
-                type_op_prove_predicate_with_cause(infcx, &mut *fulfill_cx, key, cause);
-                try_extract_error_from_fulfill_cx(
-                    fulfill_cx,
-                    infcx,
-                    placeholder_region,
-                    error_region,
-                )
-            },
-        )
+        let (ref infcx, key, _) =
+            mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query);
+        let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
+        type_op_prove_predicate_with_cause(infcx, &mut *fulfill_cx, key, cause);
+        try_extract_error_from_fulfill_cx(fulfill_cx, infcx, placeholder_region, error_region)
     }
 }
 
@@ -288,37 +279,24 @@ where
         placeholder_region: ty::Region<'tcx>,
         error_region: Option<ty::Region<'tcx>>,
     ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
-        mbcx.infcx.tcx.infer_ctxt().enter_with_canonical(
-            cause.span,
-            &self.canonical_query,
-            |ref infcx, key, _| {
-                let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
-
-                let mut selcx = SelectionContext::new(infcx);
-
-                // FIXME(lqd): Unify and de-duplicate the following with the actual
-                // `rustc_traits::type_op::type_op_normalize` query to allow the span we need in the
-                // `ObligationCause`. The normalization results are currently different between
-                // `AtExt::normalize` used in the query and `normalize` called below: the former fails
-                // to normalize the `nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs` test. Check
-                // after #85499 lands to see if its fixes have erased this difference.
-                let (param_env, value) = key.into_parts();
-                let Normalized { value: _, obligations } = rustc_trait_selection::traits::normalize(
-                    &mut selcx,
-                    param_env,
-                    cause,
-                    value.value,
-                );
-                fulfill_cx.register_predicate_obligations(infcx, obligations);
-
-                try_extract_error_from_fulfill_cx(
-                    fulfill_cx,
-                    infcx,
-                    placeholder_region,
-                    error_region,
-                )
-            },
-        )
+        let (ref infcx, key, _) =
+            mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query);
+        let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
+
+        let mut selcx = SelectionContext::new(infcx);
+
+        // FIXME(lqd): Unify and de-duplicate the following with the actual
+        // `rustc_traits::type_op::type_op_normalize` query to allow the span we need in the
+        // `ObligationCause`. The normalization results are currently different between
+        // `AtExt::normalize` used in the query and `normalize` called below: the former fails
+        // to normalize the `nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs` test. Check
+        // after #85499 lands to see if its fixes have erased this difference.
+        let (param_env, value) = key.into_parts();
+        let Normalized { value: _, obligations } =
+            rustc_trait_selection::traits::normalize(&mut selcx, param_env, cause, value.value);
+        fulfill_cx.register_predicate_obligations(infcx, obligations);
+
+        try_extract_error_from_fulfill_cx(fulfill_cx, infcx, placeholder_region, error_region)
     }
 }
 
@@ -349,21 +327,11 @@ impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> {
         placeholder_region: ty::Region<'tcx>,
         error_region: Option<ty::Region<'tcx>>,
     ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
-        mbcx.infcx.tcx.infer_ctxt().enter_with_canonical(
-            cause.span,
-            &self.canonical_query,
-            |ref infcx, key, _| {
-                let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
-                type_op_ascribe_user_type_with_span(infcx, &mut *fulfill_cx, key, Some(cause.span))
-                    .ok()?;
-                try_extract_error_from_fulfill_cx(
-                    fulfill_cx,
-                    infcx,
-                    placeholder_region,
-                    error_region,
-                )
-            },
-        )
+        let (ref infcx, key, _) =
+            mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query);
+        let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
+        type_op_ascribe_user_type_with_span(infcx, &mut *fulfill_cx, key, Some(cause.span)).ok()?;
+        try_extract_error_from_fulfill_cx(fulfill_cx, infcx, placeholder_region, error_region)
     }
 }
 
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index a1b34e94dbf..2a8bd4d30ab 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -492,11 +492,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else {
                 return false;
             };
-            tcx.infer_ctxt().enter(|infcx| {
-                infcx
-                    .type_implements_trait(default_trait, ty, ty::List::empty(), param_env)
-                    .may_apply()
-            })
+            tcx.infer_ctxt()
+                .build()
+                .type_implements_trait(default_trait, ty, ty::List::empty(), param_env)
+                .may_apply()
         };
 
         let assign_value = match ty.kind() {
@@ -606,41 +605,40 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             .and_then(|def_id| tcx.hir().get_generics(def_id))
         else { return; };
         // Try to find predicates on *generic params* that would allow copying `ty`
-        let predicates: Result<Vec<_>, _> = tcx.infer_ctxt().enter(|infcx| {
-            let mut fulfill_cx = <dyn rustc_infer::traits::TraitEngine<'_>>::new(infcx.tcx);
+        let infcx = tcx.infer_ctxt().build();
+        let mut fulfill_cx = <dyn rustc_infer::traits::TraitEngine<'_>>::new(infcx.tcx);
 
-            let copy_did = infcx.tcx.lang_items().copy_trait().unwrap();
-            let cause = ObligationCause::new(
-                span,
-                self.mir_hir_id(),
-                rustc_infer::traits::ObligationCauseCode::MiscObligation,
-            );
-            fulfill_cx.register_bound(
-                &infcx,
-                self.param_env,
-                // Erase any region vids from the type, which may not be resolved
-                infcx.tcx.erase_regions(ty),
-                copy_did,
-                cause,
-            );
-            // Select all, including ambiguous predicates
-            let errors = fulfill_cx.select_all_or_error(&infcx);
-
-            // Only emit suggestion if all required predicates are on generic
-            errors
-                .into_iter()
-                .map(|err| match err.obligation.predicate.kind().skip_binder() {
-                    PredicateKind::Trait(predicate) => match predicate.self_ty().kind() {
-                        ty::Param(param_ty) => Ok((
-                            generics.type_param(param_ty, tcx),
-                            predicate.trait_ref.print_only_trait_path().to_string(),
-                        )),
-                        _ => Err(()),
-                    },
+        let copy_did = infcx.tcx.lang_items().copy_trait().unwrap();
+        let cause = ObligationCause::new(
+            span,
+            self.mir_hir_id(),
+            rustc_infer::traits::ObligationCauseCode::MiscObligation,
+        );
+        fulfill_cx.register_bound(
+            &infcx,
+            self.param_env,
+            // Erase any region vids from the type, which may not be resolved
+            infcx.tcx.erase_regions(ty),
+            copy_did,
+            cause,
+        );
+        // Select all, including ambiguous predicates
+        let errors = fulfill_cx.select_all_or_error(&infcx);
+
+        // Only emit suggestion if all required predicates are on generic
+        let predicates: Result<Vec<_>, _> = errors
+            .into_iter()
+            .map(|err| match err.obligation.predicate.kind().skip_binder() {
+                PredicateKind::Trait(predicate) => match predicate.self_ty().kind() {
+                    ty::Param(param_ty) => Ok((
+                        generics.type_param(param_ty, tcx),
+                        predicate.trait_ref.print_only_trait_path().to_string(),
+                    )),
                     _ => Err(()),
-                })
-                .collect()
-        });
+                },
+                _ => Err(()),
+            })
+            .collect();
 
         if let Ok(predicates) = predicates {
             suggest_constraining_type_params(
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 7ccb679d88b..534d9ecae6e 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -1025,7 +1025,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
                         let ty = moved_place.ty(self.body, self.infcx.tcx).ty;
                         let suggest = match self.infcx.tcx.get_diagnostic_item(sym::IntoIterator) {
-                            Some(def_id) => self.infcx.tcx.infer_ctxt().enter(|infcx| {
+                            Some(def_id) => {
+                                let infcx = self.infcx.tcx.infer_ctxt().build();
                                 type_known_to_meet_bound_modulo_regions(
                                     &infcx,
                                     self.param_env,
@@ -1036,7 +1037,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                                     def_id,
                                     DUMMY_SP,
                                 )
-                            }),
+                            }
                             _ => false,
                         };
                         if suggest {
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 5211f2127ed..abfe253d43d 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -131,14 +131,11 @@ fn mir_borrowck<'tcx>(
     debug!("run query mir_borrowck: {}", tcx.def_path_str(def.did.to_def_id()));
     let hir_owner = tcx.hir().local_def_id_to_hir_id(def.did).owner;
 
-    let opt_closure_req = tcx
-        .infer_ctxt()
-        .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id))
-        .enter(|infcx| {
-            let input_body: &Body<'_> = &input_body.borrow();
-            let promoted: &IndexVec<_, _> = &promoted.borrow();
-            do_mir_borrowck(&infcx, input_body, promoted, false).0
-        });
+    let infcx =
+        tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id)).build();
+    let input_body: &Body<'_> = &input_body.borrow();
+    let promoted: &IndexVec<_, _> = &promoted.borrow();
+    let opt_closure_req = do_mir_borrowck(&infcx, input_body, promoted, false).0;
     debug!("mir_borrowck done");
 
     tcx.arena.alloc(opt_closure_req)
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
index bed8914b3e2..a7c4671665f 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
@@ -266,73 +266,69 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
 
         // Only check this for TAIT. RPIT already supports `src/test/ui/impl-trait/nested-return-type2.rs`
         // on stable and we'd break that.
-        if let OpaqueTyOrigin::TyAlias = origin {
-            // This logic duplicates most of `check_opaque_meets_bounds`.
-            // FIXME(oli-obk): Also do region checks here and then consider removing `check_opaque_meets_bounds` entirely.
-            let param_env = self.tcx.param_env(def_id);
-            let body_id = self.tcx.local_def_id_to_hir_id(def_id);
-            // HACK This bubble is required for this tests to pass:
-            // type-alias-impl-trait/issue-67844-nested-opaque.rs
-            self.tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).enter(
-                move |infcx| {
-                    // 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(definition_ty.into()))
-                            .to_predicate(infcx.tcx);
-                    let mut fulfillment_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx);
-
-                    // Require that the hidden type actually fulfills all the bounds of the opaque type, even without
-                    // the bounds that the function supplies.
-                    match infcx.register_hidden_type(
-                        OpaqueTypeKey { def_id, substs: id_substs },
-                        ObligationCause::misc(instantiated_ty.span, body_id),
-                        param_env,
+        let OpaqueTyOrigin::TyAlias = origin else {
+            return definition_ty;
+        };
+        // This logic duplicates most of `check_opaque_meets_bounds`.
+        // FIXME(oli-obk): Also do region checks here and then consider removing `check_opaque_meets_bounds` entirely.
+        let param_env = self.tcx.param_env(def_id);
+        let body_id = self.tcx.local_def_id_to_hir_id(def_id);
+        // HACK This bubble is required for this tests to pass:
+        // type-alias-impl-trait/issue-67844-nested-opaque.rs
+        let infcx =
+            self.tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).build();
+        // 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(definition_ty.into()))
+            .to_predicate(infcx.tcx);
+        let mut fulfillment_cx = <dyn TraitEngine<'tcx>>::new(infcx.tcx);
+
+        // Require that the hidden type actually fulfills all the bounds of the opaque type, even without
+        // the bounds that the function supplies.
+        match infcx.register_hidden_type(
+            OpaqueTypeKey { def_id, substs: id_substs },
+            ObligationCause::misc(instantiated_ty.span, body_id),
+            param_env,
+            definition_ty,
+            origin,
+        ) {
+            Ok(infer_ok) => {
+                for obligation in infer_ok.obligations {
+                    fulfillment_cx.register_predicate_obligation(&infcx, obligation);
+                }
+            }
+            Err(err) => {
+                infcx
+                    .err_ctxt()
+                    .report_mismatched_types(
+                        &ObligationCause::misc(instantiated_ty.span, body_id),
+                        self.tcx.mk_opaque(def_id.to_def_id(), id_substs),
                         definition_ty,
-                        origin,
-                    ) {
-                        Ok(infer_ok) => {
-                            for obligation in infer_ok.obligations {
-                                fulfillment_cx.register_predicate_obligation(&infcx, obligation);
-                            }
-                        }
-                        Err(err) => {
-                            infcx
-                                .err_ctxt()
-                                .report_mismatched_types(
-                                    &ObligationCause::misc(instantiated_ty.span, body_id),
-                                    self.tcx.mk_opaque(def_id.to_def_id(), id_substs),
-                                    definition_ty,
-                                    err,
-                                )
-                                .emit();
-                        }
-                    }
+                        err,
+                    )
+                    .emit();
+            }
+        }
 
-                    fulfillment_cx.register_predicate_obligation(
-                        &infcx,
-                        Obligation::misc(instantiated_ty.span, body_id, param_env, predicate),
-                    );
+        fulfillment_cx.register_predicate_obligation(
+            &infcx,
+            Obligation::misc(instantiated_ty.span, body_id, param_env, predicate),
+        );
 
-                    // Check that all obligations are satisfied by the implementation's
-                    // version.
-                    let errors = fulfillment_cx.select_all_or_error(&infcx);
+        // Check that all obligations are satisfied by the implementation's
+        // version.
+        let errors = fulfillment_cx.select_all_or_error(&infcx);
 
-                    // This is still required for many(half of the tests in ui/type-alias-impl-trait)
-                    // tests to pass
-                    let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
+        // This is still required for many(half of the tests in ui/type-alias-impl-trait)
+        // tests to pass
+        let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
 
-                    if errors.is_empty() {
-                        definition_ty
-                    } else {
-                        infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
-                        self.tcx.ty_error()
-                    }
-                },
-            )
-        } else {
+        if errors.is_empty() {
             definition_ty
+        } else {
+            infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+            self.tcx.ty_error()
         }
     }
 }
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
index 8c8cadcada8..5eaddf682c3 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -737,14 +737,16 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                     let obligation =
                         Obligation::new(ObligationCause::dummy(), param_env, poly_trait_pred);
 
-                    let implsrc = tcx.infer_ctxt().enter(|infcx| {
+                    let implsrc = {
+                        let infcx = tcx.infer_ctxt().build();
                         let mut selcx = SelectionContext::new(&infcx);
                         selcx.select(&obligation)
-                    });
+                    };
 
                     // do a well-formedness check on the trait method being called. This is because typeck only does a
                     // "non-const" check. This is required for correctness here.
-                    tcx.infer_ctxt().enter(|infcx| {
+                    {
+                        let infcx = tcx.infer_ctxt().build();
                         let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
                         let predicates = tcx.predicates_of(callee).instantiate(tcx, substs);
                         let hir_id = tcx
@@ -777,7 +779,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                         if !errors.is_empty() {
                             infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
                         }
-                    });
+                    }
 
                     match implsrc {
                         Ok(Some(ImplSource::Param(_, ty::BoundConstness::ConstIfConst))) => {
@@ -835,16 +837,15 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
 
                                 // improve diagnostics by showing what failed. Our requirements are stricter this time
                                 // as we are going to error again anyways.
-                                tcx.infer_ctxt().enter(|infcx| {
-                                    if let Err(e) = implsrc {
-                                        infcx.err_ctxt().report_selection_error(
-                                            obligation.clone(),
-                                            &obligation,
-                                            &e,
-                                            false,
-                                        );
-                                    }
-                                });
+                                let infcx = tcx.infer_ctxt().build();
+                                if let Err(e) = implsrc {
+                                    infcx.err_ctxt().report_selection_error(
+                                        obligation.clone(),
+                                        &obligation,
+                                        &e,
+                                        false,
+                                    );
+                                }
 
                                 self.check_op(ops::FnCallNonConst {
                                     caller,
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
index b56b230201e..b28d7019491 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
@@ -156,10 +156,9 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
                         }),
                     );
 
-                    let implsrc = tcx.infer_ctxt().enter(|infcx| {
-                        let mut selcx = SelectionContext::new(&infcx);
-                        selcx.select(&obligation)
-                    });
+                    let infcx = tcx.infer_ctxt().build();
+                    let mut selcx = SelectionContext::new(&infcx);
+                    let implsrc = selcx.select(&obligation);
 
                     if let Ok(Some(ImplSource::UserDefined(data))) = implsrc {
                         let span = tcx.def_span(data.impl_def_id);
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
index 60deb0abd34..b77b213b51a 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
@@ -168,30 +168,28 @@ impl Qualif for NeedsNonConstDrop {
             }),
         );
 
-        cx.tcx.infer_ctxt().enter(|infcx| {
-            let mut selcx = SelectionContext::new(&infcx);
-            let Some(impl_src) = selcx.select(&obligation).ok().flatten() else {
-                // If we couldn't select a const destruct candidate, then it's bad
-                return true;
-            };
-
-            if !matches!(
-                impl_src,
-                ImplSource::ConstDestruct(_)
-                    | ImplSource::Param(_, ty::BoundConstness::ConstIfConst)
-            ) {
-                // If our const destruct candidate is not ConstDestruct or implied by the param env,
-                // then it's bad
-                return true;
-            }
+        let infcx = cx.tcx.infer_ctxt().build();
+        let mut selcx = SelectionContext::new(&infcx);
+        let Some(impl_src) = selcx.select(&obligation).ok().flatten() else {
+            // If we couldn't select a const destruct candidate, then it's bad
+            return true;
+        };
+
+        if !matches!(
+            impl_src,
+            ImplSource::ConstDestruct(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst)
+        ) {
+            // If our const destruct candidate is not ConstDestruct or implied by the param env,
+            // then it's bad
+            return true;
+        }
 
-            if impl_src.borrow_nested_obligations().is_empty() {
-                return false;
-            }
+        if impl_src.borrow_nested_obligations().is_empty() {
+            return false;
+        }
 
-            // If we had any errors, then it's bad
-            !traits::fully_solve_obligations(&infcx, impl_src.nested_obligations()).is_empty()
-        })
+        // If we had any errors, then it's bad
+        !traits::fully_solve_obligations(&infcx, impl_src.nested_obligations()).is_empty()
     }
 
     fn in_adt_inherently<'tcx>(
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 9c95ffca19b..23276e60982 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -105,7 +105,7 @@ pub fn equal_up_to_regions<'tcx>(
             },
         )
     };
-    tcx.infer_ctxt().enter(|infcx| infcx.can_eq(param_env, normalize(src), normalize(dest)).is_ok())
+    tcx.infer_ctxt().build().can_eq(param_env, normalize(src), normalize(dest)).is_ok()
 }
 
 struct TypeChecker<'a, 'tcx> {
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/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 48b06898065..da5d0706bc0 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -732,52 +732,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 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}"),
-                    );
-                }
-            }
+    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);
+
+    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.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();
-        },
-    );
+    // 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) {
diff --git a/compiler/rustc_hir_analysis/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs
index c6f5570b297..5e5dbedb4bd 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_method.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs
@@ -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.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,
-            );
+        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.err_ctxt().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 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(),
-        );
-
-        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 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.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());
-            }
-        }
+    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.err_ctxt().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> {
@@ -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) {
@@ -1312,104 +1302,102 @@ pub(crate) fn raw_compare_const_impl<'tcx>(
 
     let impl_c_span = tcx.def_span(impl_const_item_def.to_def_id());
 
-    tcx.infer_ctxt().enter(|infcx| {
-        let param_env = tcx.param_env(impl_const_item_def.to_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_const_item_def);
-
-        // 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 infcx = tcx.infer_ctxt().build();
+    let param_env = tcx.param_env(impl_const_item_def.to_def_id());
+    let ocx = ObligationCtxt::new(&infcx);
 
-        // There is no "body" here, so just pass dummy id.
-        let impl_ty = ocx.normalize(cause.clone(), param_env, 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;
 
-        debug!("compare_const_impl: impl_ty={:?}", impl_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);
 
-        let trait_ty = ocx.normalize(cause.clone(), param_env, 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,
+        },
+    );
 
-        debug!("compare_const_impl: trait_ty={:?}", trait_ty);
+    // There is no "body" here, so just pass dummy id.
+    let impl_ty = ocx.normalize(cause.clone(), param_env, impl_ty);
 
-        let err = infcx
-            .at(&cause, param_env)
-            .sup(trait_ty, impl_ty)
-            .map(|ok| ocx.register_infer_ok_obligations(ok));
+    debug!("compare_const_impl: impl_ty={:?}", impl_ty);
 
-        if let Err(terr) = err {
-            debug!(
-                "checking associated const for compatibility: impl ty {:?}, trait ty {:?}",
-                impl_ty, trait_ty
-            );
+    let trait_ty = ocx.normalize(cause.clone(), param_env, trait_ty);
 
-            // 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 mut diag = struct_span_err!(
-                tcx.sess,
-                cause.span,
-                E0326,
-                "implemented const `{}` has an incompatible type for trait",
-                trait_const_item.name
-            );
+    debug!("compare_const_impl: trait_ty={:?}", trait_ty);
 
-            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),
-                }
-            });
+    let err = infcx
+        .at(&cause, param_env)
+        .sup(trait_ty, impl_ty)
+        .map(|ok| ocx.register_infer_ok_obligations(ok));
 
-            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());
-        };
+    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() {
-            return Err(infcx.err_ctxt().report_fulfillment_errors(&errors, None, false));
+        // 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),
         }
 
-        // 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(())
-    })
+        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>(
@@ -1490,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.err_ctxt().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));
+    }
 
-        Ok(())
-    })
+    // 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::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
@@ -1695,95 +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.err_ctxt().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
-                .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();
-        }
+    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/fn_ctxt/suggestions.rs b/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs
index 0ea150c969e..7a40def177a 100644
--- a/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs
@@ -876,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) {
diff --git a/compiler/rustc_hir_analysis/src/check/inherited.rs b/compiler/rustc_hir_analysis/src/check/inherited.rs
index 7930377abaa..0fb7651b3a1 100644
--- a/compiler/rustc_hir_analysis/src/check/inherited.rs
+++ b/compiler/rustc_hir_analysis/src/check/inherited.rs
@@ -129,7 +129,7 @@ impl<'tcx> InheritedBuilder<'tcx> {
         F: FnOnce(&Inherited<'tcx>) -> R,
     {
         let def_id = self.def_id;
-        self.infcx.enter(|infcx| f(&Inherited::new(infcx, def_id, self.typeck_results)))
+        f(&Inherited::new(self.infcx.build(), def_id, self.typeck_results))
     }
 }
 
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/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 9a2bd9c95ed..441eac03b50 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -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.err_ctxt().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) {
@@ -704,24 +703,23 @@ fn resolve_regions_with_wf_tys<'tcx>(
     // 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
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index 2cc389498af..b6c91d425df 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -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.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);
+            } 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,221 +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
-                    .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))
-            }
+    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.err_ctxt().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/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
index 7b080dc2942..53f636d19b4 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;
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 9824df0c6bc..bfe5d4751e0 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
@@ -139,34 +139,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.err_ctxt().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.
@@ -344,23 +343,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 4d3df5c0e52..b7d9fc8a2fe 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -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.err_ctxt().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.err_ctxt().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.err_ctxt().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_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 8e0c153b642..441dc3c7e88 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -602,30 +602,27 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
     /// `V` and a substitution `S`. This substitution `S` maps from
     /// the bound values in `C` to their instantiated values in `V`
     /// (in other words, `S(C) = V`).
-    pub fn enter_with_canonical<T, R>(
+    pub fn build_with_canonical<T>(
         &mut self,
         span: Span,
         canonical: &Canonical<'tcx, T>,
-        f: impl FnOnce(InferCtxt<'tcx>, T, CanonicalVarValues<'tcx>) -> R,
-    ) -> R
+    ) -> (InferCtxt<'tcx>, T, CanonicalVarValues<'tcx>)
     where
         T: TypeFoldable<'tcx>,
     {
-        self.enter(|infcx| {
-            let (value, subst) =
-                infcx.instantiate_canonical_with_fresh_inference_vars(span, canonical);
-            f(infcx, value, subst)
-        })
+        let infcx = self.build();
+        let (value, subst) = infcx.instantiate_canonical_with_fresh_inference_vars(span, canonical);
+        (infcx, value, subst)
     }
 
-    pub fn enter<R>(&mut self, f: impl FnOnce(InferCtxt<'tcx>) -> R) -> R {
+    pub fn build(&mut self) -> InferCtxt<'tcx> {
         let InferCtxtBuilder {
             tcx,
             defining_use_anchor,
             considering_regions,
             ref normalize_fn_sig_for_diagnostic,
         } = *self;
-        f(InferCtxt {
+        InferCtxt {
             tcx,
             defining_use_anchor,
             considering_regions,
@@ -643,7 +640,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
             normalize_fn_sig_for_diagnostic: normalize_fn_sig_for_diagnostic
                 .as_ref()
                 .map(|f| f.clone()),
-        })
+        }
     }
 }
 
diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs
index 9d2a23f2b5f..886b686e5e8 100644
--- a/compiler/rustc_lint/src/non_fmt_panic.rs
+++ b/compiler/rustc_lint/src/non_fmt_panic.rs
@@ -151,21 +151,19 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
                 Some(ty_def) if cx.tcx.is_diagnostic_item(sym::String, ty_def.did()),
             );
 
-            let (suggest_display, suggest_debug) = cx.tcx.infer_ctxt().enter(|infcx| {
-                let display = is_str
-                    || cx.tcx.get_diagnostic_item(sym::Display).map(|t| {
-                        infcx
-                            .type_implements_trait(t, ty, InternalSubsts::empty(), cx.param_env)
-                            .may_apply()
-                    }) == Some(true);
-                let debug = !display
-                    && cx.tcx.get_diagnostic_item(sym::Debug).map(|t| {
-                        infcx
-                            .type_implements_trait(t, ty, InternalSubsts::empty(), cx.param_env)
-                            .may_apply()
-                    }) == Some(true);
-                (display, debug)
-            });
+            let infcx = cx.tcx.infer_ctxt().build();
+            let suggest_display = is_str
+                || cx.tcx.get_diagnostic_item(sym::Display).map(|t| {
+                    infcx
+                        .type_implements_trait(t, ty, InternalSubsts::empty(), cx.param_env)
+                        .may_apply()
+                }) == Some(true);
+            let suggest_debug = !suggest_display
+                && cx.tcx.get_diagnostic_item(sym::Debug).map(|t| {
+                    infcx
+                        .type_implements_trait(t, ty, InternalSubsts::empty(), cx.param_env)
+                        .may_apply()
+                }) == Some(true);
 
             let suggest_panic_any = !is_str && panic == sym::std_panic_macro;
 
diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
index 31705624a7f..81b9f55e703 100644
--- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
+++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
@@ -62,85 +62,81 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
         let hir::ItemKind::OpaqueTy(_) = &item.kind else { return; };
         let def_id = item.def_id.def_id.to_def_id();
-        cx.tcx.infer_ctxt().enter(|ref infcx| {
-            // For every projection predicate in the opaque type's explicit bounds,
-            // check that the type that we're assigning actually satisfies the bounds
-            // of the associated type.
-            for &(pred, pred_span) in cx.tcx.explicit_item_bounds(def_id) {
-                // Liberate bound regions in the predicate since we
-                // don't actually care about lifetimes in this check.
-                let predicate = cx.tcx.liberate_late_bound_regions(
-                    def_id,
-                    pred.kind(),
-                );
-                let ty::PredicateKind::Projection(proj) = predicate else {
-                    continue;
-                };
-                // Only check types, since those are the only things that may
-                // have opaques in them anyways.
-                let Some(proj_term) = proj.term.ty() else { continue };
+        let infcx = &cx.tcx.infer_ctxt().build();
+        // For every projection predicate in the opaque type's explicit bounds,
+        // check that the type that we're assigning actually satisfies the bounds
+        // of the associated type.
+        for &(pred, pred_span) in cx.tcx.explicit_item_bounds(def_id) {
+            // Liberate bound regions in the predicate since we
+            // don't actually care about lifetimes in this check.
+            let predicate = cx.tcx.liberate_late_bound_regions(def_id, pred.kind());
+            let ty::PredicateKind::Projection(proj) = predicate else {
+                continue;
+            };
+            // Only check types, since those are the only things that may
+            // have opaques in them anyways.
+            let Some(proj_term) = proj.term.ty() else { continue };
 
-                let proj_ty =
-                    cx
-                    .tcx
-                    .mk_projection(proj.projection_ty.item_def_id, proj.projection_ty.substs);
-                // For every instance of the projection type in the bounds,
-                // replace them with the term we're assigning to the associated
-                // type in our opaque type.
-                let proj_replacer = &mut BottomUpFolder {
-                    tcx: cx.tcx,
-                    ty_op: |ty| if ty == proj_ty { proj_term } else { ty },
-                    lt_op: |lt| lt,
-                    ct_op: |ct| ct,
+            let proj_ty =
+                cx.tcx.mk_projection(proj.projection_ty.item_def_id, proj.projection_ty.substs);
+            // For every instance of the projection type in the bounds,
+            // replace them with the term we're assigning to the associated
+            // type in our opaque type.
+            let proj_replacer = &mut BottomUpFolder {
+                tcx: cx.tcx,
+                ty_op: |ty| if ty == proj_ty { proj_term } else { ty },
+                lt_op: |lt| lt,
+                ct_op: |ct| ct,
+            };
+            // For example, in `impl Trait<Assoc = impl Send>`, for all of the bounds on `Assoc`,
+            // e.g. `type Assoc: OtherTrait`, replace `<impl Trait as Trait>::Assoc: OtherTrait`
+            // with `impl Send: OtherTrait`.
+            for assoc_pred_and_span in
+                cx.tcx.bound_explicit_item_bounds(proj.projection_ty.item_def_id).transpose_iter()
+            {
+                let assoc_pred_span = assoc_pred_and_span.0.1;
+                let assoc_pred = assoc_pred_and_span
+                    .map_bound(|(pred, _)| *pred)
+                    .subst(cx.tcx, &proj.projection_ty.substs)
+                    .fold_with(proj_replacer);
+                let Ok(assoc_pred) = traits::fully_normalize(infcx, traits::ObligationCause::dummy(), cx.param_env, assoc_pred) else {
+                    continue;
                 };
-                // For example, in `impl Trait<Assoc = impl Send>`, for all of the bounds on `Assoc`,
-                // e.g. `type Assoc: OtherTrait`, replace `<impl Trait as Trait>::Assoc: OtherTrait`
-                // with `impl Send: OtherTrait`.
-                for assoc_pred_and_span in cx
-                    .tcx
-                    .bound_explicit_item_bounds(proj.projection_ty.item_def_id)
-                    .transpose_iter()
-                {
-                    let assoc_pred_span = assoc_pred_and_span.0.1;
-                    let assoc_pred = assoc_pred_and_span
-                        .map_bound(|(pred, _)| *pred)
-                        .subst(cx.tcx, &proj.projection_ty.substs)
-                        .fold_with(proj_replacer);
-                    let Ok(assoc_pred) = traits::fully_normalize(infcx, traits::ObligationCause::dummy(), cx.param_env, assoc_pred) else {
-                        continue;
+                // If that predicate doesn't hold modulo regions (but passed during type-check),
+                // then we must've taken advantage of the hack in `project_and_unify_types` where
+                // we replace opaques with inference vars. Emit a warning!
+                if !infcx.predicate_must_hold_modulo_regions(&traits::Obligation::new(
+                    traits::ObligationCause::dummy(),
+                    cx.param_env,
+                    assoc_pred,
+                )) {
+                    // If it's a trait bound and an opaque that doesn't satisfy it,
+                    // then we can emit a suggestion to add the bound.
+                    let add_bound = match (proj_term.kind(), assoc_pred.kind().skip_binder()) {
+                        (ty::Opaque(def_id, _), ty::PredicateKind::Trait(trait_pred)) => {
+                            Some(AddBound {
+                                suggest_span: cx.tcx.def_span(*def_id).shrink_to_hi(),
+                                trait_ref: trait_pred.print_modifiers_and_trait_path(),
+                            })
+                        }
+                        _ => None,
                     };
-                    // If that predicate doesn't hold modulo regions (but passed during type-check),
-                    // then we must've taken advantage of the hack in `project_and_unify_types` where
-                    // we replace opaques with inference vars. Emit a warning!
-                    if !infcx.predicate_must_hold_modulo_regions(&traits::Obligation::new(
-                        traits::ObligationCause::dummy(),
-                        cx.param_env,
-                        assoc_pred,
-                    )) {
-                        // If it's a trait bound and an opaque that doesn't satisfy it,
-                        // then we can emit a suggestion to add the bound.
-                        let add_bound =
-                            match (proj_term.kind(), assoc_pred.kind().skip_binder()) {
-                                (ty::Opaque(def_id, _), ty::PredicateKind::Trait(trait_pred)) => Some(AddBound {
-                                    suggest_span: cx.tcx.def_span(*def_id).shrink_to_hi(),
-                                    trait_ref: trait_pred.print_modifiers_and_trait_path(),
-                                }),
-                                _ => None,
-                            };
-                        cx.emit_spanned_lint(
-                            OPAQUE_HIDDEN_INFERRED_BOUND,
-                            pred_span,
-                            OpaqueHiddenInferredBoundLint {
-                                ty: cx.tcx.mk_opaque(def_id, ty::InternalSubsts::identity_for_item(cx.tcx, def_id)),
-                                proj_ty: proj_term,
-                                assoc_pred_span,
-                                add_bound,
-                            },
-                        );
-                    }
+                    cx.emit_spanned_lint(
+                        OPAQUE_HIDDEN_INFERRED_BOUND,
+                        pred_span,
+                        OpaqueHiddenInferredBoundLint {
+                            ty: cx.tcx.mk_opaque(
+                                def_id,
+                                ty::InternalSubsts::identity_for_item(cx.tcx, def_id),
+                            ),
+                            proj_ty: proj_term,
+                            assoc_pred_span,
+                            add_bound,
+                        },
+                    );
                 }
             }
-        });
+        }
     }
 }
 
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index ed7609d4c22..cbcf9cd129f 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -481,54 +481,49 @@ fn construct_fn<'tcx>(
         (None, fn_sig.output())
     };
 
-    let mut body = tcx.infer_ctxt().enter(|infcx| {
-        let mut builder = Builder::new(
-            thir,
-            infcx,
-            fn_def,
-            fn_id,
-            span_with_body,
-            arguments.len(),
-            safety,
-            return_ty,
-            return_ty_span,
-            generator_kind,
-        );
+    let infcx = tcx.infer_ctxt().build();
+    let mut builder = Builder::new(
+        thir,
+        infcx,
+        fn_def,
+        fn_id,
+        span_with_body,
+        arguments.len(),
+        safety,
+        return_ty,
+        return_ty_span,
+        generator_kind,
+    );
 
-        let call_site_scope =
-            region::Scope { id: body_id.hir_id.local_id, data: region::ScopeData::CallSite };
-        let arg_scope =
-            region::Scope { id: body_id.hir_id.local_id, data: region::ScopeData::Arguments };
-        let source_info = builder.source_info(span);
-        let call_site_s = (call_site_scope, source_info);
-        unpack!(builder.in_scope(call_site_s, LintLevel::Inherited, |builder| {
-            let arg_scope_s = (arg_scope, source_info);
-            // Attribute epilogue to function's closing brace
-            let fn_end = span_with_body.shrink_to_hi();
-            let return_block = unpack!(builder.in_breakable_scope(
-                None,
-                Place::return_place(),
-                fn_end,
-                |builder| {
-                    Some(builder.in_scope(arg_scope_s, LintLevel::Inherited, |builder| {
-                        builder.args_and_body(
-                            START_BLOCK,
-                            fn_def.did,
-                            arguments,
-                            arg_scope,
-                            &thir[expr],
-                        )
-                    }))
-                }
-            ));
-            let source_info = builder.source_info(fn_end);
-            builder.cfg.terminate(return_block, source_info, TerminatorKind::Return);
-            builder.build_drop_trees();
-            return_block.unit()
-        }));
-
-        builder.finish()
-    });
+    let call_site_scope =
+        region::Scope { id: body_id.hir_id.local_id, data: region::ScopeData::CallSite };
+    let arg_scope =
+        region::Scope { id: body_id.hir_id.local_id, data: region::ScopeData::Arguments };
+    let source_info = builder.source_info(span);
+    let call_site_s = (call_site_scope, source_info);
+    unpack!(builder.in_scope(call_site_s, LintLevel::Inherited, |builder| {
+        let arg_scope_s = (arg_scope, source_info);
+        // Attribute epilogue to function's closing brace
+        let fn_end = span_with_body.shrink_to_hi();
+        let return_block =
+            unpack!(builder.in_breakable_scope(None, Place::return_place(), fn_end, |builder| {
+                Some(builder.in_scope(arg_scope_s, LintLevel::Inherited, |builder| {
+                    builder.args_and_body(
+                        START_BLOCK,
+                        fn_def.did,
+                        arguments,
+                        arg_scope,
+                        &thir[expr],
+                    )
+                }))
+            }));
+        let source_info = builder.source_info(fn_end);
+        builder.cfg.terminate(return_block, source_info, TerminatorKind::Return);
+        builder.build_drop_trees();
+        return_block.unit()
+    }));
+
+    let mut body = builder.finish();
 
     body.spread_arg = if abi == Abi::RustCall {
         // RustCall pseudo-ABI untuples the last argument.
@@ -584,30 +579,29 @@ fn construct_const<'a, 'tcx>(
     let typeck_results = tcx.typeck_opt_const_arg(def);
     let const_ty = typeck_results.node_type(hir_id);
 
-    tcx.infer_ctxt().enter(|infcx| {
-        let mut builder = Builder::new(
-            thir,
-            infcx,
-            def,
-            hir_id,
-            span,
-            0,
-            Safety::Safe,
-            const_ty,
-            const_ty_span,
-            None,
-        );
+    let infcx = tcx.infer_ctxt().build();
+    let mut builder = Builder::new(
+        thir,
+        infcx,
+        def,
+        hir_id,
+        span,
+        0,
+        Safety::Safe,
+        const_ty,
+        const_ty_span,
+        None,
+    );
 
-        let mut block = START_BLOCK;
-        unpack!(block = builder.expr_into_dest(Place::return_place(), block, &thir[expr]));
+    let mut block = START_BLOCK;
+    unpack!(block = builder.expr_into_dest(Place::return_place(), block, &thir[expr]));
 
-        let source_info = builder.source_info(span);
-        builder.cfg.terminate(block, source_info, TerminatorKind::Return);
+    let source_info = builder.source_info(span);
+    builder.cfg.terminate(block, source_info, TerminatorKind::Return);
 
-        builder.build_drop_trees();
+    builder.build_drop_trees();
 
-        builder.finish()
-    })
+    builder.finish()
 }
 
 /// Construct MIR for an item that has had errors in type checking.
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
index d477eb1ffeb..cf8ae776be9 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -28,10 +28,9 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         span: Span,
         mir_structural_match_violation: bool,
     ) -> Box<Pat<'tcx>> {
-        self.tcx.infer_ctxt().enter(|infcx| {
-            let mut convert = ConstToPat::new(self, id, span, infcx);
-            convert.to_pat(cv, mir_structural_match_violation)
-        })
+        let infcx = self.tcx.infer_ctxt().build();
+        let mut convert = ConstToPat::new(self, id, span, infcx);
+        convert.to_pat(cv, mir_structural_match_violation)
     }
 }
 
diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs
index 91c91d4e52f..1b58c9b864e 100644
--- a/compiler/rustc_trait_selection/src/infer.rs
+++ b/compiler/rustc_trait_selection/src/infer.rs
@@ -177,18 +177,10 @@ impl<'tcx> InferCtxtBuilderExt<'tcx> for InferCtxtBuilder<'tcx> {
         R: Debug + TypeFoldable<'tcx>,
         Canonical<'tcx, QueryResponse<'tcx, R>>: ArenaAllocatable<'tcx>,
     {
-        self.enter_with_canonical(
-            DUMMY_SP,
-            canonical_key,
-            |ref infcx, key, canonical_inference_vars| {
-                let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
-                let value = operation(infcx, &mut *fulfill_cx, key)?;
-                infcx.make_canonicalized_query_response(
-                    canonical_inference_vars,
-                    value,
-                    &mut *fulfill_cx,
-                )
-            },
-        )
+        let (ref infcx, key, canonical_inference_vars) =
+            self.build_with_canonical(DUMMY_SP, canonical_key);
+        let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
+        let value = operation(infcx, &mut *fulfill_cx, key)?;
+        infcx.make_canonicalized_query_response(canonical_inference_vars, value, &mut *fulfill_cx)
     }
 }
diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
index c02265ffdd7..c716c4b0be9 100644
--- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs
+++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -10,7 +10,7 @@ use crate::traits::project::ProjectAndUnifyResult;
 use rustc_middle::mir::interpret::ErrorHandled;
 use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
 use rustc_middle::ty::visit::TypeVisitable;
-use rustc_middle::ty::{Region, RegionVid};
+use rustc_middle::ty::{PolyTraitRef, Region, RegionVid};
 
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 
@@ -90,143 +90,105 @@ impl<'tcx> AutoTraitFinder<'tcx> {
 
         let trait_pred = ty::Binder::dummy(trait_ref);
 
-        let bail_out = tcx.infer_ctxt().enter(|infcx| {
-            let mut selcx = SelectionContext::new(&infcx);
-            let result = selcx.select(&Obligation::new(
-                ObligationCause::dummy(),
-                orig_env,
-                trait_pred.to_poly_trait_predicate(),
-            ));
-
-            match result {
-                Ok(Some(ImplSource::UserDefined(_))) => {
-                    debug!(
-                        "find_auto_trait_generics({:?}): \
-                         manual impl found, bailing out",
-                        trait_ref
-                    );
-                    return true;
-                }
-                _ => {}
+        let infcx = tcx.infer_ctxt().build();
+        let mut selcx = SelectionContext::new(&infcx);
+        for f in [
+            PolyTraitRef::to_poly_trait_predicate,
+            PolyTraitRef::to_poly_trait_predicate_negative_polarity,
+        ] {
+            let result =
+                selcx.select(&Obligation::new(ObligationCause::dummy(), orig_env, f(&trait_pred)));
+            if let Ok(Some(ImplSource::UserDefined(_))) = result {
+                debug!(
+                    "find_auto_trait_generics({:?}): \
+                 manual impl found, bailing out",
+                    trait_ref
+                );
+                // If an explicit impl exists, it always takes priority over an auto impl
+                return AutoTraitResult::ExplicitImpl;
             }
-
-            let result = selcx.select(&Obligation::new(
-                ObligationCause::dummy(),
-                orig_env,
-                trait_pred.to_poly_trait_predicate_negative_polarity(),
-            ));
-
-            match result {
-                Ok(Some(ImplSource::UserDefined(_))) => {
-                    debug!(
-                        "find_auto_trait_generics({:?}): \
-                         manual impl found, bailing out",
-                        trait_ref
-                    );
-                    true
-                }
-                _ => false,
-            }
-        });
-
-        // If an explicit impl exists, it always takes priority over an auto impl
-        if bail_out {
-            return AutoTraitResult::ExplicitImpl;
         }
 
-        tcx.infer_ctxt().enter(|infcx| {
-            let mut fresh_preds = FxHashSet::default();
+        let infcx = tcx.infer_ctxt().build();
+        let mut fresh_preds = FxHashSet::default();
+
+        // Due to the way projections are handled by SelectionContext, we need to run
+        // evaluate_predicates twice: once on the original param env, and once on the result of
+        // the first evaluate_predicates call.
+        //
+        // The problem is this: most of rustc, including SelectionContext and traits::project,
+        // are designed to work with a concrete usage of a type (e.g., Vec<u8>
+        // fn<T>() { Vec<T> }. This information will generally never change - given
+        // the 'T' in fn<T>() { ... }, we'll never know anything else about 'T'.
+        // If we're unable to prove that 'T' implements a particular trait, we're done -
+        // there's nothing left to do but error out.
+        //
+        // However, synthesizing an auto trait impl works differently. Here, we start out with
+        // a set of initial conditions - the ParamEnv of the struct/enum/union we're dealing
+        // with - and progressively discover the conditions we need to fulfill for it to
+        // implement a certain auto trait. This ends up breaking two assumptions made by trait
+        // selection and projection:
+        //
+        // * We can always cache the result of a particular trait selection for the lifetime of
+        // an InfCtxt
+        // * Given a projection bound such as '<T as SomeTrait>::SomeItem = K', if 'T:
+        // SomeTrait' doesn't hold, then we don't need to care about the 'SomeItem = K'
+        //
+        // We fix the first assumption by manually clearing out all of the InferCtxt's caches
+        // in between calls to SelectionContext.select. This allows us to keep all of the
+        // intermediate types we create bound to the 'tcx lifetime, rather than needing to lift
+        // them between calls.
+        //
+        // We fix the second assumption by reprocessing the result of our first call to
+        // evaluate_predicates. Using the example of '<T as SomeTrait>::SomeItem = K', our first
+        // pass will pick up 'T: SomeTrait', but not 'SomeItem = K'. On our second pass,
+        // traits::project will see that 'T: SomeTrait' is in our ParamEnv, allowing
+        // SelectionContext to return it back to us.
+
+        let Some((new_env, user_env)) = self.evaluate_predicates(
+            &infcx,
+            trait_did,
+            ty,
+            orig_env,
+            orig_env,
+            &mut fresh_preds,
+            false,
+        ) else {
+            return AutoTraitResult::NegativeImpl;
+        };
+
+        let (full_env, full_user_env) = self
+            .evaluate_predicates(&infcx, trait_did, ty, new_env, user_env, &mut fresh_preds, true)
+            .unwrap_or_else(|| {
+                panic!("Failed to fully process: {:?} {:?} {:?}", ty, trait_did, orig_env)
+            });
 
-            // Due to the way projections are handled by SelectionContext, we need to run
-            // evaluate_predicates twice: once on the original param env, and once on the result of
-            // the first evaluate_predicates call.
-            //
-            // The problem is this: most of rustc, including SelectionContext and traits::project,
-            // are designed to work with a concrete usage of a type (e.g., Vec<u8>
-            // fn<T>() { Vec<T> }. This information will generally never change - given
-            // the 'T' in fn<T>() { ... }, we'll never know anything else about 'T'.
-            // If we're unable to prove that 'T' implements a particular trait, we're done -
-            // there's nothing left to do but error out.
-            //
-            // However, synthesizing an auto trait impl works differently. Here, we start out with
-            // a set of initial conditions - the ParamEnv of the struct/enum/union we're dealing
-            // with - and progressively discover the conditions we need to fulfill for it to
-            // implement a certain auto trait. This ends up breaking two assumptions made by trait
-            // selection and projection:
-            //
-            // * We can always cache the result of a particular trait selection for the lifetime of
-            // an InfCtxt
-            // * Given a projection bound such as '<T as SomeTrait>::SomeItem = K', if 'T:
-            // SomeTrait' doesn't hold, then we don't need to care about the 'SomeItem = K'
-            //
-            // We fix the first assumption by manually clearing out all of the InferCtxt's caches
-            // in between calls to SelectionContext.select. This allows us to keep all of the
-            // intermediate types we create bound to the 'tcx lifetime, rather than needing to lift
-            // them between calls.
-            //
-            // We fix the second assumption by reprocessing the result of our first call to
-            // evaluate_predicates. Using the example of '<T as SomeTrait>::SomeItem = K', our first
-            // pass will pick up 'T: SomeTrait', but not 'SomeItem = K'. On our second pass,
-            // traits::project will see that 'T: SomeTrait' is in our ParamEnv, allowing
-            // SelectionContext to return it back to us.
-
-            let Some((new_env, user_env)) = self.evaluate_predicates(
-                &infcx,
-                trait_did,
-                ty,
-                orig_env,
-                orig_env,
-                &mut fresh_preds,
-                false,
-            ) else {
-                return AutoTraitResult::NegativeImpl;
-            };
-
-            let (full_env, full_user_env) = self
-                .evaluate_predicates(
-                    &infcx,
-                    trait_did,
-                    ty,
-                    new_env,
-                    user_env,
-                    &mut fresh_preds,
-                    true,
-                )
-                .unwrap_or_else(|| {
-                    panic!("Failed to fully process: {:?} {:?} {:?}", ty, trait_did, orig_env)
-                });
-
-            debug!(
-                "find_auto_trait_generics({:?}): fulfilling \
-                 with {:?}",
-                trait_ref, full_env
-            );
-            infcx.clear_caches();
-
-            // At this point, we already have all of the bounds we need. FulfillmentContext is used
-            // to store all of the necessary region/lifetime bounds in the InferContext, as well as
-            // an additional sanity check.
-            let errors =
-                super::fully_solve_bound(&infcx, ObligationCause::dummy(), full_env, ty, trait_did);
-            if !errors.is_empty() {
-                panic!("Unable to fulfill trait {:?} for '{:?}': {:?}", trait_did, ty, errors);
-            }
+        debug!(
+            "find_auto_trait_generics({:?}): fulfilling \
+             with {:?}",
+            trait_ref, full_env
+        );
+        infcx.clear_caches();
+
+        // At this point, we already have all of the bounds we need. FulfillmentContext is used
+        // to store all of the necessary region/lifetime bounds in the InferContext, as well as
+        // an additional sanity check.
+        let errors =
+            super::fully_solve_bound(&infcx, ObligationCause::dummy(), full_env, ty, trait_did);
+        if !errors.is_empty() {
+            panic!("Unable to fulfill trait {:?} for '{:?}': {:?}", trait_did, ty, errors);
+        }
 
-            infcx.process_registered_region_obligations(&Default::default(), full_env);
+        infcx.process_registered_region_obligations(&Default::default(), full_env);
 
-            let region_data = infcx
-                .inner
-                .borrow_mut()
-                .unwrap_region_constraints()
-                .region_constraint_data()
-                .clone();
+        let region_data =
+            infcx.inner.borrow_mut().unwrap_region_constraints().region_constraint_data().clone();
 
-            let vid_to_region = self.map_vid_to_region(&region_data);
+        let vid_to_region = self.map_vid_to_region(&region_data);
 
-            let info = AutoTraitInfo { full_user_env, region_data, vid_to_region };
+        let info = AutoTraitInfo { full_user_env, region_data, vid_to_region };
 
-            AutoTraitResult::PositiveImpl(auto_trait_callback(info))
-        })
+        AutoTraitResult::PositiveImpl(auto_trait_callback(info))
     }
 }
 
diff --git a/compiler/rustc_trait_selection/src/traits/codegen.rs b/compiler/rustc_trait_selection/src/traits/codegen.rs
index dde7527302c..8a62bf01567 100644
--- a/compiler/rustc_trait_selection/src/traits/codegen.rs
+++ b/compiler/rustc_trait_selection/src/traits/codegen.rs
@@ -29,60 +29,61 @@ pub fn codegen_select_candidate<'tcx>(
 
     // Do the initial selection for the obligation. This yields the
     // shallow result we are looking for -- that is, what specific impl.
-    let mut infcx_builder =
-        tcx.infer_ctxt().ignoring_regions().with_opaque_type_inference(DefiningAnchor::Bubble);
-    infcx_builder.enter(|infcx| {
-        //~^ HACK `Bubble` is required for
-        // this test to pass: type-alias-impl-trait/assoc-projection-ice.rs
-        let mut selcx = SelectionContext::new(&infcx);
+    let infcx = tcx
+        .infer_ctxt()
+        .ignoring_regions()
+        .with_opaque_type_inference(DefiningAnchor::Bubble)
+        .build();
+    //~^ HACK `Bubble` is required for
+    // this test to pass: type-alias-impl-trait/assoc-projection-ice.rs
+    let mut selcx = SelectionContext::new(&infcx);
 
-        let obligation_cause = ObligationCause::dummy();
-        let obligation =
-            Obligation::new(obligation_cause, param_env, trait_ref.to_poly_trait_predicate());
+    let obligation_cause = ObligationCause::dummy();
+    let obligation =
+        Obligation::new(obligation_cause, param_env, trait_ref.to_poly_trait_predicate());
 
-        let selection = match selcx.select(&obligation) {
-            Ok(Some(selection)) => selection,
-            Ok(None) => return Err(CodegenObligationError::Ambiguity),
-            Err(Unimplemented) => return Err(CodegenObligationError::Unimplemented),
-            Err(e) => {
-                bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref)
-            }
-        };
+    let selection = match selcx.select(&obligation) {
+        Ok(Some(selection)) => selection,
+        Ok(None) => return Err(CodegenObligationError::Ambiguity),
+        Err(Unimplemented) => return Err(CodegenObligationError::Unimplemented),
+        Err(e) => {
+            bug!("Encountered error `{:?}` selecting `{:?}` during codegen", e, trait_ref)
+        }
+    };
 
-        debug!(?selection);
+    debug!(?selection);
 
-        // Currently, we use a fulfillment context to completely resolve
-        // all nested obligations. This is because they can inform the
-        // inference of the impl's type parameters.
-        let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(tcx);
-        let impl_source = selection.map(|predicate| {
-            fulfill_cx.register_predicate_obligation(&infcx, predicate);
-        });
+    // Currently, we use a fulfillment context to completely resolve
+    // all nested obligations. This is because they can inform the
+    // inference of the impl's type parameters.
+    let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(tcx);
+    let impl_source = selection.map(|predicate| {
+        fulfill_cx.register_predicate_obligation(&infcx, predicate);
+    });
 
-        // In principle, we only need to do this so long as `impl_source`
-        // contains unbound type parameters. It could be a slight
-        // optimization to stop iterating early.
-        let errors = fulfill_cx.select_all_or_error(&infcx);
-        if !errors.is_empty() {
-            // `rustc_monomorphize::collector` assumes there are no type errors.
-            // Cycle errors are the only post-monomorphization errors possible; emit them now so
-            // `rustc_ty_utils::resolve_associated_item` doesn't return `None` post-monomorphization.
-            for err in errors {
-                if let FulfillmentErrorCode::CodeCycle(cycle) = err.code {
-                    infcx.err_ctxt().report_overflow_error_cycle(&cycle);
-                }
+    // In principle, we only need to do this so long as `impl_source`
+    // contains unbound type parameters. It could be a slight
+    // optimization to stop iterating early.
+    let errors = fulfill_cx.select_all_or_error(&infcx);
+    if !errors.is_empty() {
+        // `rustc_monomorphize::collector` assumes there are no type errors.
+        // Cycle errors are the only post-monomorphization errors possible; emit them now so
+        // `rustc_ty_utils::resolve_associated_item` doesn't return `None` post-monomorphization.
+        for err in errors {
+            if let FulfillmentErrorCode::CodeCycle(cycle) = err.code {
+                infcx.err_ctxt().report_overflow_error_cycle(&cycle);
             }
-            return Err(CodegenObligationError::FulfillmentError);
         }
+        return Err(CodegenObligationError::FulfillmentError);
+    }
 
-        let impl_source = infcx.resolve_vars_if_possible(impl_source);
-        let impl_source = infcx.tcx.erase_regions(impl_source);
+    let impl_source = infcx.resolve_vars_if_possible(impl_source);
+    let impl_source = infcx.tcx.erase_regions(impl_source);
 
-        // Opaque types may have gotten their hidden types constrained, but we can ignore them safely
-        // as they will get constrained elsewhere, too.
-        // (ouz-a) This is required for `type-alias-impl-trait/assoc-projection-ice.rs` to pass
-        let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
+    // Opaque types may have gotten their hidden types constrained, but we can ignore them safely
+    // as they will get constrained elsewhere, too.
+    // (ouz-a) This is required for `type-alias-impl-trait/assoc-projection-ice.rs` to pass
+    let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
 
-        Ok(&*tcx.arena.alloc(impl_source))
-    })
+    Ok(&*tcx.arena.alloc(impl_source))
 }
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index c3144ff6b07..7fc5c2ed0ea 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -100,11 +100,10 @@ where
         return no_overlap();
     }
 
-    let overlaps = tcx.infer_ctxt().enter(|infcx| {
-        let selcx = &mut SelectionContext::intercrate(&infcx);
-        overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).is_some()
-    });
-
+    let infcx = tcx.infer_ctxt().build();
+    let selcx = &mut SelectionContext::intercrate(&infcx);
+    let overlaps =
+        overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).is_some();
     if !overlaps {
         return no_overlap();
     }
@@ -112,13 +111,10 @@ where
     // In the case where we detect an error, run the check again, but
     // this time tracking intercrate ambiguity causes for better
     // diagnostics. (These take time and can lead to false errors.)
-    tcx.infer_ctxt().enter(|infcx| {
-        let selcx = &mut SelectionContext::intercrate(&infcx);
-        selcx.enable_tracking_intercrate_ambiguity_causes();
-        on_overlap(
-            overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).unwrap(),
-        )
-    })
+    let infcx = tcx.infer_ctxt().build();
+    let selcx = &mut SelectionContext::intercrate(&infcx);
+    selcx.enable_tracking_intercrate_ambiguity_causes();
+    on_overlap(overlap(selcx, skip_leak_check, impl1_def_id, impl2_def_id, overlap_mode).unwrap())
 }
 
 fn with_fresh_ty_vars<'cx, 'tcx>(
@@ -298,33 +294,32 @@ fn negative_impl<'cx, 'tcx>(
     let tcx = selcx.infcx().tcx;
 
     // Create an infcx, taking the predicates of impl1 as assumptions:
-    tcx.infer_ctxt().enter(|infcx| {
-        // create a parameter environment corresponding to a (placeholder) instantiation of impl1
-        let impl_env = tcx.param_env(impl1_def_id);
-        let subject1 = match traits::fully_normalize(
-            &infcx,
-            ObligationCause::dummy(),
-            impl_env,
-            tcx.impl_subject(impl1_def_id),
-        ) {
-            Ok(s) => s,
-            Err(err) => {
-                tcx.sess.delay_span_bug(
-                    tcx.def_span(impl1_def_id),
-                    format!("failed to fully normalize {:?}: {:?}", impl1_def_id, err),
-                );
-                return false;
-            }
-        };
+    let infcx = tcx.infer_ctxt().build();
+    // create a parameter environment corresponding to a (placeholder) instantiation of impl1
+    let impl_env = tcx.param_env(impl1_def_id);
+    let subject1 = match traits::fully_normalize(
+        &infcx,
+        ObligationCause::dummy(),
+        impl_env,
+        tcx.impl_subject(impl1_def_id),
+    ) {
+        Ok(s) => s,
+        Err(err) => {
+            tcx.sess.delay_span_bug(
+                tcx.def_span(impl1_def_id),
+                format!("failed to fully normalize {:?}: {:?}", impl1_def_id, err),
+            );
+            return false;
+        }
+    };
 
-        // Attempt to prove that impl2 applies, given all of the above.
-        let selcx = &mut SelectionContext::new(&infcx);
-        let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id);
-        let (subject2, obligations) =
-            impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs);
+    // Attempt to prove that impl2 applies, given all of the above.
+    let selcx = &mut SelectionContext::new(&infcx);
+    let impl2_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl2_def_id);
+    let (subject2, obligations) =
+        impl_subject_and_oblig(selcx, impl_env, impl2_def_id, impl2_substs);
 
-        !equate(&infcx, impl_env, subject1, subject2, obligations, impl1_def_id)
-    })
+    !equate(&infcx, impl_env, subject1, subject2, obligations, impl1_def_id)
 }
 
 fn equate<'tcx>(
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index e7270bd7c2d..5b4fa1df426 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -1930,16 +1930,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         }
 
         let normalize = |candidate| {
-            self.tcx.infer_ctxt().enter(|ref infcx| {
-                let normalized = infcx
-                    .at(&ObligationCause::dummy(), ty::ParamEnv::empty())
-                    .normalize(candidate)
-                    .ok();
-                match normalized {
-                    Some(normalized) => normalized.value,
-                    None => candidate,
-                }
-            })
+            let infcx = self.tcx.infer_ctxt().build();
+            infcx
+                .at(&ObligationCause::dummy(), ty::ParamEnv::empty())
+                .normalize(candidate)
+                .map_or(candidate, |normalized| normalized.value)
         };
 
         // Sort impl candidates so that ordering is consistent for UI tests.
diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs
index f0346e4ae69..be603c609cb 100644
--- a/compiler/rustc_trait_selection/src/traits/misc.rs
+++ b/compiler/rustc_trait_selection/src/traits/misc.rs
@@ -23,65 +23,64 @@ pub fn can_type_implement_copy<'tcx>(
     parent_cause: ObligationCause<'tcx>,
 ) -> Result<(), CopyImplementationError<'tcx>> {
     // FIXME: (@jroesch) float this code up
-    tcx.infer_ctxt().enter(|infcx| {
-        let (adt, substs) = match self_type.kind() {
-            // These types used to have a builtin impl.
-            // Now libcore provides that impl.
-            ty::Uint(_)
-            | ty::Int(_)
-            | ty::Bool
-            | ty::Float(_)
-            | ty::Char
-            | ty::RawPtr(..)
-            | ty::Never
-            | ty::Ref(_, _, hir::Mutability::Not)
-            | ty::Array(..) => return Ok(()),
+    let infcx = tcx.infer_ctxt().build();
+    let (adt, substs) = match self_type.kind() {
+        // These types used to have a builtin impl.
+        // Now libcore provides that impl.
+        ty::Uint(_)
+        | ty::Int(_)
+        | ty::Bool
+        | ty::Float(_)
+        | ty::Char
+        | ty::RawPtr(..)
+        | ty::Never
+        | ty::Ref(_, _, hir::Mutability::Not)
+        | ty::Array(..) => return Ok(()),
 
-            ty::Adt(adt, substs) => (adt, substs),
+        ty::Adt(adt, substs) => (adt, substs),
 
-            _ => return Err(CopyImplementationError::NotAnAdt),
-        };
+        _ => return Err(CopyImplementationError::NotAnAdt),
+    };
 
-        let mut infringing = Vec::new();
-        for variant in adt.variants() {
-            for field in &variant.fields {
-                let ty = field.ty(tcx, substs);
-                if ty.references_error() {
-                    continue;
-                }
-                let span = tcx.def_span(field.did);
-                // FIXME(compiler-errors): This gives us better spans for bad
-                // projection types like in issue-50480.
-                // If the ADT has substs, point to the cause we are given.
-                // If it does not, then this field probably doesn't normalize
-                // to begin with, and point to the bad field's span instead.
-                let cause = if field
-                    .ty(tcx, traits::InternalSubsts::identity_for_item(tcx, adt.did()))
-                    .has_non_region_param()
-                {
-                    parent_cause.clone()
-                } else {
-                    ObligationCause::dummy_with_span(span)
-                };
-                match traits::fully_normalize(&infcx, cause, param_env, ty) {
-                    Ok(ty) => {
-                        if !infcx.type_is_copy_modulo_regions(param_env, ty, span) {
-                            infringing.push((field, ty));
-                        }
-                    }
-                    Err(errors) => {
-                        infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
-                    }
-                };
+    let mut infringing = Vec::new();
+    for variant in adt.variants() {
+        for field in &variant.fields {
+            let ty = field.ty(tcx, substs);
+            if ty.references_error() {
+                continue;
             }
+            let span = tcx.def_span(field.did);
+            // FIXME(compiler-errors): This gives us better spans for bad
+            // projection types like in issue-50480.
+            // If the ADT has substs, point to the cause we are given.
+            // If it does not, then this field probably doesn't normalize
+            // to begin with, and point to the bad field's span instead.
+            let cause = if field
+                .ty(tcx, traits::InternalSubsts::identity_for_item(tcx, adt.did()))
+                .has_non_region_param()
+            {
+                parent_cause.clone()
+            } else {
+                ObligationCause::dummy_with_span(span)
+            };
+            match traits::fully_normalize(&infcx, cause, param_env, ty) {
+                Ok(ty) => {
+                    if !infcx.type_is_copy_modulo_regions(param_env, ty, span) {
+                        infringing.push((field, ty));
+                    }
+                }
+                Err(errors) => {
+                    infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+                }
+            };
         }
-        if !infringing.is_empty() {
-            return Err(CopyImplementationError::InfrigingFields(infringing));
-        }
-        if adt.has_dtor(tcx) {
-            return Err(CopyImplementationError::HasDestructor);
-        }
+    }
+    if !infringing.is_empty() {
+        return Err(CopyImplementationError::InfrigingFields(infringing));
+    }
+    if adt.has_dtor(tcx) {
+        return Err(CopyImplementationError::HasDestructor);
+    }
 
-        Ok(())
-    })
+    Ok(())
 }
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 5153ce11ca0..274a366873c 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -234,54 +234,51 @@ fn do_normalize_predicates<'tcx>(
     // by wfcheck anyway, so I'm not sure we have to check
     // them here too, and we will remove this function when
     // we move over to lazy normalization *anyway*.
-    tcx.infer_ctxt().ignoring_regions().enter(|infcx| {
-        let predicates = match fully_normalize(&infcx, cause, elaborated_env, predicates) {
-            Ok(predicates) => predicates,
-            Err(errors) => {
-                let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
-                return Err(reported);
-            }
-        };
+    let infcx = tcx.infer_ctxt().ignoring_regions().build();
+    let predicates = match fully_normalize(&infcx, cause, elaborated_env, predicates) {
+        Ok(predicates) => predicates,
+        Err(errors) => {
+            let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+            return Err(reported);
+        }
+    };
 
-        debug!("do_normalize_predictes: normalized predicates = {:?}", predicates);
+    debug!("do_normalize_predictes: normalized predicates = {:?}", predicates);
 
-        // We can use the `elaborated_env` here; the region code only
-        // cares about declarations like `'a: 'b`.
-        let outlives_env = OutlivesEnvironment::new(elaborated_env);
+    // We can use the `elaborated_env` here; the region code only
+    // cares about declarations like `'a: 'b`.
+    let outlives_env = OutlivesEnvironment::new(elaborated_env);
+
+    // FIXME: It's very weird that we ignore region obligations but apparently
+    // still need to use `resolve_regions` as we need the resolved regions in
+    // the normalized predicates.
+    let errors = infcx.resolve_regions(&outlives_env);
+    if !errors.is_empty() {
+        tcx.sess.delay_span_bug(
+            span,
+            format!("failed region resolution while normalizing {elaborated_env:?}: {errors:?}"),
+        );
+    }
 
-        // FIXME: It's very weird that we ignore region obligations but apparently
-        // still need to use `resolve_regions` as we need the resolved regions in
-        // the normalized predicates.
-        let errors = infcx.resolve_regions(&outlives_env);
-        if !errors.is_empty() {
-            tcx.sess.delay_span_bug(
+    match infcx.fully_resolve(predicates) {
+        Ok(predicates) => Ok(predicates),
+        Err(fixup_err) => {
+            // If we encounter a fixup error, it means that some type
+            // variable wound up unconstrained. I actually don't know
+            // if this can happen, and I certainly don't expect it to
+            // happen often, but if it did happen it probably
+            // represents a legitimate failure due to some kind of
+            // unconstrained variable.
+            //
+            // @lcnr: Let's still ICE here for now. I want a test case
+            // for that.
+            span_bug!(
                 span,
-                format!(
-                    "failed region resolution while normalizing {elaborated_env:?}: {errors:?}"
-                ),
+                "inference variables in normalized parameter environment: {}",
+                fixup_err
             );
         }
-
-        match infcx.fully_resolve(predicates) {
-            Ok(predicates) => Ok(predicates),
-            Err(fixup_err) => {
-                // If we encounter a fixup error, it means that some type
-                // variable wound up unconstrained. I actually don't know
-                // if this can happen, and I certainly don't expect it to
-                // happen often, but if it did happen it probably
-                // represents a legitimate failure due to some kind of
-                // unconstrained variable.
-                //
-                // @lcnr: Let's still ICE here for now. I want a test case
-                // for that.
-                span_bug!(
-                    span,
-                    "inference variables in normalized parameter environment: {}",
-                    fixup_err
-                );
-            }
-        }
-    })
+    }
 }
 
 // FIXME: this is gonna need to be removed ...
@@ -473,21 +470,20 @@ pub fn impossible_predicates<'tcx>(
 ) -> bool {
     debug!("impossible_predicates(predicates={:?})", predicates);
 
-    let result = tcx.infer_ctxt().enter(|infcx| {
-        let param_env = ty::ParamEnv::reveal_all();
-        let ocx = ObligationCtxt::new(&infcx);
-        let predicates = ocx.normalize(ObligationCause::dummy(), param_env, predicates);
-        for predicate in predicates {
-            let obligation = Obligation::new(ObligationCause::dummy(), param_env, predicate);
-            ocx.register_obligation(obligation);
-        }
-        let errors = ocx.select_all_or_error();
+    let infcx = tcx.infer_ctxt().build();
+    let param_env = ty::ParamEnv::reveal_all();
+    let ocx = ObligationCtxt::new(&infcx);
+    let predicates = ocx.normalize(ObligationCause::dummy(), param_env, predicates);
+    for predicate in predicates {
+        let obligation = Obligation::new(ObligationCause::dummy(), param_env, predicate);
+        ocx.register_obligation(obligation);
+    }
+    let errors = ocx.select_all_or_error();
 
-        // Clean up after ourselves
-        let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
+    // Clean up after ourselves
+    let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
 
-        !errors.is_empty()
-    });
+    let result = !errors.is_empty();
     debug!("impossible_predicates = {:?}", result);
     result
 }
@@ -578,18 +574,16 @@ fn is_impossible_method<'tcx>(
         }
     });
 
-    tcx.infer_ctxt().ignoring_regions().enter(|ref infcx| {
-        for obligation in predicates_for_trait {
-            // Ignore overflow error, to be conservative.
-            if let Ok(result) = infcx.evaluate_obligation(&obligation)
-                && !result.may_apply()
-            {
-                return true;
-            }
+    let infcx = tcx.infer_ctxt().ignoring_regions().build();
+    for obligation in predicates_for_trait {
+        // Ignore overflow error, to be conservative.
+        if let Ok(result) = infcx.evaluate_obligation(&obligation)
+            && !result.may_apply()
+        {
+            return true;
         }
-
-        false
-    })
+    }
+    false
 }
 
 #[derive(Clone, Debug)]
@@ -952,10 +946,9 @@ pub fn vtable_trait_upcasting_coercion_new_vptr_slot<'tcx>(
         }),
     );
 
-    let implsrc = tcx.infer_ctxt().enter(|infcx| {
-        let mut selcx = SelectionContext::new(&infcx);
-        selcx.select(&obligation).unwrap()
-    });
+    let infcx = tcx.infer_ctxt().build();
+    let mut selcx = SelectionContext::new(&infcx);
+    let implsrc = selcx.select(&obligation).unwrap();
 
     let Some(ImplSource::TraitUpcasting(implsrc_traitcasting)) = implsrc else {
         bug!();
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index 8f87a7fdeba..31dd30c5c6a 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -734,10 +734,9 @@ fn receiver_is_dispatchable<'tcx>(
         Obligation::new(ObligationCause::dummy(), param_env, predicate)
     };
 
-    tcx.infer_ctxt().enter(|ref infcx| {
-        // the receiver is dispatchable iff the obligation holds
-        infcx.predicate_must_hold_modulo_regions(&obligation)
-    })
+    let infcx = tcx.infer_ctxt().build();
+    // the receiver is dispatchable iff the obligation holds
+    infcx.predicate_must_hold_modulo_regions(&obligation)
 }
 
 fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<'tcx>>(
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index bfc3b760050..c891658582a 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -149,13 +149,9 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId,
     let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap();
 
     // Create an infcx, taking the predicates of impl1 as assumptions:
-    tcx.infer_ctxt().enter(|infcx| {
-        let impl1_trait_ref = match traits::fully_normalize(
-            &infcx,
-            ObligationCause::dummy(),
-            penv,
-            impl1_trait_ref,
-        ) {
+    let infcx = tcx.infer_ctxt().build();
+    let impl1_trait_ref =
+        match traits::fully_normalize(&infcx, ObligationCause::dummy(), penv, impl1_trait_ref) {
             Ok(impl1_trait_ref) => impl1_trait_ref,
             Err(_errors) => {
                 tcx.sess.delay_span_bug(
@@ -166,9 +162,8 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId,
             }
         };
 
-        // Attempt to prove that impl2 applies, given all of the above.
-        fulfill_implication(&infcx, penv, impl1_trait_ref, impl2_def_id).is_ok()
-    })
+    // Attempt to prove that impl2 applies, given all of the above.
+    fulfill_implication(&infcx, penv, impl1_trait_ref, impl2_def_id).is_ok()
 }
 
 /// Attempt to fulfill all obligations of `target_impl` after unification with
diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs
index b6cfff9eb0a..932dbbb81e5 100644
--- a/compiler/rustc_trait_selection/src/traits/structural_match.rs
+++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs
@@ -265,9 +265,8 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
 
 pub fn provide(providers: &mut Providers) {
     providers.has_structural_eq_impls = |tcx, ty| {
-        tcx.infer_ctxt().enter(|infcx| {
-            let cause = ObligationCause::dummy();
-            type_marked_structural(&infcx, ty, cause)
-        })
+        let infcx = tcx.infer_ctxt().build();
+        let cause = ObligationCause::dummy();
+        type_marked_structural(&infcx, ty, cause)
     };
 }
diff --git a/compiler/rustc_traits/src/dropck_outlives.rs b/compiler/rustc_traits/src/dropck_outlives.rs
index 7fb7c689550..d5a8ca5ea78 100644
--- a/compiler/rustc_traits/src/dropck_outlives.rs
+++ b/compiler/rustc_traits/src/dropck_outlives.rs
@@ -27,128 +27,120 @@ fn dropck_outlives<'tcx>(
 ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, DropckOutlivesResult<'tcx>>>, NoSolution> {
     debug!("dropck_outlives(goal={:#?})", canonical_goal);
 
-    tcx.infer_ctxt().enter_with_canonical(
-        DUMMY_SP,
-        &canonical_goal,
-        |ref infcx, goal, canonical_inference_vars| {
-            let tcx = infcx.tcx;
-            let ParamEnvAnd { param_env, value: for_ty } = goal;
-
-            let mut result = DropckOutlivesResult { kinds: vec![], overflows: vec![] };
-
-            // A stack of types left to process. Each round, we pop
-            // something from the stack and invoke
-            // `dtorck_constraint_for_ty`. This may produce new types that
-            // have to be pushed on the stack. This continues until we have explored
-            // all the reachable types from the type `for_ty`.
-            //
-            // Example: Imagine that we have the following code:
-            //
-            // ```rust
-            // struct A {
-            //     value: B,
-            //     children: Vec<A>,
-            // }
-            //
-            // struct B {
-            //     value: u32
-            // }
-            //
-            // fn f() {
-            //   let a: A = ...;
-            //   ..
-            // } // here, `a` is dropped
-            // ```
-            //
-            // at the point where `a` is dropped, we need to figure out
-            // which types inside of `a` contain region data that may be
-            // accessed by any destructors in `a`. We begin by pushing `A`
-            // onto the stack, as that is the type of `a`. We will then
-            // invoke `dtorck_constraint_for_ty` which will expand `A`
-            // into the types of its fields `(B, Vec<A>)`. These will get
-            // pushed onto the stack. Eventually, expanding `Vec<A>` will
-            // lead to us trying to push `A` a second time -- to prevent
-            // infinite recursion, we notice that `A` was already pushed
-            // once and stop.
-            let mut ty_stack = vec![(for_ty, 0)];
-
-            // Set used to detect infinite recursion.
-            let mut ty_set = FxHashSet::default();
-
-            let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
-
-            let cause = ObligationCause::dummy();
-            let mut constraints = DropckConstraint::empty();
-            while let Some((ty, depth)) = ty_stack.pop() {
-                debug!(
-                    "{} kinds, {} overflows, {} ty_stack",
-                    result.kinds.len(),
-                    result.overflows.len(),
-                    ty_stack.len()
-                );
-                dtorck_constraint_for_ty(tcx, DUMMY_SP, for_ty, depth, ty, &mut constraints)?;
-
-                // "outlives" represent types/regions that may be touched
-                // by a destructor.
-                result.kinds.append(&mut constraints.outlives);
-                result.overflows.append(&mut constraints.overflows);
-
-                // If we have even one overflow, we should stop trying to evaluate further --
-                // chances are, the subsequent overflows for this evaluation won't provide useful
-                // information and will just decrease the speed at which we can emit these errors
-                // (since we'll be printing for just that much longer for the often enormous types
-                // that result here).
-                if !result.overflows.is_empty() {
-                    break;
-                }
+    let (ref infcx, goal, canonical_inference_vars) =
+        tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal);
+    let tcx = infcx.tcx;
+    let ParamEnvAnd { param_env, value: for_ty } = goal;
+
+    let mut result = DropckOutlivesResult { kinds: vec![], overflows: vec![] };
+
+    // A stack of types left to process. Each round, we pop
+    // something from the stack and invoke
+    // `dtorck_constraint_for_ty`. This may produce new types that
+    // have to be pushed on the stack. This continues until we have explored
+    // all the reachable types from the type `for_ty`.
+    //
+    // Example: Imagine that we have the following code:
+    //
+    // ```rust
+    // struct A {
+    //     value: B,
+    //     children: Vec<A>,
+    // }
+    //
+    // struct B {
+    //     value: u32
+    // }
+    //
+    // fn f() {
+    //   let a: A = ...;
+    //   ..
+    // } // here, `a` is dropped
+    // ```
+    //
+    // at the point where `a` is dropped, we need to figure out
+    // which types inside of `a` contain region data that may be
+    // accessed by any destructors in `a`. We begin by pushing `A`
+    // onto the stack, as that is the type of `a`. We will then
+    // invoke `dtorck_constraint_for_ty` which will expand `A`
+    // into the types of its fields `(B, Vec<A>)`. These will get
+    // pushed onto the stack. Eventually, expanding `Vec<A>` will
+    // lead to us trying to push `A` a second time -- to prevent
+    // infinite recursion, we notice that `A` was already pushed
+    // once and stop.
+    let mut ty_stack = vec![(for_ty, 0)];
+
+    // Set used to detect infinite recursion.
+    let mut ty_set = FxHashSet::default();
+
+    let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
+
+    let cause = ObligationCause::dummy();
+    let mut constraints = DropckConstraint::empty();
+    while let Some((ty, depth)) = ty_stack.pop() {
+        debug!(
+            "{} kinds, {} overflows, {} ty_stack",
+            result.kinds.len(),
+            result.overflows.len(),
+            ty_stack.len()
+        );
+        dtorck_constraint_for_ty(tcx, DUMMY_SP, for_ty, depth, ty, &mut constraints)?;
+
+        // "outlives" represent types/regions that may be touched
+        // by a destructor.
+        result.kinds.append(&mut constraints.outlives);
+        result.overflows.append(&mut constraints.overflows);
+
+        // If we have even one overflow, we should stop trying to evaluate further --
+        // chances are, the subsequent overflows for this evaluation won't provide useful
+        // information and will just decrease the speed at which we can emit these errors
+        // (since we'll be printing for just that much longer for the often enormous types
+        // that result here).
+        if !result.overflows.is_empty() {
+            break;
+        }
 
-                // dtorck types are "types that will get dropped but which
-                // do not themselves define a destructor", more or less. We have
-                // to push them onto the stack to be expanded.
-                for ty in constraints.dtorck_types.drain(..) {
-                    match infcx.at(&cause, param_env).normalize(ty) {
-                        Ok(Normalized { value: ty, obligations }) => {
-                            fulfill_cx.register_predicate_obligations(infcx, obligations);
-
-                            debug!("dropck_outlives: ty from dtorck_types = {:?}", ty);
-
-                            match ty.kind() {
-                                // All parameters live for the duration of the
-                                // function.
-                                ty::Param(..) => {}
-
-                                // A projection that we couldn't resolve - it
-                                // might have a destructor.
-                                ty::Projection(..) | ty::Opaque(..) => {
-                                    result.kinds.push(ty.into());
-                                }
-
-                                _ => {
-                                    if ty_set.insert(ty) {
-                                        ty_stack.push((ty, depth + 1));
-                                    }
-                                }
-                            }
+        // dtorck types are "types that will get dropped but which
+        // do not themselves define a destructor", more or less. We have
+        // to push them onto the stack to be expanded.
+        for ty in constraints.dtorck_types.drain(..) {
+            match infcx.at(&cause, param_env).normalize(ty) {
+                Ok(Normalized { value: ty, obligations }) => {
+                    fulfill_cx.register_predicate_obligations(infcx, obligations);
+
+                    debug!("dropck_outlives: ty from dtorck_types = {:?}", ty);
+
+                    match ty.kind() {
+                        // All parameters live for the duration of the
+                        // function.
+                        ty::Param(..) => {}
+
+                        // A projection that we couldn't resolve - it
+                        // might have a destructor.
+                        ty::Projection(..) | ty::Opaque(..) => {
+                            result.kinds.push(ty.into());
                         }
 
-                        // We don't actually expect to fail to normalize.
-                        // That implies a WF error somewhere else.
-                        Err(NoSolution) => {
-                            return Err(NoSolution);
+                        _ => {
+                            if ty_set.insert(ty) {
+                                ty_stack.push((ty, depth + 1));
+                            }
                         }
                     }
                 }
+
+                // We don't actually expect to fail to normalize.
+                // That implies a WF error somewhere else.
+                Err(NoSolution) => {
+                    return Err(NoSolution);
+                }
             }
+        }
+    }
 
-            debug!("dropck_outlives: result = {:#?}", result);
+    debug!("dropck_outlives: result = {:#?}", result);
 
-            infcx.make_canonicalized_query_response(
-                canonical_inference_vars,
-                result,
-                &mut *fulfill_cx,
-            )
-        },
-    )
+    infcx.make_canonicalized_query_response(canonical_inference_vars, result, &mut *fulfill_cx)
 }
 
 /// Returns a set of constraints that needs to be satisfied in
diff --git a/compiler/rustc_traits/src/evaluate_obligation.rs b/compiler/rustc_traits/src/evaluate_obligation.rs
index 49c9ba45963..493d5de0807 100644
--- a/compiler/rustc_traits/src/evaluate_obligation.rs
+++ b/compiler/rustc_traits/src/evaluate_obligation.rs
@@ -18,17 +18,15 @@ fn evaluate_obligation<'tcx>(
     debug!("evaluate_obligation(canonical_goal={:#?})", canonical_goal);
     // HACK This bubble is required for this tests to pass:
     // impl-trait/issue99642.rs
-    tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).enter_with_canonical(
-        DUMMY_SP,
-        &canonical_goal,
-        |ref infcx, goal, _canonical_inference_vars| {
-            debug!("evaluate_obligation: goal={:#?}", goal);
-            let ParamEnvAnd { param_env, value: predicate } = goal;
+    let (ref infcx, goal, _canonical_inference_vars) = tcx
+        .infer_ctxt()
+        .with_opaque_type_inference(DefiningAnchor::Bubble)
+        .build_with_canonical(DUMMY_SP, &canonical_goal);
+    debug!("evaluate_obligation: goal={:#?}", goal);
+    let ParamEnvAnd { param_env, value: predicate } = goal;
 
-            let mut selcx = SelectionContext::with_query_mode(&infcx, TraitQueryMode::Canonical);
-            let obligation = Obligation::new(ObligationCause::dummy(), param_env, predicate);
+    let mut selcx = SelectionContext::with_query_mode(&infcx, TraitQueryMode::Canonical);
+    let obligation = Obligation::new(ObligationCause::dummy(), param_env, predicate);
 
-            selcx.evaluate_root_obligation(&obligation)
-        },
-    )
+    selcx.evaluate_root_obligation(&obligation)
 }
diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs
index 5d394ed2263..58b718ed127 100644
--- a/compiler/rustc_traits/src/normalize_erasing_regions.rs
+++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs
@@ -30,30 +30,29 @@ fn try_normalize_after_erasing_regions<'tcx, T: TypeFoldable<'tcx> + PartialEq +
     goal: ParamEnvAnd<'tcx, T>,
 ) -> Result<T, NoSolution> {
     let ParamEnvAnd { param_env, value } = goal;
-    tcx.infer_ctxt().enter(|infcx| {
-        let cause = ObligationCause::dummy();
-        match infcx.at(&cause, param_env).normalize(value) {
-            Ok(Normalized { value: normalized_value, obligations: normalized_obligations }) => {
-                // We don't care about the `obligations`; they are
-                // always only region relations, and we are about to
-                // erase those anyway:
-                debug_assert_eq!(
-                    normalized_obligations.iter().find(|p| not_outlives_predicate(p.predicate)),
-                    None,
-                );
+    let infcx = tcx.infer_ctxt().build();
+    let cause = ObligationCause::dummy();
+    match infcx.at(&cause, param_env).normalize(value) {
+        Ok(Normalized { value: normalized_value, obligations: normalized_obligations }) => {
+            // We don't care about the `obligations`; they are
+            // always only region relations, and we are about to
+            // erase those anyway:
+            debug_assert_eq!(
+                normalized_obligations.iter().find(|p| not_outlives_predicate(p.predicate)),
+                None,
+            );
 
-                let resolved_value = infcx.resolve_vars_if_possible(normalized_value);
-                // It's unclear when `resolve_vars` would have an effect in a
-                // fresh `InferCtxt`. If this assert does trigger, it will give
-                // us a test case.
-                debug_assert_eq!(normalized_value, resolved_value);
-                let erased = infcx.tcx.erase_regions(resolved_value);
-                debug_assert!(!erased.needs_infer(), "{:?}", erased);
-                Ok(erased)
-            }
-            Err(NoSolution) => Err(NoSolution),
+            let resolved_value = infcx.resolve_vars_if_possible(normalized_value);
+            // It's unclear when `resolve_vars` would have an effect in a
+            // fresh `InferCtxt`. If this assert does trigger, it will give
+            // us a test case.
+            debug_assert_eq!(normalized_value, resolved_value);
+            let erased = infcx.tcx.erase_regions(resolved_value);
+            debug_assert!(!erased.needs_infer(), "{:?}", erased);
+            Ok(erased)
         }
-    })
+        Err(NoSolution) => Err(NoSolution),
+    }
 }
 
 fn not_outlives_predicate<'tcx>(p: ty::Predicate<'tcx>) -> bool {
diff --git a/compiler/rustc_ty_utils/src/common_traits.rs b/compiler/rustc_ty_utils/src/common_traits.rs
index cedc84d97c2..d3169b6d962 100644
--- a/compiler/rustc_ty_utils/src/common_traits.rs
+++ b/compiler/rustc_ty_utils/src/common_traits.rs
@@ -29,15 +29,8 @@ fn is_item_raw<'tcx>(
 ) -> bool {
     let (param_env, ty) = query.into_parts();
     let trait_def_id = tcx.require_lang_item(item, None);
-    tcx.infer_ctxt().enter(|infcx| {
-        traits::type_known_to_meet_bound_modulo_regions(
-            &infcx,
-            param_env,
-            ty,
-            trait_def_id,
-            DUMMY_SP,
-        )
-    })
+    let infcx = tcx.infer_ctxt().build();
+    traits::type_known_to_meet_bound_modulo_regions(&infcx, param_env, ty, trait_def_id, DUMMY_SP)
 }
 
 pub(crate) fn provide(providers: &mut ty::query::Providers) {
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index fe6ebd4b935..5ed40515d8a 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -134,19 +134,17 @@ fn resolve_associated_item<'tcx>(
                 .unwrap_or_else(|| {
                     bug!("{:?} not found in {:?}", trait_item_id, impl_data.impl_def_id);
                 });
-
-            let substs = tcx.infer_ctxt().enter(|infcx| {
-                let param_env = param_env.with_reveal_all_normalized(tcx);
-                let substs = rcvr_substs.rebase_onto(tcx, trait_def_id, impl_data.substs);
-                let substs = translate_substs(
-                    &infcx,
-                    param_env,
-                    impl_data.impl_def_id,
-                    substs,
-                    leaf_def.defining_node,
-                );
-                infcx.tcx.erase_regions(substs)
-            });
+            let infcx = tcx.infer_ctxt().build();
+            let param_env = param_env.with_reveal_all_normalized(tcx);
+            let substs = rcvr_substs.rebase_onto(tcx, trait_def_id, impl_data.substs);
+            let substs = translate_substs(
+                &infcx,
+                param_env,
+                impl_data.impl_def_id,
+                substs,
+                leaf_def.defining_node,
+            );
+            let substs = infcx.tcx.erase_regions(substs);
 
             // Since this is a trait item, we need to see if the item is either a trait default item
             // or a specialization because we can't resolve those unless we can `Reveal::All`.
diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs
index fd169f64885..95061ae61e3 100644
--- a/src/librustdoc/clean/blanket_impl.rs
+++ b/src/librustdoc/clean/blanket_impl.rs
@@ -27,76 +27,70 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
                 }
                 // NOTE: doesn't use `for_each_relevant_impl` to avoid looking at anything besides blanket impls
                 let trait_impls = cx.tcx.trait_impls_of(trait_def_id);
-                for &impl_def_id in trait_impls.blanket_impls() {
+                'blanket_impls: for &impl_def_id in trait_impls.blanket_impls() {
                     trace!(
                         "get_blanket_impls: Considering impl for trait '{:?}' {:?}",
                         trait_def_id,
                         impl_def_id
                     );
                     let trait_ref = cx.tcx.bound_impl_trait_ref(impl_def_id).unwrap();
-                    let is_param = matches!(trait_ref.0.self_ty().kind(), ty::Param(_));
-                    let may_apply = is_param && cx.tcx.infer_ctxt().enter(|infcx| {
-                        let substs = infcx.fresh_substs_for_item(DUMMY_SP, item_def_id);
-                        let ty = ty.subst(infcx.tcx, substs);
-                        let param_env = EarlyBinder(param_env).subst(infcx.tcx, substs);
+                    if !matches!(trait_ref.0.self_ty().kind(), ty::Param(_)) {
+                        continue;
+                    }
+                    let infcx = cx.tcx.infer_ctxt().build();
+                    let substs = infcx.fresh_substs_for_item(DUMMY_SP, item_def_id);
+                    let impl_ty = ty.subst(infcx.tcx, substs);
+                    let param_env = EarlyBinder(param_env).subst(infcx.tcx, substs);
 
-                        let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
-                        let trait_ref = trait_ref.subst(infcx.tcx, impl_substs);
+                    let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
+                    let impl_trait_ref = trait_ref.subst(infcx.tcx, impl_substs);
 
-                        // Require the type the impl is implemented on to match
-                        // our type, and ignore the impl if there was a mismatch.
-                        let cause = traits::ObligationCause::dummy();
-                        let eq_result = infcx.at(&cause, param_env).eq(trait_ref.self_ty(), ty);
-                        if let Ok(InferOk { value: (), obligations }) = eq_result {
-                            // FIXME(eddyb) ignoring `obligations` might cause false positives.
-                            drop(obligations);
+                    // Require the type the impl is implemented on to match
+                    // our type, and ignore the impl if there was a mismatch.
+                    let cause = traits::ObligationCause::dummy();
+                    let Ok(eq_result) = infcx.at(&cause, param_env).eq(impl_trait_ref.self_ty(), impl_ty) else {
+                        continue
+                    };
+                    let InferOk { value: (), obligations } = eq_result;
+                    // FIXME(eddyb) ignoring `obligations` might cause false positives.
+                    drop(obligations);
 
-                            trace!(
-                                "invoking predicate_may_hold: param_env={:?}, trait_ref={:?}, ty={:?}",
-                                param_env,
-                                trait_ref,
-                                ty
-                            );
-                            let predicates = cx
-                                .tcx
-                                .predicates_of(impl_def_id)
-                                .instantiate(cx.tcx, impl_substs)
-                                .predicates
-                                .into_iter()
-                                .chain(Some(
-                                    ty::Binder::dummy(trait_ref)
-                                        .to_poly_trait_predicate()
-                                        .map_bound(ty::PredicateKind::Trait)
-                                        .to_predicate(infcx.tcx),
-                                ));
-                            for predicate in predicates {
-                                debug!("testing predicate {:?}", predicate);
-                                let obligation = traits::Obligation::new(
-                                    traits::ObligationCause::dummy(),
-                                    param_env,
-                                    predicate,
-                                );
-                                match infcx.evaluate_obligation(&obligation) {
-                                    Ok(eval_result) if eval_result.may_apply() => {}
-                                    Err(traits::OverflowError::Canonical) => {}
-                                    Err(traits::OverflowError::ErrorReporting) => {}
-                                    _ => {
-                                        return false;
-                                    }
-                                }
-                            }
-                            true
-                        } else {
-                            false
+                    trace!(
+                        "invoking predicate_may_hold: param_env={:?}, impl_trait_ref={:?}, impl_ty={:?}",
+                        param_env,
+                        impl_trait_ref,
+                        impl_ty
+                    );
+                    let predicates = cx
+                        .tcx
+                        .predicates_of(impl_def_id)
+                        .instantiate(cx.tcx, impl_substs)
+                        .predicates
+                        .into_iter()
+                        .chain(Some(
+                            ty::Binder::dummy(impl_trait_ref)
+                                .to_poly_trait_predicate()
+                                .map_bound(ty::PredicateKind::Trait)
+                                .to_predicate(infcx.tcx),
+                        ));
+                    for predicate in predicates {
+                        debug!("testing predicate {:?}", predicate);
+                        let obligation = traits::Obligation::new(
+                            traits::ObligationCause::dummy(),
+                            param_env,
+                            predicate,
+                        );
+                        match infcx.evaluate_obligation(&obligation) {
+                            Ok(eval_result) if eval_result.may_apply() => {}
+                            Err(traits::OverflowError::Canonical) => {}
+                            Err(traits::OverflowError::ErrorReporting) => {}
+                            _ => continue 'blanket_impls,
                         }
-                    });
+                    }
                     debug!(
-                        "get_blanket_impls: found applicable impl: {} for trait_ref={:?}, ty={:?}",
-                        may_apply, trait_ref, ty
+                        "get_blanket_impls: found applicable impl for trait_ref={:?}, ty={:?}",
+                        trait_ref, ty
                     );
-                    if !may_apply {
-                        continue;
-                    }
 
                     cx.generated_synthetics.insert((ty.0, trait_def_id));
 
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 11834a53afb..279e762d597 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1564,12 +1564,11 @@ fn normalize<'tcx>(cx: &mut DocContext<'tcx>, ty: Ty<'_>) -> Option<Ty<'tcx>> {
 
     // Try to normalize `<X as Y>::T` to a type
     let lifted = ty.lift_to_tcx(cx.tcx).unwrap();
-    let normalized = cx.tcx.infer_ctxt().enter(|infcx| {
-        infcx
-            .at(&ObligationCause::dummy(), cx.param_env)
-            .normalize(lifted)
-            .map(|resolved| infcx.resolve_vars_if_possible(resolved.value))
-    });
+    let infcx = cx.tcx.infer_ctxt().build();
+    let normalized = infcx
+        .at(&ObligationCause::dummy(), cx.param_env)
+        .normalize(lifted)
+        .map(|resolved| infcx.resolve_vars_if_possible(resolved.value));
     match normalized {
         Ok(normalized_value) => {
             debug!("normalized {:?} to {:?}", ty, normalized_value);
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index 3cd8f236e7a..02a16f765b7 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -831,11 +831,10 @@ fn walk_parents<'tcx>(
                                 // Trait methods taking `self`
                                 arg_ty
                             } && impl_ty.is_ref()
-                            && cx.tcx.infer_ctxt().enter(|infcx|
-                                infcx
-                                    .type_implements_trait(trait_id, impl_ty, subs, cx.param_env)
-                                    .must_apply_modulo_regions()
-                            )
+                            && let infcx = cx.tcx.infer_ctxt().build()
+                            && infcx
+                                .type_implements_trait(trait_id, impl_ty, subs, cx.param_env)
+                                .must_apply_modulo_regions()
                         {
                             return Some(Position::MethodReceiverRefImpl)
                         }
@@ -1119,9 +1118,8 @@ fn needless_borrow_impl_arg_position<'tcx>(
 
             let predicate = EarlyBinder(predicate).subst(cx.tcx, &substs_with_referent_ty);
             let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate);
-            cx.tcx
-                .infer_ctxt()
-                .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
+            let infcx = cx.tcx.infer_ctxt().build();
+            infcx.predicate_must_hold_modulo_regions(&obligation)
         })
     };
 
diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs
index 2e608fe527f..eb0455ae404 100644
--- a/src/tools/clippy/clippy_lints/src/escape.rs
+++ b/src/tools/clippy/clippy_lints/src/escape.rs
@@ -106,9 +106,8 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
         };
 
         let fn_def_id = cx.tcx.hir().local_def_id(hir_id);
-        cx.tcx.infer_ctxt().enter(|infcx| {
-            ExprUseVisitor::new(&mut v, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body);
-        });
+        let infcx = cx.tcx.infer_ctxt().build();
+        ExprUseVisitor::new(&mut v, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body);
 
         for node in v.set {
             span_lint_hir(
diff --git a/src/tools/clippy/clippy_lints/src/future_not_send.rs b/src/tools/clippy/clippy_lints/src/future_not_send.rs
index 406c842a6d0..0519f9ac246 100644
--- a/src/tools/clippy/clippy_lints/src/future_not_send.rs
+++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs
@@ -77,10 +77,9 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
             if is_future {
                 let send_trait = cx.tcx.get_diagnostic_item(sym::Send).unwrap();
                 let span = decl.output.span();
-                let send_errors = cx.tcx.infer_ctxt().enter(|infcx| {
-                    let cause = traits::ObligationCause::misc(span, hir_id);
-                    traits::fully_solve_bound(&infcx, cause, cx.param_env, ret_ty, send_trait)
-                });
+                let infcx = cx.tcx.infer_ctxt().build();
+                let cause = traits::ObligationCause::misc(span, hir_id);
+                let send_errors = traits::fully_solve_bound(&infcx, cause, cx.param_env, ret_ty, send_trait);
                 if !send_errors.is_empty() {
                     span_lint_and_then(
                         cx,
@@ -88,18 +87,18 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
                         span,
                         "future cannot be sent between threads safely",
                         |db| {
-                            cx.tcx.infer_ctxt().enter(|infcx| {
-                                for FulfillmentError { obligation, .. } in send_errors {
-                                    infcx.err_ctxt().maybe_note_obligation_cause_for_async_await(db, &obligation);
-                                    if let Trait(trait_pred) = obligation.predicate.kind().skip_binder() {
-                                        db.note(&format!(
-                                            "`{}` doesn't implement `{}`",
-                                            trait_pred.self_ty(),
-                                            trait_pred.trait_ref.print_only_trait_path(),
-                                        ));
-                                    }
+                            for FulfillmentError { obligation, .. } in send_errors {
+                                infcx
+                                    .err_ctxt()
+                                    .maybe_note_obligation_cause_for_async_await(db, &obligation);
+                                if let Trait(trait_pred) = obligation.predicate.kind().skip_binder() {
+                                    db.note(&format!(
+                                        "`{}` doesn't implement `{}`",
+                                        trait_pred.self_ty(),
+                                        trait_pred.trait_ref.print_only_trait_path(),
+                                    ));
                                 }
-                            });
+                            }
                         },
                     );
                 }
diff --git a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
index 0ee42b61c9a..db73ab55b37 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
@@ -65,16 +65,15 @@ fn check_for_mutation<'tcx>(
         span_low: None,
         span_high: None,
     };
-    cx.tcx.infer_ctxt().enter(|infcx| {
-        ExprUseVisitor::new(
-            &mut delegate,
-            &infcx,
-            body.hir_id.owner.def_id,
-            cx.param_env,
-            cx.typeck_results(),
-        )
-        .walk_expr(body);
-    });
+    let infcx = cx.tcx.infer_ctxt().build();
+    ExprUseVisitor::new(
+        &mut delegate,
+        &infcx,
+        body.hir_id.owner.def_id,
+        cx.param_env,
+        cx.typeck_results(),
+    )
+    .walk_expr(body);
 
     delegate.mutation_span()
 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
index 9ab0d614114..6017941452c 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -420,9 +420,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
                         if trait_predicates.any(|predicate| {
                             let predicate = EarlyBinder(predicate).subst(cx.tcx, new_subst);
                             let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate);
-                            !cx.tcx
-                                .infer_ctxt()
-                                .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
+                            !cx.tcx.infer_ctxt().build().predicate_must_hold_modulo_regions(&obligation)
                         }) {
                             return false;
                         }
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
index 178c973981b..7f881e27dd2 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
@@ -138,10 +138,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
             ..
         } = {
             let mut ctx = MovedVariablesCtxt::default();
-            cx.tcx.infer_ctxt().enter(|infcx| {
-                euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results())
-                    .consume_body(body);
-            });
+            let infcx = cx.tcx.infer_ctxt().build();
+            euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body);
             ctx
         };
 
diff --git a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs
index 26bca7c306a..c7e964cf23e 100644
--- a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs
@@ -123,16 +123,15 @@ fn imm_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet
     }
 
     let mut s = S(hir::HirIdSet::default());
-    cx.tcx.infer_ctxt().enter(|infcx| {
-        let mut v = ExprUseVisitor::new(
-            &mut s,
-            &infcx,
-            cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()),
-            cx.param_env,
-            cx.typeck_results(),
-        );
-        v.consume_expr(e);
-    });
+    let infcx = cx.tcx.infer_ctxt().build();
+    let mut v = ExprUseVisitor::new(
+        &mut s,
+        &infcx,
+        cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()),
+        cx.param_env,
+        cx.typeck_results(),
+    );
+    v.consume_expr(e);
     s.0
 }
 
@@ -156,15 +155,14 @@ fn mut_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet
     }
 
     let mut s = S(hir::HirIdSet::default());
-    cx.tcx.infer_ctxt().enter(|infcx| {
-        let mut v = ExprUseVisitor::new(
-            &mut s,
-            &infcx,
-            cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()),
-            cx.param_env,
-            cx.typeck_results(),
-        );
-        v.consume_expr(e);
-    });
+    let infcx = cx.tcx.infer_ctxt().build();
+    let mut v = ExprUseVisitor::new(
+        &mut s,
+        &infcx,
+        cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()),
+        cx.param_env,
+        cx.typeck_results(),
+    );
+    v.consume_expr(e);
     s.0
 }
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index ef836e84829..3c5dd92b9cd 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -821,10 +821,9 @@ pub fn deref_closure_args<'tcx>(cx: &LateContext<'_>, closure: &'tcx hir::Expr<'
         };
 
         let fn_def_id = cx.tcx.hir().local_def_id(closure.hir_id);
-        cx.tcx.infer_ctxt().enter(|infcx| {
-            ExprUseVisitor::new(&mut visitor, &infcx, fn_def_id, cx.param_env, cx.typeck_results())
-                .consume_body(closure_body);
-        });
+        let infcx = cx.tcx.infer_ctxt().build();
+        ExprUseVisitor::new(&mut visitor, &infcx, fn_def_id, cx.param_env, cx.typeck_results())
+            .consume_body(closure_body);
 
         if !visitor.suggestion_start.is_empty() {
             return Some(DerefClosure {
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 934470bd135..a15daec7c3c 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -172,11 +172,10 @@ pub fn implements_trait_with_env<'tcx>(
         return false;
     }
     let ty_params = tcx.mk_substs(ty_params.iter());
-    tcx.infer_ctxt().enter(|infcx| {
-        infcx
-            .type_implements_trait(trait_id, ty, ty_params, param_env)
-            .must_apply_modulo_regions()
-    })
+    let infcx = tcx.infer_ctxt().build();
+    infcx
+        .type_implements_trait(trait_id, ty, ty_params, param_env)
+        .must_apply_modulo_regions()
 }
 
 /// Checks whether this type implements `Drop`.
@@ -242,27 +241,26 @@ fn is_normalizable_helper<'tcx>(
     }
     // prevent recursive loops, false-negative is better than endless loop leading to stack overflow
     cache.insert(ty, false);
-    let result = cx.tcx.infer_ctxt().enter(|infcx| {
-        let cause = rustc_middle::traits::ObligationCause::dummy();
-        if infcx.at(&cause, param_env).normalize(ty).is_ok() {
-            match ty.kind() {
-                ty::Adt(def, substs) => def.variants().iter().all(|variant| {
-                    variant
-                        .fields
-                        .iter()
-                        .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache))
-                }),
-                _ => ty.walk().all(|generic_arg| match generic_arg.unpack() {
-                    GenericArgKind::Type(inner_ty) if inner_ty != ty => {
-                        is_normalizable_helper(cx, param_env, inner_ty, cache)
-                    },
-                    _ => true, // if inner_ty == ty, we've already checked it
-                }),
-            }
-        } else {
-            false
+    let infcx = cx.tcx.infer_ctxt().build();
+    let cause = rustc_middle::traits::ObligationCause::dummy();
+    let result = if infcx.at(&cause, param_env).normalize(ty).is_ok() {
+        match ty.kind() {
+            ty::Adt(def, substs) => def.variants().iter().all(|variant| {
+                variant
+                    .fields
+                    .iter()
+                    .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache))
+            }),
+            _ => ty.walk().all(|generic_arg| match generic_arg.unpack() {
+                GenericArgKind::Type(inner_ty) if inner_ty != ty => {
+                    is_normalizable_helper(cx, param_env, inner_ty, cache)
+                },
+                _ => true, // if inner_ty == ty, we've already checked it
+            }),
         }
-    });
+    } else {
+        false
+    };
     cache.insert(ty, result);
     result
 }
diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs
index b5ec3fef3e0..e32bae6ed1f 100644
--- a/src/tools/clippy/clippy_utils/src/usage.rs
+++ b/src/tools/clippy/clippy_utils/src/usage.rs
@@ -18,16 +18,15 @@ pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) ->
         used_mutably: HirIdSet::default(),
         skip: false,
     };
-    cx.tcx.infer_ctxt().enter(|infcx| {
-        ExprUseVisitor::new(
-            &mut delegate,
-            &infcx,
-            expr.hir_id.owner.def_id,
-            cx.param_env,
-            cx.typeck_results(),
-        )
-        .walk_expr(expr);
-    });
+    let infcx = cx.tcx.infer_ctxt().build();
+    ExprUseVisitor::new(
+        &mut delegate,
+        &infcx,
+        expr.hir_id.owner.def_id,
+        cx.param_env,
+        cx.typeck_results(),
+    )
+    .walk_expr(expr);
 
     if delegate.skip {
         return None;