about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_borrowck/src/constraints/mod.rs16
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs27
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs17
-rw-r--r--compiler/rustc_borrowck/src/lib.rs2
-rw-r--r--compiler/rustc_borrowck/src/region_infer/mod.rs372
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types.rs4
-rw-r--r--compiler/rustc_borrowck/src/renumber.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs18
-rw-r--r--compiler/rustc_borrowck/src/type_check/relate_tys.rs40
-rw-r--r--compiler/rustc_codegen_cranelift/src/inline_asm.rs3
-rw-r--r--compiler/rustc_codegen_llvm/src/asm.rs100
-rw-r--r--compiler/rustc_codegen_llvm/src/context.rs8
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs5
-rw-r--r--compiler/rustc_const_eval/src/check_consts/check.rs6
-rw-r--r--compiler/rustc_const_eval/src/check_consts/ops.rs2
-rw-r--r--compiler/rustc_const_eval/src/const_eval/error.rs16
-rw-r--r--compiler/rustc_const_eval/src/const_eval/eval_queries.rs24
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs32
-rw-r--r--compiler/rustc_const_eval/src/const_eval/valtrees.rs16
-rw-r--r--compiler/rustc_const_eval/src/interpret/discriminant.rs5
-rw-r--r--compiler/rustc_const_eval/src/interpret/eval_context.rs35
-rw-r--r--compiler/rustc_const_eval/src/interpret/intern.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/operator.rs40
-rw-r--r--compiler/rustc_const_eval/src/interpret/projection.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/util.rs6
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs16
-rw-r--r--compiler/rustc_const_eval/src/lib.rs1
-rw-r--r--compiler/rustc_const_eval/src/util/caller_location.rs4
-rw-r--r--compiler/rustc_const_eval/src/util/check_validity_requirement.rs4
-rw-r--r--compiler/rustc_data_structures/Cargo.toml6
-rw-r--r--compiler/rustc_data_structures/src/graph/scc/mod.rs401
-rw-r--r--compiler/rustc_data_structures/src/graph/scc/tests.rs214
-rw-r--r--compiler/rustc_expand/src/mbe/metavar_expr.rs93
-rw-r--r--compiler/rustc_expand/src/mbe/quoted.rs25
-rw-r--r--compiler/rustc_expand/src/mbe/transcribe.rs56
-rw-r--r--compiler/rustc_feature/src/accepted.rs4
-rw-r--r--compiler/rustc_feature/src/unstable.rs12
-rw-r--r--compiler/rustc_hir/src/hir.rs38
-rw-r--r--compiler/rustc_hir/src/lang_items.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/bounds.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsicck.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/mod.rs5
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs71
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs17
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl4
-rw-r--r--compiler/rustc_hir_typeck/src/_match.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs57
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs11
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs26
-rw-r--r--compiler/rustc_hir_typeck/src/fallback.rs54
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs12
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs22
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs8
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs1
-rw-r--r--compiler/rustc_hir_typeck/src/method/confirm.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs65
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs60
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs135
-rw-r--r--compiler/rustc_infer/Cargo.toml1
-rw-r--r--compiler/rustc_infer/src/infer/at.rs89
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs38
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs6
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs105
-rw-r--r--compiler/rustc_infer/src/infer/opaque_types/mod.rs4
-rw-r--r--compiler/rustc_infer/src/infer/outlives/for_liveness.rs4
-rw-r--r--compiler/rustc_infer/src/infer/relate/generalize.rs18
-rw-r--r--compiler/rustc_infer/src/infer/relate/glb.rs7
-rw-r--r--compiler/rustc_infer/src/infer/relate/lub.rs7
-rw-r--r--compiler/rustc_infer/src/infer/relate/mod.rs2
-rw-r--r--compiler/rustc_infer/src/infer/relate/type_relating.rs10
-rw-r--r--compiler/rustc_infer/src/traits/util.rs3
-rw-r--r--compiler/rustc_lint/messages.ftl1
-rw-r--r--compiler/rustc_lint/src/deref_into_dyn_supertrait.rs4
-rw-r--r--compiler/rustc_lint/src/lints.rs14
-rw-r--r--compiler/rustc_lint/src/non_fmt_panic.rs8
-rw-r--r--compiler/rustc_lint/src/non_local_def.rs8
-rw-r--r--compiler/rustc_lint/src/shadowed_into_iter.rs4
-rw-r--r--compiler/rustc_lint/src/traits.rs4
-rw-r--r--compiler/rustc_lint/src/unused.rs4
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs58
-rw-r--r--compiler/rustc_log/Cargo.toml2
-rw-r--r--compiler/rustc_log/src/lib.rs8
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs2
-rw-r--r--compiler/rustc_middle/src/middle/lang_items.rs4
-rw-r--r--compiler/rustc_middle/src/mir/interpret/value.rs14
-rw-r--r--compiler/rustc_middle/src/mir/pretty.rs4
-rw-r--r--compiler/rustc_middle/src/mir/syntax.rs6
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs14
-rw-r--r--compiler/rustc_middle/src/traits/query.rs2
-rw-r--r--compiler/rustc_middle/src/traits/util.rs2
-rw-r--r--compiler/rustc_middle/src/ty/adt.rs27
-rw-r--r--compiler/rustc_middle/src/ty/context.rs84
-rw-r--r--compiler/rustc_middle/src/ty/generic_args.rs2
-rw-r--r--compiler/rustc_middle/src/ty/instance.rs9
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs5
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs3
-rw-r--r--compiler/rustc_middle/src/ty/predicate.rs15
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs6
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs4
-rw-r--r--compiler/rustc_middle/src/util/call_kind.rs8
-rw-r--r--compiler/rustc_middle/src/values.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_constant.rs3
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/matches/test.rs2
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs3
-rw-r--r--compiler/rustc_mir_build/src/errors.rs5
-rw-r--r--compiler/rustc_mir_build/src/thir/constant.rs3
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/block.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs4
-rw-r--r--compiler/rustc_mir_dataflow/src/elaborate_drops.rs3
-rw-r--r--compiler/rustc_mir_transform/src/add_retag.rs3
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs3
-rw-r--r--compiler/rustc_mir_transform/src/inline.rs27
-rw-r--r--compiler/rustc_mir_transform/src/known_panics_lint.rs6
-rw-r--r--compiler/rustc_mir_transform/src/promote_consts.rs2
-rw-r--r--compiler/rustc_mir_transform/src/required_consts.rs2
-rw-r--r--compiler/rustc_mir_transform/src/reveal_all.rs4
-rw-r--r--compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs2
-rw-r--r--compiler/rustc_mir_transform/src/sroa.rs3
-rw-r--r--compiler/rustc_mir_transform/src/validate.rs3
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs6
-rw-r--r--compiler/rustc_monomorphize/src/partitioning.rs3
-rw-r--r--compiler/rustc_monomorphize/src/polymorphize.rs2
-rw-r--r--compiler/rustc_passes/src/dead.rs6
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs46
-rw-r--r--compiler/rustc_resolve/src/late.rs24
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs24
-rw-r--r--compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs2
-rw-r--r--compiler/rustc_smir/src/rustc_smir/builder.rs8
-rw-r--r--compiler/rustc_smir/src/rustc_smir/context.rs19
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/mir.rs6
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/ty.rs8
-rw-r--r--compiler/rustc_smir/src/rustc_smir/mod.rs21
-rw-r--r--compiler/rustc_span/src/lib.rs21
-rw-r--r--compiler/rustc_span/src/span_encoding.rs270
-rw-r--r--compiler/rustc_span/src/symbol.rs3
-rw-r--r--compiler/rustc_target/src/asm/mod.rs12
-rw-r--r--compiler/rustc_target/src/asm/x86.rs22
-rw-r--r--compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/wasm32_wasip1.rs1
-rw-r--r--compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs1
-rw-r--r--compiler/rustc_trait_selection/src/solve/alias_relate.rs12
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/mod.rs137
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs260
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs296
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs83
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs37
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/analyse.rs22
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/build.rs7
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs11
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs72
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs85
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs32
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs191
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs38
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/_match.rs (renamed from compiler/rustc_infer/src/infer/relate/_match.rs)6
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs57
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs14
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs44
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/vtable.rs2
-rw-r--r--compiler/rustc_transmute/src/layout/tree.rs75
-rw-r--r--compiler/rustc_ty_utils/src/instance.rs12
-rw-r--r--compiler/rustc_type_ir/src/infcx.rs76
-rw-r--r--compiler/rustc_type_ir/src/inherent.rs134
-rw-r--r--compiler/rustc_type_ir/src/interner.rs61
-rw-r--r--compiler/rustc_type_ir/src/lang_items.rs8
-rw-r--r--compiler/rustc_type_ir/src/lib.rs2
-rw-r--r--compiler/rustc_type_ir/src/relate.rs20
-rw-r--r--compiler/stable_mir/src/compiler_interface.rs4
-rw-r--r--compiler/stable_mir/src/crate_def.rs37
-rw-r--r--compiler/stable_mir/src/lib.rs8
-rw-r--r--compiler/stable_mir/src/mir/body.rs27
-rw-r--r--compiler/stable_mir/src/mir/pretty.rs2
-rw-r--r--compiler/stable_mir/src/mir/visit.rs12
-rw-r--r--compiler/stable_mir/src/ty.rs37
186 files changed, 3475 insertions, 1932 deletions
diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs
index 97408fa20d7..b54e05b2b34 100644
--- a/compiler/rustc_borrowck/src/constraints/mod.rs
+++ b/compiler/rustc_borrowck/src/constraints/mod.rs
@@ -1,4 +1,4 @@
-use rustc_data_structures::graph::scc::Sccs;
+use crate::type_check::Locations;
 use rustc_index::{IndexSlice, IndexVec};
 use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo};
@@ -6,8 +6,6 @@ use rustc_span::Span;
 use std::fmt;
 use std::ops::Index;
 
-use crate::type_check::Locations;
-
 pub(crate) mod graph;
 
 /// A set of NLL region constraints. These include "outlives"
@@ -45,18 +43,6 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
         graph::ConstraintGraph::new(graph::Reverse, self, num_region_vars)
     }
 
-    /// Computes cycles (SCCs) in the graph of regions. In particular,
-    /// find all regions R1, R2 such that R1: R2 and R2: R1 and group
-    /// them into an SCC, and find the relationships between SCCs.
-    pub(crate) fn compute_sccs(
-        &self,
-        constraint_graph: &graph::NormalConstraintGraph,
-        static_region: RegionVid,
-    ) -> Sccs<RegionVid, ConstraintSccIndex> {
-        let region_graph = &constraint_graph.region_graph(self, static_region);
-        Sccs::new(region_graph)
-    }
-
     pub(crate) fn outlives(
         &self,
     ) -> &IndexSlice<OutlivesConstraintIndex, OutlivesConstraint<'tcx>> {
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index f32ff57fe88..6bc340e44f5 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -100,12 +100,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             move_site_vec.iter().map(|move_site| move_site.moi).collect();
 
         if move_out_indices.is_empty() {
-            let root_place = PlaceRef { projection: &[], ..used_place };
+            let root_local = used_place.local;
 
-            if !self.uninitialized_error_reported.insert(root_place) {
+            if !self.uninitialized_error_reported.insert(root_local) {
                 debug!(
                     "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",
-                    root_place
+                    root_local
                 );
                 return;
             }
@@ -284,7 +284,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     && let CallKind::FnCall { fn_trait_id, self_ty } = kind
                     && let ty::Param(_) = self_ty.kind()
                     && ty == self_ty
-                    && Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait()
+                    && self.infcx.tcx.is_lang_item(fn_trait_id, LangItem::FnOnce)
                 {
                     // this is a type parameter `T: FnOnce()`, don't suggest `T: FnOnce() + Clone`.
                     true
@@ -708,9 +708,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             if let ty::ClauseKind::Trait(pred) = pred.kind().skip_binder()
                 && pred.self_ty() == ty
             {
-                if Some(pred.def_id()) == tcx.lang_items().fn_trait() {
+                if tcx.is_lang_item(pred.def_id(), LangItem::Fn) {
                     return Some(hir::Mutability::Not);
-                } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() {
+                } else if tcx.is_lang_item(pred.def_id(), LangItem::FnMut) {
                     return Some(hir::Mutability::Mut);
                 }
             }
@@ -1832,7 +1832,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 if let hir::ExprKind::MethodCall(..) = ex.kind
                     && let Some(method_def_id) =
                         self.typeck_results.type_dependent_def_id(ex.hir_id)
-                    && self.tcx.lang_items().clone_trait() == Some(self.tcx.parent(method_def_id))
+                    && self.tcx.is_lang_item(self.tcx.parent(method_def_id), LangItem::Clone)
                 {
                     self.clones.push(ex);
                 }
@@ -2888,7 +2888,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             ..
         } = explanation
         {
-            if let Some(diag) = self.try_report_cannot_return_reference_to_local(
+            if let Err(diag) = self.try_report_cannot_return_reference_to_local(
                 borrow,
                 borrow_span,
                 span,
@@ -3075,7 +3075,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
             explanation
         {
-            if let Some(diag) = self.try_report_cannot_return_reference_to_local(
+            if let Err(diag) = self.try_report_cannot_return_reference_to_local(
                 borrow,
                 proper_span,
                 span,
@@ -3159,8 +3159,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         let is_format_arguments_item = if let Some(expr_ty) = expr_ty
                             && let ty::Adt(adt, _) = expr_ty.kind()
                         {
-                            self.infcx.tcx.lang_items().get(LangItem::FormatArguments)
-                                == Some(adt.did())
+                            self.infcx.tcx.is_lang_item(adt.did(), LangItem::FormatArguments)
                         } else {
                             false
                         };
@@ -3237,11 +3236,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         return_span: Span,
         category: ConstraintCategory<'tcx>,
         opt_place_desc: Option<&String>,
-    ) -> Option<Diag<'tcx>> {
+    ) -> Result<(), Diag<'tcx>> {
         let return_kind = match category {
             ConstraintCategory::Return(_) => "return",
             ConstraintCategory::Yield => "yield",
-            _ => return None,
+            _ => return Ok(()),
         };
 
         // FIXME use a better heuristic than Spans
@@ -3317,7 +3316,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             }
         }
 
-        Some(err)
+        Err(err)
     }
 
     #[instrument(level = "debug", skip(self))]
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 1eb67ea367c..abb0b5afbd8 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -6,9 +6,9 @@ use crate::session_diagnostics::{
 };
 use rustc_errors::{Applicability, Diag};
 use rustc_errors::{DiagCtxt, MultiSpan};
-use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, Namespace};
 use rustc_hir::CoroutineKind;
+use rustc_hir::{self as hir, LangItem};
 use rustc_index::IndexSlice;
 use rustc_infer::infer::BoundRegionConversionTime;
 use rustc_infer::traits::SelectionError;
@@ -116,7 +116,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         {
             if let ty::FnDef(id, _) = *const_.ty().kind() {
                 debug!("add_moved_or_invoked_closure_note: id={:?}", id);
-                if Some(self.infcx.tcx.parent(id)) == self.infcx.tcx.lang_items().fn_once_trait() {
+                if self.infcx.tcx.is_lang_item(self.infcx.tcx.parent(id), LangItem::FnOnce) {
                     let closure = match args.first() {
                         Some(Spanned {
                             node: Operand::Copy(place) | Operand::Move(place), ..
@@ -767,13 +767,12 @@ impl<'tcx> BorrowedContentSource<'tcx> {
             ty::FnDef(def_id, args) => {
                 let trait_id = tcx.trait_of_item(def_id)?;
 
-                let lang_items = tcx.lang_items();
-                if Some(trait_id) == lang_items.deref_trait()
-                    || Some(trait_id) == lang_items.deref_mut_trait()
+                if tcx.is_lang_item(trait_id, LangItem::Deref)
+                    || tcx.is_lang_item(trait_id, LangItem::DerefMut)
                 {
                     Some(BorrowedContentSource::OverloadedDeref(args.type_at(0)))
-                } else if Some(trait_id) == lang_items.index_trait()
-                    || Some(trait_id) == lang_items.index_mut_trait()
+                } else if tcx.is_lang_item(trait_id, LangItem::Index)
+                    || tcx.is_lang_item(trait_id, LangItem::IndexMut)
                 {
                     Some(BorrowedContentSource::OverloadedIndex(args.type_at(0)))
                 } else {
@@ -1041,7 +1040,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 .unwrap_or_else(|| "value".to_owned());
             match kind {
                 CallKind::FnCall { fn_trait_id, self_ty }
-                    if Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait() =>
+                    if self.infcx.tcx.is_lang_item(fn_trait_id, LangItem::FnOnce) =>
                 {
                     err.subdiagnostic(
                         self.dcx(),
@@ -1268,7 +1267,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         let ty = moved_place.ty(self.body, tcx).ty;
 
                         if let ty::Adt(def, args) = ty.peel_refs().kind()
-                            && Some(def.did()) == tcx.lang_items().pin_type()
+                            && tcx.is_lang_item(def.did(), LangItem::Pin)
                             && let ty::Ref(_, _, hir::Mutability::Mut) = args.type_at(0).kind()
                             && let self_ty = self.infcx.instantiate_binder_with_fresh_vars(
                                 fn_call_span,
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 5c9826ecca7..b3b53e9cb79 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -566,7 +566,7 @@ struct MirBorrowckCtxt<'cx, 'tcx> {
     fn_self_span_reported: FxIndexSet<Span>,
     /// This field keeps track of errors reported in the checking of uninitialized variables,
     /// so that we don't report seemingly duplicate errors.
-    uninitialized_error_reported: FxIndexSet<PlaceRef<'tcx>>,
+    uninitialized_error_reported: FxIndexSet<Local>,
     /// This field keeps track of all the local variables that are declared mut and are mutated.
     /// Used for the warning issued by an unused mutable local variable.
     used_mut: FxIndexSet<Local>,
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index 0e3140ca98b..40b58500598 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -4,10 +4,10 @@ use std::rc::Rc;
 use rustc_data_structures::binary_search_util;
 use rustc_data_structures::frozen::Frozen;
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
-use rustc_data_structures::graph::scc::Sccs;
+use rustc_data_structures::graph::scc::{self, Sccs};
 use rustc_errors::Diag;
 use rustc_hir::def_id::CRATE_DEF_ID;
-use rustc_index::{IndexSlice, IndexVec};
+use rustc_index::IndexVec;
 use rustc_infer::infer::outlives::test_type_match;
 use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq};
 use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin, RegionVariableOrigin};
@@ -19,7 +19,7 @@ use rustc_middle::mir::{
 };
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::traits::ObligationCauseCode;
-use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable};
+use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeFoldable, UniverseIndex};
 use rustc_mir_dataflow::points::DenseLocationMap;
 use rustc_span::Span;
 
@@ -46,6 +46,97 @@ mod reverse_sccs;
 
 pub mod values;
 
+pub type ConstraintSccs = Sccs<RegionVid, ConstraintSccIndex, RegionTracker>;
+
+/// An annotation for region graph SCCs that tracks
+/// the values of its elements.
+#[derive(Copy, Debug, Clone)]
+pub struct RegionTracker {
+    /// The largest universe of a placeholder reached from this SCC.
+    /// This includes placeholders within this SCC.
+    max_placeholder_universe_reached: UniverseIndex,
+
+    /// The smallest universe index reachable form the nodes of this SCC.
+    min_reachable_universe: UniverseIndex,
+
+    /// The representative Region Variable Id for this SCC. We prefer
+    /// placeholders over existentially quantified variables, otherwise
+    ///  it's the one with the smallest Region Variable ID.
+    representative: RegionVid,
+
+    /// Is the current representative a placeholder?
+    representative_is_placeholder: bool,
+
+    /// Is the current representative existentially quantified?
+    representative_is_existential: bool,
+}
+
+impl scc::Annotation for RegionTracker {
+    fn merge_scc(mut self, mut other: Self) -> Self {
+        // Prefer any placeholder over any existential
+        if other.representative_is_placeholder && self.representative_is_existential {
+            other.merge_min_max_seen(&self);
+            return other;
+        }
+
+        if self.representative_is_placeholder && other.representative_is_existential
+            || (self.representative <= other.representative)
+        {
+            self.merge_min_max_seen(&other);
+            return self;
+        }
+        other.merge_min_max_seen(&self);
+        other
+    }
+
+    fn merge_reached(mut self, other: Self) -> Self {
+        // No update to in-component values, only add seen values.
+        self.merge_min_max_seen(&other);
+        self
+    }
+}
+
+impl RegionTracker {
+    fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self {
+        let (representative_is_placeholder, representative_is_existential) = match definition.origin
+        {
+            rustc_infer::infer::NllRegionVariableOrigin::FreeRegion => (false, false),
+            rustc_infer::infer::NllRegionVariableOrigin::Placeholder(_) => (true, false),
+            rustc_infer::infer::NllRegionVariableOrigin::Existential { .. } => (false, true),
+        };
+
+        let placeholder_universe =
+            if representative_is_placeholder { definition.universe } else { UniverseIndex::ROOT };
+
+        Self {
+            max_placeholder_universe_reached: placeholder_universe,
+            min_reachable_universe: definition.universe,
+            representative: rvid,
+            representative_is_placeholder,
+            representative_is_existential,
+        }
+    }
+    fn universe(self) -> UniverseIndex {
+        self.min_reachable_universe
+    }
+
+    fn merge_min_max_seen(&mut self, other: &Self) {
+        self.max_placeholder_universe_reached = std::cmp::max(
+            self.max_placeholder_universe_reached,
+            other.max_placeholder_universe_reached,
+        );
+
+        self.min_reachable_universe =
+            std::cmp::min(self.min_reachable_universe, other.min_reachable_universe);
+    }
+
+    /// Returns `true` if during the annotated SCC reaches a placeholder
+    /// with a universe larger than the smallest reachable one, `false` otherwise.
+    pub fn has_incompatible_universes(&self) -> bool {
+        self.universe().cannot_name(self.max_placeholder_universe_reached)
+    }
+}
+
 pub struct RegionInferenceContext<'tcx> {
     pub var_infos: VarInfos,
 
@@ -72,7 +163,7 @@ pub struct RegionInferenceContext<'tcx> {
     /// The SCC computed from `constraints` and the constraint
     /// graph. We have an edge from SCC A to SCC B if `A: B`. Used to
     /// compute the values of each region.
-    constraint_sccs: Rc<Sccs<RegionVid, ConstraintSccIndex>>,
+    constraint_sccs: Rc<ConstraintSccs>,
 
     /// Reverse of the SCC constraint graph --  i.e., an edge `A -> B` exists if
     /// `B: A`. This is used to compute the universal regions that are required
@@ -91,22 +182,6 @@ pub struct RegionInferenceContext<'tcx> {
     /// Map universe indexes to information on why we created it.
     universe_causes: FxIndexMap<ty::UniverseIndex, UniverseInfo<'tcx>>,
 
-    /// Contains the minimum universe of any variable within the same
-    /// SCC. We will ensure that no SCC contains values that are not
-    /// visible from this index.
-    scc_universes: IndexVec<ConstraintSccIndex, ty::UniverseIndex>,
-
-    /// Contains the "representative" region of each SCC.
-    /// It is defined as the one with the minimal RegionVid, favoring
-    /// free regions, then placeholders, then existential regions.
-    ///
-    /// It is a hacky way to manage checking regions for equality,
-    /// since we can 'canonicalize' each region to the representative
-    /// of its SCC and be sure that -- if they have the same repr --
-    /// they *must* be equal (though not having the same repr does not
-    /// mean they are unequal).
-    scc_representatives: IndexVec<ConstraintSccIndex, ty::RegionVid>,
-
     /// The final inferred values of the region variables; we compute
     /// one value per SCC. To get the value for any given *region*,
     /// you first find which scc it is a part of.
@@ -151,7 +226,7 @@ pub(crate) struct AppliedMemberConstraint {
 }
 
 #[derive(Debug)]
-pub(crate) struct RegionDefinition<'tcx> {
+pub struct RegionDefinition<'tcx> {
     /// What kind of variable is this -- a free region? existential
     /// variable? etc. (See the `NllRegionVariableOrigin` for more
     /// info.)
@@ -250,7 +325,7 @@ pub enum ExtraConstraintInfo {
 }
 
 #[instrument(skip(infcx, sccs), level = "debug")]
-fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: Rc<Sccs<RegionVid, ConstraintSccIndex>>) {
+fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: &ConstraintSccs) {
     use crate::renumber::RegionCtxt;
 
     let var_to_origin = infcx.reg_var_to_origin.borrow();
@@ -264,7 +339,7 @@ fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: Rc<Sccs<RegionVid, Con
     }
     debug!("{}", reg_vars_to_origins_str);
 
-    let num_components = sccs.scc_data().ranges().len();
+    let num_components = sccs.num_sccs();
     let mut components = vec![FxIndexSet::default(); num_components];
 
     for (reg_var_idx, scc_idx) in sccs.scc_indices().iter().enumerate() {
@@ -301,10 +376,11 @@ fn sccs_info<'tcx>(infcx: &BorrowckInferCtxt<'tcx>, sccs: Rc<Sccs<RegionVid, Con
 
     let mut scc_node_to_edges = FxIndexMap::default();
     for (scc_idx, repr) in components_representatives.iter() {
-        let edges_range = sccs.scc_data().ranges()[*scc_idx].clone();
-        let edges = &sccs.scc_data().all_successors()[edges_range];
-        let edge_representatives =
-            edges.iter().map(|scc_idx| components_representatives[scc_idx]).collect::<Vec<_>>();
+        let edge_representatives = sccs
+            .successors(*scc_idx)
+            .iter()
+            .map(|scc_idx| components_representatives[scc_idx])
+            .collect::<Vec<_>>();
         scc_node_to_edges.insert((scc_idx, repr), edge_representatives);
     }
 
@@ -320,7 +396,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// The `outlives_constraints` and `type_tests` are an initial set
     /// of constraints produced by the MIR type check.
     pub(crate) fn new(
-        _infcx: &BorrowckInferCtxt<'tcx>,
+        infcx: &BorrowckInferCtxt<'tcx>,
         var_infos: VarInfos,
         universal_regions: Rc<UniversalRegions<'tcx>>,
         placeholder_indices: Rc<PlaceholderIndices>,
@@ -343,13 +419,20 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             .map(|info| RegionDefinition::new(info.universe, info.origin))
             .collect();
 
+        let fr_static = universal_regions.fr_static;
         let constraints = Frozen::freeze(outlives_constraints);
         let constraint_graph = Frozen::freeze(constraints.graph(definitions.len()));
-        let fr_static = universal_regions.fr_static;
-        let constraint_sccs = Rc::new(constraints.compute_sccs(&constraint_graph, fr_static));
+        let constraint_sccs = {
+            let constraint_graph = constraints.graph(definitions.len());
+            let region_graph = &constraint_graph.region_graph(&constraints, fr_static);
+            let sccs = ConstraintSccs::new_with_annotation(&region_graph, |r| {
+                RegionTracker::new(r, &definitions[r])
+            });
+            Rc::new(sccs)
+        };
 
         if cfg!(debug_assertions) {
-            sccs_info(_infcx, constraint_sccs.clone());
+            sccs_info(infcx, &constraint_sccs);
         }
 
         let mut scc_values =
@@ -360,10 +443,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             scc_values.merge_liveness(scc, region, &liveness_constraints);
         }
 
-        let scc_universes = Self::compute_scc_universes(&constraint_sccs, &definitions);
-
-        let scc_representatives = Self::compute_scc_representatives(&constraint_sccs, &definitions);
-
         let member_constraints =
             Rc::new(member_constraints_in.into_mapped(|r| constraint_sccs.scc(r)));
 
@@ -378,8 +457,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             member_constraints,
             member_constraints_applied: Vec::new(),
             universe_causes,
-            scc_universes,
-            scc_representatives,
             scc_values,
             type_tests,
             universal_regions,
@@ -391,123 +468,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         result
     }
 
-    /// Each SCC is the combination of many region variables which
-    /// have been equated. Therefore, we can associate a universe with
-    /// each SCC which is minimum of all the universes of its
-    /// constituent regions -- this is because whatever value the SCC
-    /// takes on must be a value that each of the regions within the
-    /// SCC could have as well. This implies that the SCC must have
-    /// the minimum, or narrowest, universe.
-    fn compute_scc_universes(
-        constraint_sccs: &Sccs<RegionVid, ConstraintSccIndex>,
-        definitions: &IndexSlice<RegionVid, RegionDefinition<'tcx>>,
-    ) -> IndexVec<ConstraintSccIndex, ty::UniverseIndex> {
-        let num_sccs = constraint_sccs.num_sccs();
-        let mut scc_universes = IndexVec::from_elem_n(ty::UniverseIndex::MAX, num_sccs);
-
-        debug!("compute_scc_universes()");
-
-        // For each region R in universe U, ensure that the universe for the SCC
-        // that contains R is "no bigger" than U. This effectively sets the universe
-        // for each SCC to be the minimum of the regions within.
-        for (region_vid, region_definition) in definitions.iter_enumerated() {
-            let scc = constraint_sccs.scc(region_vid);
-            let scc_universe = &mut scc_universes[scc];
-            let scc_min = std::cmp::min(region_definition.universe, *scc_universe);
-            if scc_min != *scc_universe {
-                *scc_universe = scc_min;
-                debug!(
-                    "compute_scc_universes: lowered universe of {scc:?} to {scc_min:?} \
-                    because it contains {region_vid:?} in {region_universe:?}",
-                    scc = scc,
-                    scc_min = scc_min,
-                    region_vid = region_vid,
-                    region_universe = region_definition.universe,
-                );
-            }
-        }
-
-        // Walk each SCC `A` and `B` such that `A: B`
-        // and ensure that universe(A) can see universe(B).
-        //
-        // This serves to enforce the 'empty/placeholder' hierarchy
-        // (described in more detail on `RegionKind`):
-        //
-        // ```
-        // static -----+
-        //   |         |
-        // empty(U0) placeholder(U1)
-        //   |      /
-        // empty(U1)
-        // ```
-        //
-        // In particular, imagine we have variables R0 in U0 and R1
-        // created in U1, and constraints like this;
-        //
-        // ```
-        // R1: !1 // R1 outlives the placeholder in U1
-        // R1: R0 // R1 outlives R0
-        // ```
-        //
-        // Here, we wish for R1 to be `'static`, because it
-        // cannot outlive `placeholder(U1)` and `empty(U0)` any other way.
-        //
-        // Thanks to this loop, what happens is that the `R1: R0`
-        // constraint lowers the universe of `R1` to `U0`, which in turn
-        // means that the `R1: !1` constraint will (later) cause
-        // `R1` to become `'static`.
-        for scc_a in constraint_sccs.all_sccs() {
-            for &scc_b in constraint_sccs.successors(scc_a) {
-                let scc_universe_a = scc_universes[scc_a];
-                let scc_universe_b = scc_universes[scc_b];
-                let scc_universe_min = std::cmp::min(scc_universe_a, scc_universe_b);
-                if scc_universe_a != scc_universe_min {
-                    scc_universes[scc_a] = scc_universe_min;
-
-                    debug!(
-                        "compute_scc_universes: lowered universe of {scc_a:?} to {scc_universe_min:?} \
-                        because {scc_a:?}: {scc_b:?} and {scc_b:?} is in universe {scc_universe_b:?}",
-                        scc_a = scc_a,
-                        scc_b = scc_b,
-                        scc_universe_min = scc_universe_min,
-                        scc_universe_b = scc_universe_b
-                    );
-                }
-            }
-        }
-
-        debug!("compute_scc_universes: scc_universe = {:#?}", scc_universes);
-
-        scc_universes
-    }
-
-    /// For each SCC, we compute a unique `RegionVid`. See the
-    /// `scc_representatives` field of `RegionInferenceContext` for
-    /// more details.
-    fn compute_scc_representatives(
-        constraints_scc: &Sccs<RegionVid, ConstraintSccIndex>,
-        definitions: &IndexSlice<RegionVid, RegionDefinition<'tcx>>,
-    ) -> IndexVec<ConstraintSccIndex, ty::RegionVid> {
-        let num_sccs = constraints_scc.num_sccs();
-        let mut scc_representatives = IndexVec::from_elem_n(RegionVid::MAX, num_sccs);
-
-        // Iterate over all RegionVids *in-order* and pick the least RegionVid as the
-        // representative of its SCC. This naturally prefers free regions over others.
-        for (vid, def) in definitions.iter_enumerated() {
-            let repr = &mut scc_representatives[constraints_scc.scc(vid)];
-            if *repr == ty::RegionVid::MAX {
-                *repr = vid;
-            } else if matches!(def.origin, NllRegionVariableOrigin::Placeholder(_))
-                && matches!(definitions[*repr].origin, NllRegionVariableOrigin::Existential { .. })
-            {
-                // Pick placeholders over existentials even if they have a greater RegionVid.
-                *repr = vid;
-            }
-        }
-
-        scc_representatives
-    }
-
     /// Initializes the region variables for each universally
     /// quantified region (lifetime parameter). The first N variables
     /// always correspond to the regions appearing in the function
@@ -528,12 +488,45 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// and (b) any universally quantified regions that it outlives,
     /// which in this case is just itself. R1 (`'b`) in contrast also
     /// outlives `'a` and hence contains R0 and R1.
+    ///
+    /// This bit of logic also handles invalid universe relations
+    /// for higher-kinded types.
+    ///
+    /// We Walk each SCC `A` and `B` such that `A: B`
+    /// and ensure that universe(A) can see universe(B).
+    ///
+    /// This serves to enforce the 'empty/placeholder' hierarchy
+    /// (described in more detail on `RegionKind`):
+    ///
+    /// ```ignore (illustrative)
+    /// static -----+
+    ///   |         |
+    /// empty(U0) placeholder(U1)
+    ///   |      /
+    /// empty(U1)
+    /// ```
+    ///
+    /// In particular, imagine we have variables R0 in U0 and R1
+    /// created in U1, and constraints like this;
+    ///
+    /// ```ignore (illustrative)
+    /// R1: !1 // R1 outlives the placeholder in U1
+    /// R1: R0 // R1 outlives R0
+    /// ```
+    ///
+    /// Here, we wish for R1 to be `'static`, because it
+    /// cannot outlive `placeholder(U1)` and `empty(U0)` any other way.
+    ///
+    /// Thanks to this loop, what happens is that the `R1: R0`
+    /// constraint has lowered the universe of `R1` to `U0`, which in turn
+    /// means that the `R1: !1` constraint here will cause
+    /// `R1` to become `'static`.
     fn init_free_and_bound_regions(&mut self) {
         // Update the names (if any)
         // This iterator has unstable order but we collect it all into an IndexVec
         for (external_name, variable) in self.universal_regions.named_universal_regions() {
             debug!(
-                "init_universal_regions: region {:?} has external name {:?}",
+                "init_free_and_bound_regions: region {:?} has external name {:?}",
                 variable, external_name
             );
             self.definitions[variable].external_name = Some(external_name);
@@ -559,7 +552,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                     // its universe `ui` and its extensions. So we
                     // can't just add it into `scc` unless the
                     // universe of the scc can name this region.
-                    let scc_universe = self.scc_universes[scc];
+                    let scc_universe = self.scc_universe(scc);
                     if scc_universe.can_name(placeholder.universe) {
                         self.scc_values.add_element(scc, placeholder);
                     } else {
@@ -640,8 +633,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
     /// Returns access to the value of `r` for debugging purposes.
     pub(crate) fn region_universe(&self, r: RegionVid) -> ty::UniverseIndex {
-        let scc = self.constraint_sccs.scc(r);
-        self.scc_universes[scc]
+        self.scc_universe(self.constraint_sccs.scc(r))
     }
 
     /// Once region solving has completed, this function will return the member constraints that
@@ -737,8 +729,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         // SCC. For each SCC, we visit its successors and compute
         // their values, then we union all those values to get our
         // own.
-        let constraint_sccs = self.constraint_sccs.clone();
-        for scc in constraint_sccs.all_sccs() {
+        for scc in self.constraint_sccs.all_sccs() {
             self.compute_value_for_scc(scc);
         }
 
@@ -817,20 +808,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         // if one exists.
         for c_r in &mut choice_regions {
             let scc = self.constraint_sccs.scc(*c_r);
-            *c_r = self.scc_representatives[scc];
+            *c_r = self.scc_representative(scc);
         }
 
         // If the member region lives in a higher universe, we currently choose
         // the most conservative option by leaving it unchanged.
-        if self.scc_universes[scc] != ty::UniverseIndex::ROOT {
+
+        if !self.constraint_sccs().annotation(scc).universe().is_root() {
             return;
         }
-        debug_assert!(
-            self.scc_values.placeholders_contained_in(scc).next().is_none(),
-            "scc {:?} in a member constraint has placeholder value: {:?}",
-            scc,
-            self.scc_values.region_value_str(scc),
-        );
 
         // The existing value for `scc` is a lower-bound. This will
         // consist of some set `{P} + {LB}` of points `{P}` and
@@ -900,12 +886,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// in `scc_a`. Used during constraint propagation, and only once
     /// the value of `scc_b` has been computed.
     fn universe_compatible(&self, scc_b: ConstraintSccIndex, scc_a: ConstraintSccIndex) -> bool {
-        let universe_a = self.scc_universes[scc_a];
+        let universe_a = self.constraint_sccs().annotation(scc_a).universe();
+        let universe_b = self.constraint_sccs().annotation(scc_b).universe();
 
         // Quick check: if scc_b's declared universe is a subset of
         // scc_a's declared universe (typically, both are ROOT), then
         // it cannot contain any problematic universe elements.
-        if universe_a.can_name(self.scc_universes[scc_b]) {
+        if universe_a.can_name(universe_b) {
             return true;
         }
 
@@ -1033,7 +1020,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
         debug!(
             "lower_bound = {:?} r_scc={:?} universe={:?}",
-            lower_bound, r_scc, self.scc_universes[r_scc]
+            lower_bound,
+            r_scc,
+            self.constraint_sccs.annotation(r_scc).universe()
         );
 
         // If the type test requires that `T: 'a` where `'a` is a
@@ -1321,7 +1310,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         tcx.fold_regions(value, |r, _db| {
             let vid = self.to_region_vid(r);
             let scc = self.constraint_sccs.scc(vid);
-            let repr = self.scc_representatives[scc];
+            let repr = self.scc_representative(scc);
             ty::Region::new_var(tcx, repr)
         })
     }
@@ -1547,6 +1536,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         }
     }
 
+    /// The minimum universe of any variable reachable from this
+    /// SCC, inside or outside of it.
+    fn scc_universe(&self, scc: ConstraintSccIndex) -> UniverseIndex {
+        self.constraint_sccs().annotation(scc).universe()
+    }
     /// Checks the final value for the free region `fr` to see if it
     /// grew too large. In particular, examine what `end(X)` points
     /// wound up in `fr`'s final value; for each `end(X)` where `X !=
@@ -1566,8 +1560,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
         // Because this free region must be in the ROOT universe, we
         // know it cannot contain any bound universes.
-        assert!(self.scc_universes[longer_fr_scc].is_root());
-        debug_assert!(self.scc_values.placeholders_contained_in(longer_fr_scc).next().is_none());
+        assert!(self.scc_universe(longer_fr_scc).is_root());
 
         // Only check all of the relations for the main representative of each
         // SCC, otherwise just check that we outlive said representative. This
@@ -1575,7 +1568,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         // closures.
         // Note that the representative will be a universal region if there is
         // one in this SCC, so we will always check the representative here.
-        let representative = self.scc_representatives[longer_fr_scc];
+        let representative = self.scc_representative(longer_fr_scc);
         if representative != longer_fr {
             if let RegionRelationCheckResult::Error = self.check_universal_region_relation(
                 longer_fr,
@@ -1796,16 +1789,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// `true` if `r1` cannot name that placeholder in its
     /// value; otherwise, returns `false`.
     pub(crate) fn cannot_name_placeholder(&self, r1: RegionVid, r2: RegionVid) -> bool {
-        debug!("cannot_name_value_of(r1={:?}, r2={:?})", r1, r2);
-
         match self.definitions[r2].origin {
             NllRegionVariableOrigin::Placeholder(placeholder) => {
-                let universe1 = self.definitions[r1].universe;
+                let r1_universe = self.definitions[r1].universe;
                 debug!(
-                    "cannot_name_value_of: universe1={:?} placeholder={:?}",
-                    universe1, placeholder
+                    "cannot_name_value_of: universe1={r1_universe:?} placeholder={:?}",
+                    placeholder
                 );
-                universe1.cannot_name(placeholder.universe)
+                r1_universe.cannot_name(placeholder.universe)
             }
 
             NllRegionVariableOrigin::FreeRegion | NllRegionVariableOrigin::Existential { .. } => {
@@ -1835,6 +1826,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     ///
     /// Returns: a series of constraints as well as the region `R`
     /// that passed the target test.
+    #[instrument(skip(self, target_test), ret)]
     pub(crate) fn find_constraint_paths_between_regions(
         &self,
         from_region: RegionVid,
@@ -1932,7 +1924,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     #[instrument(skip(self), level = "trace", ret)]
     pub(crate) fn find_sub_region_live_at(&self, fr1: RegionVid, location: Location) -> RegionVid {
         trace!(scc = ?self.constraint_sccs.scc(fr1));
-        trace!(universe = ?self.scc_universes[self.constraint_sccs.scc(fr1)]);
+        trace!(universe = ?self.region_universe(fr1));
         self.find_constraint_paths_between_regions(fr1, |r| {
             // First look for some `r` such that `fr1: r` and `r` is live at `location`
             trace!(?r, liveness_constraints=?self.liveness_constraints.pretty_print_live_points(r));
@@ -2252,8 +2244,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// This can be used to quickly under-approximate the regions which are equal to each other
     /// and their relative orderings.
     // This is `pub` because it's used by unstable external borrowck data users, see `consumers.rs`.
-    pub fn constraint_sccs(&self) -> &Sccs<RegionVid, ConstraintSccIndex> {
-        self.constraint_sccs.as_ref()
+    pub fn constraint_sccs(&self) -> &ConstraintSccs {
+        &self.constraint_sccs
     }
 
     /// Access to the region graph, built from the outlives constraints.
@@ -2282,6 +2274,18 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         let point = self.liveness_constraints.point_from_location(location);
         self.liveness_constraints.is_loan_live_at(loan_idx, point)
     }
+
+    /// Returns the representative `RegionVid` for a given SCC.
+    /// See `RegionTracker` for how a region variable ID is chosen.
+    ///
+    /// It is a hacky way to manage checking regions for equality,
+    /// since we can 'canonicalize' each region to the representative
+    /// of its SCC and be sure that -- if they have the same repr --
+    /// they *must* be equal (though not having the same repr does not
+    /// mean they are unequal).
+    fn scc_representative(&self, scc: ConstraintSccIndex) -> RegionVid {
+        self.constraint_sccs.annotation(scc).representative
+    }
 }
 
 impl<'tcx> RegionDefinition<'tcx> {
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
index 06adb686ed4..51c3648d730 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
@@ -85,7 +85,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                     // Use the SCC representative instead of directly using `region`.
                     // See [rustc-dev-guide chapter] § "Strict lifetime equality".
                     let scc = self.constraint_sccs.scc(region.as_var());
-                    let vid = self.scc_representatives[scc];
+                    let vid = self.scc_representative(scc);
                     let named = match self.definitions[vid].origin {
                         // Iterate over all universal regions in a consistent order and find the
                         // *first* equal region. This makes sure that equal lifetimes will have
@@ -213,7 +213,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 let scc = self.constraint_sccs.scc(vid);
 
                 // Special handling of higher-ranked regions.
-                if !self.scc_universes[scc].is_root() {
+                if !self.scc_universe(scc).is_root() {
                     match self.scc_values.placeholders_contained_in(scc).enumerate().last() {
                         // If the region contains a single placeholder then they're equal.
                         Some((0, placeholder)) => {
diff --git a/compiler/rustc_borrowck/src/renumber.rs b/compiler/rustc_borrowck/src/renumber.rs
index d382f264c37..2858a407e09 100644
--- a/compiler/rustc_borrowck/src/renumber.rs
+++ b/compiler/rustc_borrowck/src/renumber.rs
@@ -113,7 +113,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for RegionRenumberer<'a, 'tcx> {
     }
 
     #[instrument(skip(self), level = "debug")]
-    fn visit_constant(&mut self, constant: &mut ConstOperand<'tcx>, location: Location) {
+    fn visit_const_operand(&mut self, constant: &mut ConstOperand<'tcx>, location: Location) {
         let const_ = constant.const_;
         constant.const_ = self.renumber_regions(const_, || RegionCtxt::Location(location));
         debug!("constant: {:#?}", constant);
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index fcfb297d50a..c7c1b2af6a7 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -301,10 +301,10 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
         self.sanitize_place(place, location, context);
     }
 
-    fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, location: Location) {
-        debug!(?constant, ?location, "visit_constant");
+    fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, location: Location) {
+        debug!(?constant, ?location, "visit_const_operand");
 
-        self.super_constant(constant, location);
+        self.super_const_operand(constant, location);
         let ty = self.sanitize_type(constant, constant.const_.ty());
 
         self.cx.infcx.tcx.for_each_free_region(&ty, |live_region| {
@@ -328,7 +328,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
         if let Some(annotation_index) = constant.user_ty {
             if let Err(terr) = self.cx.relate_type_and_user_type(
                 constant.const_.ty(),
-                ty::Variance::Invariant,
+                ty::Invariant,
                 &UserTypeProjection { base: annotation_index, projs: vec![] },
                 locations,
                 ConstraintCategory::Boring,
@@ -451,7 +451,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
 
                 if let Err(terr) = self.cx.relate_type_and_user_type(
                     ty,
-                    ty::Variance::Invariant,
+                    ty::Invariant,
                     user_ty,
                     Locations::All(*span),
                     ConstraintCategory::TypeAnnotation,
@@ -1095,7 +1095,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
     ) -> Result<(), NoSolution> {
         // Use this order of parameters because the sup type is usually the
         // "expected" type in diagnostics.
-        self.relate_types(sup, ty::Variance::Contravariant, sub, locations, category)
+        self.relate_types(sup, ty::Contravariant, sub, locations, category)
     }
 
     #[instrument(skip(self, category), level = "debug")]
@@ -1106,7 +1106,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         locations: Locations,
         category: ConstraintCategory<'tcx>,
     ) -> Result<(), NoSolution> {
-        self.relate_types(expected, ty::Variance::Invariant, found, locations, category)
+        self.relate_types(expected, ty::Invariant, found, locations, category)
     }
 
     #[instrument(skip(self), level = "debug")]
@@ -1146,7 +1146,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         trace!(?curr_projected_ty);
 
         let ty = curr_projected_ty.ty;
-        self.relate_types(ty, v.xform(ty::Variance::Contravariant), a, locations, category)?;
+        self.relate_types(ty, v.xform(ty::Contravariant), a, locations, category)?;
 
         Ok(())
     }
@@ -1248,7 +1248,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 if let Some(annotation_index) = self.rvalue_user_ty(rv) {
                     if let Err(terr) = self.relate_type_and_user_type(
                         rv_ty,
-                        ty::Variance::Invariant,
+                        ty::Invariant,
                         &UserTypeProjection { base: annotation_index, projs: vec![] },
                         location.to_locations(),
                         ConstraintCategory::Boring,
diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
index c531c9b209b..b9a82046e59 100644
--- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs
+++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
@@ -50,14 +50,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         locations: Locations,
         category: ConstraintCategory<'tcx>,
     ) -> Result<(), NoSolution> {
-        NllTypeRelating::new(
-            self,
-            locations,
-            category,
-            UniverseInfo::other(),
-            ty::Variance::Invariant,
-        )
-        .relate(a, b)?;
+        NllTypeRelating::new(self, locations, category, UniverseInfo::other(), ty::Invariant)
+            .relate(a, b)?;
         Ok(())
     }
 }
@@ -106,15 +100,15 @@ impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> {
 
     fn ambient_covariance(&self) -> bool {
         match self.ambient_variance {
-            ty::Variance::Covariant | ty::Variance::Invariant => true,
-            ty::Variance::Contravariant | ty::Variance::Bivariant => false,
+            ty::Covariant | ty::Invariant => true,
+            ty::Contravariant | ty::Bivariant => false,
         }
     }
 
     fn ambient_contravariance(&self) -> bool {
         match self.ambient_variance {
-            ty::Variance::Contravariant | ty::Variance::Invariant => true,
-            ty::Variance::Covariant | ty::Variance::Bivariant => false,
+            ty::Contravariant | ty::Invariant => true,
+            ty::Covariant | ty::Bivariant => false,
         }
     }
 
@@ -336,11 +330,7 @@ impl<'bccx, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'bccx, 'tcx
 
         debug!(?self.ambient_variance);
         // In a bivariant context this always succeeds.
-        let r = if self.ambient_variance == ty::Variance::Bivariant {
-            Ok(a)
-        } else {
-            self.relate(a, b)
-        };
+        let r = if self.ambient_variance == ty::Bivariant { Ok(a) } else { self.relate(a, b) };
 
         self.ambient_variance = old_ambient_variance;
 
@@ -474,7 +464,7 @@ impl<'bccx, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'bccx, 'tcx
         }
 
         match self.ambient_variance {
-            ty::Variance::Covariant => {
+            ty::Covariant => {
                 // Covariance, so we want `for<..> A <: for<..> B` --
                 // therefore we compare any instantiation of A (i.e., A
                 // instantiated with existentials) against every
@@ -489,7 +479,7 @@ impl<'bccx, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'bccx, 'tcx
                 })?;
             }
 
-            ty::Variance::Contravariant => {
+            ty::Contravariant => {
                 // Contravariance, so we want `for<..> A :> for<..> B` --
                 // therefore we compare every instantiation of A (i.e., A
                 // instantiated with universals) against any
@@ -504,7 +494,7 @@ impl<'bccx, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'bccx, 'tcx
                 })?;
             }
 
-            ty::Variance::Invariant => {
+            ty::Invariant => {
                 // Invariant, so we want `for<..> A == for<..> B` --
                 // therefore we want `exists<..> A == for<..> B` and
                 // `exists<..> B == for<..> A`.
@@ -525,7 +515,7 @@ impl<'bccx, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'bccx, 'tcx
                 })?;
             }
 
-            ty::Variance::Bivariant => {}
+            ty::Bivariant => {}
         }
 
         Ok(a)
@@ -584,23 +574,23 @@ impl<'bccx, 'tcx> PredicateEmittingRelation<'tcx> for NllTypeRelating<'_, 'bccx,
 
     fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
         self.register_predicates([ty::Binder::dummy(match self.ambient_variance {
-            ty::Variance::Covariant => ty::PredicateKind::AliasRelate(
+            ty::Covariant => ty::PredicateKind::AliasRelate(
                 a.into(),
                 b.into(),
                 ty::AliasRelationDirection::Subtype,
             ),
             // a :> b is b <: a
-            ty::Variance::Contravariant => ty::PredicateKind::AliasRelate(
+            ty::Contravariant => ty::PredicateKind::AliasRelate(
                 b.into(),
                 a.into(),
                 ty::AliasRelationDirection::Subtype,
             ),
-            ty::Variance::Invariant => ty::PredicateKind::AliasRelate(
+            ty::Invariant => ty::PredicateKind::AliasRelate(
                 a.into(),
                 b.into(),
                 ty::AliasRelationDirection::Equate,
             ),
-            ty::Variance::Bivariant => {
+            ty::Bivariant => {
                 unreachable!("cannot defer an alias-relate goal with Bivariant variance (yet?)")
             }
         })]);
diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs
index 2de804f5e04..c6b26dd873b 100644
--- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs
+++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs
@@ -4,6 +4,7 @@ use std::fmt::Write;
 
 use cranelift_codegen::isa::CallConv;
 use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece};
+use rustc_hir::LangItem;
 use rustc_span::sym;
 use rustc_target::asm::*;
 use target_lexicon::BinaryFormat;
@@ -927,7 +928,7 @@ fn call_inline_asm<'tcx>(
 fn asm_clif_type<'tcx>(fx: &FunctionCx<'_, '_, 'tcx>, ty: Ty<'tcx>) -> Option<types::Type> {
     match ty.kind() {
         // Adapted from https://github.com/rust-lang/rust/blob/f3c66088610c1b80110297c2d9a8b5f9265b013f/compiler/rustc_hir_analysis/src/check/intrinsicck.rs#L136-L151
-        ty::Adt(adt, args) if Some(adt.did()) == fx.tcx.lang_items().maybe_uninit() => {
+        ty::Adt(adt, args) if fx.tcx.is_lang_item(adt.did(), LangItem::MaybeUninit) => {
             let fields = &adt.non_enum_variant().fields;
             let ty = fields[FieldIdx::from_u32(1)].ty(fx.tcx, args);
             let ty::Adt(ty, args) = ty.kind() else {
diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs
index db28c6857b7..60e63b956db 100644
--- a/compiler/rustc_codegen_llvm/src/asm.rs
+++ b/compiler/rustc_codegen_llvm/src/asm.rs
@@ -960,6 +960,43 @@ fn llvm_fixup_input<'ll, 'tcx>(
             Abi::Vector { .. },
         ) if layout.size.bytes() == 64 => bx.bitcast(value, bx.cx.type_vector(bx.cx.type_f64(), 8)),
         (
+            InlineAsmRegClass::X86(
+                X86InlineAsmRegClass::xmm_reg
+                | X86InlineAsmRegClass::ymm_reg
+                | X86InlineAsmRegClass::zmm_reg,
+            ),
+            Abi::Scalar(s),
+        ) if bx.sess().asm_arch == Some(InlineAsmArch::X86)
+            && s.primitive() == Primitive::Float(Float::F128) =>
+        {
+            bx.bitcast(value, bx.type_vector(bx.type_i32(), 4))
+        }
+        (
+            InlineAsmRegClass::X86(
+                X86InlineAsmRegClass::xmm_reg
+                | X86InlineAsmRegClass::ymm_reg
+                | X86InlineAsmRegClass::zmm_reg,
+            ),
+            Abi::Scalar(s),
+        ) if s.primitive() == Primitive::Float(Float::F16) => {
+            let value = bx.insert_element(
+                bx.const_undef(bx.type_vector(bx.type_f16(), 8)),
+                value,
+                bx.const_usize(0),
+            );
+            bx.bitcast(value, bx.type_vector(bx.type_i16(), 8))
+        }
+        (
+            InlineAsmRegClass::X86(
+                X86InlineAsmRegClass::xmm_reg
+                | X86InlineAsmRegClass::ymm_reg
+                | X86InlineAsmRegClass::zmm_reg,
+            ),
+            Abi::Vector { element, count: count @ (8 | 16) },
+        ) if element.primitive() == Primitive::Float(Float::F16) => {
+            bx.bitcast(value, bx.type_vector(bx.type_i16(), count))
+        }
+        (
             InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg | ArmInlineAsmRegClass::sreg_low16),
             Abi::Scalar(s),
         ) => {
@@ -1037,6 +1074,39 @@ fn llvm_fixup_output<'ll, 'tcx>(
             Abi::Vector { .. },
         ) if layout.size.bytes() == 64 => bx.bitcast(value, layout.llvm_type(bx.cx)),
         (
+            InlineAsmRegClass::X86(
+                X86InlineAsmRegClass::xmm_reg
+                | X86InlineAsmRegClass::ymm_reg
+                | X86InlineAsmRegClass::zmm_reg,
+            ),
+            Abi::Scalar(s),
+        ) if bx.sess().asm_arch == Some(InlineAsmArch::X86)
+            && s.primitive() == Primitive::Float(Float::F128) =>
+        {
+            bx.bitcast(value, bx.type_f128())
+        }
+        (
+            InlineAsmRegClass::X86(
+                X86InlineAsmRegClass::xmm_reg
+                | X86InlineAsmRegClass::ymm_reg
+                | X86InlineAsmRegClass::zmm_reg,
+            ),
+            Abi::Scalar(s),
+        ) if s.primitive() == Primitive::Float(Float::F16) => {
+            let value = bx.bitcast(value, bx.type_vector(bx.type_f16(), 8));
+            bx.extract_element(value, bx.const_usize(0))
+        }
+        (
+            InlineAsmRegClass::X86(
+                X86InlineAsmRegClass::xmm_reg
+                | X86InlineAsmRegClass::ymm_reg
+                | X86InlineAsmRegClass::zmm_reg,
+            ),
+            Abi::Vector { element, count: count @ (8 | 16) },
+        ) if element.primitive() == Primitive::Float(Float::F16) => {
+            bx.bitcast(value, bx.type_vector(bx.type_f16(), count))
+        }
+        (
             InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg | ArmInlineAsmRegClass::sreg_low16),
             Abi::Scalar(s),
         ) => {
@@ -1110,6 +1180,36 @@ fn llvm_fixup_output_type<'ll, 'tcx>(
             Abi::Vector { .. },
         ) if layout.size.bytes() == 64 => cx.type_vector(cx.type_f64(), 8),
         (
+            InlineAsmRegClass::X86(
+                X86InlineAsmRegClass::xmm_reg
+                | X86InlineAsmRegClass::ymm_reg
+                | X86InlineAsmRegClass::zmm_reg,
+            ),
+            Abi::Scalar(s),
+        ) if cx.sess().asm_arch == Some(InlineAsmArch::X86)
+            && s.primitive() == Primitive::Float(Float::F128) =>
+        {
+            cx.type_vector(cx.type_i32(), 4)
+        }
+        (
+            InlineAsmRegClass::X86(
+                X86InlineAsmRegClass::xmm_reg
+                | X86InlineAsmRegClass::ymm_reg
+                | X86InlineAsmRegClass::zmm_reg,
+            ),
+            Abi::Scalar(s),
+        ) if s.primitive() == Primitive::Float(Float::F16) => cx.type_vector(cx.type_i16(), 8),
+        (
+            InlineAsmRegClass::X86(
+                X86InlineAsmRegClass::xmm_reg
+                | X86InlineAsmRegClass::ymm_reg
+                | X86InlineAsmRegClass::zmm_reg,
+            ),
+            Abi::Vector { element, count: count @ (8 | 16) },
+        ) if element.primitive() == Primitive::Float(Float::F16) => {
+            cx.type_vector(cx.type_i16(), count)
+        }
+        (
             InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg | ArmInlineAsmRegClass::sreg_low16),
             Abi::Scalar(s),
         ) => {
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index 53a098d178e..7d92888feee 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -142,6 +142,14 @@ pub unsafe fn create_module<'ll>(
         }
     }
 
+    if llvm_version < (19, 0, 0) {
+        if sess.target.arch == "loongarch64" {
+            // LLVM 19 updates the LoongArch64 data layout.
+            // See https://github.com/llvm/llvm-project/pull/93814
+            target_data_layout = target_data_layout.replace("-n32:64", "-n64");
+        }
+    }
+
     // Ensure the data-layout values hardcoded remain the defaults.
     {
         let tm = crate::back::write::create_informational_target_machine(tcx.sess);
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index 53b9b530e9b..7e0f264a4ae 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -394,10 +394,15 @@ fn print_target_features(out: &mut dyn PrintBackendInfo, sess: &Session, tm: &ll
             (*feature, desc)
         })
         .collect::<Vec<_>>();
+
+    // Since we add this at the end ...
     rustc_target_features.extend_from_slice(&[(
         "crt-static",
         "Enables C Run-time Libraries to be statically linked",
     )]);
+    // ... we need to sort the list again.
+    rustc_target_features.sort();
+
     llvm_target_features.retain(|(f, _d)| !known_llvm_target_features.contains(f));
 
     let max_feature_len = llvm_target_features
diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index 9e01c59a96f..0818d9425e2 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -1,8 +1,8 @@
 //! The `Visitor` responsible for actually checking a `mir::Body` for invalid operations.
 
 use rustc_errors::{Diag, ErrorGuaranteed};
-use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
+use rustc_hir::{self as hir, LangItem};
 use rustc_index::bit_set::BitSet;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_infer::traits::ObligationCause;
@@ -801,7 +801,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                 // const-eval.
 
                 // const-eval of the `begin_panic` fn assumes the argument is `&str`
-                if Some(callee) == tcx.lang_items().begin_panic_fn() {
+                if tcx.is_lang_item(callee, LangItem::BeginPanic) {
                     match args[0].node.ty(&self.ccx.body.local_decls, tcx).kind() {
                         ty::Ref(_, ty, _) if ty.is_str() => return,
                         _ => self.check_op(ops::PanicNonStr),
@@ -819,7 +819,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                     }
                 }
 
-                if Some(callee) == tcx.lang_items().exchange_malloc_fn() {
+                if tcx.is_lang_item(callee, LangItem::ExchangeMalloc) {
                     self.check_op(ops::HeapAllocation);
                     return;
                 }
diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs
index feab5b929ac..eb9a83fb9cf 100644
--- a/compiler/rustc_const_eval/src/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/check_consts/ops.rs
@@ -218,7 +218,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
                 } else {
                     let mut sugg = None;
 
-                    if Some(trait_id) == ccx.tcx.lang_items().eq_trait() {
+                    if ccx.tcx.is_lang_item(trait_id, LangItem::PartialEq) {
                         match (args[0].unpack(), args[1].unpack()) {
                             (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
                                 if self_ty == rhs_ty
diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs
index 923b9ddf9af..b17dc7f3ddd 100644
--- a/compiler/rustc_const_eval/src/const_eval/error.rs
+++ b/compiler/rustc_const_eval/src/const_eval/error.rs
@@ -1,7 +1,6 @@
 use std::mem;
 
 use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, Diagnostic, IntoDiagArg};
-use rustc_hir::CRATE_HIR_ID;
 use rustc_middle::mir::interpret::{Provenance, ReportedErrorInfo};
 use rustc_middle::mir::AssertKind;
 use rustc_middle::query::TyCtxtAt;
@@ -9,7 +8,7 @@ use rustc_middle::ty::TyCtxt;
 use rustc_middle::ty::{layout::LayoutError, ConstInt};
 use rustc_span::{Span, Symbol};
 
-use super::CompileTimeInterpreter;
+use super::CompileTimeMachine;
 use crate::errors::{self, FrameNote, ReportErrorExt};
 use crate::interpret::{err_inval, err_machine_stop};
 use crate::interpret::{ErrorHandled, Frame, InterpError, InterpErrorInfo, MachineStopType};
@@ -156,11 +155,11 @@ where
     }
 }
 
-/// Emit a lint from a const-eval situation.
+/// Emit a lint from a const-eval situation, with a backtrace.
 // Even if this is unused, please don't remove it -- chances are we will need to emit a lint during const-eval again in the future!
 pub(super) fn lint<'tcx, L>(
     tcx: TyCtxtAt<'tcx>,
-    machine: &CompileTimeInterpreter<'tcx>,
+    machine: &CompileTimeMachine<'tcx>,
     lint: &'static rustc_session::lint::Lint,
     decorator: impl FnOnce(Vec<errors::FrameNote>) -> L,
 ) where
@@ -168,12 +167,5 @@ pub(super) fn lint<'tcx, L>(
 {
     let (span, frames) = get_span_and_frames(tcx, &machine.stack);
 
-    tcx.emit_node_span_lint(
-        lint,
-        // We use the root frame for this so the crate that defines the const defines whether the
-        // lint is emitted.
-        machine.stack.first().and_then(|frame| frame.lint_root()).unwrap_or(CRATE_HIR_ID),
-        span,
-        decorator(frames),
-    );
+    tcx.emit_node_span_lint(lint, machine.best_lint_scope(*tcx), span, decorator(frames));
 }
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index 36f468d3308..4b8145eb485 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -17,7 +17,7 @@ use rustc_span::def_id::LocalDefId;
 use rustc_span::{Span, DUMMY_SP};
 use rustc_target::abi::{self, Abi};
 
-use super::{CanAccessMutGlobal, CompileTimeEvalContext, CompileTimeInterpreter};
+use super::{CanAccessMutGlobal, CompileTimeInterpCx, CompileTimeMachine};
 use crate::const_eval::CheckAlignment;
 use crate::errors::ConstEvalError;
 use crate::errors::{self, DanglingPtrInFinal};
@@ -32,7 +32,7 @@ use crate::CTRL_C_RECEIVED;
 // Returns a pointer to where the result lives
 #[instrument(level = "trace", skip(ecx, body))]
 fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
-    ecx: &mut CompileTimeEvalContext<'tcx>,
+    ecx: &mut CompileTimeInterpCx<'tcx>,
     cid: GlobalId<'tcx>,
     body: &'tcx mir::Body<'tcx>,
 ) -> InterpResult<'tcx, R> {
@@ -114,7 +114,7 @@ fn eval_body_using_ecx<'tcx, R: InterpretationResult<'tcx>>(
             let err_diag = errors::MutablePtrInFinal { span: ecx.tcx.span, kind: intern_kind };
             ecx.tcx.emit_node_span_lint(
                 lint::builtin::CONST_EVAL_MUTABLE_PTR_IN_FINAL_VALUE,
-                ecx.best_lint_scope(),
+                ecx.machine.best_lint_scope(*ecx.tcx),
                 err_diag.span,
                 err_diag,
             )
@@ -139,13 +139,13 @@ pub(crate) fn mk_eval_cx_to_read_const_val<'tcx>(
     root_span: Span,
     param_env: ty::ParamEnv<'tcx>,
     can_access_mut_global: CanAccessMutGlobal,
-) -> CompileTimeEvalContext<'tcx> {
+) -> CompileTimeInterpCx<'tcx> {
     debug!("mk_eval_cx: {:?}", param_env);
     InterpCx::new(
         tcx,
         root_span,
         param_env,
-        CompileTimeInterpreter::new(can_access_mut_global, CheckAlignment::No),
+        CompileTimeMachine::new(can_access_mut_global, CheckAlignment::No),
     )
 }
 
@@ -156,7 +156,7 @@ pub fn mk_eval_cx_for_const_val<'tcx>(
     param_env: ty::ParamEnv<'tcx>,
     val: mir::ConstValue<'tcx>,
     ty: Ty<'tcx>,
-) -> Option<(CompileTimeEvalContext<'tcx>, OpTy<'tcx>)> {
+) -> Option<(CompileTimeInterpCx<'tcx>, OpTy<'tcx>)> {
     let ecx = mk_eval_cx_to_read_const_val(tcx.tcx, tcx.span, param_env, CanAccessMutGlobal::No);
     let op = ecx.const_val_to_op(val, ty, None).ok()?;
     Some((ecx, op))
@@ -170,7 +170,7 @@ pub fn mk_eval_cx_for_const_val<'tcx>(
 /// encounter an `Indirect` they cannot handle.
 #[instrument(skip(ecx), level = "debug")]
 pub(super) fn op_to_const<'tcx>(
-    ecx: &CompileTimeEvalContext<'tcx>,
+    ecx: &CompileTimeInterpCx<'tcx>,
     op: &OpTy<'tcx>,
     for_diagnostics: bool,
 ) -> ConstValue<'tcx> {
@@ -328,14 +328,14 @@ pub trait InterpretationResult<'tcx> {
     /// evaluation query.
     fn make_result(
         mplace: MPlaceTy<'tcx>,
-        ecx: &mut InterpCx<'tcx, CompileTimeInterpreter<'tcx>>,
+        ecx: &mut InterpCx<'tcx, CompileTimeMachine<'tcx>>,
     ) -> Self;
 }
 
 impl<'tcx> InterpretationResult<'tcx> for ConstAlloc<'tcx> {
     fn make_result(
         mplace: MPlaceTy<'tcx>,
-        _ecx: &mut InterpCx<'tcx, CompileTimeInterpreter<'tcx>>,
+        _ecx: &mut InterpCx<'tcx, CompileTimeMachine<'tcx>>,
     ) -> Self {
         ConstAlloc { alloc_id: mplace.ptr().provenance.unwrap().alloc_id(), ty: mplace.layout.ty }
     }
@@ -383,7 +383,7 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>(
         // they do not have to behave "as if" they were evaluated at runtime.
         // For consts however we want to ensure they behave "as if" they were evaluated at runtime,
         // so we have to reject reading mutable global memory.
-        CompileTimeInterpreter::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error),
+        CompileTimeMachine::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error),
     );
     let res = ecx.load_mir(cid.instance.def, cid.promoted);
     res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)).map_err(|error| {
@@ -417,7 +417,7 @@ fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>(
 
 #[inline(always)]
 fn const_validate_mplace<'tcx>(
-    ecx: &InterpCx<'tcx, CompileTimeInterpreter<'tcx>>,
+    ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>,
     mplace: &MPlaceTy<'tcx>,
     cid: GlobalId<'tcx>,
 ) -> Result<(), ErrorHandled> {
@@ -447,7 +447,7 @@ fn const_validate_mplace<'tcx>(
 
 #[inline(always)]
 fn report_validation_error<'tcx>(
-    ecx: &InterpCx<'tcx, CompileTimeInterpreter<'tcx>>,
+    ecx: &InterpCx<'tcx, CompileTimeMachine<'tcx>>,
     error: InterpErrorInfo<'tcx>,
     alloc_id: AllocId,
 ) -> ErrorHandled {
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 79a161d3f03..d3631e0d723 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -9,12 +9,13 @@ use rustc_data_structures::fx::IndexEntry;
 use rustc_hir::def_id::DefId;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::LangItem;
+use rustc_hir::{self as hir, CRATE_HIR_ID};
 use rustc_middle::bug;
 use rustc_middle::mir;
 use rustc_middle::mir::AssertMessage;
 use rustc_middle::query::TyCtxtAt;
-use rustc_middle::ty;
 use rustc_middle::ty::layout::{FnAbiOf, TyAndLayout};
+use rustc_middle::ty::{self, TyCtxt};
 use rustc_session::lint::builtin::WRITES_THROUGH_IMMUTABLE_POINTER;
 use rustc_span::symbol::{sym, Symbol};
 use rustc_span::Span;
@@ -44,7 +45,7 @@ const TINY_LINT_TERMINATOR_LIMIT: usize = 20;
 const PROGRESS_INDICATOR_START: usize = 4_000_000;
 
 /// Extra machine state for CTFE, and the Machine instance
-pub struct CompileTimeInterpreter<'tcx> {
+pub struct CompileTimeMachine<'tcx> {
     /// The number of terminators that have been evaluated.
     ///
     /// This is used to produce lints informing the user that the compiler is not stuck.
@@ -89,12 +90,12 @@ impl From<bool> for CanAccessMutGlobal {
     }
 }
 
-impl<'tcx> CompileTimeInterpreter<'tcx> {
+impl<'tcx> CompileTimeMachine<'tcx> {
     pub(crate) fn new(
         can_access_mut_global: CanAccessMutGlobal,
         check_alignment: CheckAlignment,
     ) -> Self {
-        CompileTimeInterpreter {
+        CompileTimeMachine {
             num_evaluated_steps: 0,
             stack: Vec::new(),
             can_access_mut_global,
@@ -163,7 +164,7 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxIndexMap<K, V> {
     }
 }
 
-pub(crate) type CompileTimeEvalContext<'tcx> = InterpCx<'tcx, CompileTimeInterpreter<'tcx>>;
+pub(crate) type CompileTimeInterpCx<'tcx> = InterpCx<'tcx, CompileTimeMachine<'tcx>>;
 
 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
 pub enum MemoryKind {
@@ -195,7 +196,7 @@ impl interpret::MayLeak for ! {
     }
 }
 
-impl<'tcx> CompileTimeEvalContext<'tcx> {
+impl<'tcx> CompileTimeInterpCx<'tcx> {
     fn location_triple_for_span(&self, span: Span) -> (Symbol, u32, u32) {
         let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
         let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
@@ -229,7 +230,7 @@ impl<'tcx> CompileTimeEvalContext<'tcx> {
         let def_id = instance.def_id();
 
         if self.tcx.has_attr(def_id, sym::rustc_const_panic_str)
-            || Some(def_id) == self.tcx.lang_items().begin_panic_fn()
+            || self.tcx.is_lang_item(def_id, LangItem::BeginPanic)
         {
             let args = self.copy_fn_args(args);
             // &str or &&str
@@ -244,7 +245,7 @@ impl<'tcx> CompileTimeEvalContext<'tcx> {
             let span = self.find_closest_untracked_caller_location();
             let (file, line, col) = self.location_triple_for_span(span);
             return Err(ConstEvalErrKind::Panic { msg, file, line, col }.into());
-        } else if Some(def_id) == self.tcx.lang_items().panic_fmt() {
+        } else if self.tcx.is_lang_item(def_id, LangItem::PanicFmt) {
             // For panic_fmt, call const_panic_fmt instead.
             let const_def_id = self.tcx.require_lang_item(LangItem::ConstPanicFmt, None);
             let new_instance = ty::Instance::expect_resolve(
@@ -255,7 +256,7 @@ impl<'tcx> CompileTimeEvalContext<'tcx> {
             );
 
             return Ok(Some(new_instance));
-        } else if Some(def_id) == self.tcx.lang_items().align_offset_fn() {
+        } else if self.tcx.is_lang_item(def_id, LangItem::AlignOffset) {
             let args = self.copy_fn_args(args);
             // For align_offset, we replace the function call if the pointer has no address.
             match self.align_offset(instance, &args, dest, ret)? {
@@ -369,7 +370,16 @@ impl<'tcx> CompileTimeEvalContext<'tcx> {
     }
 }
 
-impl<'tcx> interpret::Machine<'tcx> for CompileTimeInterpreter<'tcx> {
+impl<'tcx> CompileTimeMachine<'tcx> {
+    #[inline(always)]
+    /// Find the first stack frame that is within the current crate, if any.
+    /// Otherwise, return the crate's HirId
+    pub fn best_lint_scope(&self, tcx: TyCtxt<'tcx>) -> hir::HirId {
+        self.stack.iter().find_map(|frame| frame.lint_root(tcx)).unwrap_or(CRATE_HIR_ID)
+    }
+}
+
+impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
     compile_time_machine!(<'tcx>);
 
     type MemoryKind = MemoryKind;
@@ -600,7 +610,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeInterpreter<'tcx> {
                 // By default, we stop after a million steps, but the user can disable this lint
                 // to be able to run until the heat death of the universe or power loss, whichever
                 // comes first.
-                let hir_id = ecx.best_lint_scope();
+                let hir_id = ecx.machine.best_lint_scope(*ecx.tcx);
                 let is_error = ecx
                     .tcx
                     .lint_level_at_node(
diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
index 66993476bef..2e8ad445cf5 100644
--- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs
+++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
@@ -9,7 +9,7 @@ use rustc_target::abi::{Abi, VariantIdx};
 use tracing::{debug, instrument, trace};
 
 use super::eval_queries::{mk_eval_cx_to_read_const_val, op_to_const};
-use super::machine::CompileTimeEvalContext;
+use super::machine::CompileTimeInterpCx;
 use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES};
 use crate::const_eval::CanAccessMutGlobal;
 use crate::errors::MaxNumNodesInConstErr;
@@ -21,7 +21,7 @@ use crate::interpret::{
 
 #[instrument(skip(ecx), level = "debug")]
 fn branches<'tcx>(
-    ecx: &CompileTimeEvalContext<'tcx>,
+    ecx: &CompileTimeInterpCx<'tcx>,
     place: &MPlaceTy<'tcx>,
     n: usize,
     variant: Option<VariantIdx>,
@@ -59,7 +59,7 @@ fn branches<'tcx>(
 
 #[instrument(skip(ecx), level = "debug")]
 fn slice_branches<'tcx>(
-    ecx: &CompileTimeEvalContext<'tcx>,
+    ecx: &CompileTimeInterpCx<'tcx>,
     place: &MPlaceTy<'tcx>,
     num_nodes: &mut usize,
 ) -> ValTreeCreationResult<'tcx> {
@@ -77,7 +77,7 @@ fn slice_branches<'tcx>(
 
 #[instrument(skip(ecx), level = "debug")]
 fn const_to_valtree_inner<'tcx>(
-    ecx: &CompileTimeEvalContext<'tcx>,
+    ecx: &CompileTimeInterpCx<'tcx>,
     place: &MPlaceTy<'tcx>,
     num_nodes: &mut usize,
 ) -> ValTreeCreationResult<'tcx> {
@@ -219,7 +219,7 @@ fn reconstruct_place_meta<'tcx>(
 
 #[instrument(skip(ecx), level = "debug", ret)]
 fn create_valtree_place<'tcx>(
-    ecx: &mut CompileTimeEvalContext<'tcx>,
+    ecx: &mut CompileTimeInterpCx<'tcx>,
     layout: TyAndLayout<'tcx>,
     valtree: ty::ValTree<'tcx>,
 ) -> MPlaceTy<'tcx> {
@@ -364,7 +364,7 @@ pub fn valtree_to_const_value<'tcx>(
 
 /// Put a valtree into memory and return a reference to that.
 fn valtree_to_ref<'tcx>(
-    ecx: &mut CompileTimeEvalContext<'tcx>,
+    ecx: &mut CompileTimeInterpCx<'tcx>,
     valtree: ty::ValTree<'tcx>,
     pointee_ty: Ty<'tcx>,
 ) -> Immediate {
@@ -380,7 +380,7 @@ fn valtree_to_ref<'tcx>(
 
 #[instrument(skip(ecx), level = "debug")]
 fn valtree_into_mplace<'tcx>(
-    ecx: &mut CompileTimeEvalContext<'tcx>,
+    ecx: &mut CompileTimeInterpCx<'tcx>,
     place: &MPlaceTy<'tcx>,
     valtree: ty::ValTree<'tcx>,
 ) {
@@ -457,6 +457,6 @@ fn valtree_into_mplace<'tcx>(
     }
 }
 
-fn dump_place<'tcx>(ecx: &CompileTimeEvalContext<'tcx>, place: &MPlaceTy<'tcx>) {
+fn dump_place<'tcx>(ecx: &CompileTimeInterpCx<'tcx>, place: &MPlaceTy<'tcx>) {
     trace!("{:?}", ecx.dump_place(&PlaceTy::from(place.clone())));
 }
diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs
index 0dbee8c1d94..a50b50d231d 100644
--- a/compiler/rustc_const_eval/src/interpret/discriminant.rs
+++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs
@@ -241,10 +241,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         variant_index: VariantIdx,
     ) -> InterpResult<'tcx, Option<(ScalarInt, usize)>> {
         match self.layout_of(ty)?.variants {
-            abi::Variants::Single { index } => {
-                assert_eq!(index, variant_index);
-                Ok(None)
-            }
+            abi::Variants::Single { .. } => Ok(None),
 
             abi::Variants::Multiple {
                 tag_encoding: TagEncoding::Direct,
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index e28cc05cc2a..4d93038a81e 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -4,7 +4,6 @@ use std::{fmt, mem};
 use either::{Either, Left, Right};
 use tracing::{debug, info, info_span, instrument, trace};
 
-use hir::CRATE_HIR_ID;
 use rustc_errors::DiagCtxt;
 use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData};
 use rustc_index::IndexVec;
@@ -250,7 +249,7 @@ impl<'tcx, Prov: Provenance> Frame<'tcx, Prov> {
 impl<'tcx, Prov: Provenance, Extra> Frame<'tcx, Prov, Extra> {
     /// Get the current location within the Frame.
     ///
-    /// If this is `Left`, we are not currently executing any particular statement in
+    /// If this is `Right`, we are not currently executing any particular statement in
     /// this frame (can happen e.g. during frame initialization, and during unwinding on
     /// frames without cleanup code).
     ///
@@ -271,13 +270,18 @@ impl<'tcx, Prov: Provenance, Extra> Frame<'tcx, Prov, Extra> {
         }
     }
 
-    pub fn lint_root(&self) -> Option<hir::HirId> {
-        self.current_source_info().and_then(|source_info| {
-            match &self.body.source_scopes[source_info.scope].local_data {
+    pub fn lint_root(&self, tcx: TyCtxt<'tcx>) -> Option<hir::HirId> {
+        // We first try to get a HirId via the current source scope,
+        // and fall back to `body.source`.
+        self.current_source_info()
+            .and_then(|source_info| match &self.body.source_scopes[source_info.scope].local_data {
                 mir::ClearCrossCrate::Set(data) => Some(data.lint_root),
                 mir::ClearCrossCrate::Clear => None,
-            }
-        })
+            })
+            .or_else(|| {
+                let def_id = self.body.source.def_id().as_local();
+                def_id.map(|def_id| tcx.local_def_id_to_hir_id(def_id))
+            })
     }
 
     /// Returns the address of the buffer where the locals are stored. This is used by `Place` as a
@@ -500,6 +504,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         }
     }
 
+    /// Returns the span of the currently executed statement/terminator.
+    /// This is the span typically used for error reporting.
     #[inline(always)]
     pub fn cur_span(&self) -> Span {
         // This deliberately does *not* honor `requires_caller_location` since it is used for much
@@ -507,16 +513,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         self.stack().last().map_or(self.tcx.span, |f| f.current_span())
     }
 
-    #[inline(always)]
-    /// Find the first stack frame that is within the current crate, if any, otherwise return the crate's HirId
-    pub fn best_lint_scope(&self) -> hir::HirId {
-        self.stack()
-            .iter()
-            .find_map(|frame| frame.body.source.def_id().as_local())
-            .map_or(CRATE_HIR_ID, |def_id| self.tcx.local_def_id_to_hir_id(def_id))
-    }
-
-    #[inline(always)]
     pub(crate) fn stack(&self) -> &[Frame<'tcx, M::Provenance, M::FrameExtra>] {
         M::stack(self)
     }
@@ -632,7 +628,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     }
 
     /// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a
-    /// frame which is not `#[track_caller]`. This is the fancy version of `cur_span`.
+    /// frame which is not `#[track_caller]`. This matches the `caller_location` intrinsic,
+    /// and is primarily intended for the panic machinery.
     pub(crate) fn find_closest_untracked_caller_location(&self) -> Span {
         for frame in self.stack().iter().rev() {
             debug!("find_closest_untracked_caller_location: checking frame {:?}", frame.instance);
@@ -1057,7 +1054,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
 
                 ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false,
 
-                ty::Tuple(tys) => tys.last().iter().all(|ty| is_very_trivially_sized(**ty)),
+                ty::Tuple(tys) => tys.last().is_none_or(|ty| is_very_trivially_sized(*ty)),
 
                 ty::Pat(ty, ..) => is_very_trivially_sized(*ty),
 
diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs
index 3066e0933d9..8b0a2afa4d6 100644
--- a/compiler/rustc_const_eval/src/interpret/intern.rs
+++ b/compiler/rustc_const_eval/src/interpret/intern.rs
@@ -45,7 +45,7 @@ pub trait HasStaticRootDefId {
     fn static_def_id(&self) -> Option<LocalDefId>;
 }
 
-impl HasStaticRootDefId for const_eval::CompileTimeInterpreter<'_> {
+impl HasStaticRootDefId for const_eval::CompileTimeMachine<'_> {
     fn static_def_id(&self) -> Option<LocalDefId> {
         Some(self.static_root_ids?.1)
     }
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 5461e9c6ad3..9a26ac04b85 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -446,7 +446,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 let (alloc_size, _alloc_align, ret_val) = alloc_size(alloc_id, offset, prov)?;
                 // Test bounds.
                 // It is sufficient to check this for the end pointer. Also check for overflow!
-                if offset.checked_add(size, &self.tcx).map_or(true, |end| end > alloc_size) {
+                if offset.checked_add(size, &self.tcx).is_none_or(|end| end > alloc_size) {
                     throw_ub!(PointerOutOfBounds {
                         alloc_id,
                         alloc_size,
diff --git a/compiler/rustc_const_eval/src/interpret/operator.rs b/compiler/rustc_const_eval/src/interpret/operator.rs
index a6eef9f5662..73bdf96627a 100644
--- a/compiler/rustc_const_eval/src/interpret/operator.rs
+++ b/compiler/rustc_const_eval/src/interpret/operator.rs
@@ -112,25 +112,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
 
         // Shift ops can have an RHS with a different numeric type.
         if matches!(bin_op, Shl | ShlUnchecked | Shr | ShrUnchecked) {
-            let size = left.layout.size.bits();
+            let l_bits = left.layout.size.bits();
             // Compute the equivalent shift modulo `size` that is in the range `0..size`. (This is
             // the one MIR operator that does *not* directly map to a single LLVM operation.)
             let (shift_amount, overflow) = if right.layout.abi.is_signed() {
                 let shift_amount = r_signed();
-                let overflow = shift_amount < 0 || shift_amount >= i128::from(size);
-                // Deliberately wrapping `as` casts: shift_amount *can* be negative, but the result
-                // of the `as` will be equal modulo `size` (since it is a power of two).
-                let masked_amount = (shift_amount as u128) % u128::from(size);
-                assert_eq!(overflow, shift_amount != i128::try_from(masked_amount).unwrap());
-                (masked_amount, overflow)
+                let rem = shift_amount.rem_euclid(l_bits.into());
+                // `rem` is guaranteed positive, so the `unwrap` cannot fail
+                (u128::try_from(rem).unwrap(), rem != shift_amount)
             } else {
                 let shift_amount = r_unsigned();
-                let overflow = shift_amount >= u128::from(size);
-                let masked_amount = shift_amount % u128::from(size);
-                assert_eq!(overflow, shift_amount != masked_amount);
-                (masked_amount, overflow)
+                let rem = shift_amount.rem_euclid(l_bits.into());
+                (rem, rem != shift_amount)
             };
-            let shift_amount = u32::try_from(shift_amount).unwrap(); // we masked so this will always fit
+            let shift_amount = u32::try_from(shift_amount).unwrap(); // we brought this in the range `0..size` so this will always fit
             // Compute the shifted result.
             let result = if left.layout.abi.is_signed() {
                 let l = l_signed();
@@ -362,14 +357,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 let left = left.to_scalar();
                 let right = right.to_scalar();
                 Ok(match fty {
-                    FloatTy::F16 => unimplemented!("f16_f128"),
+                    FloatTy::F16 => {
+                        self.binary_float_op(bin_op, layout, left.to_f16()?, right.to_f16()?)
+                    }
                     FloatTy::F32 => {
                         self.binary_float_op(bin_op, layout, left.to_f32()?, right.to_f32()?)
                     }
                     FloatTy::F64 => {
                         self.binary_float_op(bin_op, layout, left.to_f64()?, right.to_f64()?)
                     }
-                    FloatTy::F128 => unimplemented!("f16_f128"),
+                    FloatTy::F128 => {
+                        self.binary_float_op(bin_op, layout, left.to_f128()?, right.to_f128()?)
+                    }
                 })
             }
             _ if left.layout.ty.is_integral() => {
@@ -429,11 +428,16 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             }
             ty::Float(fty) => {
                 let val = val.to_scalar();
+                if un_op != Neg {
+                    span_bug!(self.cur_span(), "Invalid float op {:?}", un_op);
+                }
+
                 // No NaN adjustment here, `-` is a bitwise operation!
-                let res = match (un_op, fty) {
-                    (Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?),
-                    (Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?),
-                    _ => span_bug!(self.cur_span(), "Invalid float op {:?}", un_op),
+                let res = match fty {
+                    FloatTy::F16 => Scalar::from_f16(-val.to_f16()?),
+                    FloatTy::F32 => Scalar::from_f32(-val.to_f32()?),
+                    FloatTy::F64 => Scalar::from_f64(-val.to_f64()?),
+                    FloatTy::F128 => Scalar::from_f128(-val.to_f128()?),
                 };
                 Ok(ImmTy::from_scalar(res, layout))
             }
diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs
index 09e1a59dfa1..efa01b54342 100644
--- a/compiler/rustc_const_eval/src/interpret/projection.rs
+++ b/compiler/rustc_const_eval/src/interpret/projection.rs
@@ -300,7 +300,7 @@ where
     ) -> InterpResult<'tcx, P> {
         let len = base.len(self)?; // also asserts that we have a type where this makes sense
         let actual_to = if from_end {
-            if from.checked_add(to).map_or(true, |to| to > len) {
+            if from.checked_add(to).is_none_or(|to| to > len) {
                 // This can only be reached in ConstProp and non-rustc-MIR.
                 throw_ub!(BoundsCheckFailed { len: len, index: from.saturating_add(to) });
             }
diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs
index 10fd6399b9a..f6537ed6ea9 100644
--- a/compiler/rustc_const_eval/src/interpret/util.rs
+++ b/compiler/rustc_const_eval/src/interpret/util.rs
@@ -1,4 +1,4 @@
-use crate::const_eval::{CompileTimeEvalContext, CompileTimeInterpreter, InterpretationResult};
+use crate::const_eval::{CompileTimeInterpCx, CompileTimeMachine, InterpretationResult};
 use rustc_hir::def_id::LocalDefId;
 use rustc_middle::mir;
 use rustc_middle::mir::interpret::{Allocation, InterpResult, Pointer};
@@ -84,7 +84,7 @@ where
 impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx> {
     fn make_result(
         mplace: MPlaceTy<'tcx>,
-        ecx: &mut InterpCx<'tcx, CompileTimeInterpreter<'tcx>>,
+        ecx: &mut InterpCx<'tcx, CompileTimeMachine<'tcx>>,
     ) -> Self {
         let alloc_id = mplace.ptr().provenance.unwrap().alloc_id();
         let alloc = ecx.memory.alloc_map.swap_remove(&alloc_id).unwrap().1;
@@ -93,7 +93,7 @@ impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx>
 }
 
 pub(crate) fn create_static_alloc<'tcx>(
-    ecx: &mut CompileTimeEvalContext<'tcx>,
+    ecx: &mut CompileTimeInterpCx<'tcx>,
     static_def_id: LocalDefId,
     layout: TyAndLayout<'tcx>,
 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index 6f75bc2af4e..ca8b9884933 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -29,7 +29,7 @@ use rustc_target::abi::{
 use std::hash::Hash;
 
 use super::{
-    err_ub, format_interp_error, machine::AllocMap, throw_ub, AllocId, CheckInAllocMsg,
+    err_ub, format_interp_error, machine::AllocMap, throw_ub, AllocId, AllocKind, CheckInAllocMsg,
     GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy,
     Pointer, Projectable, Scalar, ValueVisitor,
 };
@@ -413,8 +413,6 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
             Ub(PointerOutOfBounds { .. }) => DanglingPtrOutOfBounds {
                 ptr_kind
             },
-            // This cannot happen during const-eval (because interning already detects
-            // dangling pointers), but it can happen in Miri.
             Ub(PointerUseAfterFree(..)) => DanglingPtrUseAfterFree {
                 ptr_kind,
             },
@@ -493,9 +491,17 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
                     }
                 }
 
-                // Mutability check.
+                // Dangling and Mutability check.
+                let (size, _align, alloc_kind) = self.ecx.get_alloc_info(alloc_id);
+                if alloc_kind == AllocKind::Dead {
+                    // This can happen for zero-sized references. We can't have *any* references to non-existing
+                    // allocations though, interning rejects them all as the rest of rustc isn't happy with them...
+                    // so we throw an error, even though this isn't really UB.
+                    // A potential future alternative would be to resurrect this as a zero-sized allocation
+                    // (which codegen will then compile to an aligned dummy pointer anyway).
+                    throw_validation_failure!(self.path, DanglingPtrUseAfterFree { ptr_kind });
+                }
                 // If this allocation has size zero, there is no actual mutability here.
-                let (size, _align, _alloc_kind) = self.ecx.get_alloc_info(alloc_id);
                 if size != Size::ZERO {
                     let alloc_actual_mutbl = mutability(self.ecx, alloc_id);
                     // Mutable pointer to immutable memory is no good.
diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs
index 45ea3ec08f8..50a4d0612cc 100644
--- a/compiler/rustc_const_eval/src/lib.rs
+++ b/compiler/rustc_const_eval/src/lib.rs
@@ -6,6 +6,7 @@
 #![feature(box_patterns)]
 #![feature(decl_macro)]
 #![feature(if_let_guard)]
+#![feature(is_none_or)]
 #![feature(let_chains)]
 #![feature(never_type)]
 #![feature(rustdoc_internals)]
diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs
index 62c5f8734a2..3b07bee2d9c 100644
--- a/compiler/rustc_const_eval/src/util/caller_location.rs
+++ b/compiler/rustc_const_eval/src/util/caller_location.rs
@@ -7,12 +7,12 @@ use rustc_middle::ty::{self, Mutability};
 use rustc_span::symbol::Symbol;
 use tracing::trace;
 
-use crate::const_eval::{mk_eval_cx_to_read_const_val, CanAccessMutGlobal, CompileTimeEvalContext};
+use crate::const_eval::{mk_eval_cx_to_read_const_val, CanAccessMutGlobal, CompileTimeInterpCx};
 use crate::interpret::*;
 
 /// Allocate a `const core::panic::Location` with the provided filename and line/column numbers.
 fn alloc_caller_location<'tcx>(
-    ecx: &mut CompileTimeEvalContext<'tcx>,
+    ecx: &mut CompileTimeInterpCx<'tcx>,
     filename: Symbol,
     line: u32,
     col: u32,
diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs
index 68fb122a765..daf57285ebe 100644
--- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs
+++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs
@@ -3,7 +3,7 @@ use rustc_middle::ty::layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout, Val
 use rustc_middle::ty::{ParamEnv, ParamEnvAnd, Ty, TyCtxt};
 use rustc_target::abi::{Abi, FieldsShape, Scalar, Variants};
 
-use crate::const_eval::{CanAccessMutGlobal, CheckAlignment, CompileTimeInterpreter};
+use crate::const_eval::{CanAccessMutGlobal, CheckAlignment, CompileTimeMachine};
 use crate::interpret::{InterpCx, MemoryKind, OpTy};
 
 /// Determines if this type permits "raw" initialization by just transmuting some memory into an
@@ -45,7 +45,7 @@ fn might_permit_raw_init_strict<'tcx>(
     tcx: TyCtxt<'tcx>,
     kind: ValidityRequirement,
 ) -> Result<bool, &'tcx LayoutError<'tcx>> {
-    let machine = CompileTimeInterpreter::new(CanAccessMutGlobal::No, CheckAlignment::Error);
+    let machine = CompileTimeMachine::new(CanAccessMutGlobal::No, CheckAlignment::Error);
 
     let mut cx = InterpCx::new(tcx, rustc_span::DUMMY_SP, ParamEnv::reveal_all(), machine);
 
diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml
index 6876046a583..ff0a94f8e9b 100644
--- a/compiler/rustc_data_structures/Cargo.toml
+++ b/compiler/rustc_data_structures/Cargo.toml
@@ -12,7 +12,6 @@ elsa = "=1.7.1"
 ena = "0.14.3"
 indexmap = { version = "2.0.0" }
 jobserver_crate = { version = "0.1.28", package = "jobserver" }
-libc = "0.2"
 measureme = "11"
 rustc-hash = "1.1.0"
 rustc-rayon = { version = "0.5.0", optional = true }
@@ -41,6 +40,11 @@ features = [
     "Win32_System_Threading",
 ]
 
+[target.'cfg(unix)'.dependencies]
+# tidy-alphabetical-start
+libc = "0.2"
+# tidy-alphabetical-end
+
 [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
 # tidy-alphabetical-start
 memmap2 = "0.2.1"
diff --git a/compiler/rustc_data_structures/src/graph/scc/mod.rs b/compiler/rustc_data_structures/src/graph/scc/mod.rs
index 7f36e4ca16d..8b96b36a851 100644
--- a/compiler/rustc_data_structures/src/graph/scc/mod.rs
+++ b/compiler/rustc_data_structures/src/graph/scc/mod.rs
@@ -4,52 +4,119 @@
 //! node in the graph. This uses [Tarjan's algorithm](
 //! https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm)
 //! that completes in *O*(*n*) time.
+//! Optionally, also annotate the SCC nodes with some commutative data.
+//! Typical examples would include: minimum element in SCC, maximum element
+//! reachable from it, etc.
 
 use crate::fx::FxHashSet;
 use crate::graph::vec_graph::VecGraph;
 use crate::graph::{DirectedGraph, NumEdges, Successors};
 use rustc_index::{Idx, IndexSlice, IndexVec};
+use std::fmt::Debug;
 use std::ops::Range;
 use tracing::{debug, instrument};
 
 #[cfg(test)]
 mod tests;
 
+/// An annotation for an SCC. This can be a representative,
+/// the max/min element of the SCC, or all of the above.
+///
+/// Concretely, the both merge operations must commute, e.g. where `merge`
+/// is `merge_scc` and `merge_reached`: `a.merge(b) == b.merge(a)`
+///
+/// In general, what you want is probably always min/max according
+/// to some ordering, potentially with side constraints (min x such
+/// that P holds).
+pub trait Annotation: Debug + Copy {
+    /// Merge two existing annotations into one during
+    /// path compression.o
+    fn merge_scc(self, other: Self) -> Self;
+
+    /// Merge a successor into this annotation.
+    fn merge_reached(self, other: Self) -> Self;
+
+    fn update_scc(&mut self, other: Self) {
+        *self = self.merge_scc(other)
+    }
+
+    fn update_reachable(&mut self, other: Self) {
+        *self = self.merge_reached(other)
+    }
+}
+
+/// The empty annotation, which does nothing.
+impl Annotation for () {
+    fn merge_reached(self, _other: Self) -> Self {
+        ()
+    }
+    fn merge_scc(self, _other: Self) -> Self {
+        ()
+    }
+}
+
 /// Strongly connected components (SCC) of a graph. The type `N` is
 /// the index type for the graph nodes and `S` is the index type for
 /// the SCCs. We can map from each node to the SCC that it
 /// participates in, and we also have the successors of each SCC.
-pub struct Sccs<N: Idx, S: Idx> {
+pub struct Sccs<N: Idx, S: Idx, A: Annotation = ()> {
     /// For each node, what is the SCC index of the SCC to which it
     /// belongs.
     scc_indices: IndexVec<N, S>,
 
-    /// Data about each SCC.
-    scc_data: SccData<S>,
+    /// Data about all the SCCs.
+    scc_data: SccData<S, A>,
 }
 
-pub struct SccData<S: Idx> {
-    /// For each SCC, the range of `all_successors` where its
+/// Information about an invidividual SCC node.
+struct SccDetails<A: Annotation> {
+    /// For this SCC, the range of `all_successors` where its
     /// successors can be found.
-    ranges: IndexVec<S, Range<usize>>,
+    range: Range<usize>,
+
+    /// User-specified metadata about the SCC.
+    annotation: A,
+}
+
+// The name of this struct should discourage you from making it public and leaking
+// its representation. This message was left here by one who came before you,
+// who learnt the hard way that making even small changes in representation
+// is difficult when it's publicly inspectable.
+//
+// Obey the law of Demeter!
+struct SccData<S: Idx, A: Annotation> {
+    /// Maps SCC indices to their metadata, including
+    /// offsets into `all_successors`.
+    scc_details: IndexVec<S, SccDetails<A>>,
 
     /// Contains the successors for all the Sccs, concatenated. The
     /// range of indices corresponding to a given SCC is found in its
-    /// SccData.
+    /// `scc_details.range`.
     all_successors: Vec<S>,
 }
 
-impl<N: Idx, S: Idx + Ord> Sccs<N, S> {
+impl<N: Idx, S: Idx + Ord> Sccs<N, S, ()> {
+    /// Compute SCCs without annotations.
     pub fn new(graph: &impl Successors<Node = N>) -> Self {
-        SccsConstruction::construct(graph)
+        Self::new_with_annotation(graph, |_| ())
     }
+}
 
-    pub fn scc_indices(&self) -> &IndexSlice<N, S> {
-        &self.scc_indices
+impl<N: Idx, S: Idx + Ord, A: Annotation> Sccs<N, S, A> {
+    /// Compute SCCs and annotate them with a user-supplied annotation
+    pub fn new_with_annotation<F: Fn(N) -> A>(
+        graph: &impl Successors<Node = N>,
+        to_annotation: F,
+    ) -> Self {
+        SccsConstruction::construct(graph, to_annotation)
     }
 
-    pub fn scc_data(&self) -> &SccData<S> {
-        &self.scc_data
+    pub fn annotation(&self, scc: S) -> A {
+        self.scc_data.annotation(scc)
+    }
+
+    pub fn scc_indices(&self) -> &IndexSlice<N, S> {
+        &self.scc_indices
     }
 
     /// Returns the number of SCCs in the graph.
@@ -90,7 +157,7 @@ impl<N: Idx, S: Idx + Ord> Sccs<N, S> {
     }
 }
 
-impl<N: Idx, S: Idx + Ord> DirectedGraph for Sccs<N, S> {
+impl<N: Idx, S: Idx + Ord, A: Annotation> DirectedGraph for Sccs<N, S, A> {
     type Node = S;
 
     fn num_nodes(&self) -> usize {
@@ -98,43 +165,33 @@ impl<N: Idx, S: Idx + Ord> DirectedGraph for Sccs<N, S> {
     }
 }
 
-impl<N: Idx, S: Idx + Ord> NumEdges for Sccs<N, S> {
+impl<N: Idx, S: Idx + Ord, A: Annotation> NumEdges for Sccs<N, S, A> {
     fn num_edges(&self) -> usize {
         self.scc_data.all_successors.len()
     }
 }
 
-impl<N: Idx, S: Idx + Ord> Successors for Sccs<N, S> {
+impl<N: Idx, S: Idx + Ord, A: Annotation> Successors for Sccs<N, S, A> {
     fn successors(&self, node: S) -> impl Iterator<Item = Self::Node> {
         self.successors(node).iter().cloned()
     }
 }
 
-impl<S: Idx> SccData<S> {
+impl<S: Idx, A: Annotation> SccData<S, A> {
     /// Number of SCCs,
     fn len(&self) -> usize {
-        self.ranges.len()
-    }
-
-    pub fn ranges(&self) -> &IndexSlice<S, Range<usize>> {
-        &self.ranges
-    }
-
-    pub fn all_successors(&self) -> &Vec<S> {
-        &self.all_successors
+        self.scc_details.len()
     }
 
     /// Returns the successors of the given SCC.
     fn successors(&self, scc: S) -> &[S] {
-        // Annoyingly, `range` does not implement `Copy`, so we have
-        // to do `range.start..range.end`:
-        let range = &self.ranges[scc];
-        &self.all_successors[range.start..range.end]
+        &self.all_successors[self.scc_details[scc].range.clone()]
     }
 
     /// Creates a new SCC with `successors` as its successors and
+    /// the maximum weight of its internal nodes `scc_max_weight` and
     /// returns the resulting index.
-    fn create_scc(&mut self, successors: impl IntoIterator<Item = S>) -> S {
+    fn create_scc(&mut self, successors: impl IntoIterator<Item = S>, annotation: A) -> S {
         // Store the successors on `scc_successors_vec`, remembering
         // the range of indices.
         let all_successors_start = self.all_successors.len();
@@ -142,22 +199,35 @@ impl<S: Idx> SccData<S> {
         let all_successors_end = self.all_successors.len();
 
         debug!(
-            "create_scc({:?}) successors={:?}",
-            self.ranges.len(),
+            "create_scc({:?}) successors={:?}, annotation={:?}",
+            self.len(),
             &self.all_successors[all_successors_start..all_successors_end],
+            annotation
         );
 
-        self.ranges.push(all_successors_start..all_successors_end)
+        let range = all_successors_start..all_successors_end;
+        let metadata = SccDetails { range, annotation };
+        self.scc_details.push(metadata)
+    }
+
+    fn annotation(&self, scc: S) -> A {
+        self.scc_details[scc].annotation
     }
 }
 
-struct SccsConstruction<'c, G: DirectedGraph + Successors, S: Idx> {
+struct SccsConstruction<'c, G, S, A, F>
+where
+    G: DirectedGraph + Successors,
+    S: Idx,
+    A: Annotation,
+    F: Fn(G::Node) -> A,
+{
     graph: &'c G,
 
     /// The state of each node; used during walk to record the stack
     /// and after walk to record what cycle each node ended up being
     /// in.
-    node_states: IndexVec<G::Node, NodeState<G::Node, S>>,
+    node_states: IndexVec<G::Node, NodeState<G::Node, S, A>>,
 
     /// The stack of nodes that we are visiting as part of the DFS.
     node_stack: Vec<G::Node>,
@@ -174,26 +244,34 @@ struct SccsConstruction<'c, G: DirectedGraph + Successors, S: Idx> {
     /// around between successors to amortize memory allocation costs.
     duplicate_set: FxHashSet<S>,
 
-    scc_data: SccData<S>,
+    scc_data: SccData<S, A>,
+
+    /// A function that constructs an initial SCC annotation
+    /// out of a single node.
+    to_annotation: F,
 }
 
 #[derive(Copy, Clone, Debug)]
-enum NodeState<N, S> {
+enum NodeState<N, S, A> {
     /// This node has not yet been visited as part of the DFS.
     ///
     /// After SCC construction is complete, this state ought to be
     /// impossible.
     NotVisited,
 
-    /// This node is currently being walk as part of our DFS. It is on
-    /// the stack at the depth `depth`.
+    /// This node is currently being walked as part of our DFS. It is on
+    /// the stack at the depth `depth` and its current annotation is
+    /// `annotation`.
     ///
     /// After SCC construction is complete, this state ought to be
     /// impossible.
-    BeingVisited { depth: usize },
+    BeingVisited { depth: usize, annotation: A },
 
-    /// Indicates that this node is a member of the given cycle.
-    InCycle { scc_index: S },
+    /// Indicates that this node is a member of the given cycle where
+    /// the merged annotation is `annotation`.
+    /// Note that an SCC can have several cycles, so its final annotation
+    /// is the merged value of all its member annotations.
+    InCycle { scc_index: S, annotation: A },
 
     /// Indicates that this node is a member of whatever cycle
     /// `parent` is a member of. This state is transient: whenever we
@@ -203,16 +281,27 @@ enum NodeState<N, S> {
     InCycleWith { parent: N },
 }
 
+/// The state of walking a given node.
 #[derive(Copy, Clone, Debug)]
-enum WalkReturn<S> {
-    Cycle { min_depth: usize },
-    Complete { scc_index: S },
+enum WalkReturn<S, A> {
+    /// The walk found a cycle, but the entire component is not known to have
+    /// been fully walked yet. We only know the minimum depth of  this
+    /// component in a minimum spanning tree of the graph. This component
+    /// is tentatively represented by the state of the first node of this
+    /// cycle we met, which is at `min_depth`.
+    Cycle { min_depth: usize, annotation: A },
+    /// The SCC and everything reachable from it have been fully walked.
+    /// At this point we know what is inside the SCC as we have visited every
+    /// node reachable from it. The SCC can now be fully represented by its ID.
+    Complete { scc_index: S, annotation: A },
 }
 
-impl<'c, G, S> SccsConstruction<'c, G, S>
+impl<'c, G, S, A, F> SccsConstruction<'c, G, S, A, F>
 where
     G: DirectedGraph + Successors,
     S: Idx,
+    F: Fn(G::Node) -> A,
+    A: Annotation,
 {
     /// Identifies SCCs in the graph `G` and computes the resulting
     /// DAG. This uses a variant of [Tarjan's
@@ -225,8 +314,10 @@ where
     /// D' (i.e., D' < D), we know that N, N', and all nodes in
     /// between them on the stack are part of an SCC.
     ///
+    /// Additionally, we keep track of a current annotation of the SCC.
+    ///
     /// [wikipedia]: https://bit.ly/2EZIx84
-    fn construct(graph: &'c G) -> Sccs<G::Node, S> {
+    fn construct(graph: &'c G, to_annotation: F) -> Sccs<G::Node, S, A> {
         let num_nodes = graph.num_nodes();
 
         let mut this = Self {
@@ -234,15 +325,16 @@ where
             node_states: IndexVec::from_elem_n(NodeState::NotVisited, num_nodes),
             node_stack: Vec::with_capacity(num_nodes),
             successors_stack: Vec::new(),
-            scc_data: SccData { ranges: IndexVec::new(), all_successors: Vec::new() },
+            scc_data: SccData { scc_details: IndexVec::new(), all_successors: Vec::new() },
             duplicate_set: FxHashSet::default(),
+            to_annotation,
         };
 
         let scc_indices = (0..num_nodes)
             .map(G::Node::new)
             .map(|node| match this.start_walk_from(node) {
-                WalkReturn::Complete { scc_index } => scc_index,
-                WalkReturn::Cycle { min_depth } => {
+                WalkReturn::Complete { scc_index, .. } => scc_index,
+                WalkReturn::Cycle { min_depth, .. } => {
                     panic!("`start_walk_node({node:?})` returned cycle with depth {min_depth:?}")
                 }
             })
@@ -251,12 +343,8 @@ where
         Sccs { scc_indices, scc_data: this.scc_data }
     }
 
-    fn start_walk_from(&mut self, node: G::Node) -> WalkReturn<S> {
-        if let Some(result) = self.inspect_node(node) {
-            result
-        } else {
-            self.walk_unvisited_node(node)
-        }
+    fn start_walk_from(&mut self, node: G::Node) -> WalkReturn<S, A> {
+        self.inspect_node(node).unwrap_or_else(|| self.walk_unvisited_node(node))
     }
 
     /// Inspect a node during the DFS. We first examine its current
@@ -271,11 +359,15 @@ where
     /// Otherwise, we are looking at a node that has already been
     /// completely visited. We therefore return `WalkReturn::Complete`
     /// with its associated SCC index.
-    fn inspect_node(&mut self, node: G::Node) -> Option<WalkReturn<S>> {
+    fn inspect_node(&mut self, node: G::Node) -> Option<WalkReturn<S, A>> {
         Some(match self.find_state(node) {
-            NodeState::InCycle { scc_index } => WalkReturn::Complete { scc_index },
+            NodeState::InCycle { scc_index, annotation } => {
+                WalkReturn::Complete { scc_index, annotation }
+            }
 
-            NodeState::BeingVisited { depth: min_depth } => WalkReturn::Cycle { min_depth },
+            NodeState::BeingVisited { depth: min_depth, annotation } => {
+                WalkReturn::Cycle { min_depth, annotation }
+            }
 
             NodeState::NotVisited => return None,
 
@@ -290,7 +382,7 @@ where
     /// of `r2` (and updates `r` to reflect current result). This is
     /// basically the "find" part of a standard union-find algorithm
     /// (with path compression).
-    fn find_state(&mut self, mut node: G::Node) -> NodeState<G::Node, S> {
+    fn find_state(&mut self, mut node: G::Node) -> NodeState<G::Node, S, A> {
         // To avoid recursion we temporarily reuse the `parent` of each
         // InCycleWith link to encode a downwards link while compressing
         // the path. After we have found the root or deepest node being
@@ -306,24 +398,40 @@ where
         // found the initial self-loop.
         let mut previous_node = node;
 
-        // Ultimately assigned by the parent when following
+        // Ultimately propagated to all the transitive parents when following
         // `InCycleWith` upwards.
-        let node_state = loop {
-            debug!("find_state(r = {:?} in state {:?})", node, self.node_states[node]);
-            match self.node_states[node] {
-                NodeState::InCycle { scc_index } => break NodeState::InCycle { scc_index },
-                NodeState::BeingVisited { depth } => break NodeState::BeingVisited { depth },
-                NodeState::NotVisited => break NodeState::NotVisited,
-                NodeState::InCycleWith { parent } => {
-                    // We test this, to be extremely sure that we never
-                    // ever break our termination condition for the
-                    // reverse iteration loop.
-                    assert!(node != parent, "Node can not be in cycle with itself");
-                    // Store the previous node as an inverted list link
-                    self.node_states[node] = NodeState::InCycleWith { parent: previous_node };
-                    // Update to parent node.
-                    previous_node = node;
-                    node = parent;
+        // This loop performs the downward link encoding mentioned above. Details below!
+        // Note that there are two different states being assigned: the root state, and
+        // a potentially derived version of the root state for non-root nodes in the chain.
+        let (root_state, assigned_state) = {
+            loop {
+                debug!("find_state(r = {node:?} in state {:?})", self.node_states[node]);
+                match self.node_states[node] {
+                    // This must have been the first and only state since it is unexplored*;
+                    // no update needed! * Unless there is a bug :')
+                    s @ NodeState::NotVisited => return s,
+                    // We are in a completely discovered SCC; every node on our path is in that SCC:
+                    s @ NodeState::InCycle { .. } => break (s, s),
+                    // The Interesting Third Base Case: we are a path back to a root node
+                    // still being explored. Now we need that node to keep its state and
+                    // every other node to be recorded as being in whatever component that
+                    // ends up in.
+                    s @ NodeState::BeingVisited { depth, .. } => {
+                        break (s, NodeState::InCycleWith { parent: self.node_stack[depth] });
+                    }
+                    // We are not at the head of a path; keep compressing it!
+                    NodeState::InCycleWith { parent } => {
+                        // We test this, to be extremely sure that we never
+                        // ever break our termination condition for the
+                        // reverse iteration loop.
+                        assert!(node != parent, "Node can not be in cycle with itself");
+
+                        // Store the previous node as an inverted list link
+                        self.node_states[node] = NodeState::InCycleWith { parent: previous_node };
+                        // Update to parent node.
+                        previous_node = node;
+                        node = parent;
+                    }
                 }
             }
         };
@@ -365,10 +473,14 @@ where
         // Move backwards until we found the node where we started. We
         // will know when we hit the state where previous_node == node.
         loop {
-            // Back at the beginning, we can return.
+            // Back at the beginning, we can return. Note that we return the root state.
+            // This is becuse for components being explored, we would otherwise get a
+            // `node_state[n] = InCycleWith{ parent: n }` and that's wrong.
             if previous_node == node {
-                return node_state;
+                return root_state;
             }
+            debug!("Compressing {node:?} down to {previous_node:?} with state {assigned_state:?}");
+
             // Update to previous node in the link.
             match self.node_states[previous_node] {
                 NodeState::InCycleWith { parent: previous } => {
@@ -376,34 +488,14 @@ where
                     previous_node = previous;
                 }
                 // Only InCycleWith nodes were added to the reverse linked list.
-                other => panic!("Invalid previous link while compressing cycle: {other:?}"),
+                other => unreachable!("Invalid previous link while compressing cycle: {other:?}"),
             }
 
-            debug!("find_state: parent_state = {:?}", node_state);
-
-            // Update the node state from the parent state. The assigned
-            // state is actually a loop invariant but it will only be
-            // evaluated if there is at least one backlink to follow.
-            // Fully trusting llvm here to find this loop optimization.
-            match node_state {
-                // Path compression, make current node point to the same root.
-                NodeState::InCycle { .. } => {
-                    self.node_states[node] = node_state;
-                }
-                // Still visiting nodes, compress to cycle to the node
-                // at that depth.
-                NodeState::BeingVisited { depth } => {
-                    self.node_states[node] =
-                        NodeState::InCycleWith { parent: self.node_stack[depth] };
-                }
-                // These are never allowed as parent nodes. InCycleWith
-                // should have been followed to a real parent and
-                // NotVisited can not be part of a cycle since it should
-                // have instead gotten explored.
-                NodeState::NotVisited | NodeState::InCycleWith { .. } => {
-                    panic!("invalid parent state: {node_state:?}")
-                }
-            }
+            // Update the node state to the (potentially derived) state.
+            // If the root is still being explored, this is
+            // `InCycleWith{ parent: <root node>}`, otherwise
+            // `assigned_state == root_state`.
+            self.node_states[node] = assigned_state;
         }
     }
 
@@ -413,30 +505,36 @@ where
     /// caller decide avoids mutual recursion between the two methods and allows
     /// us to maintain an allocated stack for nodes on the path between calls.
     #[instrument(skip(self, initial), level = "debug")]
-    fn walk_unvisited_node(&mut self, initial: G::Node) -> WalkReturn<S> {
-        struct VisitingNodeFrame<G: DirectedGraph, Successors> {
+    fn walk_unvisited_node(&mut self, initial: G::Node) -> WalkReturn<S, A> {
+        debug!("Walk unvisited node: {initial:?}");
+        struct VisitingNodeFrame<G: DirectedGraph, Successors, A> {
             node: G::Node,
-            iter: Option<Successors>,
+            successors: Option<Successors>,
             depth: usize,
             min_depth: usize,
             successors_len: usize,
             min_cycle_root: G::Node,
             successor_node: G::Node,
+            /// The annotation for the SCC starting in `node`. It may or may
+            /// not contain other nodes.
+            current_component_annotation: A,
         }
 
         // Move the stack to a local variable. We want to utilize the existing allocation and
         // mutably borrow it without borrowing self at the same time.
         let mut successors_stack = core::mem::take(&mut self.successors_stack);
+
         debug_assert_eq!(successors_stack.len(), 0);
 
-        let mut stack: Vec<VisitingNodeFrame<G, _>> = vec![VisitingNodeFrame {
+        let mut stack: Vec<VisitingNodeFrame<G, _, _>> = vec![VisitingNodeFrame {
             node: initial,
             depth: 0,
             min_depth: 0,
-            iter: None,
+            successors: None,
             successors_len: 0,
             min_cycle_root: initial,
             successor_node: initial,
+            current_component_annotation: (self.to_annotation)(initial),
         }];
 
         let mut return_value = None;
@@ -445,18 +543,26 @@ where
             let VisitingNodeFrame {
                 node,
                 depth,
-                iter,
+                successors,
                 successors_len,
                 min_depth,
                 min_cycle_root,
                 successor_node,
+                current_component_annotation,
             } = frame;
-
             let node = *node;
             let depth = *depth;
 
-            let successors = match iter {
-                Some(iter) => iter,
+            // node is definitely in the current component, add it to the annotation.
+            if node != initial {
+                current_component_annotation.update_scc((self.to_annotation)(node));
+            }
+            debug!(
+                "Visiting {node:?} at depth {depth:?}, annotation: {current_component_annotation:?}"
+            );
+
+            let successors = match successors {
+                Some(successors) => successors,
                 None => {
                     // This None marks that we still have the initialize this node's frame.
                     debug!(?depth, ?node);
@@ -464,7 +570,10 @@ where
                     debug_assert!(matches!(self.node_states[node], NodeState::NotVisited));
 
                     // Push `node` onto the stack.
-                    self.node_states[node] = NodeState::BeingVisited { depth };
+                    self.node_states[node] = NodeState::BeingVisited {
+                        depth,
+                        annotation: *current_component_annotation,
+                    };
                     self.node_stack.push(node);
 
                     // Walk each successor of the node, looking to see if any of
@@ -472,11 +581,11 @@ where
                     // so, that means they can also reach us.
                     *successors_len = successors_stack.len();
                     // Set and return a reference, this is currently empty.
-                    iter.get_or_insert(self.graph.successors(node))
+                    successors.get_or_insert(self.graph.successors(node))
                 }
             };
 
-            // Now that iter is initialized, this is a constant for this frame.
+            // Now that the successors iterator is initialized, this is a constant for this frame.
             let successors_len = *successors_len;
 
             // Construct iterators for the nodes and walk results. There are two cases:
@@ -489,10 +598,17 @@ where
                 debug!(?node, ?successor_node);
                 (successor_node, self.inspect_node(successor_node))
             });
-
             for (successor_node, walk) in returned_walk.chain(successor_walk) {
                 match walk {
-                    Some(WalkReturn::Cycle { min_depth: successor_min_depth }) => {
+                    // The starting node `node` leads to a cycle whose earliest node,
+                    // `successor_node`, is at `min_depth`. There may be more cycles.
+                    Some(WalkReturn::Cycle {
+                        min_depth: successor_min_depth,
+                        annotation: successor_annotation,
+                    }) => {
+                        debug!(
+                            "Cycle found from {node:?}, minimum depth: {successor_min_depth:?}, annotation: {successor_annotation:?}"
+                        );
                         // Track the minimum depth we can reach.
                         assert!(successor_min_depth <= depth);
                         if successor_min_depth < *min_depth {
@@ -500,41 +616,56 @@ where
                             *min_depth = successor_min_depth;
                             *min_cycle_root = successor_node;
                         }
+                        current_component_annotation.update_scc(successor_annotation);
                     }
-
-                    Some(WalkReturn::Complete { scc_index: successor_scc_index }) => {
+                    // The starting node `node` is succeeded by a fully identified SCC
+                    // which is now added to the set under `scc_index`.
+                    Some(WalkReturn::Complete {
+                        scc_index: successor_scc_index,
+                        annotation: successor_annotation,
+                    }) => {
+                        debug!(
+                            "Complete; {node:?} is root of complete-visited SCC idx {successor_scc_index:?} with annotation {successor_annotation:?}"
+                        );
                         // Push the completed SCC indices onto
                         // the `successors_stack` for later.
                         debug!(?node, ?successor_scc_index);
                         successors_stack.push(successor_scc_index);
+                        current_component_annotation.update_reachable(successor_annotation);
                     }
-
+                    // `node` has no more (direct) successors; search recursively.
                     None => {
                         let depth = depth + 1;
+                        debug!("Recursing down into {successor_node:?} at depth {depth:?}");
                         debug!(?depth, ?successor_node);
                         // Remember which node the return value will come from.
                         frame.successor_node = successor_node;
-                        // Start a new stack frame the step into it.
+                        // Start a new stack frame, then step into it.
                         stack.push(VisitingNodeFrame {
                             node: successor_node,
                             depth,
-                            iter: None,
+                            successors: None,
                             successors_len: 0,
                             min_depth: depth,
                             min_cycle_root: successor_node,
                             successor_node,
+                            current_component_annotation: (self.to_annotation)(successor_node),
                         });
                         continue 'recurse;
                     }
                 }
             }
 
+            debug!("Finished walk from {node:?} with annotation: {current_component_annotation:?}");
+
             // Completed walk, remove `node` from the stack.
             let r = self.node_stack.pop();
             debug_assert_eq!(r, Some(node));
 
             // Remove the frame, it's done.
             let frame = stack.pop().unwrap();
+            let current_component_annotation = frame.current_component_annotation;
+            debug_assert_eq!(frame.node, node);
 
             // If `min_depth == depth`, then we are the root of the
             // cycle: we can't reach anyone further down the stack.
@@ -543,6 +674,8 @@ where
             // We return one frame at a time so there can't be another return value.
             debug_assert!(return_value.is_none());
             return_value = Some(if frame.min_depth == depth {
+                // We are at the head of the component.
+
                 // Note that successor stack may have duplicates, so we
                 // want to remove those:
                 let deduplicated_successors = {
@@ -552,15 +685,25 @@ where
                         .drain(successors_len..)
                         .filter(move |&i| duplicate_set.insert(i))
                 };
-                let scc_index = self.scc_data.create_scc(deduplicated_successors);
-                self.node_states[node] = NodeState::InCycle { scc_index };
-                WalkReturn::Complete { scc_index }
+
+                debug!("Creating SCC rooted in {node:?} with successor {:?}", frame.successor_node);
+
+                let scc_index =
+                    self.scc_data.create_scc(deduplicated_successors, current_component_annotation);
+
+                self.node_states[node] =
+                    NodeState::InCycle { scc_index, annotation: current_component_annotation };
+
+                WalkReturn::Complete { scc_index, annotation: current_component_annotation }
             } else {
                 // We are not the head of the cycle. Return back to our
                 // caller. They will take ownership of the
                 // `self.successors` data that we pushed.
                 self.node_states[node] = NodeState::InCycleWith { parent: frame.min_cycle_root };
-                WalkReturn::Cycle { min_depth: frame.min_depth }
+                WalkReturn::Cycle {
+                    min_depth: frame.min_depth,
+                    annotation: current_component_annotation,
+                }
             });
         }
 
diff --git a/compiler/rustc_data_structures/src/graph/scc/tests.rs b/compiler/rustc_data_structures/src/graph/scc/tests.rs
index 513df666d0d..373f87bfdbc 100644
--- a/compiler/rustc_data_structures/src/graph/scc/tests.rs
+++ b/compiler/rustc_data_structures/src/graph/scc/tests.rs
@@ -3,10 +3,53 @@ extern crate test;
 use super::*;
 use crate::graph::tests::TestGraph;
 
+#[derive(Copy, Clone, Debug)]
+struct MaxReached(usize);
+type UsizeSccs = Sccs<usize, usize, ()>;
+type MaxReachedSccs = Sccs<usize, usize, MaxReached>;
+
+impl Annotation for MaxReached {
+    fn merge_scc(self, other: Self) -> Self {
+        Self(std::cmp::max(other.0, self.0))
+    }
+
+    fn merge_reached(self, other: Self) -> Self {
+        self.merge_scc(other)
+    }
+}
+
+impl PartialEq<usize> for MaxReached {
+    fn eq(&self, other: &usize) -> bool {
+        &self.0 == other
+    }
+}
+
+impl MaxReached {
+    fn from_usize(nr: usize) -> Self {
+        Self(nr)
+    }
+}
+
+#[derive(Copy, Clone, Debug)]
+struct MinMaxIn {
+    min: usize,
+    max: usize,
+}
+
+impl Annotation for MinMaxIn {
+    fn merge_scc(self, other: Self) -> Self {
+        Self { min: std::cmp::min(self.min, other.min), max: std::cmp::max(self.max, other.max) }
+    }
+
+    fn merge_reached(self, _other: Self) -> Self {
+        self
+    }
+}
+
 #[test]
 fn diamond() {
     let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 3)]);
-    let sccs: Sccs<_, usize> = Sccs::new(&graph);
+    let sccs: UsizeSccs = Sccs::new(&graph);
     assert_eq!(sccs.num_sccs(), 4);
     assert_eq!(sccs.num_sccs(), 4);
 }
@@ -34,7 +77,7 @@ fn test_big_scc() {
     +-- 2 <--+
          */
     let graph = TestGraph::new(0, &[(0, 1), (1, 2), (1, 3), (2, 0), (3, 2)]);
-    let sccs: Sccs<_, usize> = Sccs::new(&graph);
+    let sccs: UsizeSccs = Sccs::new(&graph);
     assert_eq!(sccs.num_sccs(), 1);
 }
 
@@ -50,7 +93,7 @@ fn test_three_sccs() {
     +-- 2 <--+
          */
     let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 1), (3, 2)]);
-    let sccs: Sccs<_, usize> = Sccs::new(&graph);
+    let sccs: UsizeSccs = Sccs::new(&graph);
     assert_eq!(sccs.num_sccs(), 3);
     assert_eq!(sccs.scc(0), 1);
     assert_eq!(sccs.scc(1), 0);
@@ -106,7 +149,7 @@ fn test_find_state_2() {
     // 2 InCycleWith { 1 }
     // 3 InCycleWith { 0 }
 
-    let sccs: Sccs<_, usize> = Sccs::new(&graph);
+    let sccs: UsizeSccs = Sccs::new(&graph);
     assert_eq!(sccs.num_sccs(), 1);
     assert_eq!(sccs.scc(0), 0);
     assert_eq!(sccs.scc(1), 0);
@@ -130,7 +173,7 @@ fn test_find_state_3() {
          */
     let graph =
         TestGraph::new(0, &[(0, 1), (0, 4), (1, 2), (1, 3), (2, 1), (3, 0), (4, 2), (5, 2)]);
-    let sccs: Sccs<_, usize> = Sccs::new(&graph);
+    let sccs: UsizeSccs = Sccs::new(&graph);
     assert_eq!(sccs.num_sccs(), 2);
     assert_eq!(sccs.scc(0), 0);
     assert_eq!(sccs.scc(1), 0);
@@ -165,7 +208,7 @@ fn test_deep_linear() {
         nodes.push((i - 1, i));
     }
     let graph = TestGraph::new(0, nodes.as_slice());
-    let sccs: Sccs<_, usize> = Sccs::new(&graph);
+    let sccs: UsizeSccs = Sccs::new(&graph);
     assert_eq!(sccs.num_sccs(), NR_NODES);
     assert_eq!(sccs.scc(0), NR_NODES - 1);
     assert_eq!(sccs.scc(NR_NODES - 1), 0);
@@ -210,7 +253,164 @@ fn bench_sccc(b: &mut test::Bencher) {
     graph[21] = (7, 4);
     let graph = TestGraph::new(0, &graph[..]);
     b.iter(|| {
-        let sccs: Sccs<_, usize> = Sccs::new(&graph);
+        let sccs: UsizeSccs = Sccs::new(&graph);
         assert_eq!(sccs.num_sccs(), 3);
     });
 }
+
+#[test]
+fn test_max_self_loop() {
+    let graph = TestGraph::new(0, &[(0, 0)]);
+    let sccs: MaxReachedSccs =
+        Sccs::new_with_annotation(&graph, |n| if n == 0 { MaxReached(17) } else { MaxReached(0) });
+    assert_eq!(sccs.annotation(0), 17);
+}
+
+#[test]
+fn test_max_branch() {
+    let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (2, 4)]);
+    let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize);
+    assert_eq!(sccs.annotation(sccs.scc(0)), 4);
+    assert_eq!(sccs.annotation(sccs.scc(1)), 3);
+    assert_eq!(sccs.annotation(sccs.scc(2)), 4);
+}
+#[test]
+fn test_single_cycle_max() {
+    let graph = TestGraph::new(0, &[(0, 2), (2, 3), (2, 4), (4, 1), (1, 2)]);
+    let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize);
+    assert_eq!(sccs.annotation(sccs.scc(2)), 4);
+    assert_eq!(sccs.annotation(sccs.scc(0)), 4);
+}
+
+#[test]
+fn test_simple_cycle_max() {
+    let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 0)]);
+    let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, MaxReached::from_usize);
+    assert_eq!(sccs.num_sccs(), 1);
+}
+
+#[test]
+fn test_double_cycle_max() {
+    let graph =
+        TestGraph::new(0, &[(0, 1), (1, 2), (1, 4), (2, 3), (2, 4), (3, 5), (4, 1), (5, 4)]);
+    let sccs: MaxReachedSccs =
+        Sccs::new_with_annotation(&graph, |n| if n == 5 { MaxReached(2) } else { MaxReached(1) });
+
+    assert_eq!(sccs.annotation(sccs.scc(0)).0, 2);
+}
+
+#[test]
+fn test_bug_minimised() {
+    let graph = TestGraph::new(0, &[(0, 3), (0, 1), (3, 2), (2, 3), (1, 4), (4, 5), (5, 4)]);
+    let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |n| match n {
+        3 => MaxReached(1),
+        _ => MaxReached(0),
+    });
+    assert_eq!(sccs.annotation(sccs.scc(2)), 1);
+    assert_eq!(sccs.annotation(sccs.scc(1)), 0);
+    assert_eq!(sccs.annotation(sccs.scc(4)), 0);
+}
+
+#[test]
+fn test_bug_max_leak_minimised() {
+    let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (3, 0), (3, 4), (4, 3)]);
+    let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w {
+        4 => MaxReached(1),
+        _ => MaxReached(0),
+    });
+
+    assert_eq!(sccs.annotation(sccs.scc(2)), 0);
+    assert_eq!(sccs.annotation(sccs.scc(3)), 1);
+    assert_eq!(sccs.annotation(sccs.scc(0)), 1);
+}
+
+#[test]
+fn test_bug_max_leak() {
+    let graph = TestGraph::new(
+        8,
+        &[
+            (0, 0),
+            (0, 18),
+            (0, 19),
+            (0, 1),
+            (0, 2),
+            (0, 7),
+            (0, 8),
+            (0, 23),
+            (18, 0),
+            (18, 12),
+            (19, 0),
+            (19, 25),
+            (12, 18),
+            (12, 3),
+            (12, 5),
+            (3, 12),
+            (3, 21),
+            (3, 22),
+            (5, 13),
+            (21, 3),
+            (22, 3),
+            (13, 5),
+            (13, 4),
+            (4, 13),
+            (4, 0),
+            (2, 11),
+            (7, 6),
+            (6, 20),
+            (20, 6),
+            (8, 17),
+            (17, 9),
+            (9, 16),
+            (16, 26),
+            (26, 15),
+            (15, 10),
+            (10, 14),
+            (14, 27),
+            (23, 24),
+        ],
+    );
+    let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w {
+        22 => MaxReached(1),
+        24 => MaxReached(2),
+        27 => MaxReached(2),
+        _ => MaxReached(0),
+    });
+
+    assert_eq!(sccs.annotation(sccs.scc(2)), 0);
+    assert_eq!(sccs.annotation(sccs.scc(7)), 0);
+    assert_eq!(sccs.annotation(sccs.scc(8)), 2);
+    assert_eq!(sccs.annotation(sccs.scc(23)), 2);
+    assert_eq!(sccs.annotation(sccs.scc(3)), 2);
+    assert_eq!(sccs.annotation(sccs.scc(0)), 2);
+}
+
+#[test]
+fn test_bug_max_zero_stick_shape() {
+    let graph = TestGraph::new(0, &[(0, 1), (1, 2), (2, 3), (3, 2), (3, 4)]);
+
+    let sccs: MaxReachedSccs = Sccs::new_with_annotation(&graph, |w| match w {
+        4 => MaxReached(1),
+        _ => MaxReached(0),
+    });
+
+    assert_eq!(sccs.annotation(sccs.scc(0)), 1);
+    assert_eq!(sccs.annotation(sccs.scc(1)), 1);
+    assert_eq!(sccs.annotation(sccs.scc(2)), 1);
+    assert_eq!(sccs.annotation(sccs.scc(3)), 1);
+    assert_eq!(sccs.annotation(sccs.scc(4)), 1);
+}
+
+#[test]
+fn test_min_max_in() {
+    let graph = TestGraph::new(0, &[(0, 1), (0, 2), (1, 3), (3, 0), (3, 4), (4, 3), (3, 5)]);
+    let sccs: Sccs<usize, usize, MinMaxIn> =
+        Sccs::new_with_annotation(&graph, |w| MinMaxIn { min: w, max: w });
+
+    assert_eq!(sccs.annotation(sccs.scc(2)).min, 2);
+    assert_eq!(sccs.annotation(sccs.scc(2)).max, 2);
+    assert_eq!(sccs.annotation(sccs.scc(0)).min, 0);
+    assert_eq!(sccs.annotation(sccs.scc(0)).max, 4);
+    assert_eq!(sccs.annotation(sccs.scc(3)).min, 0);
+    assert_eq!(sccs.annotation(sccs.scc(3)).max, 4);
+    assert_eq!(sccs.annotation(sccs.scc(5)).min, 5);
+}
diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs
index 128e9f48ff5..3295a91029e 100644
--- a/compiler/rustc_expand/src/mbe/metavar_expr.rs
+++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs
@@ -8,9 +8,14 @@ use rustc_session::parse::ParseSess;
 use rustc_span::symbol::Ident;
 use rustc_span::Span;
 
+pub(crate) const RAW_IDENT_ERR: &str = "`${concat(..)}` currently does not support raw identifiers";
+
 /// A meta-variable expression, for expansions based on properties of meta-variables.
-#[derive(Debug, Clone, PartialEq, Encodable, Decodable)]
+#[derive(Debug, PartialEq, Encodable, Decodable)]
 pub(crate) enum MetaVarExpr {
+    /// Unification of two or more identifiers.
+    Concat(Box<[MetaVarExprConcatElem]>),
+
     /// The number of repetitions of an identifier.
     Count(Ident, usize),
 
@@ -42,6 +47,31 @@ impl MetaVarExpr {
         check_trailing_token(&mut tts, psess)?;
         let mut iter = args.trees();
         let rslt = match ident.as_str() {
+            "concat" => {
+                let mut result = Vec::new();
+                loop {
+                    let is_var = try_eat_dollar(&mut iter);
+                    let element_ident = parse_ident(&mut iter, psess, outer_span)?;
+                    let element = if is_var {
+                        MetaVarExprConcatElem::Var(element_ident)
+                    } else {
+                        MetaVarExprConcatElem::Ident(element_ident)
+                    };
+                    result.push(element);
+                    if iter.look_ahead(0).is_none() {
+                        break;
+                    }
+                    if !try_eat_comma(&mut iter) {
+                        return Err(psess.dcx.struct_span_err(outer_span, "expected comma"));
+                    }
+                }
+                if result.len() < 2 {
+                    return Err(psess
+                        .dcx
+                        .struct_span_err(ident.span, "`concat` must have at least two elements"));
+                }
+                MetaVarExpr::Concat(result.into())
+            }
             "count" => parse_count(&mut iter, psess, ident.span)?,
             "ignore" => {
                 eat_dollar(&mut iter, psess, ident.span)?;
@@ -68,11 +98,21 @@ impl MetaVarExpr {
     pub(crate) fn ident(&self) -> Option<Ident> {
         match *self {
             MetaVarExpr::Count(ident, _) | MetaVarExpr::Ignore(ident) => Some(ident),
-            MetaVarExpr::Index(..) | MetaVarExpr::Len(..) => None,
+            MetaVarExpr::Concat { .. } | MetaVarExpr::Index(..) | MetaVarExpr::Len(..) => None,
         }
     }
 }
 
+#[derive(Debug, Decodable, Encodable, PartialEq)]
+pub(crate) enum MetaVarExprConcatElem {
+    /// There is NO preceding dollar sign, which means that this identifier should be interpreted
+    /// as a literal.
+    Ident(Ident),
+    /// There is a preceding dollar sign, which means that this identifier should be expanded
+    /// and interpreted as a variable.
+    Var(Ident),
+}
+
 // Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}`
 fn check_trailing_token<'psess>(
     iter: &mut RefTokenTreeCursor<'_>,
@@ -138,26 +178,30 @@ fn parse_depth<'psess>(
 fn parse_ident<'psess>(
     iter: &mut RefTokenTreeCursor<'_>,
     psess: &'psess ParseSess,
-    span: Span,
+    fallback_span: Span,
 ) -> PResult<'psess, Ident> {
-    if let Some(tt) = iter.next()
-        && let TokenTree::Token(token, _) = tt
-    {
-        if let Some((elem, IdentIsRaw::No)) = token.ident() {
-            return Ok(elem);
+    let Some(tt) = iter.next() else {
+        return Err(psess.dcx.struct_span_err(fallback_span, "expected identifier"));
+    };
+    let TokenTree::Token(token, _) = tt else {
+        return Err(psess.dcx.struct_span_err(tt.span(), "expected identifier"));
+    };
+    if let Some((elem, is_raw)) = token.ident() {
+        if let IdentIsRaw::Yes = is_raw {
+            return Err(psess.dcx.struct_span_err(elem.span, RAW_IDENT_ERR));
         }
-        let token_str = pprust::token_to_string(token);
-        let mut err =
-            psess.dcx.struct_span_err(span, format!("expected identifier, found `{}`", &token_str));
-        err.span_suggestion(
-            token.span,
-            format!("try removing `{}`", &token_str),
-            "",
-            Applicability::MaybeIncorrect,
-        );
-        return Err(err);
+        return Ok(elem);
     }
-    Err(psess.dcx.struct_span_err(span, "expected identifier"))
+    let token_str = pprust::token_to_string(token);
+    let mut err =
+        psess.dcx.struct_span_err(token.span, format!("expected identifier, found `{token_str}`"));
+    err.span_suggestion(
+        token.span,
+        format!("try removing `{token_str}`"),
+        "",
+        Applicability::MaybeIncorrect,
+    );
+    Err(err)
 }
 
 /// Tries to move the iterator forward returning `true` if there is a comma. If not, then the
@@ -170,6 +214,17 @@ fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool {
     false
 }
 
+/// Tries to move the iterator forward returning `true` if there is a dollar sign. If not, then the
+/// iterator is not modified and the result is `false`.
+fn try_eat_dollar(iter: &mut RefTokenTreeCursor<'_>) -> bool {
+    if let Some(TokenTree::Token(token::Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0)
+    {
+        let _ = iter.next();
+        return true;
+    }
+    false
+}
+
 /// Expects that the next item is a dollar sign.
 fn eat_dollar<'psess>(
     iter: &mut RefTokenTreeCursor<'_>,
diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs
index 8ad7cb15c92..74f78c0ef78 100644
--- a/compiler/rustc_expand/src/mbe/quoted.rs
+++ b/compiler/rustc_expand/src/mbe/quoted.rs
@@ -155,6 +155,13 @@ fn maybe_emit_macro_metavar_expr_feature(features: &Features, sess: &Session, sp
     }
 }
 
+fn maybe_emit_macro_metavar_expr_concat_feature(features: &Features, sess: &Session, span: Span) {
+    if !features.macro_metavar_expr_concat {
+        let msg = "the `concat` meta-variable expression is unstable";
+        feature_err(sess, sym::macro_metavar_expr_concat, span, msg).emit();
+    }
+}
+
 /// Takes a `tokenstream::TokenTree` and returns a `self::TokenTree`. Specifically, this takes a
 /// generic `TokenTree`, such as is used in the rest of the compiler, and returns a `TokenTree`
 /// for use in parsing a macro.
@@ -217,11 +224,19 @@ fn parse_tree<'a>(
                                         return TokenTree::token(token::Dollar, dollar_span);
                                     }
                                     Ok(elem) => {
-                                        maybe_emit_macro_metavar_expr_feature(
-                                            features,
-                                            sess,
-                                            delim_span.entire(),
-                                        );
+                                        if let MetaVarExpr::Concat(_) = elem {
+                                            maybe_emit_macro_metavar_expr_concat_feature(
+                                                features,
+                                                sess,
+                                                delim_span.entire(),
+                                            );
+                                        } else {
+                                            maybe_emit_macro_metavar_expr_feature(
+                                                features,
+                                                sess,
+                                                delim_span.entire(),
+                                            );
+                                        }
                                         return TokenTree::MetaVarExpr(delim_span, elem);
                                     }
                                 }
diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs
index 25e961d6009..914bd03675a 100644
--- a/compiler/rustc_expand/src/mbe/transcribe.rs
+++ b/compiler/rustc_expand/src/mbe/transcribe.rs
@@ -3,18 +3,19 @@ use crate::errors::{
     NoSyntaxVarsExprRepeat, VarStillRepeating,
 };
 use crate::mbe::macro_parser::{NamedMatch, NamedMatch::*};
+use crate::mbe::metavar_expr::{MetaVarExprConcatElem, RAW_IDENT_ERR};
 use crate::mbe::{self, KleeneOp, MetaVarExpr};
 use rustc_ast::mut_visit::{self, MutVisitor};
+use rustc_ast::token::IdentIsRaw;
 use rustc_ast::token::{self, Delimiter, Token, TokenKind};
 use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{pluralize, Diag, DiagCtxt, PResult};
 use rustc_parse::parser::ParseNtResult;
+use rustc_session::parse::ParseSess;
 use rustc_span::hygiene::{LocalExpnId, Transparency};
 use rustc_span::symbol::{sym, Ident, MacroRulesNormalizedIdent};
-use rustc_span::{with_metavar_spans, Span, SyntaxContext};
-
-use rustc_session::parse::ParseSess;
+use rustc_span::{with_metavar_spans, Span, Symbol, SyntaxContext};
 use smallvec::{smallvec, SmallVec};
 use std::mem;
 
@@ -30,7 +31,7 @@ impl MutVisitor for Marker {
         // it's some advanced case with macro-generated macros. So if we cache the marked version
         // of that context once, we'll typically have a 100% cache hit rate after that.
         let Marker(expn_id, transparency, ref mut cache) = *self;
-        span.update_ctxt(|ctxt| {
+        *span = span.map_ctxt(|ctxt| {
             *cache
                 .entry(ctxt)
                 .or_insert_with(|| ctxt.apply_mark(expn_id.to_expn_id(), transparency))
@@ -675,6 +676,23 @@ fn transcribe_metavar_expr<'a>(
         span
     };
     match *expr {
+        MetaVarExpr::Concat(ref elements) => {
+            let mut concatenated = String::new();
+            for element in elements.into_iter() {
+                let string = match element {
+                    MetaVarExprConcatElem::Ident(ident) => ident.to_string(),
+                    MetaVarExprConcatElem::Var(ident) => extract_ident(dcx, *ident, interp)?,
+                };
+                concatenated.push_str(&string);
+            }
+            // The current implementation marks the span as coming from the macro regardless of
+            // contexts of the concatenated identifiers but this behavior may change in the
+            // future.
+            result.push(TokenTree::Token(
+                Token::from_ast_ident(Ident::new(Symbol::intern(&concatenated), visited_span())),
+                Spacing::Alone,
+            ));
+        }
         MetaVarExpr::Count(original_ident, depth) => {
             let matched = matched_from_ident(dcx, original_ident, interp)?;
             let count = count_repetitions(dcx, depth, matched, repeats, sp)?;
@@ -709,3 +727,33 @@ fn transcribe_metavar_expr<'a>(
     }
     Ok(())
 }
+
+/// Extracts an identifier that can be originated from a `$var:ident` variable or from a token tree.
+fn extract_ident<'a>(
+    dcx: &'a DiagCtxt,
+    ident: Ident,
+    interp: &FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
+) -> PResult<'a, String> {
+    if let NamedMatch::MatchedSingle(pnr) = matched_from_ident(dcx, ident, interp)? {
+        if let ParseNtResult::Ident(nt_ident, is_raw) = pnr {
+            if let IdentIsRaw::Yes = is_raw {
+                return Err(dcx.struct_span_err(ident.span, RAW_IDENT_ERR));
+            }
+            return Ok(nt_ident.to_string());
+        }
+        if let ParseNtResult::Tt(TokenTree::Token(
+            Token { kind: TokenKind::Ident(token_ident, is_raw), .. },
+            _,
+        )) = pnr
+        {
+            if let IdentIsRaw::Yes = is_raw {
+                return Err(dcx.struct_span_err(ident.span, RAW_IDENT_ERR));
+            }
+            return Ok(token_ident.to_string());
+        }
+    }
+    Err(dcx.struct_span_err(
+        ident.span,
+        "`${concat(..)}` currently only accepts identifiers or meta-variables as parameters",
+    ))
+}
diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs
index 7c76392858c..ef141c7c25e 100644
--- a/compiler/rustc_feature/src/accepted.rs
+++ b/compiler/rustc_feature/src/accepted.rs
@@ -139,7 +139,7 @@ declare_features! (
     /// Allows `crate` in paths.
     (accepted, crate_in_paths, "1.30.0", Some(45477)),
     /// Allows users to provide classes for fenced code block using `class:classname`.
-    (accepted, custom_code_classes_in_docs, "CURRENT_RUSTC_VERSION", Some(79483)),
+    (accepted, custom_code_classes_in_docs, "1.80.0", Some(79483)),
     /// Allows using `#[debugger_visualizer]` attribute.
     (accepted, debugger_visualizer, "1.71.0", Some(95939)),
     /// Allows rustc to inject a default alloc_error_handler
@@ -165,7 +165,7 @@ declare_features! (
     /// Allows using `dyn Trait` as a syntax for trait objects.
     (accepted, dyn_trait, "1.27.0", Some(44662)),
     /// Allows `X..Y` patterns.
-    (accepted, exclusive_range_pattern, "CURRENT_RUSTC_VERSION", Some(37854)),
+    (accepted, exclusive_range_pattern, "1.80.0", Some(37854)),
     /// Allows integer match exhaustiveness checking (RFC 2591).
     (accepted, exhaustive_integer_patterns, "1.33.0", Some(50907)),
     /// Allows explicit generic arguments specification with `impl Trait` present.
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 2410019868a..58832cb1087 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -457,7 +457,7 @@ declare_features! (
     /// Allows explicit tail calls via `become` expression.
     (incomplete, explicit_tail_calls, "1.72.0", Some(112788)),
     /// Uses 2024 rules for matching `expr` fragments in macros. Also enables `expr_2021` fragment.
-    (incomplete, expr_fragment_specifier_2024, "CURRENT_RUSTC_VERSION", Some(123742)),
+    (incomplete, expr_fragment_specifier_2024, "1.80.0", Some(123742)),
     /// Allows using `efiapi`, `sysv64` and `win64` as calling convention
     /// for functions with varargs.
     (unstable, extended_varargs_abi_support, "1.65.0", Some(100189)),
@@ -488,7 +488,7 @@ declare_features! (
     /// Allows generic parameters and where-clauses on free & associated const items.
     (incomplete, generic_const_items, "1.73.0", Some(113521)),
     /// Allows registering static items globally, possibly across crates, to iterate over at runtime.
-    (unstable, global_registration, "CURRENT_RUSTC_VERSION", Some(125119)),
+    (unstable, global_registration, "1.80.0", Some(125119)),
     /// Allows using `..=X` as a patterns in slices.
     (unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)),
     /// Allows `if let` guard in match arms.
@@ -516,6 +516,8 @@ declare_features! (
     (unstable, lint_reasons, "1.31.0", Some(54503)),
     /// Give access to additional metadata about declarative macro meta-variables.
     (unstable, macro_metavar_expr, "1.61.0", Some(83527)),
+    /// Provides a way to concatenate identifiers using metavariable expressions.
+    (unstable, macro_metavar_expr_concat, "CURRENT_RUSTC_VERSION", Some(124225)),
     /// Allows `#[marker]` on certain traits allowing overlapping implementations.
     (unstable, marker_trait_attr, "1.30.0", Some(29864)),
     /// Allows exhaustive pattern matching on types that contain uninhabited types in cases that are
@@ -581,7 +583,7 @@ declare_features! (
     (unstable, repr_simd, "1.4.0", Some(27731)),
     /// Allows enums like Result<T, E> to be used across FFI, if T's niche value can
     /// be used to describe E or vise-versa.
-    (unstable, result_ffi_guarantees, "CURRENT_RUSTC_VERSION", Some(110503)),
+    (unstable, result_ffi_guarantees, "1.80.0", Some(110503)),
     /// Allows bounding the return type of AFIT/RPITIT.
     (incomplete, return_type_notation, "1.70.0", Some(109417)),
     /// Allows `extern "rust-cold"`.
@@ -623,9 +625,9 @@ declare_features! (
     /// Allows unnamed fields of struct and union type
     (incomplete, unnamed_fields, "1.74.0", Some(49804)),
     /// Allows unsafe attributes.
-    (unstable, unsafe_attributes, "CURRENT_RUSTC_VERSION", Some(123757)),
+    (unstable, unsafe_attributes, "1.80.0", Some(123757)),
     /// Allows unsafe on extern declarations and safety qualifiers over internal items.
-    (unstable, unsafe_extern_blocks, "CURRENT_RUSTC_VERSION", Some(123743)),
+    (unstable, unsafe_extern_blocks, "1.80.0", Some(123743)),
     /// Allows unsized fn parameters.
     (unstable, unsized_fn_params, "1.49.0", Some(48055)),
     /// Allows unsized rvalues at arguments and parameters.
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 042894beec2..d4a22c4c31f 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1633,6 +1633,13 @@ pub struct ConstBlock {
 }
 
 /// An expression.
+///
+/// For more details, see the [rust lang reference].
+/// Note that the reference does not document nightly-only features.
+/// There may be also slight differences in the names and representation of AST nodes between
+/// the compiler and the reference.
+///
+/// [rust lang reference]: https://doc.rust-lang.org/reference/expressions.html
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
 pub struct Expr<'hir> {
     pub hir_id: HirId,
@@ -2464,6 +2471,15 @@ pub enum AssocItemConstraintKind<'hir> {
     Bound { bounds: &'hir [GenericBound<'hir>] },
 }
 
+impl<'hir> AssocItemConstraintKind<'hir> {
+    pub fn descr(&self) -> &'static str {
+        match self {
+            AssocItemConstraintKind::Equality { .. } => "binding",
+            AssocItemConstraintKind::Bound { .. } => "constraint",
+        }
+    }
+}
+
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
 pub struct Ty<'hir> {
     pub hir_id: HirId,
@@ -3147,6 +3163,13 @@ impl ItemId {
 /// An item
 ///
 /// The name might be a dummy name in case of anonymous items
+///
+/// For more details, see the [rust lang reference].
+/// Note that the reference does not document nightly-only features.
+/// There may be also slight differences in the names and representation of AST nodes between
+/// the compiler and the reference.
+///
+/// [rust lang reference]: https://doc.rust-lang.org/reference/items.html
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
 pub struct Item<'hir> {
     pub ident: Ident,
@@ -3749,6 +3772,21 @@ impl<'hir> Node<'hir> {
         }
     }
 
+    /// Get a `hir::Impl` if the node is an impl block for the given `trait_def_id`.
+    pub fn impl_block_of_trait(self, trait_def_id: DefId) -> Option<&'hir Impl<'hir>> {
+        match self {
+            Node::Item(Item { kind: ItemKind::Impl(impl_block), .. })
+                if impl_block
+                    .of_trait
+                    .and_then(|trait_ref| trait_ref.trait_def_id())
+                    .is_some_and(|trait_id| trait_id == trait_def_id) =>
+            {
+                Some(impl_block)
+            }
+            _ => None,
+        }
+    }
+
     pub fn fn_sig(self) -> Option<&'hir FnSig<'hir>> {
         match self {
             Node::TraitItem(TraitItem { kind: TraitItemKind::Fn(fn_sig, _), .. })
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index c3ccba487ed..69461957f80 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -244,7 +244,9 @@ language_item_table! {
     AsyncIterator,           sym::async_iterator,      async_iterator_trait,       Target::Trait,          GenericRequirement::Exact(0);
 
     CoroutineState,          sym::coroutine_state,     coroutine_state,            Target::Enum,           GenericRequirement::None;
-    Coroutine,               sym::coroutine,           coroutine_trait,            Target::Trait,          GenericRequirement::Minimum(1);
+    Coroutine,               sym::coroutine,           coroutine_trait,            Target::Trait,          GenericRequirement::Exact(1);
+    CoroutineReturn,         sym::coroutine_return,    coroutine_return,           Target::AssocTy,        GenericRequirement::Exact(1);
+    CoroutineYield,          sym::coroutine_yield,     coroutine_yield,            Target::AssocTy,        GenericRequirement::Exact(1);
     CoroutineResume,         sym::coroutine_resume,    coroutine_resume,           Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
 
     Unpin,                   sym::unpin,               unpin_trait,                Target::Trait,          GenericRequirement::None;
diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs
index 38ecd7dd082..7f0d72b3a8d 100644
--- a/compiler/rustc_hir_analysis/src/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/bounds.rs
@@ -53,7 +53,7 @@ impl<'tcx> Bounds<'tcx> {
             span,
         );
         // FIXME(-Znext-solver): We can likely remove this hack once the new trait solver lands.
-        if tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) {
+        if tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) {
             self.clauses.insert(0, clause);
         } else {
             self.clauses.push(clause);
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
index 2672614a895..2e965c59ebb 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
@@ -1,6 +1,6 @@
 use rustc_ast::InlineAsmTemplatePiece;
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_hir as hir;
+use rustc_hir::{self as hir, LangItem};
 use rustc_middle::bug;
 use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy};
 use rustc_session::lint;
@@ -62,8 +62,10 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
             ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Some(InlineAsmType::I64),
             ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Some(InlineAsmType::I128),
             ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(asm_ty_isize),
+            ty::Float(FloatTy::F16) => Some(InlineAsmType::F16),
             ty::Float(FloatTy::F32) => Some(InlineAsmType::F32),
             ty::Float(FloatTy::F64) => Some(InlineAsmType::F64),
+            ty::Float(FloatTy::F128) => Some(InlineAsmType::F128),
             ty::FnPtr(_) => Some(asm_ty_isize),
             ty::RawPtr(ty, _) if self.is_thin_ptr_ty(ty) => Some(asm_ty_isize),
             ty::Adt(adt, args) if adt.repr().simd() => {
@@ -105,8 +107,10 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                             width => bug!("unsupported pointer width: {width}"),
                         })
                     }
+                    ty::Float(FloatTy::F16) => Some(InlineAsmType::VecF16(size)),
                     ty::Float(FloatTy::F32) => Some(InlineAsmType::VecF32(size)),
                     ty::Float(FloatTy::F64) => Some(InlineAsmType::VecF64(size)),
+                    ty::Float(FloatTy::F128) => Some(InlineAsmType::VecF128(size)),
                     _ => None,
                 }
             }
@@ -134,7 +138,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
             // `!` is allowed for input but not for output (issue #87802)
             ty::Never if is_input => return None,
             _ if ty.references_error() => return None,
-            ty::Adt(adt, args) if Some(adt.did()) == self.tcx.lang_items().maybe_uninit() => {
+            ty::Adt(adt, args) if self.tcx.is_lang_item(adt.did(), LangItem::MaybeUninit) => {
                 let fields = &adt.non_enum_variant().fields;
                 let ty = fields[FieldIdx::from_u32(1)].ty(self.tcx, args);
                 // FIXME: Are we just trying to map to the `T` in `MaybeUninit<T>`?
diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs
index eae41d28e89..e9961d3ad08 100644
--- a/compiler/rustc_hir_analysis/src/coherence/mod.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs
@@ -8,6 +8,7 @@
 use crate::errors;
 use rustc_errors::{codes::*, struct_span_code_err};
 use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::LangItem;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
 use rustc_session::parse::feature_err;
@@ -49,7 +50,7 @@ fn enforce_trait_manually_implementable(
 ) -> Result<(), ErrorGuaranteed> {
     let impl_header_span = tcx.def_span(impl_def_id);
 
-    if tcx.lang_items().freeze_trait() == Some(trait_def_id) {
+    if tcx.is_lang_item(trait_def_id, LangItem::Freeze) {
         if !tcx.features().freeze_impls {
             feature_err(
                 &tcx.sess,
@@ -75,7 +76,7 @@ fn enforce_trait_manually_implementable(
 
         // Maintain explicit error code for `Unsize`, since it has a useful
         // explanation about using `CoerceUnsized` instead.
-        if Some(trait_def_id) == tcx.lang_items().unsize_trait() {
+        if tcx.is_lang_item(trait_def_id, LangItem::Unsize) {
             err.code(E0328);
         }
 
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index 40204961e9c..3421c8da4e9 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -169,12 +169,11 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
         predicates.insert((trait_ref.upcast(tcx), tcx.def_span(def_id)));
     }
 
-    // Collect the predicates that were written inline by the user on each
-    // type parameter (e.g., `<T: Foo>`). Also add `ConstArgHasType` predicates
-    // for each const parameter.
+    // Add implicit predicates that should be treated as if the user has written them,
+    // including the implicit `T: Sized` for all generic parameters, and `ConstArgHasType`
+    // for const params.
     for param in hir_generics.params {
         match param.kind {
-            // We already dealt with early bound lifetimes above.
             GenericParamKind::Lifetime { .. } => (),
             GenericParamKind::Type { .. } => {
                 let param_ty = icx.lowerer().lower_ty_param(param.hir_id);
@@ -204,7 +203,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
     }
 
     trace!(?predicates);
-    // Add in the bounds that appear in the where-clause.
+    // Add inline `<T: Foo>` bounds and bounds in the where clause.
     for predicate in hir_generics.predicates {
         match predicate {
             hir::WherePredicate::BoundPredicate(bound_pred) => {
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
index 3a9ef244fd3..2d240699105 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -1217,7 +1217,6 @@ pub fn prohibit_assoc_item_constraint(
     // otherwise suggest the removal of the binding.
     if let Some((def_id, segment, _)) = segment
         && segment.args().parenthesized == hir::GenericArgsParentheses::No
-        && let hir::AssocItemConstraintKind::Equality { term } = constraint.kind
     {
         // Suggests removal of the offending binding
         let suggest_removal = |e: &mut Diag<'_>| {
@@ -1263,7 +1262,7 @@ pub fn prohibit_assoc_item_constraint(
             if let Ok(suggestion) = tcx.sess.source_map().span_to_snippet(removal_span) {
                 e.span_suggestion_verbose(
                     removal_span,
-                    "consider removing this associated item binding",
+                    format!("consider removing this associated item {}", constraint.kind.descr()),
                     suggestion,
                     Applicability::MaybeIncorrect,
                 );
@@ -1286,19 +1285,73 @@ pub fn prohibit_assoc_item_constraint(
         // Check if the type has a generic param with the same name
         // as the assoc type name in the associated item binding.
         let generics = tcx.generics_of(def_id);
-        let matching_param =
-            generics.own_params.iter().find(|p| p.name.as_str() == constraint.ident.as_str());
+        let matching_param = generics.own_params.iter().find(|p| p.name == constraint.ident.name);
 
         // Now emit the appropriate suggestion
         if let Some(matching_param) = matching_param {
-            match (&matching_param.kind, term) {
-                (GenericParamDefKind::Type { .. }, hir::Term::Ty(ty)) => {
-                    suggest_direct_use(&mut err, ty.span);
-                }
-                (GenericParamDefKind::Const { .. }, hir::Term::Const(c)) => {
+            match (constraint.kind, &matching_param.kind) {
+                (
+                    hir::AssocItemConstraintKind::Equality { term: hir::Term::Ty(ty) },
+                    GenericParamDefKind::Type { .. },
+                ) => suggest_direct_use(&mut err, ty.span),
+                (
+                    hir::AssocItemConstraintKind::Equality { term: hir::Term::Const(c) },
+                    GenericParamDefKind::Const { .. },
+                ) => {
                     let span = tcx.hir().span(c.hir_id);
                     suggest_direct_use(&mut err, span);
                 }
+                (hir::AssocItemConstraintKind::Bound { bounds }, _) => {
+                    // Suggest `impl<T: Bound> Trait<T> for Foo` when finding
+                    // `impl Trait<T: Bound> for Foo`
+
+                    // Get the parent impl block based on the binding we have
+                    // and the trait DefId
+                    let impl_block = tcx
+                        .hir()
+                        .parent_iter(constraint.hir_id)
+                        .find_map(|(_, node)| node.impl_block_of_trait(def_id));
+
+                    let type_with_constraints =
+                        tcx.sess.source_map().span_to_snippet(constraint.span);
+
+                    if let Some(impl_block) = impl_block
+                        && let Ok(type_with_constraints) = type_with_constraints
+                    {
+                        // Filter out the lifetime parameters because
+                        // they should be declared before the type parameter
+                        let lifetimes: String = bounds
+                            .iter()
+                            .filter_map(|bound| {
+                                if let hir::GenericBound::Outlives(lifetime) = bound {
+                                    Some(format!("{lifetime}, "))
+                                } else {
+                                    None
+                                }
+                            })
+                            .collect();
+                        // Figure out a span and suggestion string based on
+                        // whether there are any existing parameters
+                        let param_decl = if let Some(param_span) =
+                            impl_block.generics.span_for_param_suggestion()
+                        {
+                            (param_span, format!(", {lifetimes}{type_with_constraints}"))
+                        } else {
+                            (
+                                impl_block.generics.span.shrink_to_lo(),
+                                format!("<{lifetimes}{type_with_constraints}>"),
+                            )
+                        };
+                        let suggestions =
+                            vec![param_decl, (constraint.span, format!("{}", matching_param.name))];
+
+                        err.multipart_suggestion_verbose(
+                            format!("declare the type parameter right after the `impl` keyword"),
+                            suggestions,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
                 _ => suggest_removal(&mut err),
             }
         } else {
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
index 26cabb69d25..3f888c4e272 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
@@ -531,6 +531,7 @@ pub(crate) fn check_generic_arg_count(
 
         let num_default_params = expected_max - expected_min;
 
+        let mut all_params_are_binded = false;
         let gen_args_info = if provided > expected_max {
             invalid_args.extend((expected_max..provided).map(|i| i + args_offset));
             let num_redundant_args = provided - expected_max;
@@ -547,6 +548,20 @@ pub(crate) fn check_generic_arg_count(
         } else {
             let num_missing_args = expected_max - provided;
 
+            let constraint_names: Vec<_> =
+                gen_args.constraints.iter().map(|b| b.ident.name).collect();
+            let param_names: Vec<_> = gen_params
+                .own_params
+                .iter()
+                .filter(|param| !has_self || param.index != 0) // Assumes `Self` will always be the first parameter
+                .map(|param| param.name)
+                .collect();
+            if constraint_names == param_names {
+                // We set this to true and delay emitting `WrongNumberOfGenericArgs`
+                // to provide a succinct error for cases like issue #113073
+                all_params_are_binded = true;
+            };
+
             GenericArgsInfo::MissingTypesOrConsts {
                 num_missing_args,
                 num_default_params,
@@ -567,7 +582,7 @@ pub(crate) fn check_generic_arg_count(
                 def_id,
             )
             .diagnostic()
-            .emit()
+            .emit_unless(all_params_are_binded)
         });
 
         Err(reported)
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl
index 6f499947d5c..d6f3f4d640b 100644
--- a/compiler/rustc_hir_typeck/messages.ftl
+++ b/compiler/rustc_hir_typeck/messages.ftl
@@ -44,6 +44,10 @@ hir_typeck_convert_using_method = try using `{$sugg}` to convert `{$found}` to `
 
 hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private
 
+hir_typeck_dependency_on_unit_never_type_fallback = this function depends on never type fallback being `()`
+    .note = in edition 2024, the requirement `{$obligation}` will fail
+    .help = specify the types explicitly
+
 hir_typeck_deref_is_empty = this expression `Deref`s to `{$deref_ty}` which implements `is_empty`
 
 hir_typeck_expected_default_return_type = expected `()` because of default return type
diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs
index a599e8d05fd..281af80bff5 100644
--- a/compiler/rustc_hir_typeck/src/_match.rs
+++ b/compiler/rustc_hir_typeck/src/_match.rs
@@ -234,7 +234,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let ret_ty = ret_coercion.borrow().expected_ty();
                 let ret_ty = self.infcx.shallow_resolve(ret_ty);
                 self.can_coerce(arm_ty, ret_ty)
-                    && prior_arm.map_or(true, |(_, ty, _)| self.can_coerce(ty, ret_ty))
+                    && prior_arm.is_none_or(|(_, ty, _)| self.can_coerce(ty, ret_ty))
                     // The match arms need to unify for the case of `impl Trait`.
                     && !matches!(ret_ty.kind(), ty::Alias(ty::Opaque, ..))
             }
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index 9736c8b8920..46c85515575 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -5,9 +5,9 @@ use super::{Expectation, FnCtxt, TupleArgumentsFlag};
 use crate::errors;
 use rustc_ast::util::parser::PREC_POSTFIX;
 use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey};
-use rustc_hir as hir;
 use rustc_hir::def::{self, CtorKind, Namespace, Res};
 use rustc_hir::def_id::DefId;
+use rustc_hir::{self as hir, LangItem};
 use rustc_hir_analysis::autoderef::Autoderef;
 use rustc_infer::traits::ObligationCauseCode;
 use rustc_infer::{
@@ -41,7 +41,7 @@ pub fn check_legal_trait_for_method_call(
     trait_id: DefId,
     body_id: DefId,
 ) -> Result<(), ErrorGuaranteed> {
-    if tcx.lang_items().drop_trait() == Some(trait_id)
+    if tcx.is_lang_item(trait_id, LangItem::Drop)
         && tcx.lang_items().fallback_surface_drop_fn() != Some(body_id)
     {
         let sugg = if let Some(receiver) = receiver.filter(|s| !s.is_empty()) {
@@ -628,7 +628,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 return;
             };
 
-            let pick = self.confirm_method(
+            let pick = self.confirm_method_for_diagnostic(
                 call_expr.span,
                 callee_expr,
                 call_expr,
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index e54a07786cd..fcd22b74676 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -913,7 +913,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                 if self
                     .tcx
                     .upvars_mentioned(closure_def_id_a.expect_local())
-                    .map_or(true, |u| u.is_empty()) =>
+                    .is_none_or(|u| u.is_empty()) =>
             {
                 // We coerce the closure, which has fn type
                 //     `extern "rust-call" fn((arg0,arg1,...)) -> _`
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index d2a5924c8bb..f9720c9c307 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -4,7 +4,7 @@ use rustc_errors::{Applicability, Diag};
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_hir::intravisit::Visitor;
-use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
+use rustc_infer::infer::DefineOpaqueTypes;
 use rustc_middle::bug;
 use rustc_middle::ty::adjustment::AllowTwoPhase;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
@@ -166,7 +166,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Requires that the two types unify, and prints an error message if
     /// they don't.
     pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
-        if let Some(e) = self.demand_suptype_diag(sp, expected, actual) {
+        if let Err(e) = self.demand_suptype_diag(sp, expected, actual) {
             e.emit();
         }
     }
@@ -176,7 +176,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         sp: Span,
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
-    ) -> Option<Diag<'tcx>> {
+    ) -> Result<(), Diag<'tcx>> {
         self.demand_suptype_with_origin(&self.misc(sp), expected, actual)
     }
 
@@ -186,18 +186,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         cause: &ObligationCause<'tcx>,
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
-    ) -> Option<Diag<'tcx>> {
-        match self.at(cause, self.param_env).sup(DefineOpaqueTypes::Yes, expected, actual) {
-            Ok(InferOk { obligations, value: () }) => {
-                self.register_predicates(obligations);
-                None
-            }
-            Err(e) => Some(self.err_ctxt().report_mismatched_types(cause, expected, actual, e)),
-        }
+    ) -> Result<(), Diag<'tcx>> {
+        self.at(cause, self.param_env)
+            .sup(DefineOpaqueTypes::Yes, expected, actual)
+            .map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
+            .map_err(|e| self.err_ctxt().report_mismatched_types(cause, expected, actual, e))
     }
 
     pub fn demand_eqtype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
-        if let Some(err) = self.demand_eqtype_diag(sp, expected, actual) {
+        if let Err(err) = self.demand_eqtype_diag(sp, expected, actual) {
             err.emit();
         }
     }
@@ -207,7 +204,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         sp: Span,
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
-    ) -> Option<Diag<'tcx>> {
+    ) -> Result<(), Diag<'tcx>> {
         self.demand_eqtype_with_origin(&self.misc(sp), expected, actual)
     }
 
@@ -216,14 +213,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         cause: &ObligationCause<'tcx>,
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
-    ) -> Option<Diag<'tcx>> {
-        match self.at(cause, self.param_env).eq(DefineOpaqueTypes::Yes, expected, actual) {
-            Ok(InferOk { obligations, value: () }) => {
-                self.register_predicates(obligations);
-                None
-            }
-            Err(e) => Some(self.err_ctxt().report_mismatched_types(cause, expected, actual, e)),
-        }
+    ) -> Result<(), Diag<'tcx>> {
+        self.at(cause, self.param_env)
+            .eq(DefineOpaqueTypes::Yes, expected, actual)
+            .map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
+            .map_err(|e| self.err_ctxt().report_mismatched_types(cause, expected, actual, e))
     }
 
     pub fn demand_coerce(
@@ -234,12 +228,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
         allow_two_phase: AllowTwoPhase,
     ) -> Ty<'tcx> {
-        let (ty, err) =
-            self.demand_coerce_diag(expr, checked_ty, expected, expected_ty_expr, allow_two_phase);
-        if let Some(err) = err {
-            err.emit();
+        match self.demand_coerce_diag(expr, checked_ty, expected, expected_ty_expr, allow_two_phase)
+        {
+            Ok(ty) => ty,
+            Err(err) => {
+                err.emit();
+                // Return the original type instead of an error type here, otherwise the type of `x` in
+                // `let x: u32 = ();` will be a type error, causing all subsequent usages of `x` to not
+                // report errors, even though `x` is definitely `u32`.
+                expected
+            }
         }
-        ty
     }
 
     /// Checks that the type of `expr` can be coerced to `expected`.
@@ -254,11 +253,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Ty<'tcx>,
         mut expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
         allow_two_phase: AllowTwoPhase,
-    ) -> (Ty<'tcx>, Option<Diag<'tcx>>) {
+    ) -> Result<Ty<'tcx>, Diag<'tcx>> {
         let expected = self.resolve_vars_with_obligations(expected);
 
         let e = match self.coerce(expr, checked_ty, expected, allow_two_phase, None) {
-            Ok(ty) => return (ty, None),
+            Ok(ty) => return Ok(ty),
             Err(e) => e,
         };
 
@@ -275,7 +274,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         self.emit_coerce_suggestions(&mut err, expr, expr_ty, expected, expected_ty_expr, Some(e));
 
-        (expected, Some(err))
+        Err(err)
     }
 
     /// Notes the point at which a variable is constrained to some type incompatible
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 3f12f252654..24f039b8e90 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -7,7 +7,7 @@ use rustc_errors::{
     SubdiagMessageOp, Subdiagnostic,
 };
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
-use rustc_middle::ty::Ty;
+use rustc_middle::ty::{self, Ty};
 use rustc_span::{
     edition::{Edition, LATEST_STABLE_EDITION},
     symbol::Ident,
@@ -183,6 +183,15 @@ pub enum NeverTypeFallbackFlowingIntoUnsafe {
     Deref,
 }
 
+#[derive(LintDiagnostic)]
+#[help]
+#[diag(hir_typeck_dependency_on_unit_never_type_fallback)]
+pub struct DependencyOnUnitNeverTypeFallback<'tcx> {
+    #[note]
+    pub obligation_span: Span,
+    pub obligation: ty::Predicate<'tcx>,
+}
+
 #[derive(Subdiagnostic)]
 #[multipart_suggestion(
     hir_typeck_add_missing_parentheses_in_range,
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 5b27ebe3416..fe497498c4b 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -87,7 +87,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             ty = adj_ty;
         }
 
-        if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) {
+        if let Err(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) {
             let _ = self.emit_type_mismatch_suggestions(
                 &mut err,
                 expr.peel_drop_temps(),
@@ -1132,7 +1132,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // say that the user intended to write `lhs == rhs` instead of `lhs = rhs`.
             // The likely cause of this is `if foo = bar { .. }`.
             let actual_ty = self.tcx.types.unit;
-            let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap();
+            let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap_err();
             let lhs_ty = self.check_expr(lhs);
             let rhs_ty = self.check_expr(rhs);
             let refs_can_coerce = |lhs: Ty<'tcx>, rhs: Ty<'tcx>| {
@@ -1236,7 +1236,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // This is (basically) inlined `check_expr_coercible_to_type`, but we want
         // to suggest an additional fixup here in `suggest_deref_binop`.
         let rhs_ty = self.check_expr_with_hint(rhs, lhs_ty);
-        if let (_, Some(mut diag)) =
+        if let Err(mut diag) =
             self.demand_coerce_diag(rhs, rhs_ty, lhs_ty, Some(lhs), AllowTwoPhase::No)
         {
             suggest_deref_binop(&mut diag, rhs_ty);
@@ -1342,14 +1342,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 Ok(method)
             }
             Err(error) => {
-                if segment.ident.name != kw::Empty {
-                    if let Some(err) =
-                        self.report_method_error(expr.hir_id, rcvr_t, error, expected, false)
-                    {
-                        err.emit();
-                    }
+                if segment.ident.name == kw::Empty {
+                    span_bug!(rcvr.span, "empty method name")
+                } else {
+                    Err(self.report_method_error(expr.hir_id, rcvr_t, error, expected, false))
                 }
-                Err(())
             }
         };
 
@@ -1560,7 +1557,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // If the length is 0, we don't create any elements, so we don't copy any. If the length is 1, we
         // don't copy that one element, we move it. Only check for Copy if the length is larger.
-        if count.try_eval_target_usize(tcx, self.param_env).map_or(true, |len| len > 1) {
+        if count.try_eval_target_usize(tcx, self.param_env).is_none_or(|len| len > 1) {
             let lang_item = self.tcx.require_lang_item(LangItem::Copy, None);
             let code = traits::ObligationCauseCode::RepeatElementCopy {
                 is_constable,
@@ -1741,10 +1738,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // Make sure to give a type to the field even if there's
             // an error, so we can continue type-checking.
             let ty = self.check_expr_with_hint(field.expr, field_type);
-            let (_, diag) =
-                self.demand_coerce_diag(field.expr, ty, field_type, None, AllowTwoPhase::No);
+            let diag = self.demand_coerce_diag(field.expr, ty, field_type, None, AllowTwoPhase::No);
 
-            if let Some(diag) = diag {
+            if let Err(diag) = diag {
                 if idx == hir_fields.len() - 1 {
                     if remaining_fields.is_empty() {
                         self.suggest_fru_from_range_and_emit(field, variant, args, diag);
@@ -3154,7 +3150,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 error.obligation.predicate.kind().skip_binder(),
             ) {
                 (ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)), _)
-                    if self.tcx.lang_items().index_trait() == Some(predicate.trait_ref.def_id) =>
+                    if self.tcx.is_lang_item(predicate.trait_ref.def_id, LangItem::Index) =>
                 {
                     seen_preds.insert(error.obligation.predicate.kind().skip_binder());
                 }
diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs
index 5ba67c52ef2..3cecbfd4275 100644
--- a/compiler/rustc_hir_typeck/src/fallback.rs
+++ b/compiler/rustc_hir_typeck/src/fallback.rs
@@ -14,6 +14,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
 use rustc_session::lint;
 use rustc_span::DUMMY_SP;
 use rustc_span::{def_id::LocalDefId, Span};
+use rustc_trait_selection::traits::{ObligationCause, ObligationCtxt};
 
 #[derive(Copy, Clone)]
 pub enum DivergingFallbackBehavior {
@@ -344,6 +345,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         // `!`.
         let mut diverging_fallback = UnordMap::with_capacity(diverging_vids.len());
         let unsafe_infer_vars = OnceCell::new();
+
+        self.lint_obligations_broken_by_never_type_fallback_change(behavior, &diverging_vids);
+
         for &diverging_vid in &diverging_vids {
             let diverging_ty = Ty::new_var(self.tcx, diverging_vid);
             let root_vid = self.root_var(diverging_vid);
@@ -468,6 +472,56 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         }
     }
 
+    fn lint_obligations_broken_by_never_type_fallback_change(
+        &self,
+        behavior: DivergingFallbackBehavior,
+        diverging_vids: &[ty::TyVid],
+    ) {
+        let DivergingFallbackBehavior::ToUnit = behavior else { return };
+
+        // Fallback happens if and only if there are diverging variables
+        if diverging_vids.is_empty() {
+            return;
+        }
+
+        // Returns errors which happen if fallback is set to `fallback`
+        let remaining_errors_if_fallback_to = |fallback| {
+            self.probe(|_| {
+                let obligations = self.fulfillment_cx.borrow().pending_obligations();
+                let ocx = ObligationCtxt::new_with_diagnostics(&self.infcx);
+                ocx.register_obligations(obligations.iter().cloned());
+
+                for &diverging_vid in diverging_vids {
+                    let diverging_ty = Ty::new_var(self.tcx, diverging_vid);
+
+                    ocx.eq(&ObligationCause::dummy(), self.param_env, diverging_ty, fallback)
+                        .expect("expected diverging var to be unconstrained");
+                }
+
+                ocx.select_where_possible()
+            })
+        };
+
+        // If we have no errors with `fallback = ()`, but *do* have errors with `fallback = !`,
+        // then this code will be broken by the never type fallback change.qba
+        let unit_errors = remaining_errors_if_fallback_to(self.tcx.types.unit);
+        if unit_errors.is_empty()
+            && let mut never_errors = remaining_errors_if_fallback_to(self.tcx.types.never)
+            && let [ref mut never_error, ..] = never_errors.as_mut_slice()
+        {
+            self.adjust_fulfillment_error_for_expr_obligation(never_error);
+            self.tcx.emit_node_span_lint(
+                lint::builtin::DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK,
+                self.tcx.local_def_id_to_hir_id(self.body_id),
+                self.tcx.def_span(self.body_id),
+                errors::DependencyOnUnitNeverTypeFallback {
+                    obligation_span: never_error.obligation.cause.span,
+                    obligation: never_error.obligation.predicate,
+                },
+            )
+        }
+    }
+
     /// Returns a graph whose nodes are (unresolved) inference variables and where
     /// an edge `?A -> ?B` indicates that the variable `?A` is coerced to `?B`.
     fn create_coercion_graph(&self) -> VecGraph<ty::TyVid, true> {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index e354e1ec59c..9531c002829 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -660,8 +660,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         })
     }
 
-    pub(crate) fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> {
-        let ty_error = Ty::new_misc_error(self.tcx);
+    pub(crate) fn err_args(&self, len: usize, guar: ErrorGuaranteed) -> Vec<Ty<'tcx>> {
+        let ty_error = Ty::new_error(self.tcx, guar);
         vec![ty_error; len]
     }
 
@@ -846,15 +846,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
 
                 if item_name.name != kw::Empty {
-                    if let Some(e) = self.report_method_error(
+                    self.report_method_error(
                         hir_id,
                         ty.normalized,
                         error,
                         Expectation::NoExpectation,
                         trait_missing_method && span.edition().at_least_rust_2021(), // emits missing method for trait only after edition 2021
-                    ) {
-                        e.emit();
-                    }
+                    );
                 }
 
                 result
@@ -1420,7 +1418,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let impl_ty = self.normalize(span, tcx.type_of(impl_def_id).instantiate(tcx, args));
             let self_ty = self.normalize(span, self_ty);
             match self.at(&self.misc(span), self.param_env).eq(
-                DefineOpaqueTypes::No,
+                DefineOpaqueTypes::Yes,
                 impl_ty,
                 self_ty,
             ) {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index b8333d47493..10d9e07db6f 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -113,17 +113,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         sp: Span,
         expr: &'tcx hir::Expr<'tcx>,
-        method: Result<MethodCallee<'tcx>, ()>,
+        method: Result<MethodCallee<'tcx>, ErrorGuaranteed>,
         args_no_rcvr: &'tcx [hir::Expr<'tcx>],
         tuple_arguments: TupleArgumentsFlag,
         expected: Expectation<'tcx>,
     ) -> Ty<'tcx> {
         let has_error = match method {
-            Ok(method) => method.args.references_error() || method.sig.references_error(),
-            Err(_) => true,
+            Ok(method) => method.args.error_reported().and(method.sig.error_reported()),
+            Err(guar) => Err(guar),
         };
-        if has_error {
-            let err_inputs = self.err_args(args_no_rcvr.len());
+        if let Err(guar) = has_error {
+            let err_inputs = self.err_args(args_no_rcvr.len(), guar);
 
             let err_inputs = match tuple_arguments {
                 DontTupleArguments => err_inputs,
@@ -140,7 +140,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 tuple_arguments,
                 method.ok().map(|method| method.def_id),
             );
-            return Ty::new_misc_error(self.tcx);
+            return Ty::new_error(self.tcx, guar);
         }
 
         let method = method.unwrap();
@@ -237,7 +237,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 _ => {
                     // Otherwise, there's a mismatch, so clear out what we're expecting, and set
                     // our input types to err_args so we don't blow up the error messages
-                    struct_span_code_err!(
+                    let guar = struct_span_code_err!(
                         tcx.dcx(),
                         call_span,
                         E0059,
@@ -245,7 +245,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                          for the function trait is neither a tuple nor unit"
                     )
                     .emit();
-                    (self.err_args(provided_args.len()), None)
+                    (self.err_args(provided_args.len(), guar), None)
                 }
             }
         } else {
@@ -1578,7 +1578,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // type of the place it is referencing, and not some
             // supertype thereof.
             let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m));
-            if let Some(mut diag) = self.demand_eqtype_diag(init.span, local_ty, init_ty) {
+            if let Err(mut diag) = self.demand_eqtype_diag(init.span, local_ty, init_ty) {
                 self.emit_type_mismatch_suggestions(
                     &mut diag,
                     init.peel_drop_temps(),
@@ -1624,7 +1624,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let previous_diverges = self.diverges.get();
             let else_ty = self.check_block_with_expected(blk, NoExpectation);
             let cause = self.cause(blk.span, ObligationCauseCode::LetElse);
-            if let Some(err) = self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty)
+            if let Err(err) = self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty)
             {
                 err.emit();
             }
@@ -2240,7 +2240,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             for (idx, (generic_param, param)) in
                 params_with_generics.iter().enumerate().filter(|(idx, _)| {
                     check_for_matched_generics
-                        || expected_idx.map_or(true, |expected_idx| expected_idx == *idx)
+                        || expected_idx.is_none_or(|expected_idx| expected_idx == *idx)
                 })
             {
                 let Some(generic_param) = generic_param else {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 9ab89f3444c..9743dc7c69f 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -440,7 +440,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             };
             // Given `Result<_, E>`, check our expected ty is `Result<_, &E>` for
             // `as_ref` and `as_deref` compatibility.
-            let error_tys_equate_as_ref = error_tys.map_or(true, |(found, expected)| {
+            let error_tys_equate_as_ref = error_tys.is_none_or(|(found, expected)| {
                 self.can_eq(
                     self.param_env,
                     Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, found),
@@ -489,10 +489,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 );
                 return true;
             } else if let ty::Adt(adt, _) = found_ty_inner.peel_refs().kind()
-                && Some(adt.did()) == self.tcx.lang_items().string()
+                && self.tcx.is_lang_item(adt.did(), LangItem::String)
                 && peeled.is_str()
                 // `Result::map`, conversely, does not take ref of the error type.
-                && error_tys.map_or(true, |(found, expected)| {
+                && error_tys.is_none_or(|(found, expected)| {
                     self.can_eq(self.param_env, found, expected)
                 })
             {
@@ -3147,7 +3147,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return;
         }
         if let ty::Adt(adt, _) = expected_ty.kind()
-            && self.tcx.lang_items().range_struct() == Some(adt.did())
+            && self.tcx.is_lang_item(adt.did(), LangItem::Range)
         {
             return;
         }
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index b0fd6de3496..a87ee7b4554 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -4,6 +4,7 @@
 #![feature(box_patterns)]
 #![feature(control_flow_enum)]
 #![feature(if_let_guard)]
+#![feature(is_none_or)]
 #![feature(let_chains)]
 #![feature(never_type)]
 #![feature(try_blocks)]
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
index 3c9a49e91a3..120e3239d1f 100644
--- a/compiler/rustc_hir_typeck/src/method/confirm.rs
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -497,7 +497,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
                 args,
             })),
         );
-        match self.at(&cause, self.param_env).sup(DefineOpaqueTypes::No, method_self_ty, self_ty) {
+        match self.at(&cause, self.param_env).sup(DefineOpaqueTypes::Yes, method_self_ty, self_ty) {
             Ok(InferOk { obligations, value: () }) => {
                 self.register_predicates(obligations);
             }
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index e842bba34bf..3986374a343 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -634,8 +634,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         }
     }
 
+    #[instrument(level = "debug", skip(self))]
     fn assemble_probe(&mut self, self_ty: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>) {
-        debug!("assemble_probe: self_ty={:?}", self_ty);
         let raw_self_ty = self_ty.value.value;
         match *raw_self_ty.kind() {
             ty::Dynamic(data, ..) if let Some(p) = data.principal() => {
@@ -713,13 +713,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         }
     }
 
+    #[instrument(level = "debug", skip(self))]
     fn assemble_inherent_impl_probe(&mut self, impl_def_id: DefId) {
         if !self.impl_dups.insert(impl_def_id) {
             return; // already visited
         }
 
-        debug!("assemble_inherent_impl_probe {:?}", impl_def_id);
-
         for item in self.impl_or_trait_item(impl_def_id) {
             if !self.has_applicable_self(&item) {
                 // No receiver declared. Not a candidate.
@@ -737,9 +736,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         }
     }
 
+    #[instrument(level = "debug", skip(self))]
     fn assemble_inherent_candidates_from_object(&mut self, self_ty: Ty<'tcx>) {
-        debug!("assemble_inherent_candidates_from_object(self_ty={:?})", self_ty);
-
         let principal = match self_ty.kind() {
             ty::Dynamic(ref data, ..) => Some(data),
             _ => None,
@@ -768,9 +766,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         });
     }
 
+    #[instrument(level = "debug", skip(self))]
     fn assemble_inherent_candidates_from_param(&mut self, param_ty: ty::ParamTy) {
         // FIXME: do we want to commit to this behavior for param bounds?
-        debug!("assemble_inherent_candidates_from_param(param_ty={:?})", param_ty);
 
         let bounds = self.param_env.caller_bounds().iter().filter_map(|predicate| {
             let bound_predicate = predicate.kind();
@@ -826,6 +824,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         }
     }
 
+    #[instrument(level = "debug", skip(self))]
     fn assemble_extension_candidates_for_traits_in_scope(&mut self) {
         let mut duplicates = FxHashSet::default();
         let opt_applicable_traits = self.tcx.in_scope_traits(self.scope_expr_id);
@@ -842,6 +841,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         }
     }
 
+    #[instrument(level = "debug", skip(self))]
     fn assemble_extension_candidates_for_all_traits(&mut self) {
         let mut duplicates = FxHashSet::default();
         for trait_info in suggest::all_traits(self.tcx) {
@@ -863,12 +863,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         }
     }
 
+    #[instrument(level = "debug", skip(self))]
     fn assemble_extension_candidates_for_trait(
         &mut self,
         import_ids: &SmallVec<[LocalDefId; 1]>,
         trait_def_id: DefId,
     ) {
-        debug!("assemble_extension_candidates_for_trait(trait_def_id={:?})", trait_def_id);
         let trait_args = self.fresh_args_for_item(self.span, trait_def_id);
         let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, trait_args);
 
@@ -958,6 +958,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
     ///////////////////////////////////////////////////////////////////////////
     // THE ACTUAL SEARCH
 
+    #[instrument(level = "debug", skip(self))]
     fn pick(mut self) -> PickResult<'tcx> {
         assert!(self.method_name.is_some());
 
@@ -1386,6 +1387,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         }
     }
 
+    #[instrument(level = "trace", skip(self, possibly_unsatisfied_predicates), ret)]
     fn consider_probe(
         &self,
         self_ty: Ty<'tcx>,
@@ -1415,15 +1417,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                     (xform_self_ty, xform_ret_ty) =
                         self.xform_self_ty(probe.item, impl_ty, impl_args);
                     xform_self_ty = ocx.normalize(cause, self.param_env, xform_self_ty);
-                    // FIXME: Make this `ocx.sup` once we define opaques more eagerly.
-                    match self.at(cause, self.param_env).sup(
-                        DefineOpaqueTypes::No,
-                        xform_self_ty,
-                        self_ty,
-                    ) {
-                        Ok(infer_ok) => {
-                            ocx.register_infer_ok_obligations(infer_ok);
-                        }
+                    match ocx.sup(cause, self.param_env, xform_self_ty, self_ty) {
+                        Ok(()) => {}
                         Err(err) => {
                             debug!("--> cannot relate self-types {:?}", err);
                             return ProbeResult::NoMatch;
@@ -1484,19 +1479,23 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                     (xform_self_ty, xform_ret_ty) =
                         self.xform_self_ty(probe.item, trait_ref.self_ty(), trait_ref.args);
                     xform_self_ty = ocx.normalize(cause, self.param_env, xform_self_ty);
-                    // FIXME: Make this `ocx.sup` once we define opaques more eagerly.
-                    match self.at(cause, self.param_env).sup(
-                        DefineOpaqueTypes::No,
-                        xform_self_ty,
-                        self_ty,
-                    ) {
-                        Ok(infer_ok) => {
-                            ocx.register_infer_ok_obligations(infer_ok);
-                        }
-                        Err(err) => {
-                            debug!("--> cannot relate self-types {:?}", err);
+                    match self_ty.kind() {
+                        // HACK: opaque types will match anything for which their bounds hold.
+                        // Thus we need to prevent them from trying to match the `&_` autoref
+                        // candidates that get created for `&self` trait methods.
+                        ty::Alias(ty::Opaque, alias_ty)
+                            if self.infcx.can_define_opaque_ty(alias_ty.def_id)
+                                && !xform_self_ty.is_ty_var() =>
+                        {
                             return ProbeResult::NoMatch;
                         }
+                        _ => match ocx.sup(cause, self.param_env, xform_self_ty, self_ty) {
+                            Ok(()) => {}
+                            Err(err) => {
+                                debug!("--> cannot relate self-types {:?}", err);
+                                return ProbeResult::NoMatch;
+                            }
+                        },
                     }
                     let obligation = traits::Obligation::new(
                         self.tcx,
@@ -1536,15 +1535,8 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                     (xform_self_ty, xform_ret_ty) =
                         self.xform_self_ty(probe.item, trait_ref.self_ty(), trait_ref.args);
                     xform_self_ty = ocx.normalize(cause, self.param_env, xform_self_ty);
-                    // FIXME: Make this `ocx.sup` once we define opaques more eagerly.
-                    match self.at(cause, self.param_env).sup(
-                        DefineOpaqueTypes::No,
-                        xform_self_ty,
-                        self_ty,
-                    ) {
-                        Ok(infer_ok) => {
-                            ocx.register_infer_ok_obligations(infer_ok);
-                        }
+                    match ocx.sup(cause, self.param_env, xform_self_ty, self_ty) {
+                        Ok(()) => {}
                         Err(err) => {
                             debug!("--> cannot relate self-types {:?}", err);
                             return ProbeResult::NoMatch;
@@ -1665,6 +1657,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
     /// Similarly to `probe_for_return_type`, this method attempts to find the best matching
     /// candidate method where the method name may have been misspelled. Similarly to other
     /// edit distance based suggestions, we provide at most one such suggestion.
+    #[instrument(level = "debug", skip(self))]
     pub(crate) fn probe_for_similar_candidate(
         &mut self,
     ) -> Result<Option<ty::AssocItem>, MethodError<'tcx>> {
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 803d1554324..abbfe452f5f 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -33,7 +33,7 @@ use rustc_middle::ty::IsSuggestable;
 use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::def_id::DefIdSet;
 use rustc_span::symbol::{kw, sym, Ident};
-use rustc_span::{edit_distance, ExpnKind, FileName, MacroKind, Span};
+use rustc_span::{edit_distance, ErrorGuaranteed, ExpnKind, FileName, MacroKind, Span};
 use rustc_span::{Symbol, DUMMY_SP};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedNote;
@@ -192,7 +192,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         error: MethodError<'tcx>,
         expected: Expectation<'tcx>,
         trait_missing_method: bool,
-    ) -> Option<Diag<'_>> {
+    ) -> ErrorGuaranteed {
         let (span, sugg_span, source, item_name, args) = match self.tcx.hir_node(call_id) {
             hir::Node::Expr(&hir::Expr {
                 kind: hir::ExprKind::MethodCall(segment, rcvr, args, _),
@@ -226,8 +226,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
 
         // Avoid suggestions when we don't know what's going on.
-        if rcvr_ty.references_error() {
-            return None;
+        if let Err(guar) = rcvr_ty.error_reported() {
+            return guar;
         }
 
         match error {
@@ -265,7 +265,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     &mut sources,
                     Some(sugg_span),
                 );
-                err.emit();
+                return err.emit();
             }
 
             MethodError::PrivateMatch(kind, def_id, out_of_scope_traits) => {
@@ -286,7 +286,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .unwrap_or_else(|| self.tcx.def_span(def_id));
                 err.span_label(sp, format!("private {kind} defined here"));
                 self.suggest_valid_traits(&mut err, item_name, out_of_scope_traits, true);
-                err.emit();
+                return err.emit();
             }
 
             MethodError::IllegalSizedBound { candidates, needs_mut, bound_span, self_expr } => {
@@ -343,12 +343,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         }
                     }
                 }
-                err.emit();
+                return err.emit();
             }
 
             MethodError::BadReturnType => bug!("no return type expectations but got BadReturnType"),
         }
-        None
     }
 
     fn suggest_missing_writer(&self, rcvr_ty: Ty<'tcx>, rcvr_expr: &hir::Expr<'tcx>) -> Diag<'_> {
@@ -564,7 +563,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    pub fn report_no_match_method_error(
+    fn report_no_match_method_error(
         &self,
         mut span: Span,
         rcvr_ty: Ty<'tcx>,
@@ -576,7 +575,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         no_match_data: &mut NoMatchData<'tcx>,
         expected: Expectation<'tcx>,
         trait_missing_method: bool,
-    ) -> Option<Diag<'_>> {
+    ) -> ErrorGuaranteed {
         let mode = no_match_data.mode;
         let tcx = self.tcx;
         let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty);
@@ -608,14 +607,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // We could pass the file for long types into these two, but it isn't strictly necessary
         // given how targeted they are.
-        if self.suggest_wrapping_range_with_parens(
+        if let Err(guar) = self.report_failed_method_call_on_range_end(
             tcx,
             rcvr_ty,
             source,
             span,
             item_name,
             &short_ty_str,
-        ) || self.suggest_constraining_numerical_ty(
+        ) {
+            return guar;
+        }
+        if let Err(guar) = self.report_failed_method_call_on_numerical_infer_var(
             tcx,
             rcvr_ty,
             source,
@@ -624,7 +626,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             item_name,
             &short_ty_str,
         ) {
-            return None;
+            return guar;
         }
         span = item_name.span;
 
@@ -881,7 +883,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 vec![(span.shrink_to_lo(), format!("into_iter()."))],
                 Applicability::MaybeIncorrect,
             );
-            return Some(err);
+            return err.emit();
         } else if !unsatisfied_predicates.is_empty() && matches!(rcvr_ty.kind(), ty::Param(_)) {
             // We special case the situation where we are looking for `_` in
             // `<TypeParam as _>::method` because otherwise the machinery will look for blanket
@@ -1100,7 +1102,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             unsatisfied_predicates.iter().any(|(pred, _, _)| {
                                 match pred.kind().skip_binder() {
                                     ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
-                                        Some(pred.def_id()) == self.tcx.lang_items().sized_trait()
+                                        self.tcx.is_lang_item(pred.def_id(), LangItem::Sized)
                                             && pred.polarity == ty::PredicatePolarity::Positive
                                     }
                                     _ => false,
@@ -1373,10 +1375,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         ty.is_str()
                             || matches!(
                                 ty.kind(),
-                                ty::Adt(adt, _) if Some(adt.did()) == self.tcx.lang_items().string()
+                                ty::Adt(adt, _) if self.tcx.is_lang_item(adt.did(), LangItem::String)
                             )
                     }
-                    ty::Adt(adt, _) => Some(adt.did()) == self.tcx.lang_items().string(),
+                    ty::Adt(adt, _) => self.tcx.is_lang_item(adt.did(), LangItem::String),
                     _ => false,
                 };
                 if is_string_or_ref_str && item_name.name == sym::iter {
@@ -1606,7 +1608,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_name, expected);
-        Some(err)
+        err.emit()
     }
 
     /// If an appropriate error source is not found, check method chain for possible candidates
@@ -2251,7 +2253,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     /// Suggest possible range with adding parentheses, for example:
     /// when encountering `0..1.map(|i| i + 1)` suggest `(0..1).map(|i| i + 1)`.
-    fn suggest_wrapping_range_with_parens(
+    fn report_failed_method_call_on_range_end(
         &self,
         tcx: TyCtxt<'tcx>,
         actual: Ty<'tcx>,
@@ -2259,7 +2261,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         span: Span,
         item_name: Ident,
         ty_str: &str,
-    ) -> bool {
+    ) -> Result<(), ErrorGuaranteed> {
         if let SelfSource::MethodCall(expr) = source {
             for (_, parent) in tcx.hir().parent_iter(expr.hir_id).take(5) {
                 if let Node::Expr(parent_expr) = parent {
@@ -2316,7 +2318,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     );
                     if pick.is_ok() {
                         let range_span = parent_expr.span.with_hi(expr.span.hi());
-                        tcx.dcx().emit_err(errors::MissingParenthesesInRange {
+                        return Err(tcx.dcx().emit_err(errors::MissingParenthesesInRange {
                             span,
                             ty_str: ty_str.to_string(),
                             method_name: item_name.as_str().to_string(),
@@ -2325,16 +2327,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 left: range_span.shrink_to_lo(),
                                 right: range_span.shrink_to_hi(),
                             }),
-                        });
-                        return true;
+                        }));
                     }
                 }
             }
         }
-        false
+        Ok(())
     }
 
-    fn suggest_constraining_numerical_ty(
+    fn report_failed_method_call_on_numerical_infer_var(
         &self,
         tcx: TyCtxt<'tcx>,
         actual: Ty<'tcx>,
@@ -2343,7 +2344,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         item_kind: &str,
         item_name: Ident,
         ty_str: &str,
-    ) -> bool {
+    ) -> Result<(), ErrorGuaranteed> {
         let found_candidate = all_traits(self.tcx)
             .into_iter()
             .any(|info| self.associated_value(info.def_id, item_name).is_some());
@@ -2447,10 +2448,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
                 _ => {}
             }
-            err.emit();
-            return true;
+            return Err(err.emit());
         }
-        false
+        Ok(())
     }
 
     /// For code `rect::area(...)`,
@@ -2723,7 +2723,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                 if tcx.is_diagnostic_item(sym::LocalKey, inner_id) {
                     err.help("use `with` or `try_with` to access thread local storage");
-                } else if Some(kind.did()) == tcx.lang_items().maybe_uninit() {
+                } else if tcx.is_lang_item(kind.did(), LangItem::MaybeUninit) {
                     err.help(format!(
                         "if this `{name}` has been initialized, \
                         use one of the `assume_init` methods to access the inner value"
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 9476dc70483..aaf3d3ec34d 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -7,7 +7,7 @@ use rustc_errors::{
 };
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::pat_util::EnumerateAndAdjustIterator;
-use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability, Pat, PatKind};
+use rustc_hir::{self as hir, BindingMode, ByRef, HirId, LangItem, Mutability, Pat, PatKind};
 use rustc_infer::infer;
 use rustc_middle::mir::interpret::ErrorHandled;
 use rustc_middle::ty::{self, Ty, TypeVisitableExt};
@@ -105,15 +105,16 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
         ti: &TopInfo<'tcx>,
-    ) -> Option<Diag<'tcx>> {
-        let mut diag =
-            self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)?;
-        if let Some(expr) = ti.origin_expr {
-            self.suggest_fn_call(&mut diag, expr, expected, |output| {
-                self.can_eq(self.param_env, output, actual)
-            });
-        }
-        Some(diag)
+    ) -> Result<(), Diag<'tcx>> {
+        self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)
+            .map_err(|mut diag| {
+                if let Some(expr) = ti.origin_expr {
+                    self.suggest_fn_call(&mut diag, expr, expected, |output| {
+                        self.can_eq(self.param_env, output, actual)
+                    });
+                }
+                diag
+            })
     }
 
     fn demand_eqtype_pat(
@@ -122,10 +123,8 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
         ti: &TopInfo<'tcx>,
-    ) {
-        if let Some(err) = self.demand_eqtype_pat_diag(cause_span, expected, actual, ti) {
-            err.emit();
-        }
+    ) -> Result<(), ErrorGuaranteed> {
+        self.demand_eqtype_pat_diag(cause_span, expected, actual, ti).map_err(|err| err.emit())
     }
 }
 
@@ -492,7 +491,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let tcx = self.tcx;
             let expected = self.resolve_vars_if_possible(expected);
             pat_ty = match expected.kind() {
-                ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().string() => expected,
+                ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::String) => expected,
                 ty::Str => Ty::new_static_str(tcx),
                 _ => pat_ty,
             };
@@ -509,7 +508,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         //
         // then that's equivalent to there existing a LUB.
         let cause = self.pattern_cause(ti, span);
-        if let Some(err) = self.demand_suptype_with_origin(&cause, expected, pat_ty) {
+        if let Err(err) = self.demand_suptype_with_origin(&cause, expected, pat_ty) {
             err.emit_unless(
                 ti.span
                     .filter(|&s| {
@@ -562,7 +561,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // Subtyping doesn't matter here, as the value is some kind of scalar.
         let demand_eqtype = |x: &mut _, y| {
             if let Some((ref mut fail, x_ty, x_span)) = *x
-                && let Some(mut err) = self.demand_eqtype_pat_diag(x_span, expected, x_ty, ti)
+                && let Err(mut err) = self.demand_eqtype_pat_diag(x_span, expected, x_ty, ti)
             {
                 if let Some((_, y_ty, y_span)) = y {
                     self.endpoint_has_type(&mut err, y_span, y_ty);
@@ -736,7 +735,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // Otherwise, the type of x is the expected type `T`.
             ByRef::No => expected, // As above, `T <: typeof(x)` is required, but we use equality, see (note_1).
         };
-        self.demand_eqtype_pat(pat.span, eq_ty, local_ty, ti);
+
+        // We have a concrete type for the local, so we do not need to taint it and hide follow up errors *using* the local.
+        let _ = self.demand_eqtype_pat(pat.span, eq_ty, local_ty, ti);
 
         // If there are multiple arms, make sure they all agree on
         // what the type of the binding `x` ought to be.
@@ -763,7 +764,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         ti: &TopInfo<'tcx>,
     ) {
         let var_ty = self.local_ty(span, var_id);
-        if let Some(mut err) = self.demand_eqtype_pat_diag(span, var_ty, ty, ti) {
+        if let Err(mut err) = self.demand_eqtype_pat_diag(span, var_ty, ty, ti) {
             let hir = self.tcx.hir();
             let var_ty = self.resolve_vars_if_possible(var_ty);
             let msg = format!("first introduced with type `{var_ty}` here");
@@ -986,13 +987,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
 
         // Type-check the path.
-        self.demand_eqtype_pat(pat.span, expected, pat_ty, pat_info.top_info);
+        let _ = self.demand_eqtype_pat(pat.span, expected, pat_ty, pat_info.top_info);
 
         // Type-check subpatterns.
-        if self.check_struct_pat_fields(pat_ty, pat, variant, fields, has_rest_pat, pat_info) {
-            pat_ty
-        } else {
-            Ty::new_misc_error(self.tcx)
+        match self.check_struct_pat_fields(pat_ty, pat, variant, fields, has_rest_pat, pat_info) {
+            Ok(()) => pat_ty,
+            Err(guar) => Ty::new_error(self.tcx, guar),
         }
     }
 
@@ -1050,7 +1050,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // Type-check the path.
         let (pat_ty, pat_res) =
             self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.span, pat.hir_id);
-        if let Some(err) =
+        if let Err(err) =
             self.demand_suptype_with_origin(&self.pattern_cause(ti, pat.span), expected, pat_ty)
         {
             self.emit_bad_pat_path(err, pat, res, pat_res, pat_ty, segments);
@@ -1223,12 +1223,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Type-check the tuple struct pattern against the expected type.
         let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, pat_info.top_info);
-        let had_err = if let Some(err) = diag {
-            err.emit();
-            true
-        } else {
-            false
-        };
+        let had_err = diag.map_err(|diag| diag.emit());
 
         // Type-check subpatterns.
         if subpats.len() == variant.fields.len()
@@ -1249,6 +1244,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     None,
                 );
             }
+            if let Err(e) = had_err {
+                on_error(e);
+                return Ty::new_error(tcx, e);
+            }
         } else {
             let e = self.emit_err_pat_wrong_number_of_fields(
                 pat.span,
@@ -1273,7 +1272,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         subpats: &'tcx [Pat<'tcx>],
         fields: &'tcx [ty::FieldDef],
         expected: Ty<'tcx>,
-        had_err: bool,
+        had_err: Result<(), ErrorGuaranteed>,
     ) -> ErrorGuaranteed {
         let subpats_ending = pluralize!(subpats.len());
         let fields_ending = pluralize!(fields.len());
@@ -1330,7 +1329,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // #67037: only do this if we could successfully type-check the expected type against
             // the tuple struct pattern. Otherwise the args could get out of range on e.g.,
             // `let P() = U;` where `P != U` with `struct P<T>(T);`.
-            (ty::Adt(_, args), [field], false) => {
+            (ty::Adt(_, args), [field], Ok(())) => {
                 let field_ty = self.field_ty(pat_span, field, args);
                 match field_ty.kind() {
                     ty::Tuple(fields) => fields.len() == subpats.len(),
@@ -1445,8 +1444,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let element_tys_iter = (0..max_len).map(|_| self.next_ty_var(span));
         let element_tys = tcx.mk_type_list_from_iter(element_tys_iter);
         let pat_ty = Ty::new_tup(tcx, element_tys);
-        if let Some(err) = self.demand_eqtype_pat_diag(span, expected, pat_ty, pat_info.top_info) {
-            let reported = err.emit();
+        if let Err(reported) = self.demand_eqtype_pat(span, expected, pat_ty, pat_info.top_info) {
             // Walk subpatterns with an expected type of `err` in this case to silence
             // further errors being emitted when using the bindings. #50333
             let element_tys_iter = (0..max_len).map(|_| Ty::new_error(tcx, reported));
@@ -1470,7 +1468,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         fields: &'tcx [hir::PatField<'tcx>],
         has_rest_pat: bool,
         pat_info: PatInfo<'tcx, '_>,
-    ) -> bool {
+    ) -> Result<(), ErrorGuaranteed> {
         let tcx = self.tcx;
 
         let ty::Adt(adt, args) = adt_ty.kind() else {
@@ -1486,7 +1484,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Keep track of which fields have already appeared in the pattern.
         let mut used_fields = FxHashMap::default();
-        let mut no_field_errors = true;
+        let mut result = Ok(());
 
         let mut inexistent_fields = vec![];
         // Typecheck each field.
@@ -1495,8 +1493,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let ident = tcx.adjust_ident(field.ident, variant.def_id);
             let field_ty = match used_fields.entry(ident) {
                 Occupied(occupied) => {
-                    no_field_errors = false;
                     let guar = self.error_field_already_bound(span, field.ident, *occupied.get());
+                    result = Err(guar);
                     Ty::new_error(tcx, guar)
                 }
                 Vacant(vacant) => {
@@ -1511,7 +1509,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         })
                         .unwrap_or_else(|| {
                             inexistent_fields.push(field);
-                            no_field_errors = false;
                             Ty::new_misc_error(tcx)
                         })
                 }
@@ -1585,37 +1582,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
         match (inexistent_fields_err, unmentioned_err) {
             (Some(i), Some(u)) => {
-                if let Some(e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) {
+                if let Err(e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) {
                     // We don't want to show the nonexistent fields error when this was
                     // `Foo { a, b }` when it should have been `Foo(a, b)`.
                     i.delay_as_bug();
                     u.delay_as_bug();
-                    e.emit();
+                    Err(e)
                 } else {
                     i.emit();
-                    u.emit();
+                    Err(u.emit())
                 }
             }
             (None, Some(u)) => {
-                if let Some(e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) {
+                if let Err(e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) {
                     u.delay_as_bug();
-                    e.emit();
+                    Err(e)
                 } else {
-                    u.emit();
+                    Err(u.emit())
                 }
             }
-            (Some(err), None) => {
-                err.emit();
-            }
-            (None, None)
-                if let Some(err) =
-                    self.error_tuple_variant_index_shorthand(variant, pat, fields) =>
-            {
-                err.emit();
+            (Some(err), None) => Err(err.emit()),
+            (None, None) => {
+                self.error_tuple_variant_index_shorthand(variant, pat, fields)?;
+                result
             }
-            (None, None) => {}
         }
-        no_field_errors
     }
 
     fn error_tuple_variant_index_shorthand(
@@ -1623,7 +1614,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         variant: &VariantDef,
         pat: &'_ Pat<'_>,
         fields: &[hir::PatField<'_>],
-    ) -> Option<Diag<'_>> {
+    ) -> Result<(), ErrorGuaranteed> {
         // if this is a tuple struct, then all field names will be numbers
         // so if any fields in a struct pattern use shorthand syntax, they will
         // be invalid identifiers (for example, Foo { 0, 1 }).
@@ -1645,10 +1636,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     format!("({})", self.get_suggested_tuple_struct_pattern(fields, variant)),
                     Applicability::MaybeIncorrect,
                 );
-                return Some(err);
+                return Err(err.emit());
             }
         }
-        None
+        Ok(())
     }
 
     fn error_foreign_non_exhaustive_spat(&self, pat: &Pat<'_>, descr: &str, no_fields: bool) {
@@ -1804,14 +1795,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         pat: &Pat<'_>,
         fields: &'tcx [hir::PatField<'tcx>],
         variant: &ty::VariantDef,
-    ) -> Option<Diag<'tcx>> {
+    ) -> Result<(), ErrorGuaranteed> {
         if let (Some(CtorKind::Fn), PatKind::Struct(qpath, pattern_fields, ..)) =
             (variant.ctor_kind(), &pat.kind)
         {
             let is_tuple_struct_match = !pattern_fields.is_empty()
                 && pattern_fields.iter().map(|field| field.ident.name.as_str()).all(is_number);
             if is_tuple_struct_match {
-                return None;
+                return Ok(());
             }
 
             let path = rustc_hir_pretty::qpath_to_string(&self.tcx, qpath);
@@ -1839,9 +1830,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 format!("({sugg})"),
                 appl,
             );
-            return Some(err);
+            return Err(err.emit());
         }
-        None
+        Ok(())
     }
 
     fn get_suggested_tuple_struct_pattern(
@@ -2065,20 +2056,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         pat_info: PatInfo<'tcx, '_>,
     ) -> Ty<'tcx> {
         let tcx = self.tcx;
-        let (box_ty, inner_ty) = match self.check_dereferenceable(span, expected, inner) {
-            Ok(()) => {
+        let (box_ty, inner_ty) = self
+            .check_dereferenceable(span, expected, inner)
+            .and_then(|()| {
                 // Here, `demand::subtype` is good enough, but I don't
                 // think any errors can be introduced by using `demand::eqtype`.
                 let inner_ty = self.next_ty_var(inner.span);
                 let box_ty = Ty::new_box(tcx, inner_ty);
-                self.demand_eqtype_pat(span, expected, box_ty, pat_info.top_info);
-                (box_ty, inner_ty)
-            }
-            Err(guar) => {
+                self.demand_eqtype_pat(span, expected, box_ty, pat_info.top_info)?;
+                Ok((box_ty, inner_ty))
+            })
+            .unwrap_or_else(|guar| {
                 let err = Ty::new_error(tcx, guar);
                 (err, err)
-            }
-        };
+            });
         self.check_pat(inner, inner_ty, pat_info);
         box_ty
     }
@@ -2222,7 +2213,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                         // Look for a case like `fn foo(&foo: u32)` and suggest
                         // `fn foo(foo: &u32)`
-                        if let Some(mut err) = err {
+                        if let Err(mut err) = err {
                             self.borrow_pat_suggestion(&mut err, pat);
                             err.emit();
                         }
@@ -2327,7 +2318,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 self.try_resolve_slice_ty_to_array_ty(before, slice, span)
             {
                 debug!(?resolved_arr_ty);
-                self.demand_eqtype(span, expected, resolved_arr_ty);
+                let _ = self.demand_eqtype(span, expected, resolved_arr_ty);
             }
         }
 
diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml
index 5136ab79a0f..c1565a7d40f 100644
--- a/compiler/rustc_infer/Cargo.toml
+++ b/compiler/rustc_infer/Cargo.toml
@@ -18,7 +18,6 @@ rustc_macros = { path = "../rustc_macros" }
 rustc_middle = { path = "../rustc_middle" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
-rustc_type_ir = { path = "../rustc_type_ir" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs
index 01bd732a4cd..fc7a22833ff 100644
--- a/compiler/rustc_infer/src/infer/at.rs
+++ b/compiler/rustc_infer/src/infer/at.rs
@@ -212,19 +212,63 @@ impl<'a, 'tcx> At<'a, 'tcx> {
         T: ToTrace<'tcx>,
     {
         match variance {
-            ty::Variance::Covariant => self.sub(define_opaque_types, expected, actual),
-            ty::Variance::Invariant => self.eq(define_opaque_types, expected, actual),
-            ty::Variance::Contravariant => self.sup(define_opaque_types, expected, actual),
+            ty::Covariant => self.sub(define_opaque_types, expected, actual),
+            ty::Invariant => self.eq(define_opaque_types, expected, actual),
+            ty::Contravariant => self.sup(define_opaque_types, expected, actual),
 
             // We could make this make sense but it's not readily
             // exposed and I don't feel like dealing with it. Note
             // that bivariance in general does a bit more than just
             // *nothing*, it checks that the types are the same
             // "modulo variance" basically.
-            ty::Variance::Bivariant => panic!("Bivariant given to `relate()`"),
+            ty::Bivariant => panic!("Bivariant given to `relate()`"),
         }
     }
 
+    /// Used in the new solver since we don't care about tracking an `ObligationCause`.
+    pub fn relate_no_trace<T>(
+        self,
+        expected: T,
+        variance: ty::Variance,
+        actual: T,
+    ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution>
+    where
+        T: Relate<TyCtxt<'tcx>>,
+    {
+        let mut fields = CombineFields::new(
+            self.infcx,
+            TypeTrace::dummy(self.cause),
+            self.param_env,
+            DefineOpaqueTypes::Yes,
+        );
+        fields.sub().relate_with_variance(
+            variance,
+            ty::VarianceDiagInfo::default(),
+            expected,
+            actual,
+        )?;
+        Ok(fields.goals)
+    }
+
+    /// Used in the new solver since we don't care about tracking an `ObligationCause`.
+    pub fn eq_structurally_relating_aliases_no_trace<T>(
+        self,
+        expected: T,
+        actual: T,
+    ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution>
+    where
+        T: Relate<TyCtxt<'tcx>>,
+    {
+        let mut fields = CombineFields::new(
+            self.infcx,
+            TypeTrace::dummy(self.cause),
+            self.param_env,
+            DefineOpaqueTypes::Yes,
+        );
+        fields.equate(StructurallyRelateAliases::Yes).relate(expected, actual)?;
+        Ok(fields.goals)
+    }
+
     /// Computes the least-upper-bound, or mutual supertype, of two
     /// values. The order of the arguments doesn't matter, but since
     /// this can result in an error (e.g., if asked to compute LUB of
@@ -303,7 +347,7 @@ impl<'tcx> ToTrace<'tcx> for Ty<'tcx> {
     ) -> TypeTrace<'tcx> {
         TypeTrace {
             cause: cause.clone(),
-            values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())),
+            values: ValuePairs::Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())),
         }
     }
 }
@@ -315,7 +359,10 @@ impl<'tcx> ToTrace<'tcx> for ty::Region<'tcx> {
         a: Self,
         b: Self,
     ) -> TypeTrace<'tcx> {
-        TypeTrace { cause: cause.clone(), values: Regions(ExpectedFound::new(a_is_expected, a, b)) }
+        TypeTrace {
+            cause: cause.clone(),
+            values: ValuePairs::Regions(ExpectedFound::new(a_is_expected, a, b)),
+        }
     }
 }
 
@@ -328,7 +375,7 @@ impl<'tcx> ToTrace<'tcx> for Const<'tcx> {
     ) -> TypeTrace<'tcx> {
         TypeTrace {
             cause: cause.clone(),
-            values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())),
+            values: ValuePairs::Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())),
         }
     }
 }
@@ -344,13 +391,13 @@ impl<'tcx> ToTrace<'tcx> for ty::GenericArg<'tcx> {
             cause: cause.clone(),
             values: match (a.unpack(), b.unpack()) {
                 (GenericArgKind::Lifetime(a), GenericArgKind::Lifetime(b)) => {
-                    Regions(ExpectedFound::new(a_is_expected, a, b))
+                    ValuePairs::Regions(ExpectedFound::new(a_is_expected, a, b))
                 }
                 (GenericArgKind::Type(a), GenericArgKind::Type(b)) => {
-                    Terms(ExpectedFound::new(a_is_expected, a.into(), b.into()))
+                    ValuePairs::Terms(ExpectedFound::new(a_is_expected, a.into(), b.into()))
                 }
                 (GenericArgKind::Const(a), GenericArgKind::Const(b)) => {
-                    Terms(ExpectedFound::new(a_is_expected, a.into(), b.into()))
+                    ValuePairs::Terms(ExpectedFound::new(a_is_expected, a.into(), b.into()))
                 }
 
                 (
@@ -379,7 +426,10 @@ impl<'tcx> ToTrace<'tcx> for ty::Term<'tcx> {
         a: Self,
         b: Self,
     ) -> TypeTrace<'tcx> {
-        TypeTrace { cause: cause.clone(), values: Terms(ExpectedFound::new(a_is_expected, a, b)) }
+        TypeTrace {
+            cause: cause.clone(),
+            values: ValuePairs::Terms(ExpectedFound::new(a_is_expected, a, b)),
+        }
     }
 }
 
@@ -392,7 +442,7 @@ impl<'tcx> ToTrace<'tcx> for ty::TraitRef<'tcx> {
     ) -> TypeTrace<'tcx> {
         TypeTrace {
             cause: cause.clone(),
-            values: TraitRefs(ExpectedFound::new(a_is_expected, a, b)),
+            values: ValuePairs::TraitRefs(ExpectedFound::new(a_is_expected, a, b)),
         }
     }
 }
@@ -406,7 +456,7 @@ impl<'tcx> ToTrace<'tcx> for ty::AliasTy<'tcx> {
     ) -> TypeTrace<'tcx> {
         TypeTrace {
             cause: cause.clone(),
-            values: Aliases(ExpectedFound::new(a_is_expected, a.into(), b.into())),
+            values: ValuePairs::Aliases(ExpectedFound::new(a_is_expected, a.into(), b.into())),
         }
     }
 }
@@ -418,7 +468,10 @@ impl<'tcx> ToTrace<'tcx> for ty::AliasTerm<'tcx> {
         a: Self,
         b: Self,
     ) -> TypeTrace<'tcx> {
-        TypeTrace { cause: cause.clone(), values: Aliases(ExpectedFound::new(a_is_expected, a, b)) }
+        TypeTrace {
+            cause: cause.clone(),
+            values: ValuePairs::Aliases(ExpectedFound::new(a_is_expected, a, b)),
+        }
     }
 }
 
@@ -431,7 +484,7 @@ impl<'tcx> ToTrace<'tcx> for ty::FnSig<'tcx> {
     ) -> TypeTrace<'tcx> {
         TypeTrace {
             cause: cause.clone(),
-            values: PolySigs(ExpectedFound::new(
+            values: ValuePairs::PolySigs(ExpectedFound::new(
                 a_is_expected,
                 ty::Binder::dummy(a),
                 ty::Binder::dummy(b),
@@ -449,7 +502,7 @@ impl<'tcx> ToTrace<'tcx> for ty::PolyFnSig<'tcx> {
     ) -> TypeTrace<'tcx> {
         TypeTrace {
             cause: cause.clone(),
-            values: PolySigs(ExpectedFound::new(a_is_expected, a, b)),
+            values: ValuePairs::PolySigs(ExpectedFound::new(a_is_expected, a, b)),
         }
     }
 }
@@ -463,7 +516,7 @@ impl<'tcx> ToTrace<'tcx> for ty::PolyExistentialTraitRef<'tcx> {
     ) -> TypeTrace<'tcx> {
         TypeTrace {
             cause: cause.clone(),
-            values: ExistentialTraitRef(ExpectedFound::new(a_is_expected, a, b)),
+            values: ValuePairs::ExistentialTraitRef(ExpectedFound::new(a_is_expected, a, b)),
         }
     }
 }
@@ -477,7 +530,7 @@ impl<'tcx> ToTrace<'tcx> for ty::PolyExistentialProjection<'tcx> {
     ) -> TypeTrace<'tcx> {
         TypeTrace {
             cause: cause.clone(),
-            values: ExistentialProjection(ExpectedFound::new(a_is_expected, a, b)),
+            values: ValuePairs::ExistentialProjection(ExpectedFound::new(a_is_expected, a, b)),
         }
     }
 }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index ed483c6cbeb..e15866f3f0f 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -64,11 +64,11 @@ use rustc_errors::{
     codes::*, pluralize, struct_span_code_err, Applicability, Diag, DiagCtxt, DiagStyledString,
     ErrorGuaranteed, IntoDiagArg, StringPart,
 };
-use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::lang_items::LangItem;
+use rustc_hir::{self as hir, ParamName};
 use rustc_macros::extension;
 use rustc_middle::bug;
 use rustc_middle::dep_graph::DepContext;
@@ -1707,6 +1707,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     ValuePairs::ExistentialProjection(_) => {
                         (false, Mismatch::Fixed("existential projection"))
                     }
+                    ValuePairs::Dummy => {
+                        bug!("do not expect to report a type error from a ValuePairs::Dummy")
+                    }
                 };
                 let Some(vals) = self.values_str(values) else {
                     // Derived error. Cancel the emitter.
@@ -2250,12 +2253,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         values: ValuePairs<'tcx>,
     ) -> Option<(DiagStyledString, DiagStyledString, Option<PathBuf>)> {
         match values {
-            infer::Regions(exp_found) => self.expected_found_str(exp_found),
-            infer::Terms(exp_found) => self.expected_found_str_term(exp_found),
-            infer::Aliases(exp_found) => self.expected_found_str(exp_found),
-            infer::ExistentialTraitRef(exp_found) => self.expected_found_str(exp_found),
-            infer::ExistentialProjection(exp_found) => self.expected_found_str(exp_found),
-            infer::TraitRefs(exp_found) => {
+            ValuePairs::Regions(exp_found) => self.expected_found_str(exp_found),
+            ValuePairs::Terms(exp_found) => self.expected_found_str_term(exp_found),
+            ValuePairs::Aliases(exp_found) => self.expected_found_str(exp_found),
+            ValuePairs::ExistentialTraitRef(exp_found) => self.expected_found_str(exp_found),
+            ValuePairs::ExistentialProjection(exp_found) => self.expected_found_str(exp_found),
+            ValuePairs::TraitRefs(exp_found) => {
                 let pretty_exp_found = ty::error::ExpectedFound {
                     expected: exp_found.expected.print_trait_sugared(),
                     found: exp_found.found.print_trait_sugared(),
@@ -2267,7 +2270,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     ret => ret,
                 }
             }
-            infer::PolySigs(exp_found) => {
+            ValuePairs::PolySigs(exp_found) => {
                 let exp_found = self.resolve_vars_if_possible(exp_found);
                 if exp_found.references_error() {
                     return None;
@@ -2275,6 +2278,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 let (exp, fnd) = self.cmp_fn_sig(&exp_found.expected, &exp_found.found);
                 Some((exp, fnd, None))
             }
+            ValuePairs::Dummy => {
+                bug!("do not expect to report a type error from a ValuePairs::Dummy")
+            }
         }
     }
 
@@ -2423,7 +2429,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             let (type_scope, type_param_sugg_span) = match bound_kind {
                 GenericKind::Param(param) => {
                     let generics = self.tcx.generics_of(generic_param_scope);
-                    let def_id = generics.type_param(param, self.tcx).def_id.expect_local();
+                    let type_param = generics.type_param(param, self.tcx);
+                    let def_id = type_param.def_id.expect_local();
                     let scope = self.tcx.local_def_id_to_hir_id(def_id).owner.def_id;
                     // Get the `hir::Param` to verify whether it already has any bounds.
                     // We do this to avoid suggesting code that ends up as `T: 'a'b`,
@@ -2433,7 +2440,18 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         Some((span, open_paren_sp)) => Some((span, true, open_paren_sp)),
                         // If `param` corresponds to `Self`, no usable suggestion span.
                         None if generics.has_self && param.index == 0 => None,
-                        None => Some((self.tcx.def_span(def_id).shrink_to_hi(), false, None)),
+                        None => {
+                            let span = if let Some(param) =
+                                hir_generics.params.iter().find(|param| param.def_id == def_id)
+                                && let ParamName::Plain(ident) = param.name
+                            {
+                                ident.span.shrink_to_hi()
+                            } else {
+                                let span = self.tcx.def_span(def_id);
+                                span.shrink_to_hi()
+                            };
+                            Some((span, false, None))
+                        }
                     };
                     (scope, sugg_span)
                 }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
index effb4090692..bc59b5e033b 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
@@ -546,7 +546,7 @@ impl<T> Trait<T> for X {
         for pred in hir_generics.bounds_for_param(def_id) {
             if self.constrain_generic_bound_associated_type_structured_suggestion(
                 diag,
-                &trait_ref,
+                trait_ref,
                 pred.bounds,
                 assoc,
                 assoc_args,
@@ -715,7 +715,7 @@ fn foo(&self) -> Self::T { String::new() }
 
             self.constrain_generic_bound_associated_type_structured_suggestion(
                 diag,
-                &trait_ref,
+                trait_ref,
                 opaque_hir_ty.bounds,
                 assoc,
                 assoc_args,
@@ -869,7 +869,7 @@ fn foo(&self) -> Self::T { String::new() }
     fn constrain_generic_bound_associated_type_structured_suggestion(
         &self,
         diag: &mut Diag<'_>,
-        trait_ref: &ty::TraitRef<'tcx>,
+        trait_ref: ty::TraitRef<'tcx>,
         bounds: hir::GenericBounds<'_>,
         assoc: ty::AssocItem,
         assoc_args: &[ty::GenericArg<'tcx>],
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 32b50053b50..9447ea06fdd 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -9,9 +9,8 @@ pub use rustc_middle::ty::IntVarValue;
 pub use BoundRegionConversionTime::*;
 pub use RegionVariableOrigin::*;
 pub use SubregionOrigin::*;
-pub use ValuePairs::*;
 
-use crate::infer::relate::RelateResult;
+use crate::infer::relate::{Relate, RelateResult};
 use crate::traits::{self, ObligationCause, ObligationInspector, PredicateObligation, TraitEngine};
 use error_reporting::TypeErrCtxt;
 use free_regions::RegionRelations;
@@ -35,6 +34,7 @@ use rustc_middle::infer::unify_key::{ConstVidKey, EffectVidKey};
 use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult};
 use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::traits::select;
+use rustc_middle::traits::solve::{Goal, NoSolution};
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::fold::BoundVarReplacerDelegate;
 use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
@@ -44,7 +44,7 @@ use rustc_middle::ty::{ConstVid, EffectVid, FloatVid, IntVid, TyVid};
 use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgs, GenericArgsRef};
 use rustc_middle::{bug, span_bug};
 use rustc_span::symbol::Symbol;
-use rustc_span::Span;
+use rustc_span::{Span, DUMMY_SP};
 use snapshot::undo_log::InferCtxtUndoLogs;
 use std::cell::{Cell, RefCell};
 use std::fmt;
@@ -352,27 +352,27 @@ impl<'tcx> ty::InferCtxtLike for InferCtxt<'tcx> {
         }
     }
 
-    fn universe_of_ct(&self, ct: ConstVid) -> Option<ty::UniverseIndex> {
-        // Same issue as with `universe_of_ty`
-        match self.probe_const_var(ct) {
+    fn universe_of_lt(&self, lt: ty::RegionVid) -> Option<ty::UniverseIndex> {
+        match self.inner.borrow_mut().unwrap_region_constraints().probe_value(lt) {
             Err(universe) => Some(universe),
             Ok(_) => None,
         }
     }
 
-    fn universe_of_lt(&self, lt: ty::RegionVid) -> Option<ty::UniverseIndex> {
-        match self.inner.borrow_mut().unwrap_region_constraints().probe_value(lt) {
+    fn universe_of_ct(&self, ct: ConstVid) -> Option<ty::UniverseIndex> {
+        // Same issue as with `universe_of_ty`
+        match self.probe_const_var(ct) {
             Err(universe) => Some(universe),
             Ok(_) => None,
         }
     }
 
-    fn opportunistic_resolve_lt_var(&self, vid: ty::RegionVid) -> ty::Region<'tcx> {
-        self.inner.borrow_mut().unwrap_region_constraints().opportunistic_resolve_var(self.tcx, vid)
+    fn root_ty_var(&self, var: TyVid) -> TyVid {
+        self.root_var(var)
     }
 
-    fn defining_opaque_types(&self) -> &'tcx ty::List<LocalDefId> {
-        self.defining_opaque_types
+    fn root_const_var(&self, var: ConstVid) -> ConstVid {
+        self.root_const_var(var)
     }
 
     fn opportunistic_resolve_ty_var(&self, vid: TyVid) -> Ty<'tcx> {
@@ -405,6 +405,76 @@ impl<'tcx> ty::InferCtxtLike for InferCtxt<'tcx> {
             }
         }
     }
+
+    fn opportunistic_resolve_lt_var(&self, vid: ty::RegionVid) -> ty::Region<'tcx> {
+        self.inner.borrow_mut().unwrap_region_constraints().opportunistic_resolve_var(self.tcx, vid)
+    }
+
+    fn defining_opaque_types(&self) -> &'tcx ty::List<LocalDefId> {
+        self.defining_opaque_types
+    }
+
+    fn next_ty_infer(&self) -> Ty<'tcx> {
+        self.next_ty_var(DUMMY_SP)
+    }
+
+    fn next_const_infer(&self) -> ty::Const<'tcx> {
+        self.next_const_var(DUMMY_SP)
+    }
+
+    fn fresh_args_for_item(&self, def_id: DefId) -> ty::GenericArgsRef<'tcx> {
+        self.fresh_args_for_item(DUMMY_SP, def_id)
+    }
+
+    fn instantiate_binder_with_infer<T: TypeFoldable<Self::Interner> + Copy>(
+        &self,
+        value: ty::Binder<'tcx, T>,
+    ) -> T {
+        self.instantiate_binder_with_fresh_vars(
+            DUMMY_SP,
+            BoundRegionConversionTime::HigherRankedType,
+            value,
+        )
+    }
+
+    fn enter_forall<T: TypeFoldable<TyCtxt<'tcx>> + Copy, U>(
+        &self,
+        value: ty::Binder<'tcx, T>,
+        f: impl FnOnce(T) -> U,
+    ) -> U {
+        self.enter_forall(value, f)
+    }
+
+    fn relate<T: Relate<TyCtxt<'tcx>>>(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        lhs: T,
+        variance: ty::Variance,
+        rhs: T,
+    ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> {
+        self.at(&ObligationCause::dummy(), param_env).relate_no_trace(lhs, variance, rhs)
+    }
+
+    fn eq_structurally_relating_aliases<T: Relate<TyCtxt<'tcx>>>(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        lhs: T,
+        rhs: T,
+    ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> {
+        self.at(&ObligationCause::dummy(), param_env)
+            .eq_structurally_relating_aliases_no_trace(lhs, rhs)
+    }
+
+    fn resolve_vars_if_possible<T>(&self, value: T) -> T
+    where
+        T: TypeFoldable<TyCtxt<'tcx>>,
+    {
+        self.resolve_vars_if_possible(value)
+    }
+
+    fn probe<T>(&self, probe: impl FnOnce() -> T) -> T {
+        self.probe(|_| probe())
+    }
 }
 
 /// See the `error_reporting` module for more details.
@@ -417,6 +487,7 @@ pub enum ValuePairs<'tcx> {
     PolySigs(ExpectedFound<ty::PolyFnSig<'tcx>>),
     ExistentialTraitRef(ExpectedFound<ty::PolyExistentialTraitRef<'tcx>>),
     ExistentialProjection(ExpectedFound<ty::PolyExistentialProjection<'tcx>>),
+    Dummy,
 }
 
 impl<'tcx> ValuePairs<'tcx> {
@@ -1812,7 +1883,7 @@ impl<'tcx> TypeTrace<'tcx> {
     ) -> TypeTrace<'tcx> {
         TypeTrace {
             cause: cause.clone(),
-            values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())),
+            values: ValuePairs::Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())),
         }
     }
 
@@ -1824,7 +1895,7 @@ impl<'tcx> TypeTrace<'tcx> {
     ) -> TypeTrace<'tcx> {
         TypeTrace {
             cause: cause.clone(),
-            values: TraitRefs(ExpectedFound::new(a_is_expected, a, b)),
+            values: ValuePairs::TraitRefs(ExpectedFound::new(a_is_expected, a, b)),
         }
     }
 
@@ -1836,9 +1907,13 @@ impl<'tcx> TypeTrace<'tcx> {
     ) -> TypeTrace<'tcx> {
         TypeTrace {
             cause: cause.clone(),
-            values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())),
+            values: ValuePairs::Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())),
         }
     }
+
+    fn dummy(cause: &ObligationCause<'tcx>) -> TypeTrace<'tcx> {
+        TypeTrace { cause: cause.clone(), values: ValuePairs::Dummy }
+    }
 }
 
 impl<'tcx> SubregionOrigin<'tcx> {
diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs
index 7114b888718..b8dd501a721 100644
--- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs
@@ -345,7 +345,7 @@ impl<'tcx> InferCtxt<'tcx> {
                 .args
                 .iter()
                 .enumerate()
-                .filter(|(i, _)| variances[*i] == ty::Variance::Invariant)
+                .filter(|(i, _)| variances[*i] == ty::Invariant)
                 .filter_map(|(_, arg)| match arg.unpack() {
                     GenericArgKind::Lifetime(r) => Some(r),
                     GenericArgKind::Type(_) | GenericArgKind::Const(_) => None,
@@ -441,7 +441,7 @@ where
                 let variances = self.tcx.variances_of(*def_id);
 
                 for (v, s) in std::iter::zip(variances, args.iter()) {
-                    if *v != ty::Variance::Bivariant {
+                    if *v != ty::Bivariant {
                         s.visit_with(self);
                     }
                 }
diff --git a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs
index 488f435994d..c02ab98b2ba 100644
--- a/compiler/rustc_infer/src/infer/outlives/for_liveness.rs
+++ b/compiler/rustc_infer/src/infer/outlives/for_liveness.rs
@@ -102,9 +102,7 @@ where
                     };
 
                     for (idx, s) in args.iter().enumerate() {
-                        if variances.map(|variances| variances[idx])
-                            != Some(ty::Variance::Bivariant)
-                        {
+                        if variances.map(|variances| variances[idx]) != Some(ty::Bivariant) {
                             s.visit_with(self);
                         }
                     }
diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs
index 87a2f0b4580..d6e57d85387 100644
--- a/compiler/rustc_infer/src/infer/relate/generalize.rs
+++ b/compiler/rustc_infer/src/infer/relate/generalize.rs
@@ -83,16 +83,16 @@ impl<'tcx> InferCtxt<'tcx> {
             // mention `?0`.
             if self.next_trait_solver() {
                 let (lhs, rhs, direction) = match instantiation_variance {
-                    ty::Variance::Invariant => {
+                    ty::Invariant => {
                         (generalized_ty.into(), source_ty.into(), AliasRelationDirection::Equate)
                     }
-                    ty::Variance::Covariant => {
+                    ty::Covariant => {
                         (generalized_ty.into(), source_ty.into(), AliasRelationDirection::Subtype)
                     }
-                    ty::Variance::Contravariant => {
+                    ty::Contravariant => {
                         (source_ty.into(), generalized_ty.into(), AliasRelationDirection::Subtype)
                     }
-                    ty::Variance::Bivariant => unreachable!("bivariant generalization"),
+                    ty::Bivariant => unreachable!("bivariant generalization"),
                 };
 
                 relation.register_predicates([ty::PredicateKind::AliasRelate(lhs, rhs, direction)]);
@@ -192,7 +192,7 @@ impl<'tcx> InferCtxt<'tcx> {
                 relation.span(),
                 relation.structurally_relate_aliases(),
                 target_vid,
-                ty::Variance::Invariant,
+                ty::Invariant,
                 source_ct,
             )?;
 
@@ -210,14 +210,14 @@ impl<'tcx> InferCtxt<'tcx> {
         // generalized const and the source.
         if target_is_expected {
             relation.relate_with_variance(
-                ty::Variance::Invariant,
+                ty::Invariant,
                 ty::VarianceDiagInfo::default(),
                 generalized_ct,
                 source_ct,
             )?;
         } else {
             relation.relate_with_variance(
-                ty::Variance::Invariant,
+                ty::Invariant,
                 ty::VarianceDiagInfo::default(),
                 source_ct,
                 generalized_ct,
@@ -411,7 +411,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for Generalizer<'_, 'tcx> {
         a_arg: ty::GenericArgsRef<'tcx>,
         b_arg: ty::GenericArgsRef<'tcx>,
     ) -> RelateResult<'tcx, ty::GenericArgsRef<'tcx>> {
-        if self.ambient_variance == ty::Variance::Invariant {
+        if self.ambient_variance == ty::Invariant {
             // Avoid fetching the variance if we are in an invariant
             // context; no need, and it can induce dependency cycles
             // (e.g., #41849).
@@ -667,7 +667,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for Generalizer<'_, 'tcx> {
             // structural.
             ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => {
                 let args = self.relate_with_variance(
-                    ty::Variance::Invariant,
+                    ty::Invariant,
                     ty::VarianceDiagInfo::default(),
                     args,
                     args,
diff --git a/compiler/rustc_infer/src/infer/relate/glb.rs b/compiler/rustc_infer/src/infer/relate/glb.rs
index cc17e60a79b..067004ecaeb 100644
--- a/compiler/rustc_infer/src/infer/relate/glb.rs
+++ b/compiler/rustc_infer/src/infer/relate/glb.rs
@@ -94,12 +94,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for Glb<'_, '_, 'tcx> {
             // When higher-ranked types are involved, computing the GLB is
             // very challenging, switch to invariance. This is obviously
             // overly conservative but works ok in practice.
-            self.relate_with_variance(
-                ty::Variance::Invariant,
-                ty::VarianceDiagInfo::default(),
-                a,
-                b,
-            )?;
+            self.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b)?;
             Ok(a)
         } else {
             Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?))
diff --git a/compiler/rustc_infer/src/infer/relate/lub.rs b/compiler/rustc_infer/src/infer/relate/lub.rs
index e9d300d349c..2184416b4cc 100644
--- a/compiler/rustc_infer/src/infer/relate/lub.rs
+++ b/compiler/rustc_infer/src/infer/relate/lub.rs
@@ -94,12 +94,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for Lub<'_, '_, 'tcx> {
             // When higher-ranked types are involved, computing the LUB is
             // very challenging, switch to invariance. This is obviously
             // overly conservative but works ok in practice.
-            self.relate_with_variance(
-                ty::Variance::Invariant,
-                ty::VarianceDiagInfo::default(),
-                a,
-                b,
-            )?;
+            self.relate_with_variance(ty::Invariant, ty::VarianceDiagInfo::default(), a, b)?;
             Ok(a)
         } else {
             Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?))
diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs
index e7b50479b85..41cc945492d 100644
--- a/compiler/rustc_infer/src/infer/relate/mod.rs
+++ b/compiler/rustc_infer/src/infer/relate/mod.rs
@@ -4,11 +4,9 @@
 
 pub use rustc_middle::ty::relate::*;
 
-pub use self::_match::MatchAgainstFreshVars;
 pub use self::combine::CombineFields;
 pub use self::combine::PredicateEmittingRelation;
 
-pub mod _match;
 pub(super) mod combine;
 mod generalize;
 mod glb;
diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs
index f7b2f11e3d7..f2bec9392d5 100644
--- a/compiler/rustc_infer/src/infer/relate/type_relating.rs
+++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs
@@ -42,7 +42,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for TypeRelating<'_, '_, 'tcx> {
         a_arg: ty::GenericArgsRef<'tcx>,
         b_arg: ty::GenericArgsRef<'tcx>,
     ) -> RelateResult<'tcx, ty::GenericArgsRef<'tcx>> {
-        if self.ambient_variance == ty::Variance::Invariant {
+        if self.ambient_variance == ty::Invariant {
             // Avoid fetching the variance if we are in an invariant
             // context; no need, and it can induce dependency cycles
             // (e.g., #41849).
@@ -325,23 +325,23 @@ impl<'tcx> PredicateEmittingRelation<'tcx> for TypeRelating<'_, '_, 'tcx> {
 
     fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
         self.register_predicates([ty::Binder::dummy(match self.ambient_variance {
-            ty::Variance::Covariant => ty::PredicateKind::AliasRelate(
+            ty::Covariant => ty::PredicateKind::AliasRelate(
                 a.into(),
                 b.into(),
                 ty::AliasRelationDirection::Subtype,
             ),
             // a :> b is b <: a
-            ty::Variance::Contravariant => ty::PredicateKind::AliasRelate(
+            ty::Contravariant => ty::PredicateKind::AliasRelate(
                 b.into(),
                 a.into(),
                 ty::AliasRelationDirection::Subtype,
             ),
-            ty::Variance::Invariant => ty::PredicateKind::AliasRelate(
+            ty::Invariant => ty::PredicateKind::AliasRelate(
                 a.into(),
                 b.into(),
                 ty::AliasRelationDirection::Equate,
             ),
-            ty::Variance::Bivariant => {
+            ty::Bivariant => {
                 unreachable!("Expected bivariance to be handled in relate_with_variance")
             }
         })]);
diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs
index cc12a4bf091..ab4148faaab 100644
--- a/compiler/rustc_infer/src/traits/util.rs
+++ b/compiler/rustc_infer/src/traits/util.rs
@@ -285,8 +285,7 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
                 let obligations =
                     predicates.predicates.iter().enumerate().map(|(index, &(clause, span))| {
                         elaboratable.child_with_derived_cause(
-                            clause
-                                .instantiate_supertrait(tcx, &bound_clause.rebind(data.trait_ref)),
+                            clause.instantiate_supertrait(tcx, bound_clause.rebind(data.trait_ref)),
                             span,
                             bound_clause.rebind(data),
                             index,
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 733c73bc3d0..043042a492b 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -550,6 +550,7 @@ lint_non_local_definitions_impl = non-local `impl` definition, `impl` blocks sho
     .bounds = `impl` may be usable in bounds, etc. from outside the expression, which might e.g. make something constructible that previously wasn't, because it's still on a publicly-visible type
     .exception = items in an anonymous const item (`const _: () = {"{"} ... {"}"}`) are treated as in the same scope as the anonymous const's declaration
     .const_anon = use a const-anon item to suppress this lint
+    .macro_to_change = the {$macro_kind} `{$macro_to_change}` defines the non-local `impl`, and may need to be changed
 
 lint_non_local_definitions_impl_move_help =
     move the `impl` block outside of this {$body_kind_descr} {$depth ->
diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs
index b671dfaa0d1..911975f6179 100644
--- a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs
+++ b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs
@@ -3,7 +3,7 @@ use crate::{
     LateContext, LateLintPass, LintContext,
 };
 
-use rustc_hir as hir;
+use rustc_hir::{self as hir, LangItem};
 use rustc_middle::ty;
 use rustc_session::lint::FutureIncompatibilityReason;
 use rustc_session::{declare_lint, declare_lint_pass};
@@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait {
             // the trait is a `Deref` implementation
             && let Some(trait_) = &impl_.of_trait
             && let Some(did) = trait_.trait_def_id()
-            && Some(did) == tcx.lang_items().deref_trait()
+            && tcx.is_lang_item(did, LangItem::Deref)
             // the self type is `dyn t_principal`
             && let self_ty = tcx.type_of(item.owner_id).instantiate_identity()
             && let ty::Dynamic(data, _, ty::Dyn) = self_ty.kind()
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index b377da31a58..8059f5c1a2e 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -1362,6 +1362,7 @@ pub enum NonLocalDefinitionsDiag {
         has_trait: bool,
         self_ty_str: String,
         of_trait_str: Option<String>,
+        macro_to_change: Option<(String, &'static str)>,
     },
     MacroRules {
         depth: u32,
@@ -1387,6 +1388,7 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag {
                 has_trait,
                 self_ty_str,
                 of_trait_str,
+                macro_to_change,
             } => {
                 diag.primary_message(fluent::lint_non_local_definitions_impl);
                 diag.arg("depth", depth);
@@ -1397,6 +1399,15 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag {
                     diag.arg("of_trait_str", of_trait_str);
                 }
 
+                if let Some((macro_to_change, macro_kind)) = macro_to_change {
+                    diag.arg("macro_to_change", macro_to_change);
+                    diag.arg("macro_kind", macro_kind);
+                    diag.note(fluent::lint_macro_to_change);
+                }
+                if let Some(cargo_update) = cargo_update {
+                    diag.subdiagnostic(&diag.dcx, cargo_update);
+                }
+
                 if has_trait {
                     diag.note(fluent::lint_bounds);
                     diag.note(fluent::lint_with_trait);
@@ -1422,9 +1433,6 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag {
                     );
                 }
 
-                if let Some(cargo_update) = cargo_update {
-                    diag.subdiagnostic(&diag.dcx, cargo_update);
-                }
                 if let Some(const_anon) = const_anon {
                     diag.note(fluent::lint_exception);
                     if let Some(const_anon) = const_anon {
diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs
index c9d67854112..2dc2a0efdf0 100644
--- a/compiler/rustc_lint/src/non_fmt_panic.rs
+++ b/compiler/rustc_lint/src/non_fmt_panic.rs
@@ -2,7 +2,7 @@ use crate::lints::{NonFmtPanicBraces, NonFmtPanicUnused};
 use crate::{fluent_generated as fluent, LateContext, LateLintPass, LintContext};
 use rustc_ast as ast;
 use rustc_errors::Applicability;
-use rustc_hir as hir;
+use rustc_hir::{self as hir, LangItem};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::bug;
 use rustc_middle::lint::in_external_macro;
@@ -53,8 +53,8 @@ impl<'tcx> LateLintPass<'tcx> for NonPanicFmt {
             if let &ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(f).kind() {
                 let f_diagnostic_name = cx.tcx.get_diagnostic_name(def_id);
 
-                if Some(def_id) == cx.tcx.lang_items().begin_panic_fn()
-                    || Some(def_id) == cx.tcx.lang_items().panic_fn()
+                if cx.tcx.is_lang_item(def_id, LangItem::BeginPanic)
+                    || cx.tcx.is_lang_item(def_id, LangItem::Panic)
                     || f_diagnostic_name == Some(sym::panic_str_2015)
                 {
                     if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id {
@@ -153,7 +153,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
                 ty::Ref(_, r, _) if r.is_str(),
             ) || matches!(
                 ty.ty_adt_def(),
-                Some(ty_def) if Some(ty_def.did()) == cx.tcx.lang_items().string(),
+                Some(ty_def) if cx.tcx.is_lang_item(ty_def.did(), LangItem::String),
             );
 
             let infcx = cx.tcx.infer_ctxt().build();
diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs
index d7ffc34d824..b0ec6e06658 100644
--- a/compiler/rustc_lint/src/non_local_def.rs
+++ b/compiler/rustc_lint/src/non_local_def.rs
@@ -258,6 +258,13 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
                     Some((cx.tcx.def_span(parent), may_move))
                 };
 
+                let macro_to_change =
+                    if let ExpnKind::Macro(kind, name) = item.span.ctxt().outer_expn_data().kind {
+                        Some((name.to_string(), kind.descr()))
+                    } else {
+                        None
+                    };
+
                 cx.emit_span_lint(
                     NON_LOCAL_DEFINITIONS,
                     ms,
@@ -274,6 +281,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
                         move_to,
                         may_remove,
                         has_trait: impl_.of_trait.is_some(),
+                        macro_to_change,
                     },
                 )
             }
diff --git a/compiler/rustc_lint/src/shadowed_into_iter.rs b/compiler/rustc_lint/src/shadowed_into_iter.rs
index 41ec84faa78..85006421fdd 100644
--- a/compiler/rustc_lint/src/shadowed_into_iter.rs
+++ b/compiler/rustc_lint/src/shadowed_into_iter.rs
@@ -49,10 +49,10 @@ declare_lint! {
     ///
     /// ### Explanation
     ///
-    /// Since Rust CURRENT_RUSTC_VERSION, boxed slices implement `IntoIterator`. However, to avoid
+    /// Since Rust 1.80.0, boxed slices implement `IntoIterator`. However, to avoid
     /// breakage, `boxed_slice.into_iter()` in Rust 2015, 2018, and 2021 code will still
     /// behave as `(&boxed_slice).into_iter()`, returning an iterator over
-    /// references, just like in Rust CURRENT_RUSTC_VERSION and earlier.
+    /// references, just like in Rust 1.80.0 and earlier.
     /// This only applies to the method call syntax `boxed_slice.into_iter()`, not to
     /// any other syntax such as `for _ in boxed_slice` or `IntoIterator::into_iter(boxed_slice)`.
     pub BOXED_SLICE_INTO_ITER,
diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs
index 4d1b1dc4fb7..6983e7abbd6 100644
--- a/compiler/rustc_lint/src/traits.rs
+++ b/compiler/rustc_lint/src/traits.rs
@@ -2,7 +2,7 @@ use crate::lints::{DropGlue, DropTraitConstraintsDiag};
 use crate::LateContext;
 use crate::LateLintPass;
 use crate::LintContext;
-use rustc_hir as hir;
+use rustc_hir::{self as hir, LangItem};
 use rustc_session::{declare_lint, declare_lint_pass};
 use rustc_span::symbol::sym;
 
@@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
                 continue;
             };
             let def_id = trait_predicate.trait_ref.def_id;
-            if cx.tcx.lang_items().drop_trait() == Some(def_id) {
+            if cx.tcx.is_lang_item(def_id, LangItem::Drop) {
                 // Explicitly allow `impl Drop`, a drop-guards-as-unnameable-type pattern.
                 if trait_predicate.trait_ref.self_ty().is_impl_trait() {
                     continue;
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index c8da9f179e7..7a1aa4043be 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -10,9 +10,9 @@ use rustc_ast as ast;
 use rustc_ast::util::{classify, parser};
 use rustc_ast::{ExprKind, StmtKind};
 use rustc_errors::{pluralize, MultiSpan};
-use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
+use rustc_hir::{self as hir, LangItem};
 use rustc_infer::traits::util::elaborate;
 use rustc_middle::ty::adjustment;
 use rustc_middle::ty::{self, Ty};
@@ -289,7 +289,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
                     is_ty_must_use(cx, boxed_ty, expr, span)
                         .map(|inner| MustUsePath::Boxed(Box::new(inner)))
                 }
-                ty::Adt(def, args) if cx.tcx.lang_items().pin_type() == Some(def.did()) => {
+                ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => {
                     let pinned_ty = args.type_at(0);
                     is_ty_must_use(cx, pinned_ty, expr, span)
                         .map(|inner| MustUsePath::Pinned(Box::new(inner)))
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 726bd0de129..a12c76037e7 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -34,6 +34,7 @@ declare_lint_pass! {
         CONST_EVALUATABLE_UNCHECKED,
         CONST_ITEM_MUTATION,
         DEAD_CODE,
+        DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK,
         DEPRECATED,
         DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
         DEPRECATED_IN_FUTURE,
@@ -3257,7 +3258,11 @@ declare_lint! {
     /// See the [Checking Conditional Configurations][check-cfg] section for more
     /// details.
     ///
+    /// See the [Cargo Specifics][unexpected_cfgs_lint_config] section for configuring this lint in
+    /// `Cargo.toml`.
+    ///
     /// [check-cfg]: https://doc.rust-lang.org/nightly/rustc/check-cfg.html
+    /// [unexpected_cfgs_lint_config]: https://doc.rust-lang.org/nightly/rustc/check-cfg/cargo-specifics.html#check-cfg-in-lintsrust-table
     pub UNEXPECTED_CFGS,
     Warn,
     "detects unexpected names and values in `#[cfg]` conditions",
@@ -4196,6 +4201,59 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `dependency_on_unit_never_type_fallback` lint detects cases where code compiles with
+    /// [never type fallback] being [`()`], but will stop compiling with fallback being [`!`].
+    ///
+    /// [never type fallback]: https://doc.rust-lang.org/nightly/core/primitive.never.html#never-type-fallback
+    /// [`!`]: https://doc.rust-lang.org/core/primitive.never.html
+    /// [`()`]: https://doc.rust-lang.org/core/primitive.unit.html
+    ///
+    /// ### Example
+    ///
+    /// ```rust,compile_fail
+    /// #![deny(dependency_on_unit_never_type_fallback)]
+    /// fn main() {
+    ///     if true {
+    ///         // return has type `!` which, is some cases, causes never type fallback
+    ///         return
+    ///     } else {
+    ///         // the type produced by this call is not specified explicitly,
+    ///         // so it will be inferred from the previous branch
+    ///         Default::default()
+    ///     };
+    ///     // depending on the fallback, this may compile (because `()` implements `Default`),
+    ///     // or it may not (because `!` does not implement `Default`)
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Due to historic reasons never type fallback was `()`, meaning that `!` got spontaneously
+    /// coerced to `()`. There are plans to change that, but they may make the code such as above
+    /// not compile. Instead of depending on the fallback, you should specify the type explicitly:
+    /// ```
+    /// if true {
+    ///     return
+    /// } else {
+    ///     // type is explicitly specified, fallback can't hurt us no more
+    ///     <() as Default>::default()
+    /// };
+    /// ```
+    ///
+    /// See [Tracking Issue for making `!` fall back to `!`](https://github.com/rust-lang/rust/issues/123748).
+    pub DEPENDENCY_ON_UNIT_NEVER_TYPE_FALLBACK,
+    Warn,
+    "never type fallback affecting unsafe function calls",
+    @future_incompatible = FutureIncompatibleInfo {
+        reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
+        reference: "issue #123748 <https://github.com/rust-lang/rust/issues/123748>",
+    };
+    report_in_external_macro
+}
+
+declare_lint! {
     /// The `byte_slice_in_packed_struct_with_derive` lint detects cases where a byte slice field
     /// (`[u8]`) or string slice field (`str`) is used in a `packed` struct that derives one or
     /// more built-in traits.
diff --git a/compiler/rustc_log/Cargo.toml b/compiler/rustc_log/Cargo.toml
index 3ff86f700a5..fe399bc77e3 100644
--- a/compiler/rustc_log/Cargo.toml
+++ b/compiler/rustc_log/Cargo.toml
@@ -8,7 +8,7 @@ edition = "2021"
 tracing = "0.1.28"
 tracing-core = "=0.1.30" # FIXME(Nilstrieb) tracing has a deadlock: https://github.com/tokio-rs/tracing/issues/2635
 tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] }
-tracing-tree = "0.3.0"
+tracing-tree = "0.3.1"
 # tidy-alphabetical-end
 
 [dev-dependencies]
diff --git a/compiler/rustc_log/src/lib.rs b/compiler/rustc_log/src/lib.rs
index 41b26ecce3c..01b6e342df0 100644
--- a/compiler/rustc_log/src/lib.rs
+++ b/compiler/rustc_log/src/lib.rs
@@ -58,6 +58,7 @@ pub struct LoggerConfig {
     pub verbose_thread_ids: Result<String, VarError>,
     pub backtrace: Result<String, VarError>,
     pub wraptree: Result<String, VarError>,
+    pub lines: Result<String, VarError>,
 }
 
 impl LoggerConfig {
@@ -69,6 +70,7 @@ impl LoggerConfig {
             verbose_thread_ids: env::var(format!("{env}_THREAD_IDS")),
             backtrace: env::var(format!("{env}_BACKTRACE")),
             wraptree: env::var(format!("{env}_WRAPTREE")),
+            lines: env::var(format!("{env}_LINES")),
         }
     }
 }
@@ -101,6 +103,11 @@ pub fn init_logger(cfg: LoggerConfig) -> Result<(), Error> {
         Err(_) => false,
     };
 
+    let lines = match cfg.lines {
+        Ok(v) => &v == "1",
+        Err(_) => false,
+    };
+
     let mut layer = tracing_tree::HierarchicalLayer::default()
         .with_writer(io::stderr)
         .with_ansi(color_logs)
@@ -108,6 +115,7 @@ pub fn init_logger(cfg: LoggerConfig) -> Result<(), Error> {
         .with_verbose_exit(verbose_entry_exit)
         .with_verbose_entry(verbose_entry_exit)
         .with_indent_amount(2)
+        .with_indent_lines(lines)
         .with_thread_ids(verbose_thread_ids)
         .with_thread_names(verbose_thread_ids);
 
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 89da0df8575..d7840a2d516 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -2020,7 +2020,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
 
                 // if this is an impl of `CoerceUnsized`, create its
                 // "unsized info", else just store None
-                if Some(trait_ref.def_id) == tcx.lang_items().coerce_unsized_trait() {
+                if tcx.is_lang_item(trait_ref.def_id, LangItem::CoerceUnsized) {
                     let coerce_unsized_info = tcx.coerce_unsized_info(def_id).unwrap();
                     record!(self.tables.coerce_unsized_info[def_id] <- coerce_unsized_info);
                 }
diff --git a/compiler/rustc_middle/src/middle/lang_items.rs b/compiler/rustc_middle/src/middle/lang_items.rs
index 4fd1c1f4a1b..e76d7af6e4a 100644
--- a/compiler/rustc_middle/src/middle/lang_items.rs
+++ b/compiler/rustc_middle/src/middle/lang_items.rs
@@ -23,6 +23,10 @@ impl<'tcx> TyCtxt<'tcx> {
         })
     }
 
+    pub fn is_lang_item(self, def_id: DefId, lang_item: LangItem) -> bool {
+        self.lang_items().get(lang_item) == Some(def_id)
+    }
+
     /// Given a [`DefId`] of one of the [`Fn`], [`FnMut`] or [`FnOnce`] traits,
     /// returns a corresponding [`ty::ClosureKind`].
     /// For any other [`DefId`] return `None`.
diff --git a/compiler/rustc_middle/src/mir/interpret/value.rs b/compiler/rustc_middle/src/mir/interpret/value.rs
index 70e5ad0635b..a84a4c583ed 100644
--- a/compiler/rustc_middle/src/mir/interpret/value.rs
+++ b/compiler/rustc_middle/src/mir/interpret/value.rs
@@ -69,6 +69,13 @@ impl<Prov: Provenance> fmt::LowerHex for Scalar<Prov> {
     }
 }
 
+impl<Prov> From<Half> for Scalar<Prov> {
+    #[inline(always)]
+    fn from(f: Half) -> Self {
+        Scalar::from_f16(f)
+    }
+}
+
 impl<Prov> From<Single> for Scalar<Prov> {
     #[inline(always)]
     fn from(f: Single) -> Self {
@@ -83,6 +90,13 @@ impl<Prov> From<Double> for Scalar<Prov> {
     }
 }
 
+impl<Prov> From<Quad> for Scalar<Prov> {
+    #[inline(always)]
+    fn from(f: Quad) -> Self {
+        Scalar::from_f128(f)
+    }
+}
+
 impl<Prov> From<ScalarInt> for Scalar<Prov> {
     #[inline(always)]
     fn from(ptr: ScalarInt) -> Self {
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index b64b7e2b1dc..f1c3cb69896 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -1287,7 +1287,7 @@ fn use_verbose(ty: Ty<'_>, fn_def: bool) -> bool {
 }
 
 impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
-    fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, _location: Location) {
+    fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, _location: Location) {
         let ConstOperand { span, user_ty, const_ } = constant;
         if use_verbose(const_.ty(), true) {
             self.push("mir::ConstOperand");
@@ -1415,7 +1415,7 @@ pub fn write_allocations<'tcx>(
     struct CollectAllocIds(BTreeSet<AllocId>);
 
     impl<'tcx> Visitor<'tcx> for CollectAllocIds {
-        fn visit_constant(&mut self, c: &ConstOperand<'tcx>, _: Location) {
+        fn visit_const_operand(&mut self, c: &ConstOperand<'tcx>, _: Location) {
             match c.const_ {
                 Const::Ty(_, _) | Const::Unevaluated(..) => {}
                 Const::Val(val, _) => {
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index ebe77a1abfd..3edc5fe36cd 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -1490,7 +1490,8 @@ pub enum BinOp {
     BitOr,
     /// The `<<` operator (shift left)
     ///
-    /// The offset is (uniquely) determined as follows:
+    /// The offset is given by `RHS.rem_euclid(LHS::BITS)`.
+    /// In other words, it is (uniquely) determined as follows:
     /// - it is "equal modulo LHS::BITS" to the RHS
     /// - it is in the range `0..LHS::BITS`
     Shl,
@@ -1498,7 +1499,8 @@ pub enum BinOp {
     ShlUnchecked,
     /// The `>>` operator (shift right)
     ///
-    /// The offset is (uniquely) determined as follows:
+    /// The offset is given by `RHS.rem_euclid(LHS::BITS)`.
+    /// In other words, it is (uniquely) determined as follows:
     /// - it is "equal modulo LHS::BITS" to the RHS
     /// - it is in the range `0..LHS::BITS`
     ///
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index f553b417294..10609e56f2c 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -184,12 +184,12 @@ macro_rules! make_mir_visitor {
 
             /// This is called for every constant in the MIR body and every `required_consts`
             /// (i.e., including consts that have been dead-code-eliminated).
-            fn visit_constant(
+            fn visit_const_operand(
                 &mut self,
                 constant: & $($mutability)? ConstOperand<'tcx>,
                 location: Location,
             ) {
-                self.super_constant(constant, location);
+                self.super_const_operand(constant, location);
             }
 
             fn visit_ty_const(
@@ -597,7 +597,7 @@ macro_rules! make_mir_visitor {
                                 }
                                 InlineAsmOperand::Const { value }
                                 | InlineAsmOperand::SymFn { value } => {
-                                    self.visit_constant(value, location);
+                                    self.visit_const_operand(value, location);
                                 }
                                 InlineAsmOperand::Out { place: None, .. }
                                 | InlineAsmOperand::SymStatic { def_id: _ }
@@ -788,7 +788,7 @@ macro_rules! make_mir_visitor {
                         );
                     }
                     Operand::Constant(constant) => {
-                        self.visit_constant(constant, location);
+                        self.visit_const_operand(constant, location);
                     }
                 }
             }
@@ -867,7 +867,7 @@ macro_rules! make_mir_visitor {
                     }
                 }
                 match value {
-                    VarDebugInfoContents::Const(c) => self.visit_constant(c, location),
+                    VarDebugInfoContents::Const(c) => self.visit_const_operand(c, location),
                     VarDebugInfoContents::Place(place) =>
                         self.visit_place(
                             place,
@@ -882,7 +882,7 @@ macro_rules! make_mir_visitor {
                 _scope: $(& $mutability)? SourceScope
             ) {}
 
-            fn super_constant(
+            fn super_const_operand(
                 &mut self,
                 constant: & $($mutability)? ConstOperand<'tcx>,
                 location: Location
@@ -1057,7 +1057,7 @@ macro_rules! super_body {
 
         for const_ in &$($mutability)? $body.required_consts {
             let location = Location::START;
-            $self.visit_constant(const_, location);
+            $self.visit_const_operand(const_, location);
         }
     }
 }
diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs
index 50b6c77e1b2..4fad721ce98 100644
--- a/compiler/rustc_middle/src/traits/query.rs
+++ b/compiler/rustc_middle/src/traits/query.rs
@@ -156,7 +156,7 @@ pub struct CandidateStep<'tcx> {
 
 #[derive(Copy, Clone, Debug, HashStable)]
 pub struct MethodAutoderefStepsResult<'tcx> {
-    /// The valid autoderef steps that could be find.
+    /// The valid autoderef steps that could be found.
     pub steps: &'tcx [CandidateStep<'tcx>],
     /// If Some(T), a type autoderef reported an error on.
     pub opt_bad_ty: Option<&'tcx MethodAutoderefBadTy<'tcx>>,
diff --git a/compiler/rustc_middle/src/traits/util.rs b/compiler/rustc_middle/src/traits/util.rs
index 707e076921b..adbb6cf2ddc 100644
--- a/compiler/rustc_middle/src/traits/util.rs
+++ b/compiler/rustc_middle/src/traits/util.rs
@@ -37,7 +37,7 @@ impl<'tcx> Elaborator<'tcx> {
         let super_predicates =
             self.tcx.super_predicates_of(trait_ref.def_id()).predicates.iter().filter_map(
                 |&(pred, _)| {
-                    let clause = pred.instantiate_supertrait(self.tcx, &trait_ref);
+                    let clause = pred.instantiate_supertrait(self.tcx, trait_ref);
                     self.visited.insert(clause).then_some(clause)
                 },
             );
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index 886dbd317af..684b3233cfd 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -8,9 +8,9 @@ use rustc_data_structures::intern::Interned;
 use rustc_data_structures::stable_hasher::HashingControls;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_errors::ErrorGuaranteed;
-use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::DefId;
+use rustc_hir::{self as hir, LangItem};
 use rustc_index::{IndexSlice, IndexVec};
 use rustc_macros::{HashStable, TyDecodable, TyEncodable};
 use rustc_query_system::ich::StableHashingContext;
@@ -204,6 +204,23 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> {
     fn def_id(self) -> DefId {
         self.did()
     }
+
+    fn is_phantom_data(self) -> bool {
+        self.is_phantom_data()
+    }
+
+    fn all_field_tys(
+        self,
+        tcx: TyCtxt<'tcx>,
+    ) -> ty::EarlyBinder<'tcx, impl Iterator<Item = Ty<'tcx>>> {
+        ty::EarlyBinder::bind(
+            self.all_fields().map(move |field| tcx.type_of(field.did).skip_binder()),
+        )
+    }
+
+    fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> {
+        self.sized_constraint(tcx)
+    }
 }
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable, TyEncodable, TyDecodable)]
@@ -257,16 +274,16 @@ impl AdtDefData {
         if tcx.has_attr(did, sym::fundamental) {
             flags |= AdtFlags::IS_FUNDAMENTAL;
         }
-        if Some(did) == tcx.lang_items().phantom_data() {
+        if tcx.is_lang_item(did, LangItem::PhantomData) {
             flags |= AdtFlags::IS_PHANTOM_DATA;
         }
-        if Some(did) == tcx.lang_items().owned_box() {
+        if tcx.is_lang_item(did, LangItem::OwnedBox) {
             flags |= AdtFlags::IS_BOX;
         }
-        if Some(did) == tcx.lang_items().manually_drop() {
+        if tcx.is_lang_item(did, LangItem::ManuallyDrop) {
             flags |= AdtFlags::IS_MANUALLY_DROP;
         }
-        if Some(did) == tcx.lang_items().unsafe_cell_type() {
+        if tcx.is_lang_item(did, LangItem::UnsafeCell) {
             flags |= AdtFlags::IS_UNSAFE_CELL;
         }
         if is_anonymous {
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 65d744239a6..3651c990c97 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -70,9 +70,9 @@ use rustc_span::{Span, DUMMY_SP};
 use rustc_target::abi::{FieldIdx, Layout, LayoutS, TargetDataLayout, VariantIdx};
 use rustc_target::spec::abi;
 use rustc_type_ir::fold::TypeFoldable;
+use rustc_type_ir::lang_items::TraitSolverLangItem;
 use rustc_type_ir::TyKind::*;
-use rustc_type_ir::WithCachedTypeInfo;
-use rustc_type_ir::{CollectAndApply, Interner, TypeFlags};
+use rustc_type_ir::{CollectAndApply, Interner, TypeFlags, WithCachedTypeInfo};
 use tracing::{debug, instrument};
 
 use std::assert_matches::assert_matches;
@@ -154,7 +154,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
 
     type VariancesOf = &'tcx [ty::Variance];
 
-    fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf {
+    fn variances_of(self, def_id: DefId) -> Self::VariancesOf {
         self.variances_of(def_id)
     }
 
@@ -198,7 +198,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
 
     fn trait_ref_and_own_args_for_alias(
         self,
-        def_id: Self::DefId,
+        def_id: DefId,
         args: Self::GenericArgs,
     ) -> (rustc_type_ir::TraitRef<Self>, Self::GenericArgsSlice) {
         assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::AssocConst);
@@ -246,7 +246,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.mk_type_list_from_iter(args)
     }
 
-    fn parent(self, def_id: Self::DefId) -> Self::DefId {
+    fn parent(self, def_id: DefId) -> DefId {
         self.parent(def_id)
     }
 
@@ -259,15 +259,85 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     fn features(self) -> Self::Features {
         self.features()
     }
+
+    fn bound_coroutine_hidden_types(
+        self,
+        def_id: DefId,
+    ) -> impl IntoIterator<Item = ty::EarlyBinder<'tcx, ty::Binder<'tcx, Ty<'tcx>>>> {
+        self.bound_coroutine_hidden_types(def_id)
+    }
+
+    fn fn_sig(self, def_id: DefId) -> ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>> {
+        self.fn_sig(def_id)
+    }
+
+    fn coroutine_movability(self, def_id: DefId) -> rustc_ast::Movability {
+        self.coroutine_movability(def_id)
+    }
+
+    fn coroutine_for_closure(self, def_id: DefId) -> DefId {
+        self.coroutine_for_closure(def_id)
+    }
+
+    fn generics_require_sized_self(self, def_id: DefId) -> bool {
+        self.generics_require_sized_self(def_id)
+    }
+
+    fn item_bounds(
+        self,
+        def_id: DefId,
+    ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = ty::Clause<'tcx>>> {
+        self.item_bounds(def_id).map_bound(IntoIterator::into_iter)
+    }
+
+    fn super_predicates_of(
+        self,
+        def_id: DefId,
+    ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = ty::Clause<'tcx>>> {
+        ty::EarlyBinder::bind(
+            self.super_predicates_of(def_id).instantiate_identity(self).predicates.into_iter(),
+        )
+    }
+
+    fn has_target_features(self, def_id: DefId) -> bool {
+        !self.codegen_fn_attrs(def_id).target_features.is_empty()
+    }
+
+    fn require_lang_item(self, lang_item: TraitSolverLangItem) -> DefId {
+        self.require_lang_item(
+            match lang_item {
+                TraitSolverLangItem::Future => hir::LangItem::Future,
+                TraitSolverLangItem::FutureOutput => hir::LangItem::FutureOutput,
+                TraitSolverLangItem::AsyncFnKindHelper => hir::LangItem::AsyncFnKindHelper,
+                TraitSolverLangItem::AsyncFnKindUpvars => hir::LangItem::AsyncFnKindUpvars,
+            },
+            None,
+        )
+    }
+
+    fn associated_type_def_ids(self, def_id: DefId) -> impl IntoIterator<Item = DefId> {
+        self.associated_items(def_id)
+            .in_definition_order()
+            .filter(|assoc_item| matches!(assoc_item.kind, ty::AssocKind::Type))
+            .map(|assoc_item| assoc_item.def_id)
+    }
 }
 
 impl<'tcx> rustc_type_ir::inherent::Abi<TyCtxt<'tcx>> for abi::Abi {
+    fn rust() -> Self {
+        abi::Abi::Rust
+    }
+
     fn is_rust(self) -> bool {
         matches!(self, abi::Abi::Rust)
     }
 }
 
 impl<'tcx> rustc_type_ir::inherent::Safety<TyCtxt<'tcx>> for hir::Safety {
+    fn safe() -> Self {
+        hir::Safety::Safe
+    }
+
     fn is_safe(self) -> bool {
         matches!(self, hir::Safety::Safe)
     }
@@ -281,6 +351,10 @@ impl<'tcx> rustc_type_ir::inherent::Features<TyCtxt<'tcx>> for &'tcx rustc_featu
     fn generic_const_exprs(self) -> bool {
         self.generic_const_exprs
     }
+
+    fn coroutine_clone(self) -> bool {
+        self.coroutine_clone
+    }
 }
 
 type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>;
diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs
index 7fff3d01324..54c88e48614 100644
--- a/compiler/rustc_middle/src/ty/generic_args.rs
+++ b/compiler/rustc_middle/src/ty/generic_args.rs
@@ -41,6 +41,8 @@ pub struct GenericArg<'tcx> {
     marker: PhantomData<(Ty<'tcx>, ty::Region<'tcx>, ty::Const<'tcx>)>,
 }
 
+impl<'tcx> rustc_type_ir::inherent::GenericArg<TyCtxt<'tcx>> for GenericArg<'tcx> {}
+
 impl<'tcx> rustc_type_ir::inherent::GenericArgs<TyCtxt<'tcx>> for ty::GenericArgsRef<'tcx> {
     fn type_at(self, i: usize) -> Ty<'tcx> {
         self.type_at(i)
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index a887047f62f..bdf90cbb25b 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -699,26 +699,25 @@ impl<'tcx> Instance<'tcx> {
         };
         let coroutine_kind = tcx.coroutine_kind(coroutine_def_id).unwrap();
 
-        let lang_items = tcx.lang_items();
-        let coroutine_callable_item = if Some(trait_id) == lang_items.future_trait() {
+        let coroutine_callable_item = if tcx.is_lang_item(trait_id, LangItem::Future) {
             assert_matches!(
                 coroutine_kind,
                 hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)
             );
             hir::LangItem::FuturePoll
-        } else if Some(trait_id) == lang_items.iterator_trait() {
+        } else if tcx.is_lang_item(trait_id, LangItem::Iterator) {
             assert_matches!(
                 coroutine_kind,
                 hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)
             );
             hir::LangItem::IteratorNext
-        } else if Some(trait_id) == lang_items.async_iterator_trait() {
+        } else if tcx.is_lang_item(trait_id, LangItem::AsyncIterator) {
             assert_matches!(
                 coroutine_kind,
                 hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _)
             );
             hir::LangItem::AsyncIteratorPollNext
-        } else if Some(trait_id) == lang_items.coroutine_trait() {
+        } else if tcx.is_lang_item(trait_id, LangItem::Coroutine) {
             assert_matches!(coroutine_kind, hir::CoroutineKind::Coroutine(_));
             hir::LangItem::CoroutineResume
         } else {
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 56945bf6be4..02c0d41f619 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -9,6 +9,7 @@ use rustc_errors::{
 };
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
+use rustc_hir::LangItem;
 use rustc_index::IndexVec;
 use rustc_macros::{extension, HashStable, TyDecodable, TyEncodable};
 use rustc_session::config::OptLevel;
@@ -850,7 +851,7 @@ where
                         // and we rely on this layout information to trigger a panic in
                         // `std::mem::uninitialized::<&dyn Trait>()`, for example.
                         if let ty::Adt(def, args) = metadata.kind()
-                            && Some(def.did()) == tcx.lang_items().dyn_metadata()
+                            && tcx.is_lang_item(def.did(), LangItem::DynMetadata)
                             && let ty::Dynamic(data, _, ty::Dyn) = args.type_at(0).kind()
                         {
                             mk_dyn_vtable(data.principal())
@@ -1169,7 +1170,7 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: SpecAbi) ->
         // This is not part of `codegen_fn_attrs` as it can differ between crates
         // and therefore cannot be computed in core.
         if tcx.sess.opts.unstable_opts.panic_in_drop == PanicStrategy::Abort {
-            if Some(did) == tcx.lang_items().drop_in_place_fn() {
+            if tcx.is_lang_item(did, LangItem::DropInPlace) {
                 return false;
             }
         }
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 83f8de6b6f9..5a94a53e175 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -16,7 +16,6 @@ pub use self::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeV
 pub use self::AssocItemContainer::*;
 pub use self::BorrowKind::*;
 pub use self::IntVarValue::*;
-pub use self::Variance::*;
 use crate::error::{OpaqueHiddenTypeMismatch, TypeMismatchReason};
 use crate::metadata::ModChild;
 use crate::middle::privacy::EffectiveVisibilities;
@@ -488,6 +487,8 @@ pub struct Term<'tcx> {
     marker: PhantomData<(Ty<'tcx>, Const<'tcx>)>,
 }
 
+impl<'tcx> rustc_type_ir::inherent::Term<TyCtxt<'tcx>> for Term<'tcx> {}
+
 impl<'tcx> rustc_type_ir::inherent::IntoKind for Term<'tcx> {
     type Kind = TermKind<'tcx>;
 
diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs
index c730f5117c5..ae36f2624ca 100644
--- a/compiler/rustc_middle/src/ty/predicate.rs
+++ b/compiler/rustc_middle/src/ty/predicate.rs
@@ -49,6 +49,18 @@ impl<'tcx> rustc_type_ir::inherent::Predicate<TyCtxt<'tcx>> for Predicate<'tcx>
     fn is_coinductive(self, interner: TyCtxt<'tcx>) -> bool {
         self.is_coinductive(interner)
     }
+
+    fn allow_normalization(self) -> bool {
+        self.allow_normalization()
+    }
+}
+
+impl<'tcx> rustc_type_ir::inherent::IntoKind for Predicate<'tcx> {
+    type Kind = ty::Binder<'tcx, ty::PredicateKind<'tcx>>;
+
+    fn kind(self) -> Self::Kind {
+        self.kind()
+    }
 }
 
 impl<'tcx> rustc_type_ir::visit::Flags for Predicate<'tcx> {
@@ -120,6 +132,7 @@ impl<'tcx> Predicate<'tcx> {
     /// unsoundly accept some programs. See #91068.
     #[inline]
     pub fn allow_normalization(self) -> bool {
+        // Keep this in sync with the one in `rustc_type_ir::inherent`!
         match self.kind().skip_binder() {
             PredicateKind::Clause(ClauseKind::WellFormed(_))
             | PredicateKind::AliasRelate(..)
@@ -313,7 +326,7 @@ impl<'tcx> Clause<'tcx> {
     pub fn instantiate_supertrait(
         self,
         tcx: TyCtxt<'tcx>,
-        trait_ref: &ty::PolyTraitRef<'tcx>,
+        trait_ref: ty::PolyTraitRef<'tcx>,
     ) -> Clause<'tcx> {
         // The interaction between HRTB and supertraits is not entirely
         // obvious. Let me walk you (and myself) through an example.
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 662eafd0ccb..de1796d4800 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -999,7 +999,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                     let trait_ref = bound_predicate.rebind(pred.trait_ref);
 
                     // Don't print `+ Sized`, but rather `+ ?Sized` if absent.
-                    if Some(trait_ref.def_id()) == tcx.lang_items().sized_trait() {
+                    if tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) {
                         match pred.polarity {
                             ty::PredicatePolarity::Positive => {
                                 has_sized_bound = true;
@@ -1254,14 +1254,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                 }
                 entry.has_fn_once = true;
                 return;
-            } else if Some(trait_def_id) == self.tcx().lang_items().fn_mut_trait() {
+            } else if self.tcx().is_lang_item(trait_def_id, LangItem::FnMut) {
                 let super_trait_ref = supertraits_for_pretty_printing(self.tcx(), trait_ref)
                     .find(|super_trait_ref| super_trait_ref.def_id() == fn_once_trait)
                     .unwrap();
 
                 fn_traits.entry(super_trait_ref).or_default().fn_mut_trait_ref = Some(trait_ref);
                 return;
-            } else if Some(trait_def_id) == self.tcx().lang_items().fn_trait() {
+            } else if self.tcx().is_lang_item(trait_def_id, LangItem::Fn) {
                 let super_trait_ref = supertraits_for_pretty_printing(self.tcx(), trait_ref)
                     .find(|super_trait_ref| super_trait_ref.def_id() == fn_once_trait)
                     .unwrap();
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index ba9ed0d5b70..8308e537e5e 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -786,6 +786,10 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> {
         tcx.types.bool
     }
 
+    fn new_u8(tcx: TyCtxt<'tcx>) -> Self {
+        tcx.types.u8
+    }
+
     fn new_infer(tcx: TyCtxt<'tcx>, infer: ty::InferTy) -> Self {
         Ty::new_infer(tcx, infer)
     }
diff --git a/compiler/rustc_middle/src/util/call_kind.rs b/compiler/rustc_middle/src/util/call_kind.rs
index a4cbfe86711..dc1d73684f4 100644
--- a/compiler/rustc_middle/src/util/call_kind.rs
+++ b/compiler/rustc_middle/src/util/call_kind.rs
@@ -117,19 +117,19 @@ pub fn call_kind<'tcx>(
     kind.unwrap_or_else(|| {
         // This isn't a 'special' use of `self`
         debug!(?method_did, ?fn_call_span);
-        let desugaring = if Some(method_did) == tcx.lang_items().into_iter_fn()
+        let desugaring = if tcx.is_lang_item(method_did, LangItem::IntoIterIntoIter)
             && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
         {
             Some((CallDesugaringKind::ForLoopIntoIter, method_args.type_at(0)))
         } else if fn_call_span.desugaring_kind() == Some(DesugaringKind::QuestionMark) {
-            if Some(method_did) == tcx.lang_items().branch_fn() {
+            if tcx.is_lang_item(method_did, LangItem::TryTraitBranch) {
                 Some((CallDesugaringKind::QuestionBranch, method_args.type_at(0)))
-            } else if Some(method_did) == tcx.lang_items().from_residual_fn() {
+            } else if tcx.is_lang_item(method_did, LangItem::TryTraitFromResidual) {
                 Some((CallDesugaringKind::QuestionFromResidual, method_args.type_at(0)))
             } else {
                 None
             }
-        } else if Some(method_did) == tcx.lang_items().from_output_fn()
+        } else if tcx.is_lang_item(method_did, LangItem::TryTraitFromOutput)
             && fn_call_span.desugaring_kind() == Some(DesugaringKind::TryBlock)
         {
             Some((CallDesugaringKind::TryBlockFromOutput, method_args.type_at(0)))
diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs
index 4ae475d35d1..8c323188826 100644
--- a/compiler/rustc_middle/src/values.rs
+++ b/compiler/rustc_middle/src/values.rs
@@ -142,7 +142,7 @@ impl<'tcx> Value<TyCtxt<'tcx>> for &[ty::Variance] {
             && let Some(def_id) = frame.query.def_id
         {
             let n = tcx.generics_of(def_id).own_params.len();
-            vec![ty::Variance::Bivariant; n].leak()
+            vec![ty::Bivariant; n].leak()
         } else {
             span_bug!(
                 cycle_error.usage.as_ref().unwrap().0,
diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
index b783eba8c4e..3b69058d3cb 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
@@ -2,6 +2,7 @@
 
 use crate::build::{parse_float_into_constval, Builder};
 use rustc_ast as ast;
+use rustc_hir::LangItem;
 use rustc_middle::mir;
 use rustc_middle::mir::interpret::{Allocation, LitToConstError, LitToConstInput, Scalar};
 use rustc_middle::mir::*;
@@ -142,7 +143,7 @@ fn lit_to_mir_constant<'tcx>(
             let id = tcx.allocate_bytes(data);
             ConstValue::Scalar(Scalar::from_pointer(id.into(), &tcx))
         }
-        (ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().c_str()) =>
+        (ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::CStr)) =>
         {
             let allocation = Allocation::from_bytes_byte_aligned_immutable(data as &[u8]);
             let allocation = tcx.mk_const_alloc(allocation);
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 54138789369..544f27b84e9 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -699,7 +699,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                             // exactly `T` (i.e., with invariance). The variance field, in
                             // contrast, is intended to be used to relate `T` to the type of
                             // `<expr>`.
-                            ty::Variance::Invariant,
+                            ty::Invariant,
                         ),
                     },
                 );
diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs
index 2040071e76e..11d3e2a8180 100644
--- a/compiler/rustc_mir_build/src/build/matches/test.rs
+++ b/compiler/rustc_mir_build/src/build/matches/test.rs
@@ -141,7 +141,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 let success_block = target_block(TestBranch::Success);
                 let fail_block = target_block(TestBranch::Failure);
                 if let ty::Adt(def, _) = ty.kind()
-                    && Some(def.did()) == tcx.lang_items().string()
+                    && tcx.is_lang_item(def.did(), LangItem::String)
                 {
                     if !tcx.features().string_deref_patterns {
                         bug!(
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index d781fb1c297..a65586ccdb7 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -97,6 +97,7 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
                 if !span.at_least_rust_2024()
                     && self.tcx.has_attr(id, sym::rustc_deprecated_safe_2024) =>
             {
+                let sm = self.tcx.sess.source_map();
                 self.tcx.emit_node_span_lint(
                     DEPRECATED_SAFE,
                     self.hir_context,
@@ -105,6 +106,8 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
                         span,
                         function: with_no_trimmed_paths!(self.tcx.def_path_str(id)),
                         sub: CallToDeprecatedSafeFnRequiresUnsafeSub {
+                            indent: sm.indentation_before(span).unwrap_or_default(),
+                            start_of_line: sm.span_extend_to_line(span).shrink_to_lo(),
                             left: span.shrink_to_lo(),
                             right: span.shrink_to_hi(),
                         },
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index cf324c03dc9..3bd2e47976b 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -33,6 +33,11 @@ pub(crate) struct CallToDeprecatedSafeFnRequiresUnsafe {
 #[derive(Subdiagnostic)]
 #[multipart_suggestion(mir_build_suggestion, applicability = "machine-applicable")]
 pub(crate) struct CallToDeprecatedSafeFnRequiresUnsafeSub {
+    pub(crate) indent: String,
+    #[suggestion_part(
+        code = "{indent}// TODO: Audit that the environment access only happens in single-threaded code.\n" // ignore-tidy-todo
+    )]
+    pub(crate) start_of_line: Span,
     #[suggestion_part(code = "unsafe {{ ")]
     pub(crate) left: Span,
     #[suggestion_part(code = " }}")]
diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs
index a98e046d4dc..7b94867114d 100644
--- a/compiler/rustc_mir_build/src/thir/constant.rs
+++ b/compiler/rustc_mir_build/src/thir/constant.rs
@@ -1,4 +1,5 @@
 use rustc_ast as ast;
+use rustc_hir::LangItem;
 use rustc_middle::bug;
 use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
 use rustc_middle::ty::{self, ParamEnv, ScalarInt, TyCtxt};
@@ -46,7 +47,7 @@ pub(crate) fn lit_to_const<'tcx>(
         (ast::LitKind::Byte(n), ty::Uint(ty::UintTy::U8)) => {
             ty::ValTree::from_scalar_int((*n).into())
         }
-        (ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().c_str()) =>
+        (ast::LitKind::CStr(data, _), ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::CStr)) =>
         {
             let bytes = data as &[u8];
             ty::ValTree::from_raw_bytes(tcx, bytes)
diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs
index 54eafdd828a..95cd703dbb3 100644
--- a/compiler/rustc_mir_build/src/thir/cx/block.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/block.rs
@@ -92,7 +92,7 @@ impl<'tcx> Cx<'tcx> {
                                     kind: PatKind::AscribeUserType {
                                         ascription: Ascription {
                                             annotation,
-                                            variance: ty::Variance::Covariant,
+                                            variance: ty::Covariant,
                                         },
                                         subpattern: pattern,
                                     },
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 158ca91fcf1..93db1f61853 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -525,7 +525,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
             };
             kind = PatKind::AscribeUserType {
                 subpattern: Box::new(Pat { span, ty, kind }),
-                ascription: Ascription { annotation, variance: ty::Variance::Covariant },
+                ascription: Ascription { annotation, variance: ty::Covariant },
             };
         }
 
@@ -612,7 +612,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
                                 annotation,
                                 // Note that use `Contravariant` here. See the
                                 // `variance` field documentation for details.
-                                variance: ty::Variance::Contravariant,
+                                variance: ty::Contravariant,
                             },
                         },
                         ty: const_.ty(),
diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs
index bdc59e84356..654020164db 100644
--- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs
+++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs
@@ -454,8 +454,7 @@ where
             });
         }
 
-        let skip_contents =
-            adt.is_union() || Some(adt.did()) == self.tcx().lang_items().manually_drop();
+        let skip_contents = adt.is_union() || adt.is_manually_drop();
         let contents_drop = if skip_contents {
             (self.succ, self.unwind)
         } else {
diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs
index f880476cec2..16977a63c59 100644
--- a/compiler/rustc_mir_transform/src/add_retag.rs
+++ b/compiler/rustc_mir_transform/src/add_retag.rs
@@ -4,6 +4,7 @@
 //! of MIR building, and only after this pass we think of the program has having the
 //! normal MIR semantics.
 
+use rustc_hir::LangItem;
 use rustc_middle::mir::*;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 
@@ -27,7 +28,7 @@ fn may_contain_reference<'tcx>(ty: Ty<'tcx>, depth: u32, tcx: TyCtxt<'tcx>) -> b
         // References and Boxes (`noalias` sources)
         ty::Ref(..) => true,
         ty::Adt(..) if ty.is_box() => true,
-        ty::Adt(adt, _) if Some(adt.did()) == tcx.lang_items().ptr_unique() => true,
+        ty::Adt(adt, _) if tcx.is_lang_item(adt.did(), LangItem::PtrUnique) => true,
         // Compound types: recurse
         ty::Array(ty, _) | ty::Slice(ty) => {
             // This does not branch so we keep the depth the same.
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index 121a3b99a39..0f8f28e3462 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -330,8 +330,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         let is_sized = !self.feature_unsized_locals
             || self.local_decls[local].ty.is_sized(self.tcx, self.param_env);
         if is_sized {
-            self.rev_locals.ensure_contains_elem(value, SmallVec::new);
-            self.rev_locals[value].push(local);
+            self.rev_locals.ensure_contains_elem(value, SmallVec::new).push(local);
         }
     }
 
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index fe2237dd2e9..2cbe0a01e9e 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -225,13 +225,8 @@ impl<'tcx> Inliner<'tcx> {
         // Normally, this shouldn't be required, but trait normalization failure can create a
         // validation ICE.
         let output_type = callee_body.return_ty();
-        if !util::relate_types(
-            self.tcx,
-            self.param_env,
-            ty::Variance::Covariant,
-            output_type,
-            destination_ty,
-        ) {
+        if !util::relate_types(self.tcx, self.param_env, ty::Covariant, output_type, destination_ty)
+        {
             trace!(?output_type, ?destination_ty);
             return Err("failed to normalize return type");
         }
@@ -261,13 +256,8 @@ impl<'tcx> Inliner<'tcx> {
                 self_arg_ty.into_iter().chain(arg_tuple_tys).zip(callee_body.args_iter())
             {
                 let input_type = callee_body.local_decls[input].ty;
-                if !util::relate_types(
-                    self.tcx,
-                    self.param_env,
-                    ty::Variance::Covariant,
-                    input_type,
-                    arg_ty,
-                ) {
+                if !util::relate_types(self.tcx, self.param_env, ty::Covariant, input_type, arg_ty)
+                {
                     trace!(?arg_ty, ?input_type);
                     return Err("failed to normalize tuple argument type");
                 }
@@ -276,13 +266,8 @@ impl<'tcx> Inliner<'tcx> {
             for (arg, input) in args.iter().zip(callee_body.args_iter()) {
                 let input_type = callee_body.local_decls[input].ty;
                 let arg_ty = arg.node.ty(&caller_body.local_decls, self.tcx);
-                if !util::relate_types(
-                    self.tcx,
-                    self.param_env,
-                    ty::Variance::Covariant,
-                    input_type,
-                    arg_ty,
-                ) {
+                if !util::relate_types(self.tcx, self.param_env, ty::Covariant, input_type, arg_ty)
+                {
                     trace!(?arg_ty, ?input_type);
                     return Err("failed to normalize argument type");
                 }
diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs
index 6a20b46e7f9..8d6c00bbedb 100644
--- a/compiler/rustc_mir_transform/src/known_panics_lint.rs
+++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs
@@ -706,9 +706,9 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
         self.super_operand(operand, location);
     }
 
-    fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, location: Location) {
-        trace!("visit_constant: {:?}", constant);
-        self.super_constant(constant, location);
+    fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, location: Location) {
+        trace!("visit_const_operand: {:?}", constant);
+        self.super_const_operand(constant, location);
         self.eval_constant(constant);
     }
 
diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs
index ecd1179ca99..ecdca8292b4 100644
--- a/compiler/rustc_mir_transform/src/promote_consts.rs
+++ b/compiler/rustc_mir_transform/src/promote_consts.rs
@@ -956,7 +956,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
         }
     }
 
-    fn visit_constant(&mut self, constant: &mut ConstOperand<'tcx>, _location: Location) {
+    fn visit_const_operand(&mut self, constant: &mut ConstOperand<'tcx>, _location: Location) {
         if constant.const_.is_required_const() {
             self.promoted.required_consts.push(*constant);
         }
diff --git a/compiler/rustc_mir_transform/src/required_consts.rs b/compiler/rustc_mir_transform/src/required_consts.rs
index 71ac929d35e..00bfb5e6600 100644
--- a/compiler/rustc_mir_transform/src/required_consts.rs
+++ b/compiler/rustc_mir_transform/src/required_consts.rs
@@ -12,7 +12,7 @@ impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> {
 }
 
 impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> {
-    fn visit_constant(&mut self, constant: &ConstOperand<'tcx>, _: Location) {
+    fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, _: Location) {
         if constant.const_.is_required_const() {
             self.required_consts.push(*constant);
         }
diff --git a/compiler/rustc_mir_transform/src/reveal_all.rs b/compiler/rustc_mir_transform/src/reveal_all.rs
index 4d2eca57840..5eaa024f846 100644
--- a/compiler/rustc_mir_transform/src/reveal_all.rs
+++ b/compiler/rustc_mir_transform/src/reveal_all.rs
@@ -49,14 +49,14 @@ impl<'tcx> MutVisitor<'tcx> for RevealAllVisitor<'tcx> {
     }
 
     #[inline]
-    fn visit_constant(&mut self, constant: &mut ConstOperand<'tcx>, location: Location) {
+    fn visit_const_operand(&mut self, constant: &mut ConstOperand<'tcx>, location: Location) {
         // We have to use `try_normalize_erasing_regions` here, since it's
         // possible that we visit impossible-to-satisfy where clauses here,
         // see #91745
         if let Ok(c) = self.tcx.try_normalize_erasing_regions(self.param_env, constant.const_) {
             constant.const_ = c;
         }
-        self.super_constant(constant, location);
+        self.super_const_operand(constant, location);
     }
 
     #[inline]
diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs
index aa9c87d8f80..41643f20285 100644
--- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs
+++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs
@@ -561,7 +561,7 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> {
 
             // If projection of Discriminant then compare with `Ty::discriminant_ty`
             if let ty::Alias(ty::Projection, ty::AliasTy { args, def_id, .. }) = expected_ty.kind()
-                && Some(*def_id) == self.tcx.lang_items().discriminant_type()
+                && self.tcx.is_lang_item(*def_id, LangItem::Discriminant)
                 && args.first().unwrap().as_type().unwrap().discriminant_ty(self.tcx) == operand_ty
             {
                 return;
diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs
index f19c34cae7a..c2108795372 100644
--- a/compiler/rustc_mir_transform/src/sroa.rs
+++ b/compiler/rustc_mir_transform/src/sroa.rs
@@ -1,4 +1,5 @@
 use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
+use rustc_hir::LangItem;
 use rustc_index::bit_set::{BitSet, GrowableBitSet};
 use rustc_index::IndexVec;
 use rustc_middle::bug;
@@ -70,7 +71,7 @@ fn escaping_locals<'tcx>(
                 // Exclude #[repr(simd)] types so that they are not de-optimized into an array
                 return true;
             }
-            if Some(def.did()) == tcx.lang_items().dyn_metadata() {
+            if tcx.is_lang_item(def.did(), LangItem::DynMetadata) {
                 // codegen wants to see the `DynMetadata<T>`,
                 // not the inner reference-to-opaque-type.
                 return true;
diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs
index 586c1254995..ea23bbc2a38 100644
--- a/compiler/rustc_mir_transform/src/validate.rs
+++ b/compiler/rustc_mir_transform/src/validate.rs
@@ -1,6 +1,7 @@
 //! Validates the MIR to ensure that invariants are upheld.
 
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_hir::LangItem;
 use rustc_index::bit_set::BitSet;
 use rustc_index::IndexVec;
 use rustc_infer::traits::Reveal;
@@ -689,7 +690,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     }
                     ty::Adt(adt_def, args) => {
                         // see <https://github.com/rust-lang/rust/blob/7601adcc764d42c9f2984082b49948af652df986/compiler/rustc_middle/src/ty/layout.rs#L861-L864>
-                        if Some(adt_def.did()) == self.tcx.lang_items().dyn_metadata() {
+                        if self.tcx.is_lang_item(adt_def.did(), LangItem::DynMetadata) {
                             self.fail(
                                 location,
                                 format!(
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 61680dbfaf5..04de1654c1a 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -593,7 +593,7 @@ fn check_recursion_limit<'tcx>(
     let recursion_depth = recursion_depths.get(&def_id).cloned().unwrap_or(0);
     debug!(" => recursion depth={}", recursion_depth);
 
-    let adjusted_recursion_depth = if Some(def_id) == tcx.lang_items().drop_in_place_fn() {
+    let adjusted_recursion_depth = if tcx.is_lang_item(def_id, LangItem::DropInPlace) {
         // HACK: drop_in_place creates tight monomorphization loops. Give
         // it more margin.
         recursion_depth / 4
@@ -799,7 +799,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
     /// This does not walk the MIR of the constant as that is not needed for codegen, all we need is
     /// to ensure that the constant evaluates successfully and walk the result.
     #[instrument(skip(self), level = "debug")]
-    fn visit_constant(&mut self, constant: &mir::ConstOperand<'tcx>, location: Location) {
+    fn visit_const_operand(&mut self, constant: &mir::ConstOperand<'tcx>, location: Location) {
         // No `super_constant` as we don't care about `visit_ty`/`visit_ty_const`.
         let Some(val) = self.eval_constant(constant) else { return };
         collect_const_value(self.tcx, val, self.used_items);
@@ -945,7 +945,7 @@ fn visit_instance_use<'tcx>(
             // be lowered in codegen to nothing or a call to panic_nounwind. So if we encounter any
             // of those intrinsics, we need to include a mono item for panic_nounwind, else we may try to
             // codegen a call to that function without generating code for the function itself.
-            let def_id = tcx.lang_items().get(LangItem::PanicNounwind).unwrap();
+            let def_id = tcx.require_lang_item(LangItem::PanicNounwind, None);
             let panic_instance = Instance::mono(tcx, def_id);
             if should_codegen_locally(tcx, panic_instance) {
                 output.push(create_fn_mono_item(tcx, panic_instance, source));
diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs
index 336341f4e74..14b5b22dc64 100644
--- a/compiler/rustc_monomorphize/src/partitioning.rs
+++ b/compiler/rustc_monomorphize/src/partitioning.rs
@@ -104,6 +104,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet};
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, DefIdSet, LOCAL_CRATE};
 use rustc_hir::definitions::DefPathDataName;
+use rustc_hir::LangItem;
 use rustc_middle::bug;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
@@ -813,7 +814,7 @@ fn mono_item_visibility<'tcx>(
     //        from the `main` symbol we'll generate later.
     //
     //        This may be fixable with a new `InstanceDef` perhaps? Unsure!
-    if tcx.lang_items().start_fn() == Some(def_id) {
+    if tcx.is_lang_item(def_id, LangItem::Start) {
         *can_be_internalized = false;
         return Visibility::Hidden;
     }
diff --git a/compiler/rustc_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs
index a3ca9e9f9cf..c187cb63ee1 100644
--- a/compiler/rustc_monomorphize/src/polymorphize.rs
+++ b/compiler/rustc_monomorphize/src/polymorphize.rs
@@ -261,7 +261,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> {
         self.super_local_decl(local, local_decl);
     }
 
-    fn visit_constant(&mut self, ct: &mir::ConstOperand<'tcx>, location: Location) {
+    fn visit_const_operand(&mut self, ct: &mir::ConstOperand<'tcx>, location: Location) {
         match ct.const_ {
             mir::Const::Ty(_, c) => {
                 c.visit_with(self);
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 2cb3c5d8965..6a74ddc5508 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -472,7 +472,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
                             && let ItemKind::Impl(impl_ref) =
                                 self.tcx.hir().expect_item(local_impl_id).kind
                         {
-                            if matches!(trait_item.kind, hir::TraitItemKind::Fn(..))
+                            if !matches!(trait_item.kind, hir::TraitItemKind::Type(..))
                                 && !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty)
                                     .ty_and_all_fields_are_public
                             {
@@ -802,7 +802,7 @@ fn check_item<'tcx>(
             // And we access the Map here to get HirId from LocalDefId
             for local_def_id in local_def_ids {
                 // check the function may construct Self
-                let mut may_construct_self = true;
+                let mut may_construct_self = false;
                 if let Some(fn_sig) =
                     tcx.hir().fn_sig_by_hir_id(tcx.local_def_id_to_hir_id(local_def_id))
                 {
@@ -898,7 +898,7 @@ fn create_and_seed_worklist(
                     match tcx.def_kind(id) {
                         DefKind::Impl { .. } => false,
                         DefKind::AssocConst | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer),
-                        DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()),
+                        DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(),
                         _ => true
                     })
                 .map(|id| (id, ComesFromAllowExpect::No))
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index d1541527cf5..8d470c6c61e 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -2783,33 +2783,65 @@ fn show_candidates(
     // by iterating through a hash map, so make sure they are ordered:
     for path_strings in [&mut accessible_path_strings, &mut inaccessible_path_strings] {
         path_strings.sort_by(|a, b| a.0.cmp(&b.0));
+        path_strings.dedup_by(|a, b| a.0 == b.0);
         let core_path_strings =
             path_strings.extract_if(|p| p.0.starts_with("core::")).collect::<Vec<_>>();
-        path_strings.extend(core_path_strings);
-        path_strings.dedup_by(|a, b| a.0 == b.0);
+        let std_path_strings =
+            path_strings.extract_if(|p| p.0.starts_with("std::")).collect::<Vec<_>>();
+        let foreign_crate_path_strings =
+            path_strings.extract_if(|p| !p.0.starts_with("crate::")).collect::<Vec<_>>();
+
+        // We list the `crate` local paths first.
+        // Then we list the `std`/`core` paths.
+        if std_path_strings.len() == core_path_strings.len() {
+            // Do not list `core::` paths if we are already listing the `std::` ones.
+            path_strings.extend(std_path_strings);
+        } else {
+            path_strings.extend(std_path_strings);
+            path_strings.extend(core_path_strings);
+        }
+        // List all paths from foreign crates last.
+        path_strings.extend(foreign_crate_path_strings);
     }
-    accessible_path_strings.sort();
 
     if !accessible_path_strings.is_empty() {
-        let (determiner, kind, name, through) =
+        let (determiner, kind, s, name, through) =
             if let [(name, descr, _, _, via_import)] = &accessible_path_strings[..] {
                 (
                     "this",
                     *descr,
+                    "",
                     format!(" `{name}`"),
                     if *via_import { " through its public re-export" } else { "" },
                 )
             } else {
-                ("one of these", "items", String::new(), "")
+                // Get the unique item kinds and if there's only one, we use the right kind name
+                // instead of the more generic "items".
+                let mut kinds = accessible_path_strings
+                    .iter()
+                    .map(|(_, descr, _, _, _)| *descr)
+                    .collect::<FxHashSet<&str>>()
+                    .into_iter();
+                let kind = if let Some(kind) = kinds.next()
+                    && let None = kinds.next()
+                {
+                    kind
+                } else {
+                    "item"
+                };
+                let s = if kind.ends_with('s') { "es" } else { "s" };
+
+                ("one of these", kind, s, String::new(), "")
             };
 
         let instead = if let Instead::Yes = instead { " instead" } else { "" };
         let mut msg = if let DiagMode::Pattern = mode {
             format!(
-                "if you meant to match on {kind}{instead}{name}, use the full path in the pattern",
+                "if you meant to match on {kind}{s}{instead}{name}, use the full path in the \
+                 pattern",
             )
         } else {
-            format!("consider importing {determiner} {kind}{through}{instead}")
+            format!("consider importing {determiner} {kind}{s}{through}{instead}")
         };
 
         for note in accessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) {
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index b0adc3775f6..fa711d932b6 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -3281,17 +3281,19 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
         }
         self.visit_path(&delegation.path, delegation.id);
         if let Some(body) = &delegation.body {
-            // `PatBoundCtx` is not necessary in this context
-            let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
-
-            let span = delegation.path.segments.last().unwrap().ident.span;
-            self.fresh_binding(
-                Ident::new(kw::SelfLower, span),
-                delegation.id,
-                PatternSource::FnParam,
-                &mut bindings,
-            );
-            self.visit_block(body);
+            self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| {
+                // `PatBoundCtx` is not necessary in this context
+                let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
+
+                let span = delegation.path.segments.last().unwrap().ident.span;
+                this.fresh_binding(
+                    Ident::new(kw::SelfLower, span),
+                    delegation.id,
+                    PatternSource::FnParam,
+                    &mut bindings,
+                );
+                this.visit_block(body);
+            });
         }
     }
 
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 9eeb0da7ed2..be24755d4c5 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -1021,12 +1021,14 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
             },
         );
         let is_assoc_fn = self.self_type_is_available();
+        let self_from_macro = "a `self` parameter, but a macro invocation can only \
+                               access identifiers it receives from parameters";
         if let Some((fn_kind, span)) = &self.diag_metadata.current_function {
             // The current function has a `self` parameter, but we were unable to resolve
             // a reference to `self`. This can only happen if the `self` identifier we
             // are resolving came from a different hygiene context.
             if fn_kind.decl().inputs.get(0).is_some_and(|p| p.is_self()) {
-                err.span_label(*span, "this function has a `self` parameter, but a macro invocation can only access identifiers it receives from parameters");
+                err.span_label(*span, format!("this function has {self_from_macro}"));
             } else {
                 let doesnt = if is_assoc_fn {
                     let (span, sugg) = fn_kind
@@ -1068,14 +1070,18 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                 }
             }
         } else if let Some(item_kind) = self.diag_metadata.current_item {
-            err.span_label(
-                item_kind.ident.span,
-                format!(
-                    "`self` not allowed in {} {}",
-                    item_kind.kind.article(),
-                    item_kind.kind.descr()
-                ),
-            );
+            if matches!(item_kind.kind, ItemKind::Delegation(..)) {
+                err.span_label(item_kind.span, format!("delegation supports {self_from_macro}"));
+            } else {
+                err.span_label(
+                    item_kind.ident.span,
+                    format!(
+                        "`self` not allowed in {} {}",
+                        item_kind.kind.article(),
+                        item_kind.kind.descr()
+                    ),
+                );
+            }
         }
         true
     }
diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
index 2fbfb93150e..2cb0c06e336 100644
--- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
+++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
@@ -289,7 +289,7 @@ pub fn transform_instance<'tcx>(
     options: TransformTyOptions,
 ) -> Instance<'tcx> {
     if (matches!(instance.def, ty::InstanceDef::Virtual(..))
-        && Some(instance.def_id()) == tcx.lang_items().drop_in_place_fn())
+        && tcx.is_lang_item(instance.def_id(), LangItem::DropInPlace))
         || matches!(instance.def, ty::InstanceDef::DropGlue(..))
     {
         // Adjust the type ids of DropGlues
diff --git a/compiler/rustc_smir/src/rustc_smir/builder.rs b/compiler/rustc_smir/src/rustc_smir/builder.rs
index 221224eed01..577373cbc95 100644
--- a/compiler/rustc_smir/src/rustc_smir/builder.rs
+++ b/compiler/rustc_smir/src/rustc_smir/builder.rs
@@ -52,7 +52,11 @@ impl<'tcx> BodyBuilder<'tcx> {
 }
 
 impl<'tcx> MutVisitor<'tcx> for BodyBuilder<'tcx> {
-    fn visit_constant(&mut self, constant: &mut mir::ConstOperand<'tcx>, location: mir::Location) {
+    fn visit_const_operand(
+        &mut self,
+        constant: &mut mir::ConstOperand<'tcx>,
+        location: mir::Location,
+    ) {
         let const_ = constant.const_;
         let val = match const_.eval(self.tcx, ty::ParamEnv::reveal_all(), constant.span) {
             Ok(v) => v,
@@ -63,7 +67,7 @@ impl<'tcx> MutVisitor<'tcx> for BodyBuilder<'tcx> {
         };
         let ty = constant.ty();
         constant.const_ = mir::Const::Val(val, ty);
-        self.super_constant(constant, location);
+        self.super_const_operand(constant, location);
     }
 
     fn tcx(&self) -> TyCtxt<'tcx> {
diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs
index a8688c88601..4348bc31bf9 100644
--- a/compiler/rustc_smir/src/rustc_smir/context.rs
+++ b/compiler/rustc_smir/src/rustc_smir/context.rs
@@ -6,6 +6,7 @@
 #![allow(rustc::usage_of_qualified_ty)]
 
 use rustc_abi::HasDataLayout;
+use rustc_hir::LangItem;
 use rustc_middle::ty::layout::{
     FnAbiOf, FnAbiOfHelpers, HasParamEnv, HasTyCtxt, LayoutOf, LayoutOfHelpers,
 };
@@ -63,9 +64,10 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
     }
 
     fn has_body(&self, def: DefId) -> bool {
-        let tables = self.0.borrow();
-        let def_id = tables[def];
-        tables.tcx.is_mir_available(def_id)
+        let mut tables = self.0.borrow_mut();
+        let tcx = tables.tcx;
+        let def_id = def.internal(&mut *tables, tcx);
+        tables.item_has_body(def_id)
     }
 
     fn foreign_modules(&self, crate_num: CrateNum) -> Vec<stable_mir::ty::ForeignModuleDef> {
@@ -295,7 +297,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
         let mut tables = self.0.borrow_mut();
         let tcx = tables.tcx;
         let def_id = def.0.internal(&mut *tables, tcx);
-        tables.tcx.lang_items().c_str() == Some(def_id)
+        tables.tcx.is_lang_item(def_id, LangItem::CStr)
     }
 
     fn fn_sig(&self, def: FnDef, args: &GenericArgs) -> PolyFnSig {
@@ -322,13 +324,6 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
         tcx.intrinsic(def_id).unwrap().name.to_string()
     }
 
-    fn intrinsic_must_be_overridden(&self, def: IntrinsicDef) -> bool {
-        let mut tables = self.0.borrow_mut();
-        let tcx = tables.tcx;
-        let def_id = def.0.internal(&mut *tables, tcx);
-        tcx.intrinsic_raw(def_id).unwrap().must_be_overridden
-    }
-
     fn closure_sig(&self, args: &GenericArgs) -> PolyFnSig {
         let mut tables = self.0.borrow_mut();
         let tcx = tables.tcx;
@@ -515,7 +510,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
         let mut tables = self.0.borrow_mut();
         let instance = tables.instances[def];
         tables
-            .has_body(instance)
+            .instance_has_body(instance)
             .then(|| BodyBuilder::new(tables.tcx, instance).build(&mut *tables))
     }
 
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
index bcacf54baf3..f15b82d0c03 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
@@ -328,13 +328,13 @@ impl<'tcx> Stable<'tcx> for mir::Operand<'tcx> {
 }
 
 impl<'tcx> Stable<'tcx> for mir::ConstOperand<'tcx> {
-    type T = stable_mir::mir::Constant;
+    type T = stable_mir::mir::ConstOperand;
 
     fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        stable_mir::mir::Constant {
+        stable_mir::mir::ConstOperand {
             span: self.span.stable(tables),
             user_ty: self.user_ty.map(|u| u.as_usize()).or(None),
-            literal: self.const_.stable(tables),
+            const_: self.const_.stable(tables),
         }
     }
 }
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
index 73bc87dc9ab..e3cd7187e77 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
@@ -866,10 +866,10 @@ impl<'tcx> Stable<'tcx> for ty::Variance {
     type T = stable_mir::mir::Variance;
     fn stable(&self, _: &mut Tables<'_>) -> Self::T {
         match self {
-            ty::Variance::Bivariant => stable_mir::mir::Variance::Bivariant,
-            ty::Variance::Contravariant => stable_mir::mir::Variance::Contravariant,
-            ty::Variance::Covariant => stable_mir::mir::Variance::Covariant,
-            ty::Variance::Invariant => stable_mir::mir::Variance::Invariant,
+            ty::Bivariant => stable_mir::mir::Variance::Bivariant,
+            ty::Contravariant => stable_mir::mir::Variance::Contravariant,
+            ty::Covariant => stable_mir::mir::Variance::Covariant,
+            ty::Invariant => stable_mir::mir::Variance::Invariant,
         }
     }
 }
diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs
index d13e7803326..89f9adfcfd6 100644
--- a/compiler/rustc_smir/src/rustc_smir/mod.rs
+++ b/compiler/rustc_smir/src/rustc_smir/mod.rs
@@ -51,9 +51,13 @@ impl<'tcx> Tables<'tcx> {
         self.mir_consts.create_or_fetch(constant)
     }
 
-    pub(crate) fn has_body(&self, instance: Instance<'tcx>) -> bool {
+    /// Return whether the instance as a body available.
+    ///
+    /// Items and intrinsics may have a body available from its definition.
+    /// Shims body may be generated depending on their type.
+    pub(crate) fn instance_has_body(&self, instance: Instance<'tcx>) -> bool {
         let def_id = instance.def_id();
-        self.tcx.is_mir_available(def_id)
+        self.item_has_body(def_id)
             || !matches!(
                 instance.def,
                 ty::InstanceDef::Virtual(..)
@@ -61,6 +65,19 @@ impl<'tcx> Tables<'tcx> {
                     | ty::InstanceDef::Item(..)
             )
     }
+
+    /// Return whether the item has a body defined by the user.
+    ///
+    /// Note that intrinsics may have a placeholder body that shouldn't be used in practice.
+    /// In StableMIR, we handle this case as if the body is not available.
+    pub(crate) fn item_has_body(&self, def_id: DefId) -> bool {
+        let must_override = if let Some(intrinsic) = self.tcx.intrinsic(def_id) {
+            intrinsic.must_be_overridden
+        } else {
+            false
+        };
+        !must_override && self.tcx.is_mir_available(def_id)
+    }
 }
 
 /// Build a stable mir crate from a given crate number.
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index 99fcaf917fe..b7ffe6c618a 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -520,6 +520,7 @@ impl SpanData {
     pub fn with_hi(&self, hi: BytePos) -> Span {
         Span::new(self.lo, hi, self.ctxt, self.parent)
     }
+    /// Avoid if possible, `Span::map_ctxt` should be preferred.
     #[inline]
     fn with_ctxt(&self, ctxt: SyntaxContext) -> Span {
         Span::new(self.lo, self.hi, ctxt, self.parent)
@@ -576,9 +577,8 @@ impl Span {
         self.data().with_hi(hi)
     }
     #[inline]
-    pub fn with_ctxt(mut self, ctxt: SyntaxContext) -> Span {
-        self.update_ctxt(|_| ctxt);
-        self
+    pub fn with_ctxt(self, ctxt: SyntaxContext) -> Span {
+        self.map_ctxt(|_| ctxt)
     }
     #[inline]
     pub fn parent(self) -> Option<LocalDefId> {
@@ -1059,9 +1059,8 @@ impl Span {
     }
 
     #[inline]
-    pub fn apply_mark(mut self, expn_id: ExpnId, transparency: Transparency) -> Span {
-        self.update_ctxt(|ctxt| ctxt.apply_mark(expn_id, transparency));
-        self
+    pub fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> Span {
+        self.map_ctxt(|ctxt| ctxt.apply_mark(expn_id, transparency))
     }
 
     #[inline]
@@ -1109,15 +1108,13 @@ impl Span {
     }
 
     #[inline]
-    pub fn normalize_to_macros_2_0(mut self) -> Span {
-        self.update_ctxt(|ctxt| ctxt.normalize_to_macros_2_0());
-        self
+    pub fn normalize_to_macros_2_0(self) -> Span {
+        self.map_ctxt(|ctxt| ctxt.normalize_to_macros_2_0())
     }
 
     #[inline]
-    pub fn normalize_to_macro_rules(mut self) -> Span {
-        self.update_ctxt(|ctxt| ctxt.normalize_to_macro_rules());
-        self
+    pub fn normalize_to_macro_rules(self) -> Span {
+        self.map_ctxt(|ctxt| ctxt.normalize_to_macro_rules())
     }
 }
 
diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs
index 52a1267f891..9d5bc5b0512 100644
--- a/compiler/rustc_span/src/span_encoding.rs
+++ b/compiler/rustc_span/src/span_encoding.rs
@@ -87,43 +87,150 @@ pub struct Span {
     ctxt_or_parent_or_marker: u16,
 }
 
-impl Span {
+// Convenience structures for all span formats.
+#[derive(Clone, Copy)]
+struct InlineCtxt {
+    lo: u32,
+    len: u16,
+    ctxt: u16,
+}
+
+#[derive(Clone, Copy)]
+struct InlineParent {
+    lo: u32,
+    len_with_tag: u16,
+    parent: u16,
+}
+
+#[derive(Clone, Copy)]
+struct PartiallyInterned {
+    index: u32,
+    ctxt: u16,
+}
+
+#[derive(Clone, Copy)]
+struct Interned {
+    index: u32,
+}
+
+impl InlineCtxt {
     #[inline]
-    fn data_inline_ctxt(self) -> SpanData {
-        let len = self.len_with_tag_or_marker as u32;
+    fn data(self) -> SpanData {
+        let len = self.len as u32;
         debug_assert!(len <= MAX_LEN);
         SpanData {
-            lo: BytePos(self.lo_or_index),
-            hi: BytePos(self.lo_or_index.debug_strict_add(len)),
-            ctxt: SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32),
+            lo: BytePos(self.lo),
+            hi: BytePos(self.lo.debug_strict_add(len)),
+            ctxt: SyntaxContext::from_u32(self.ctxt as u32),
             parent: None,
         }
     }
     #[inline]
-    fn data_inline_parent(self) -> SpanData {
-        let len = (self.len_with_tag_or_marker & !PARENT_TAG) as u32;
+    fn span(lo: u32, len: u16, ctxt: u16) -> Span {
+        Span { lo_or_index: lo, len_with_tag_or_marker: len, ctxt_or_parent_or_marker: ctxt }
+    }
+    #[inline]
+    fn from_span(span: Span) -> InlineCtxt {
+        let (lo, len, ctxt) =
+            (span.lo_or_index, span.len_with_tag_or_marker, span.ctxt_or_parent_or_marker);
+        InlineCtxt { lo, len, ctxt }
+    }
+}
+
+impl InlineParent {
+    #[inline]
+    fn data(self) -> SpanData {
+        let len = (self.len_with_tag & !PARENT_TAG) as u32;
         debug_assert!(len <= MAX_LEN);
-        let parent = LocalDefId {
-            local_def_index: DefIndex::from_u32(self.ctxt_or_parent_or_marker as u32),
-        };
         SpanData {
-            lo: BytePos(self.lo_or_index),
-            hi: BytePos(self.lo_or_index.debug_strict_add(len)),
+            lo: BytePos(self.lo),
+            hi: BytePos(self.lo.debug_strict_add(len)),
             ctxt: SyntaxContext::root(),
-            parent: Some(parent),
+            parent: Some(LocalDefId { local_def_index: DefIndex::from_u32(self.parent as u32) }),
         }
     }
     #[inline]
-    fn data_partially_interned(self) -> SpanData {
+    fn span(lo: u32, len: u16, parent: u16) -> Span {
+        let (lo_or_index, len_with_tag_or_marker, ctxt_or_parent_or_marker) =
+            (lo, PARENT_TAG | len, parent);
+        Span { lo_or_index, len_with_tag_or_marker, ctxt_or_parent_or_marker }
+    }
+    #[inline]
+    fn from_span(span: Span) -> InlineParent {
+        let (lo, len_with_tag, parent) =
+            (span.lo_or_index, span.len_with_tag_or_marker, span.ctxt_or_parent_or_marker);
+        InlineParent { lo, len_with_tag, parent }
+    }
+}
+
+impl PartiallyInterned {
+    #[inline]
+    fn data(self) -> SpanData {
         SpanData {
-            ctxt: SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32),
-            ..with_span_interner(|interner| interner.spans[self.lo_or_index as usize])
+            ctxt: SyntaxContext::from_u32(self.ctxt as u32),
+            ..with_span_interner(|interner| interner.spans[self.index as usize])
         }
     }
     #[inline]
-    fn data_interned(self) -> SpanData {
-        with_span_interner(|interner| interner.spans[self.lo_or_index as usize])
+    fn span(index: u32, ctxt: u16) -> Span {
+        let (lo_or_index, len_with_tag_or_marker, ctxt_or_parent_or_marker) =
+            (index, BASE_LEN_INTERNED_MARKER, ctxt);
+        Span { lo_or_index, len_with_tag_or_marker, ctxt_or_parent_or_marker }
+    }
+    #[inline]
+    fn from_span(span: Span) -> PartiallyInterned {
+        PartiallyInterned { index: span.lo_or_index, ctxt: span.ctxt_or_parent_or_marker }
+    }
+}
+
+impl Interned {
+    #[inline]
+    fn data(self) -> SpanData {
+        with_span_interner(|interner| interner.spans[self.index as usize])
+    }
+    #[inline]
+    fn span(index: u32) -> Span {
+        let (lo_or_index, len_with_tag_or_marker, ctxt_or_parent_or_marker) =
+            (index, BASE_LEN_INTERNED_MARKER, CTXT_INTERNED_MARKER);
+        Span { lo_or_index, len_with_tag_or_marker, ctxt_or_parent_or_marker }
     }
+    #[inline]
+    fn from_span(span: Span) -> Interned {
+        Interned { index: span.lo_or_index }
+    }
+}
+
+// This code is very hot, and converting span to an enum and matching on it doesn't optimize away
+// properly. So we are using a macro emulating such a match, but expand it directly to an if-else
+// chain.
+macro_rules! match_span_kind {
+    (
+        $span:expr,
+        InlineCtxt($span1:ident) => $arm1:expr,
+        InlineParent($span2:ident) => $arm2:expr,
+        PartiallyInterned($span3:ident) => $arm3:expr,
+        Interned($span4:ident) => $arm4:expr,
+    ) => {
+        if $span.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
+            if $span.len_with_tag_or_marker & PARENT_TAG == 0 {
+                // Inline-context format.
+                let $span1 = InlineCtxt::from_span($span);
+                $arm1
+            } else {
+                // Inline-parent format.
+                let $span2 = InlineParent::from_span($span);
+                $arm2
+            }
+        } else if $span.ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER {
+            // Partially-interned format.
+            let $span3 = PartiallyInterned::from_span($span);
+            $arm3
+        } else {
+            // Interned format.
+            let $span4 = Interned::from_span($span);
+            $arm4
+        }
+    };
 }
 
 // `MAX_LEN` is chosen so that `PARENT_TAG | MAX_LEN` is distinct from
@@ -154,23 +261,13 @@ impl Span {
         let (len, ctxt32) = (hi.0 - lo.0, ctxt.as_u32());
         if len <= MAX_LEN {
             if ctxt32 <= MAX_CTXT && parent.is_none() {
-                // Inline-context format.
-                return Span {
-                    lo_or_index: lo.0,
-                    len_with_tag_or_marker: len as u16,
-                    ctxt_or_parent_or_marker: ctxt32 as u16,
-                };
+                return InlineCtxt::span(lo.0, len as u16, ctxt32 as u16);
             } else if ctxt32 == 0
                 && let Some(parent) = parent
                 && let parent32 = parent.local_def_index.as_u32()
                 && parent32 <= MAX_CTXT
             {
-                // Inline-parent format.
-                return Span {
-                    lo_or_index: lo.0,
-                    len_with_tag_or_marker: PARENT_TAG | len as u16,
-                    ctxt_or_parent_or_marker: parent32 as u16,
-                };
+                return InlineParent::span(lo.0, len as u16, parent32 as u16);
             }
         }
 
@@ -179,20 +276,10 @@ impl Span {
             with_span_interner(|interner| interner.intern(&SpanData { lo, hi, ctxt, parent }))
         };
         if ctxt32 <= MAX_CTXT {
-            // Partially-interned format.
-            Span {
-                // Interned ctxt should never be read, so it can use any value.
-                lo_or_index: index(SyntaxContext::from_u32(u32::MAX)),
-                len_with_tag_or_marker: BASE_LEN_INTERNED_MARKER,
-                ctxt_or_parent_or_marker: ctxt32 as u16,
-            }
+            // Interned ctxt should never be read, so it can use any value.
+            PartiallyInterned::span(index(SyntaxContext::from_u32(u32::MAX)), ctxt32 as u16)
         } else {
-            // Interned format.
-            Span {
-                lo_or_index: index(ctxt),
-                len_with_tag_or_marker: BASE_LEN_INTERNED_MARKER,
-                ctxt_or_parent_or_marker: CTXT_INTERNED_MARKER,
-            }
+            Interned::span(index(ctxt))
         }
     }
 
@@ -209,20 +296,12 @@ impl Span {
     /// This function must not be used outside the incremental engine.
     #[inline]
     pub fn data_untracked(self) -> SpanData {
-        if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
-            if self.len_with_tag_or_marker & PARENT_TAG == 0 {
-                // Inline-context format.
-                self.data_inline_ctxt()
-            } else {
-                // Inline-parent format.
-                self.data_inline_parent()
-            }
-        } else if self.ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER {
-            // Partially-interned format.
-            self.data_partially_interned()
-        } else {
-            // Interned format.
-            self.data_interned()
+        match_span_kind! {
+            self,
+            InlineCtxt(span) => span.data(),
+            InlineParent(span) => span.data(),
+            PartiallyInterned(span) => span.data(),
+            Interned(span) => span.data(),
         }
     }
 
@@ -247,68 +326,57 @@ impl Span {
     // update doesn't change format. All non-inline or format changing scenarios require accessing
     // interner and can fall back to `Span::new`.
     #[inline]
-    pub fn update_ctxt(&mut self, update: impl FnOnce(SyntaxContext) -> SyntaxContext) {
+    pub fn map_ctxt(self, update: impl FnOnce(SyntaxContext) -> SyntaxContext) -> Span {
         let (updated_ctxt32, data);
-        if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
-            if self.len_with_tag_or_marker & PARENT_TAG == 0 {
-                // Inline-context format.
+        match_span_kind! {
+            self,
+            InlineCtxt(span) => {
                 updated_ctxt32 =
-                    update(SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32)).as_u32();
+                    update(SyntaxContext::from_u32(span.ctxt as u32)).as_u32();
                 // Any small new context including zero will preserve the format.
                 if updated_ctxt32 <= MAX_CTXT {
-                    self.ctxt_or_parent_or_marker = updated_ctxt32 as u16;
-                    return;
+                    return InlineCtxt::span(span.lo, span.len, updated_ctxt32 as u16);
                 }
-                data = self.data_inline_ctxt();
-            } else {
-                // Inline-parent format.
+                data = span.data();
+            },
+            InlineParent(span) => {
                 updated_ctxt32 = update(SyntaxContext::root()).as_u32();
                 // Only if the new context is zero the format will be preserved.
                 if updated_ctxt32 == 0 {
                     // Do nothing.
-                    return;
+                    return self;
                 }
-                data = self.data_inline_parent();
-            }
-        } else if self.ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER {
-            // Partially-interned format.
-            updated_ctxt32 =
-                update(SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32)).as_u32();
-            // Any small new context excluding zero will preserve the format.
-            // Zero may change the format to `InlineParent` if parent and len are small enough.
-            if updated_ctxt32 <= MAX_CTXT && updated_ctxt32 != 0 {
-                self.ctxt_or_parent_or_marker = updated_ctxt32 as u16;
-                return;
-            }
-            data = self.data_partially_interned();
-        } else {
-            // Interned format.
-            data = self.data_interned();
-            updated_ctxt32 = update(data.ctxt).as_u32();
+                data = span.data();
+            },
+            PartiallyInterned(span) => {
+                updated_ctxt32 = update(SyntaxContext::from_u32(span.ctxt as u32)).as_u32();
+                // Any small new context excluding zero will preserve the format.
+                // Zero may change the format to `InlineParent` if parent and len are small enough.
+                if updated_ctxt32 <= MAX_CTXT && updated_ctxt32 != 0 {
+                    return PartiallyInterned::span(span.index, updated_ctxt32 as u16);
+                }
+                data = span.data();
+            },
+            Interned(span) => {
+                data = span.data();
+                updated_ctxt32 = update(data.ctxt).as_u32();
+            },
         }
 
         // We could not keep the span in the same inline format, fall back to the complete logic.
-        *self = data.with_ctxt(SyntaxContext::from_u32(updated_ctxt32));
+        data.with_ctxt(SyntaxContext::from_u32(updated_ctxt32))
     }
 
     // Returns either syntactic context, if it can be retrieved without taking the interner lock,
     // or an index into the interner if it cannot.
     #[inline]
     fn inline_ctxt(self) -> Result<SyntaxContext, usize> {
-        if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
-            if self.len_with_tag_or_marker & PARENT_TAG == 0 {
-                // Inline-context format.
-                Ok(SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32))
-            } else {
-                // Inline-parent format.
-                Ok(SyntaxContext::root())
-            }
-        } else if self.ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER {
-            // Partially-interned format.
-            Ok(SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32))
-        } else {
-            // Interned format.
-            Err(self.lo_or_index as usize)
+        match_span_kind! {
+            self,
+            InlineCtxt(span) => Ok(SyntaxContext::from_u32(span.ctxt as u32)),
+            InlineParent(_span) => Ok(SyntaxContext::root()),
+            PartiallyInterned(span) => Ok(SyntaxContext::from_u32(span.ctxt as u32)),
+            Interned(span) => Err(span.index as usize),
         }
     }
 
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index e245dfb9f5d..f44fa1bcb4f 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -635,7 +635,9 @@ symbols! {
         coroutine,
         coroutine_clone,
         coroutine_resume,
+        coroutine_return,
         coroutine_state,
+        coroutine_yield,
         coroutines,
         cosf128,
         cosf16,
@@ -1116,6 +1118,7 @@ symbols! {
         macro_lifetime_matcher,
         macro_literal_matcher,
         macro_metavar_expr,
+        macro_metavar_expr_concat,
         macro_reexport,
         macro_use,
         macro_vis_matcher,
diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs
index 5f4ce5ed599..b057bf94a08 100644
--- a/compiler/rustc_target/src/asm/mod.rs
+++ b/compiler/rustc_target/src/asm/mod.rs
@@ -707,15 +707,19 @@ pub enum InlineAsmType {
     I32,
     I64,
     I128,
+    F16,
     F32,
     F64,
+    F128,
     VecI8(u64),
     VecI16(u64),
     VecI32(u64),
     VecI64(u64),
     VecI128(u64),
+    VecF16(u64),
     VecF32(u64),
     VecF64(u64),
+    VecF128(u64),
 }
 
 impl InlineAsmType {
@@ -730,15 +734,19 @@ impl InlineAsmType {
             Self::I32 => 4,
             Self::I64 => 8,
             Self::I128 => 16,
+            Self::F16 => 2,
             Self::F32 => 4,
             Self::F64 => 8,
+            Self::F128 => 16,
             Self::VecI8(n) => n * 1,
             Self::VecI16(n) => n * 2,
             Self::VecI32(n) => n * 4,
             Self::VecI64(n) => n * 8,
             Self::VecI128(n) => n * 16,
+            Self::VecF16(n) => n * 2,
             Self::VecF32(n) => n * 4,
             Self::VecF64(n) => n * 8,
+            Self::VecF128(n) => n * 16,
         })
     }
 }
@@ -751,15 +759,19 @@ impl fmt::Display for InlineAsmType {
             Self::I32 => f.write_str("i32"),
             Self::I64 => f.write_str("i64"),
             Self::I128 => f.write_str("i128"),
+            Self::F16 => f.write_str("f16"),
             Self::F32 => f.write_str("f32"),
             Self::F64 => f.write_str("f64"),
+            Self::F128 => f.write_str("f128"),
             Self::VecI8(n) => write!(f, "i8x{n}"),
             Self::VecI16(n) => write!(f, "i16x{n}"),
             Self::VecI32(n) => write!(f, "i32x{n}"),
             Self::VecI64(n) => write!(f, "i64x{n}"),
             Self::VecI128(n) => write!(f, "i128x{n}"),
+            Self::VecF16(n) => write!(f, "f16x{n}"),
             Self::VecF32(n) => write!(f, "f32x{n}"),
             Self::VecF64(n) => write!(f, "f64x{n}"),
+            Self::VecF128(n) => write!(f, "f128x{n}"),
         }
     }
 }
diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs
index 28413a5bcbd..8452961c17c 100644
--- a/compiler/rustc_target/src/asm/x86.rs
+++ b/compiler/rustc_target/src/asm/x86.rs
@@ -107,26 +107,26 @@ impl X86InlineAsmRegClass {
         match self {
             Self::reg | Self::reg_abcd => {
                 if arch == InlineAsmArch::X86_64 {
-                    types! { _: I16, I32, I64, F32, F64; }
+                    types! { _: I16, I32, I64, F16, F32, F64; }
                 } else {
-                    types! { _: I16, I32, F32; }
+                    types! { _: I16, I32, F16, F32; }
                 }
             }
             Self::reg_byte => types! { _: I8; },
             Self::xmm_reg => types! {
-                sse: I32, I64, F32, F64,
-                  VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2);
+                sse: I32, I64, F16, F32, F64, F128,
+                  VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF16(8), VecF32(4), VecF64(2);
             },
             Self::ymm_reg => types! {
-                avx: I32, I64, F32, F64,
-                    VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2),
-                    VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF32(8), VecF64(4);
+                avx: I32, I64, F16, F32, F64, F128,
+                    VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF16(8), VecF32(4), VecF64(2),
+                    VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF16(16), VecF32(8), VecF64(4);
             },
             Self::zmm_reg => types! {
-                avx512f: I32, I64, F32, F64,
-                    VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4), VecF64(2),
-                    VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF32(8), VecF64(4),
-                    VecI8(64), VecI16(32), VecI32(16), VecI64(8), VecF32(16), VecF64(8);
+                avx512f: I32, I64, F16, F32, F64, F128,
+                    VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF16(8), VecF32(4), VecF64(2),
+                    VecI8(32), VecI16(16), VecI32(8), VecI64(4), VecF16(16), VecF32(8), VecF64(4),
+                    VecI8(64), VecI16(32), VecI32(16), VecI64(8), VecF16(32), VecF32(16), VecF64(8);
             },
             Self::kreg => types! {
                 avx512f: I8, I16;
diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs
index f15a8798940..3e88180012e 100644
--- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs
+++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs
@@ -10,7 +10,7 @@ pub fn target() -> Target {
             std: None,
         },
         pointer_width: 64,
-        data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(),
+        data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(),
         arch: "loongarch64".into(),
         options: TargetOptions {
             cpu: "generic".into(),
diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs
index 032a29708d1..c45bc438350 100644
--- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_musl.rs
@@ -10,7 +10,7 @@ pub fn target() -> Target {
             std: None,
         },
         pointer_width: 64,
-        data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(),
+        data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(),
         arch: "loongarch64".into(),
         options: TargetOptions {
             cpu: "generic".into(),
diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs
index b2bfc8e4272..69533e82c3c 100644
--- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs
+++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs
@@ -11,7 +11,7 @@ pub fn target() -> Target {
             std: None,
         },
         pointer_width: 64,
-        data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(),
+        data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(),
         arch: "loongarch64".into(),
         options: TargetOptions {
             cpu: "generic".into(),
diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs
index 02e44c187ab..bc4c5bcde5e 100644
--- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs
+++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs
@@ -11,7 +11,7 @@ pub fn target() -> Target {
             std: None,
         },
         pointer_width: 64,
-        data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(),
+        data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(),
         arch: "loongarch64".into(),
         options: TargetOptions {
             cpu: "generic".into(),
diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wasip1.rs b/compiler/rustc_target/src/spec/targets/wasm32_wasip1.rs
index 7cbe9f09e6c..4c2d222b590 100644
--- a/compiler/rustc_target/src/spec/targets/wasm32_wasip1.rs
+++ b/compiler/rustc_target/src/spec/targets/wasm32_wasip1.rs
@@ -18,6 +18,7 @@ pub fn target() -> Target {
     let mut options = base::wasm::options();
 
     options.os = "wasi".into();
+    options.env = "p1".into();
     options.add_pre_link_args(LinkerFlavor::WasmLld(Cc::Yes), &["--target=wasm32-wasi"]);
 
     options.pre_link_objects_self_contained = crt_objects::pre_wasi_self_contained();
diff --git a/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs b/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs
index c592b944d44..38af48ab266 100644
--- a/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs
+++ b/compiler/rustc_target/src/spec/targets/wasm32_wasip1_threads.rs
@@ -13,6 +13,7 @@ pub fn target() -> Target {
     let mut options = base::wasm::options();
 
     options.os = "wasi".into();
+    options.env = "p1".into();
 
     options.add_pre_link_args(
         LinkerFlavor::WasmLld(Cc::No),
diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
index 4d7e2fc2cef..4e52caa5a5b 100644
--- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs
+++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
@@ -48,6 +48,12 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             rhs
         };
 
+        // Add a `make_canonical_response` probe step so that we treat this as
+        // a candidate, even if `try_evaluate_added_goals` bails due to an error.
+        // It's `Certainty::AMBIGUOUS` because this candidate is not "finished",
+        // since equating the normalized terms will lead to additional constraints.
+        self.inspect.make_canonical_response(Certainty::AMBIGUOUS);
+
         // Apply the constraints.
         self.try_evaluate_added_goals()?;
         let lhs = self.resolve_vars_if_possible(lhs);
@@ -55,8 +61,8 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         trace!(?lhs, ?rhs);
 
         let variance = match direction {
-            ty::AliasRelationDirection::Equate => ty::Variance::Invariant,
-            ty::AliasRelationDirection::Subtype => ty::Variance::Covariant,
+            ty::AliasRelationDirection::Equate => ty::Invariant,
+            ty::AliasRelationDirection::Subtype => ty::Covariant,
         };
         match (lhs.to_alias_term(), rhs.to_alias_term()) {
             (None, None) => {
@@ -72,7 +78,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
                 self.relate_rigid_alias_non_alias(
                     param_env,
                     alias,
-                    variance.xform(ty::Variance::Contravariant),
+                    variance.xform(ty::Contravariant),
                     lhs,
                 )?;
                 self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index b51efd339c4..72cdfb5e57b 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -1,20 +1,21 @@
 //! Code shared by trait and projection goals for candidate assembly.
 
+use derivative::Derivative;
 use rustc_hir::def_id::DefId;
+use rustc_hir::LangItem;
 use rustc_infer::infer::InferCtxt;
 use rustc_infer::traits::query::NoSolution;
 use rustc_middle::bug;
 use rustc_middle::traits::solve::inspect::ProbeKind;
-use rustc_middle::traits::solve::{
-    CandidateSource, CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult,
-};
+use rustc_middle::traits::solve::{Certainty, Goal, MaybeCause, QueryResult};
 use rustc_middle::traits::BuiltinImplSource;
 use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::ty::{fast_reject, TypeFoldable};
 use rustc_middle::ty::{TypeVisitableExt, Upcast};
 use rustc_span::{ErrorGuaranteed, DUMMY_SP};
-use std::fmt::Debug;
+use rustc_type_ir::solve::{CandidateSource, CanonicalResponse};
+use rustc_type_ir::Interner;
 
 use crate::solve::GoalSource;
 use crate::solve::{EvalCtxt, SolverMode};
@@ -25,10 +26,11 @@ pub(super) mod structural_traits;
 ///
 /// It consists of both the `source`, which describes how that goal would be proven,
 /// and the `result` when using the given `source`.
-#[derive(Debug, Clone)]
-pub(super) struct Candidate<'tcx> {
-    pub(super) source: CandidateSource<'tcx>,
-    pub(super) result: CanonicalResponse<'tcx>,
+#[derive(Derivative)]
+#[derivative(Debug(bound = ""), Clone(bound = ""))]
+pub(super) struct Candidate<I: Interner> {
+    pub(super) source: CandidateSource<I>,
+    pub(super) result: CanonicalResponse<I>,
 }
 
 /// Methods used to assemble candidates for either trait or projection goals.
@@ -49,22 +51,22 @@ pub(super) trait GoalKind<'tcx>:
     /// [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]).
     fn probe_and_match_goal_against_assumption(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        source: CandidateSource<'tcx>,
+        source: CandidateSource<TyCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
         assumption: ty::Clause<'tcx>,
         then: impl FnOnce(&mut EvalCtxt<'_, InferCtxt<'tcx>>) -> QueryResult<'tcx>,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     /// Consider a clause, which consists of a "assumption" and some "requirements",
     /// to satisfy a goal. If the requirements hold, then attempt to satisfy our
     /// goal by equating it with the assumption.
     fn probe_and_consider_implied_clause(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        parent_source: CandidateSource<'tcx>,
+        parent_source: CandidateSource<TyCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
         assumption: ty::Clause<'tcx>,
         requirements: impl IntoIterator<Item = (GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         Self::probe_and_match_goal_against_assumption(ecx, parent_source, goal, assumption, |ecx| {
             for (nested_source, goal) in requirements {
                 ecx.add_goal(nested_source, goal);
@@ -78,10 +80,10 @@ pub(super) trait GoalKind<'tcx>:
     /// since they're not implied by the well-formedness of the object type.
     fn probe_and_consider_object_bound_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        source: CandidateSource<'tcx>,
+        source: CandidateSource<TyCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
         assumption: ty::Clause<'tcx>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         Self::probe_and_match_goal_against_assumption(ecx, source, goal, assumption, |ecx| {
             let tcx = ecx.interner();
             let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else {
@@ -104,7 +106,7 @@ pub(super) trait GoalKind<'tcx>:
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
         impl_def_id: DefId,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     /// If the predicate contained an error, we want to avoid emitting unnecessary trait
     /// errors but still want to emit errors for other trait goals. We have some special
@@ -115,7 +117,7 @@ pub(super) trait GoalKind<'tcx>:
     fn consider_error_guaranteed_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         guar: ErrorGuaranteed,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     /// A type implements an `auto trait` if its components do as well.
     ///
@@ -124,13 +126,13 @@ pub(super) trait GoalKind<'tcx>:
     fn consider_auto_trait_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     /// A trait alias holds if the RHS traits and `where` clauses hold.
     fn consider_trait_alias_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     /// A type is `Sized` if its tail component is `Sized`.
     ///
@@ -139,7 +141,7 @@ pub(super) trait GoalKind<'tcx>:
     fn consider_builtin_sized_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     /// A type is `Copy` or `Clone` if its components are `Copy` or `Clone`.
     ///
@@ -148,20 +150,20 @@ pub(super) trait GoalKind<'tcx>:
     fn consider_builtin_copy_clone_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     /// A type is `PointerLike` if we can compute its layout, and that layout
     /// matches the layout of `usize`.
     fn consider_builtin_pointer_like_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     /// A type is a `FnPtr` if it is of `FnPtr` type.
     fn consider_builtin_fn_ptr_trait_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     /// A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn<A>`
     /// family of traits where `A` is given by the signature of the type.
@@ -169,7 +171,7 @@ pub(super) trait GoalKind<'tcx>:
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
         kind: ty::ClosureKind,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     /// An async closure is known to implement the `AsyncFn<A>` family of traits
     /// where `A` is given by the signature of the type.
@@ -177,7 +179,7 @@ pub(super) trait GoalKind<'tcx>:
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
         kind: ty::ClosureKind,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     /// Compute the built-in logic of the `AsyncFnKindHelper` helper trait, which
     /// is used internally to delay computation for async closures until after
@@ -185,13 +187,13 @@ pub(super) trait GoalKind<'tcx>:
     fn consider_builtin_async_fn_kind_helper_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     /// `Tuple` is implemented if the `Self` type is a tuple.
     fn consider_builtin_tuple_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     /// `Pointee` is always implemented.
     ///
@@ -201,7 +203,7 @@ pub(super) trait GoalKind<'tcx>:
     fn consider_builtin_pointee_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     /// A coroutine (that comes from an `async` desugaring) is known to implement
     /// `Future<Output = O>`, where `O` is given by the coroutine's return type
@@ -209,7 +211,7 @@ pub(super) trait GoalKind<'tcx>:
     fn consider_builtin_future_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     /// A coroutine (that comes from a `gen` desugaring) is known to implement
     /// `Iterator<Item = O>`, where `O` is given by the generator's yield type
@@ -217,19 +219,19 @@ pub(super) trait GoalKind<'tcx>:
     fn consider_builtin_iterator_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     /// A coroutine (that comes from a `gen` desugaring) is known to implement
     /// `FusedIterator`
     fn consider_builtin_fused_iterator_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     fn consider_builtin_async_iterator_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     /// A coroutine (that doesn't come from an `async` or `gen` desugaring) is known to
     /// implement `Coroutine<R, Yield = Y, Return = O>`, given the resume, yield,
@@ -237,27 +239,27 @@ pub(super) trait GoalKind<'tcx>:
     fn consider_builtin_coroutine_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     fn consider_builtin_discriminant_kind_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     fn consider_builtin_async_destruct_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     fn consider_builtin_destruct_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     fn consider_builtin_transmute_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution>;
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
 
     /// Consider (possibly several) candidates to upcast or unsize a type to another
     /// type, excluding the coercion of a sized type into a `dyn Trait`.
@@ -269,14 +271,14 @@ pub(super) trait GoalKind<'tcx>:
     fn consider_structural_builtin_unsize_candidates(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Vec<Candidate<'tcx>>;
+    ) -> Vec<Candidate<TyCtxt<'tcx>>>;
 }
 
 impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     pub(super) fn assemble_and_evaluate_candidates<G: GoalKind<'tcx>>(
         &mut self,
         goal: Goal<'tcx, G>,
-    ) -> Vec<Candidate<'tcx>> {
+    ) -> Vec<Candidate<TyCtxt<'tcx>>> {
         let Ok(normalized_self_ty) =
             self.structurally_normalize_ty(goal.param_env, goal.predicate.self_ty())
         else {
@@ -323,7 +325,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     pub(super) fn forced_ambiguity(
         &mut self,
         cause: MaybeCause,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         // This may fail if `try_evaluate_added_goals` overflows because it
         // fails to reach a fixpoint but ends up getting an error after
         // running for some additional step.
@@ -339,7 +341,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     fn assemble_non_blanket_impl_candidates<G: GoalKind<'tcx>>(
         &mut self,
         goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<'tcx>>,
+        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
     ) {
         let tcx = self.interner();
         let self_ty = goal.predicate.self_ty();
@@ -455,7 +457,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     fn assemble_blanket_impl_candidates<G: GoalKind<'tcx>>(
         &mut self,
         goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<'tcx>>,
+        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
     ) {
         let tcx = self.interner();
         let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx));
@@ -478,10 +480,9 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     fn assemble_builtin_impl_candidates<G: GoalKind<'tcx>>(
         &mut self,
         goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<'tcx>>,
+        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
     ) {
         let tcx = self.interner();
-        let lang_items = tcx.lang_items();
         let trait_def_id = goal.predicate.trait_def_id(tcx);
 
         // N.B. When assembling built-in candidates for lang items that are also
@@ -497,43 +498,43 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             G::consider_auto_trait_candidate(self, goal)
         } else if tcx.trait_is_alias(trait_def_id) {
             G::consider_trait_alias_candidate(self, goal)
-        } else if lang_items.sized_trait() == Some(trait_def_id) {
+        } else if tcx.is_lang_item(trait_def_id, LangItem::Sized) {
             G::consider_builtin_sized_candidate(self, goal)
-        } else if lang_items.copy_trait() == Some(trait_def_id)
-            || lang_items.clone_trait() == Some(trait_def_id)
+        } else if tcx.is_lang_item(trait_def_id, LangItem::Copy)
+            || tcx.is_lang_item(trait_def_id, LangItem::Clone)
         {
             G::consider_builtin_copy_clone_candidate(self, goal)
-        } else if lang_items.pointer_like() == Some(trait_def_id) {
+        } else if tcx.is_lang_item(trait_def_id, LangItem::PointerLike) {
             G::consider_builtin_pointer_like_candidate(self, goal)
-        } else if lang_items.fn_ptr_trait() == Some(trait_def_id) {
+        } else if tcx.is_lang_item(trait_def_id, LangItem::FnPtrTrait) {
             G::consider_builtin_fn_ptr_trait_candidate(self, goal)
         } else if let Some(kind) = self.interner().fn_trait_kind_from_def_id(trait_def_id) {
             G::consider_builtin_fn_trait_candidates(self, goal, kind)
         } else if let Some(kind) = self.interner().async_fn_trait_kind_from_def_id(trait_def_id) {
             G::consider_builtin_async_fn_trait_candidates(self, goal, kind)
-        } else if lang_items.async_fn_kind_helper() == Some(trait_def_id) {
+        } else if tcx.is_lang_item(trait_def_id, LangItem::AsyncFnKindHelper) {
             G::consider_builtin_async_fn_kind_helper_candidate(self, goal)
-        } else if lang_items.tuple_trait() == Some(trait_def_id) {
+        } else if tcx.is_lang_item(trait_def_id, LangItem::Tuple) {
             G::consider_builtin_tuple_candidate(self, goal)
-        } else if lang_items.pointee_trait() == Some(trait_def_id) {
+        } else if tcx.is_lang_item(trait_def_id, LangItem::PointeeTrait) {
             G::consider_builtin_pointee_candidate(self, goal)
-        } else if lang_items.future_trait() == Some(trait_def_id) {
+        } else if tcx.is_lang_item(trait_def_id, LangItem::Future) {
             G::consider_builtin_future_candidate(self, goal)
-        } else if lang_items.iterator_trait() == Some(trait_def_id) {
+        } else if tcx.is_lang_item(trait_def_id, LangItem::Iterator) {
             G::consider_builtin_iterator_candidate(self, goal)
-        } else if lang_items.fused_iterator_trait() == Some(trait_def_id) {
+        } else if tcx.is_lang_item(trait_def_id, LangItem::FusedIterator) {
             G::consider_builtin_fused_iterator_candidate(self, goal)
-        } else if lang_items.async_iterator_trait() == Some(trait_def_id) {
+        } else if tcx.is_lang_item(trait_def_id, LangItem::AsyncIterator) {
             G::consider_builtin_async_iterator_candidate(self, goal)
-        } else if lang_items.coroutine_trait() == Some(trait_def_id) {
+        } else if tcx.is_lang_item(trait_def_id, LangItem::Coroutine) {
             G::consider_builtin_coroutine_candidate(self, goal)
-        } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) {
+        } else if tcx.is_lang_item(trait_def_id, LangItem::DiscriminantKind) {
             G::consider_builtin_discriminant_kind_candidate(self, goal)
-        } else if lang_items.async_destruct_trait() == Some(trait_def_id) {
+        } else if tcx.is_lang_item(trait_def_id, LangItem::AsyncDestruct) {
             G::consider_builtin_async_destruct_candidate(self, goal)
-        } else if lang_items.destruct_trait() == Some(trait_def_id) {
+        } else if tcx.is_lang_item(trait_def_id, LangItem::Destruct) {
             G::consider_builtin_destruct_candidate(self, goal)
-        } else if lang_items.transmute_trait() == Some(trait_def_id) {
+        } else if tcx.is_lang_item(trait_def_id, LangItem::TransmuteTrait) {
             G::consider_builtin_transmute_candidate(self, goal)
         } else {
             Err(NoSolution)
@@ -543,7 +544,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
 
         // There may be multiple unsize candidates for a trait with several supertraits:
         // `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>`
-        if lang_items.unsize_trait() == Some(trait_def_id) {
+        if tcx.is_lang_item(trait_def_id, LangItem::Unsize) {
             candidates.extend(G::consider_structural_builtin_unsize_candidates(self, goal));
         }
     }
@@ -552,7 +553,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     fn assemble_param_env_candidates<G: GoalKind<'tcx>>(
         &mut self,
         goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<'tcx>>,
+        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
     ) {
         for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() {
             candidates.extend(G::probe_and_consider_implied_clause(
@@ -569,7 +570,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     fn assemble_alias_bound_candidates<G: GoalKind<'tcx>>(
         &mut self,
         goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<'tcx>>,
+        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
     ) {
         let () = self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| {
             ecx.assemble_alias_bound_candidates_recur(goal.predicate.self_ty(), goal, candidates);
@@ -589,7 +590,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         &mut self,
         self_ty: Ty<'tcx>,
         goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<'tcx>>,
+        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
     ) {
         let (kind, alias_ty) = match *self_ty.kind() {
             ty::Bool
@@ -673,7 +674,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     fn assemble_object_bound_candidates<G: GoalKind<'tcx>>(
         &mut self,
         goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<'tcx>>,
+        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
     ) {
         let tcx = self.interner();
         if !tcx.trait_def(goal.predicate.trait_def_id(tcx)).implement_via_object {
@@ -764,7 +765,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     fn assemble_coherence_unknowable_candidates<G: GoalKind<'tcx>>(
         &mut self,
         goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<'tcx>>,
+        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
     ) {
         let tcx = self.interner();
 
@@ -793,7 +794,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     fn discard_impls_shadowed_by_env<G: GoalKind<'tcx>>(
         &mut self,
         goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<'tcx>>,
+        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
     ) {
         let tcx = self.interner();
         let trait_goal: Goal<'tcx, ty::TraitPredicate<'tcx>> =
@@ -841,7 +842,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     #[instrument(level = "debug", skip(self), ret)]
     pub(super) fn merge_candidates(
         &mut self,
-        candidates: Vec<Candidate<'tcx>>,
+        candidates: Vec<Candidate<TyCtxt<'tcx>>>,
     ) -> QueryResult<'tcx> {
         // First try merging all candidates. This is complete and fully sound.
         let responses = candidates.iter().map(|c| c.result).collect::<Vec<_>>();
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
index 98f98d9992d..d6074617caf 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
@@ -1,14 +1,14 @@
 //! Code which is used by built-in goals that match "structurally", such a auto
 //! traits, `Copy`/`Clone`.
+
+use rustc_ast_ir::{Movability, Mutability};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::LangItem;
-use rustc_hir::{def_id::DefId, Movability, Mutability};
-use rustc_infer::infer::InferCtxt;
-use rustc_infer::traits::query::NoSolution;
-use rustc_macros::{TypeFoldable, TypeVisitable};
-use rustc_middle::bug;
-use rustc_middle::traits::solve::Goal;
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, Upcast};
+use rustc_next_trait_solver::solve::{Goal, NoSolution};
+use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
+use rustc_type_ir::inherent::*;
+use rustc_type_ir::lang_items::TraitSolverLangItem;
+use rustc_type_ir::{self as ty, InferCtxtLike, Interner, Upcast};
+use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic};
 
 use crate::solve::EvalCtxt;
 
@@ -17,12 +17,16 @@ use crate::solve::EvalCtxt;
 // For types with an "existential" binder, i.e. coroutine witnesses, we also
 // instantiate the binder with placeholders eagerly.
 #[instrument(level = "trace", skip(ecx), ret)]
-pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
-    ecx: &EvalCtxt<'_, InferCtxt<'tcx>>,
-    ty: Ty<'tcx>,
-) -> Result<Vec<ty::Binder<'tcx, Ty<'tcx>>>, NoSolution> {
+pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<Infcx, I>(
+    ecx: &EvalCtxt<'_, Infcx>,
+    ty: I::Ty,
+) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
     let tcx = ecx.interner();
-    match *ty.kind() {
+    match ty.kind() {
         ty::Uint(_)
         | ty::Int(_)
         | ty::Bool
@@ -34,7 +38,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
         | ty::Char => Ok(vec![]),
 
         // Treat `str` like it's defined as `struct str([u8]);`
-        ty::Str => Ok(vec![ty::Binder::dummy(Ty::new_slice(tcx, tcx.types.u8))]),
+        ty::Str => Ok(vec![ty::Binder::dummy(Ty::new_slice(tcx, Ty::new_u8(tcx)))]),
 
         ty::Dynamic(..)
         | ty::Param(..)
@@ -43,7 +47,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
         | ty::Placeholder(..)
         | ty::Bound(..)
         | ty::Infer(_) => {
-            bug!("unexpected type `{ty}`")
+            panic!("unexpected type `{ty:?}`")
         }
 
         ty::RawPtr(element_ty, _) | ty::Ref(_, element_ty, _) => {
@@ -56,7 +60,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
 
         ty::Tuple(tys) => {
             // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet
-            Ok(tys.iter().map(ty::Binder::dummy).collect())
+            Ok(tys.into_iter().map(ty::Binder::dummy).collect())
         }
 
         ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]),
@@ -76,31 +80,38 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
         ty::CoroutineWitness(def_id, args) => Ok(ecx
             .interner()
             .bound_coroutine_hidden_types(def_id)
-            .map(|bty| bty.instantiate(tcx, args))
+            .into_iter()
+            .map(|bty| bty.instantiate(tcx, &args))
             .collect()),
 
         // For `PhantomData<T>`, we pass `T`.
         ty::Adt(def, args) if def.is_phantom_data() => Ok(vec![ty::Binder::dummy(args.type_at(0))]),
 
-        ty::Adt(def, args) => {
-            Ok(def.all_fields().map(|f| ty::Binder::dummy(f.ty(tcx, args))).collect())
-        }
+        ty::Adt(def, args) => Ok(def
+            .all_field_tys(tcx)
+            .iter_instantiated(tcx, &args)
+            .map(ty::Binder::dummy)
+            .collect()),
 
         ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
             // We can resolve the `impl Trait` to its concrete type,
             // which enforces a DAG between the functions requiring
             // the auto trait bounds in question.
-            Ok(vec![ty::Binder::dummy(tcx.type_of(def_id).instantiate(tcx, args))])
+            Ok(vec![ty::Binder::dummy(tcx.type_of(def_id).instantiate(tcx, &args))])
         }
     }
 }
 
 #[instrument(level = "trace", skip(ecx), ret)]
-pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
-    ecx: &EvalCtxt<'_, InferCtxt<'tcx>>,
-    ty: Ty<'tcx>,
-) -> Result<Vec<ty::Binder<'tcx, Ty<'tcx>>>, NoSolution> {
-    match *ty.kind() {
+pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<Infcx, I>(
+    ecx: &EvalCtxt<'_, Infcx>,
+    ty: I::Ty,
+) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
+    match ty.kind() {
         // impl Sized for u*, i*, bool, f*, FnDef, FnPtr, *(const/mut) T, char, &mut? T, [T; N], dyn* Trait, !
         // impl Sized for Coroutine, CoroutineWitness, Closure, CoroutineClosure
         ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
@@ -133,7 +144,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
 
         ty::Bound(..)
         | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
-            bug!("unexpected type `{ty}`")
+            panic!("unexpected type `{ty:?}`")
         }
 
         // impl Sized for ()
@@ -151,7 +162,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
         //   if the ADT is sized for all possible args.
         ty::Adt(def, args) => {
             if let Some(sized_crit) = def.sized_constraint(ecx.interner()) {
-                Ok(vec![ty::Binder::dummy(sized_crit.instantiate(ecx.interner(), args))])
+                Ok(vec![ty::Binder::dummy(sized_crit.instantiate(ecx.interner(), &args))])
             } else {
                 Ok(vec![])
             }
@@ -160,11 +171,15 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
 }
 
 #[instrument(level = "trace", skip(ecx), ret)]
-pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
-    ecx: &EvalCtxt<'_, InferCtxt<'tcx>>,
-    ty: Ty<'tcx>,
-) -> Result<Vec<ty::Binder<'tcx, Ty<'tcx>>>, NoSolution> {
-    match *ty.kind() {
+pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<Infcx, I>(
+    ecx: &EvalCtxt<'_, Infcx>,
+    ty: I::Ty,
+) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
+    match ty.kind() {
         // impl Copy/Clone for FnDef, FnPtr
         ty::FnDef(..) | ty::FnPtr(_) | ty::Error(_) => Ok(vec![]),
 
@@ -196,11 +211,11 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
 
         ty::Bound(..)
         | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
-            bug!("unexpected type `{ty}`")
+            panic!("unexpected type `{ty:?}`")
         }
 
         // impl Copy/Clone for (T1, T2, .., Tn) where T1: Copy/Clone, T2: Copy/Clone, .. Tn: Copy/Clone
-        ty::Tuple(tys) => Ok(tys.iter().map(ty::Binder::dummy).collect()),
+        ty::Tuple(tys) => Ok(tys.into_iter().map(ty::Binder::dummy).collect()),
 
         // impl Copy/Clone for Closure where Self::TupledUpvars: Copy/Clone
         ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]),
@@ -212,7 +227,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
         ty::Coroutine(def_id, args) => match ecx.interner().coroutine_movability(def_id) {
             Movability::Static => Err(NoSolution),
             Movability::Movable => {
-                if ecx.interner().features().coroutine_clone {
+                if ecx.interner().features().coroutine_clone() {
                     let coroutine = args.as_coroutine();
                     Ok(vec![
                         ty::Binder::dummy(coroutine.tupled_upvars_ty()),
@@ -228,27 +243,26 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
         ty::CoroutineWitness(def_id, args) => Ok(ecx
             .interner()
             .bound_coroutine_hidden_types(def_id)
-            .map(|bty| bty.instantiate(ecx.interner(), args))
+            .into_iter()
+            .map(|bty| bty.instantiate(ecx.interner(), &args))
             .collect()),
     }
 }
 
 // Returns a binder of the tupled inputs types and output type from a builtin callable type.
-pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    self_ty: Ty<'tcx>,
+pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<I: Interner>(
+    tcx: I,
+    self_ty: I::Ty,
     goal_kind: ty::ClosureKind,
-) -> Result<Option<ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>)>>, NoSolution> {
-    match *self_ty.kind() {
+) -> Result<Option<ty::Binder<I, (I::Ty, I::Ty)>>, NoSolution> {
+    match self_ty.kind() {
         // keep this in sync with assemble_fn_pointer_candidates until the old solver is removed.
         ty::FnDef(def_id, args) => {
             let sig = tcx.fn_sig(def_id);
-            if sig.skip_binder().is_fn_trait_compatible()
-                && tcx.codegen_fn_attrs(def_id).target_features.is_empty()
-            {
+            if sig.skip_binder().is_fn_trait_compatible() && !tcx.has_target_features(def_id) {
                 Ok(Some(
-                    sig.instantiate(tcx, args)
-                        .map_bound(|sig| (Ty::new_tup(tcx, sig.inputs()), sig.output())),
+                    sig.instantiate(tcx, &args)
+                        .map_bound(|sig| (Ty::new_tup(tcx, &sig.inputs()), sig.output())),
                 ))
             } else {
                 Err(NoSolution)
@@ -257,7 +271,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
         // keep this in sync with assemble_fn_pointer_candidates until the old solver is removed.
         ty::FnPtr(sig) => {
             if sig.is_fn_trait_compatible() {
-                Ok(Some(sig.map_bound(|sig| (Ty::new_tup(tcx, sig.inputs()), sig.output()))))
+                Ok(Some(sig.map_bound(|sig| (Ty::new_tup(tcx, &sig.inputs()), sig.output()))))
             } else {
                 Err(NoSolution)
             }
@@ -311,7 +325,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
                     tcx,
                     goal_kind,
                     // No captures by ref, so this doesn't matter.
-                    tcx.lifetimes.re_static,
+                    Region::new_static(tcx),
                     def_id,
                     args,
                     sig,
@@ -326,7 +340,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
                 coroutine_closure_to_ambiguous_coroutine(
                     tcx,
                     goal_kind, // No captures by ref, so this doesn't matter.
-                    tcx.lifetimes.re_static,
+                    Region::new_static(tcx),
                     def_id,
                     args,
                     sig,
@@ -362,22 +376,24 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
 
         ty::Bound(..)
         | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
-            bug!("unexpected type `{self_ty}`")
+            panic!("unexpected type `{self_ty:?}`")
         }
     }
 }
 
 /// Relevant types for an async callable, including its inputs, output,
 /// and the return type you get from awaiting the output.
-#[derive(Copy, Clone, Debug, TypeVisitable, TypeFoldable)]
-pub(in crate::solve) struct AsyncCallableRelevantTypes<'tcx> {
-    pub tupled_inputs_ty: Ty<'tcx>,
+#[derive(derivative::Derivative)]
+#[derivative(Clone(bound = ""), Copy(bound = ""), Debug(bound = ""))]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
+pub(in crate::solve) struct AsyncCallableRelevantTypes<I: Interner> {
+    pub tupled_inputs_ty: I::Ty,
     /// Type returned by calling the closure
     /// i.e. `f()`.
-    pub output_coroutine_ty: Ty<'tcx>,
+    pub output_coroutine_ty: I::Ty,
     /// Type returned by `await`ing the output
     /// i.e. `f().await`.
-    pub coroutine_return_ty: Ty<'tcx>,
+    pub coroutine_return_ty: I::Ty,
 }
 
 // Returns a binder of the tupled inputs types, output type, and coroutine type
@@ -385,16 +401,13 @@ pub(in crate::solve) struct AsyncCallableRelevantTypes<'tcx> {
 // the coroutine-closure, emit an additional trait predicate for `AsyncFnKindHelper`
 // which enforces the closure is actually callable with the given trait. When we
 // know the kind already, we can short-circuit this check.
-pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    self_ty: Ty<'tcx>,
+pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<I: Interner>(
+    tcx: I,
+    self_ty: I::Ty,
     goal_kind: ty::ClosureKind,
-    env_region: ty::Region<'tcx>,
-) -> Result<
-    (ty::Binder<'tcx, AsyncCallableRelevantTypes<'tcx>>, Vec<ty::Predicate<'tcx>>),
-    NoSolution,
-> {
-    match *self_ty.kind() {
+    env_region: I::Region,
+) -> Result<(ty::Binder<I, AsyncCallableRelevantTypes<I>>, Vec<I::Predicate>), NoSolution> {
+    match self_ty.kind() {
         ty::CoroutineClosure(def_id, args) => {
             let args = args.as_coroutine_closure();
             let kind_ty = args.kind_ty();
@@ -421,7 +434,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
                 nested.push(
                     ty::TraitRef::new(
                         tcx,
-                        tcx.require_lang_item(LangItem::AsyncFnKindHelper, None),
+                        tcx.require_lang_item(TraitSolverLangItem::AsyncFnKindHelper),
                         [kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
                     )
                     .upcast(tcx),
@@ -445,7 +458,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
         ty::FnDef(..) | ty::FnPtr(..) => {
             let bound_sig = self_ty.fn_sig(tcx);
             let sig = bound_sig.skip_binder();
-            let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
+            let future_trait_def_id = tcx.require_lang_item(TraitSolverLangItem::Future);
             // `FnDef` and `FnPtr` only implement `AsyncFn*` when their
             // return type implements `Future`.
             let nested = vec![
@@ -453,11 +466,11 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
                     .rebind(ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()]))
                     .upcast(tcx),
             ];
-            let future_output_def_id = tcx.require_lang_item(LangItem::FutureOutput, None);
+            let future_output_def_id = tcx.require_lang_item(TraitSolverLangItem::FutureOutput);
             let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
             Ok((
                 bound_sig.rebind(AsyncCallableRelevantTypes {
-                    tupled_inputs_ty: Ty::new_tup(tcx, sig.inputs()),
+                    tupled_inputs_ty: Ty::new_tup(tcx, &sig.inputs()),
                     output_coroutine_ty: sig.output(),
                     coroutine_return_ty: future_output_ty,
                 }),
@@ -468,7 +481,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
             let args = args.as_closure();
             let bound_sig = args.sig();
             let sig = bound_sig.skip_binder();
-            let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
+            let future_trait_def_id = tcx.require_lang_item(TraitSolverLangItem::Future);
             // `Closure`s only implement `AsyncFn*` when their return type
             // implements `Future`.
             let mut nested = vec![
@@ -486,7 +499,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
                 }
             } else {
                 let async_fn_kind_trait_def_id =
-                    tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
+                    tcx.require_lang_item(TraitSolverLangItem::AsyncFnKindHelper);
                 // When we don't know the closure kind (and therefore also the closure's upvars,
                 // which are computed at the same time), we must delay the computation of the
                 // generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait
@@ -504,7 +517,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
                 );
             }
 
-            let future_output_def_id = tcx.require_lang_item(LangItem::FutureOutput, None);
+            let future_output_def_id = tcx.require_lang_item(TraitSolverLangItem::FutureOutput);
             let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
             Ok((
                 bound_sig.rebind(AsyncCallableRelevantTypes {
@@ -542,21 +555,21 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
 
         ty::Bound(..)
         | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
-            bug!("unexpected type `{self_ty}`")
+            panic!("unexpected type `{self_ty:?}`")
         }
     }
 }
 
 /// Given a coroutine-closure, project to its returned coroutine when we are *certain*
 /// that the closure's kind is compatible with the goal.
-fn coroutine_closure_to_certain_coroutine<'tcx>(
-    tcx: TyCtxt<'tcx>,
+fn coroutine_closure_to_certain_coroutine<I: Interner>(
+    tcx: I,
     goal_kind: ty::ClosureKind,
-    goal_region: ty::Region<'tcx>,
-    def_id: DefId,
-    args: ty::CoroutineClosureArgs<TyCtxt<'tcx>>,
-    sig: ty::CoroutineClosureSignature<TyCtxt<'tcx>>,
-) -> Ty<'tcx> {
+    goal_region: I::Region,
+    def_id: I::DefId,
+    args: ty::CoroutineClosureArgs<I>,
+    sig: ty::CoroutineClosureSignature<I>,
+) -> I::Ty {
     sig.to_coroutine_given_kind_and_upvars(
         tcx,
         args.parent_args(),
@@ -573,20 +586,20 @@ fn coroutine_closure_to_certain_coroutine<'tcx>(
 /// yet what the closure's upvars are.
 ///
 /// Note that we do not also push a `AsyncFnKindHelper` goal here.
-fn coroutine_closure_to_ambiguous_coroutine<'tcx>(
-    tcx: TyCtxt<'tcx>,
+fn coroutine_closure_to_ambiguous_coroutine<I: Interner>(
+    tcx: I,
     goal_kind: ty::ClosureKind,
-    goal_region: ty::Region<'tcx>,
-    def_id: DefId,
-    args: ty::CoroutineClosureArgs<TyCtxt<'tcx>>,
-    sig: ty::CoroutineClosureSignature<TyCtxt<'tcx>>,
-) -> Ty<'tcx> {
-    let upvars_projection_def_id = tcx.require_lang_item(LangItem::AsyncFnKindUpvars, None);
+    goal_region: I::Region,
+    def_id: I::DefId,
+    args: ty::CoroutineClosureArgs<I>,
+    sig: ty::CoroutineClosureSignature<I>,
+) -> I::Ty {
+    let upvars_projection_def_id = tcx.require_lang_item(TraitSolverLangItem::AsyncFnKindUpvars);
     let tupled_upvars_ty = Ty::new_projection(
         tcx,
         upvars_projection_def_id,
         [
-            ty::GenericArg::from(args.kind_ty()),
+            I::GenericArg::from(args.kind_ty()),
             Ty::from_closure_kind(tcx, goal_kind).into(),
             goal_region.into(),
             sig.tupled_inputs_ty.into(),
@@ -643,41 +656,44 @@ fn coroutine_closure_to_ambiguous_coroutine<'tcx>(
 // This is unsound in general and once that is fixed, we don't need to
 // normalize eagerly here. See https://github.com/lcnr/solver-woes/issues/9
 // for more details.
-pub(in crate::solve) fn predicates_for_object_candidate<'tcx>(
-    ecx: &EvalCtxt<'_, InferCtxt<'tcx>>,
-    param_env: ty::ParamEnv<'tcx>,
-    trait_ref: ty::TraitRef<'tcx>,
-    object_bound: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
-) -> Vec<Goal<'tcx, ty::Predicate<'tcx>>> {
+pub(in crate::solve) fn predicates_for_object_candidate<Infcx, I>(
+    ecx: &EvalCtxt<'_, Infcx>,
+    param_env: I::ParamEnv,
+    trait_ref: ty::TraitRef<I>,
+    object_bounds: I::BoundExistentialPredicates,
+) -> Vec<Goal<I, I::Predicate>>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
     let tcx = ecx.interner();
     let mut requirements = vec![];
-    requirements.extend(
-        tcx.super_predicates_of(trait_ref.def_id).instantiate(tcx, trait_ref.args).predicates,
-    );
-    for item in tcx.associated_items(trait_ref.def_id).in_definition_order() {
-        // FIXME(associated_const_equality): Also add associated consts to
-        // the requirements here.
-        if item.kind == ty::AssocKind::Type {
-            // associated types that require `Self: Sized` do not show up in the built-in
-            // implementation of `Trait for dyn Trait`, and can be dropped here.
-            if tcx.generics_require_sized_self(item.def_id) {
-                continue;
-            }
-
-            requirements
-                .extend(tcx.item_bounds(item.def_id).iter_instantiated(tcx, trait_ref.args));
+    requirements
+        .extend(tcx.super_predicates_of(trait_ref.def_id).iter_instantiated(tcx, &trait_ref.args));
+
+    // FIXME(associated_const_equality): Also add associated consts to
+    // the requirements here.
+    for associated_type_def_id in tcx.associated_type_def_ids(trait_ref.def_id) {
+        // associated types that require `Self: Sized` do not show up in the built-in
+        // implementation of `Trait for dyn Trait`, and can be dropped here.
+        if tcx.generics_require_sized_self(associated_type_def_id) {
+            continue;
         }
+
+        requirements.extend(
+            tcx.item_bounds(associated_type_def_id).iter_instantiated(tcx, &trait_ref.args),
+        );
     }
 
     let mut replace_projection_with = FxHashMap::default();
-    for bound in object_bound {
+    for bound in object_bounds {
         if let ty::ExistentialPredicate::Projection(proj) = bound.skip_binder() {
             let proj = proj.with_self_ty(tcx, trait_ref.self_ty());
             let old_ty = replace_projection_with.insert(proj.def_id(), bound.rebind(proj));
             assert_eq!(
                 old_ty,
                 None,
-                "{} has two generic parameters: {} and {}",
+                "{:?} has two generic parameters: {:?} and {:?}",
                 proj.projection_term,
                 proj.term,
                 old_ty.unwrap()
@@ -696,20 +712,22 @@ pub(in crate::solve) fn predicates_for_object_candidate<'tcx>(
         .collect()
 }
 
-struct ReplaceProjectionWith<'a, 'tcx> {
-    ecx: &'a EvalCtxt<'a, InferCtxt<'tcx>>,
-    param_env: ty::ParamEnv<'tcx>,
-    mapping: FxHashMap<DefId, ty::PolyProjectionPredicate<'tcx>>,
-    nested: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
+struct ReplaceProjectionWith<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> {
+    ecx: &'a EvalCtxt<'a, Infcx>,
+    param_env: I::ParamEnv,
+    mapping: FxHashMap<I::DefId, ty::Binder<I, ty::ProjectionPredicate<I>>>,
+    nested: Vec<Goal<I, I::Predicate>>,
 }
 
-impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceProjectionWith<'_, 'tcx> {
-    fn interner(&self) -> TyCtxt<'tcx> {
+impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I>
+    for ReplaceProjectionWith<'_, Infcx, I>
+{
+    fn interner(&self) -> I {
         self.ecx.interner()
     }
 
-    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        if let ty::Alias(ty::Projection, alias_ty) = *ty.kind()
+    fn fold_ty(&mut self, ty: I::Ty) -> I::Ty {
+        if let ty::Alias(ty::Projection, alias_ty) = ty.kind()
             && let Some(replacement) = self.mapping.get(&alias_ty.def_id)
         {
             // We may have a case where our object type's projection bound is higher-ranked,
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
index 43013a01069..860c580374d 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
@@ -1,9 +1,6 @@
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_hir::def_id::DefId;
-use rustc_infer::infer::at::ToTrace;
-use rustc_infer::infer::{
-    BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt,
-};
+use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
 use rustc_infer::traits::query::NoSolution;
 use rustc_infer::traits::solve::{MaybeCause, NestedNormalizationGoals};
 use rustc_infer::traits::ObligationCause;
@@ -15,11 +12,13 @@ use rustc_middle::traits::solve::{
 use rustc_middle::ty::AliasRelationDirection;
 use rustc_middle::ty::TypeFolder;
 use rustc_middle::ty::{
-    self, InferCtxtLike, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
-    TypeVisitable, TypeVisitableExt, TypeVisitor,
+    self, InferCtxtLike, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeVisitableExt,
 };
 use rustc_span::DUMMY_SP;
 use rustc_type_ir::fold::TypeSuperFoldable;
+use rustc_type_ir::inherent::*;
+use rustc_type_ir::relate::Relate;
+use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
 use rustc_type_ir::{self as ir, CanonicalVarValues, Interner};
 use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic};
 use std::ops::ControlFlow;
@@ -456,28 +455,6 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
         }
     }
 
-    #[instrument(level = "trace", skip(self))]
-    pub(super) fn add_normalizes_to_goal(&mut self, mut goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) {
-        goal.predicate = goal
-            .predicate
-            .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env });
-        self.inspect.add_normalizes_to_goal(self.infcx, self.max_input_universe, goal);
-        self.nested_goals.normalizes_to_goals.push(goal);
-    }
-
-    #[instrument(level = "debug", skip(self))]
-    pub(super) fn add_goal(
-        &mut self,
-        source: GoalSource,
-        mut goal: Goal<'tcx, ty::Predicate<'tcx>>,
-    ) {
-        goal.predicate = goal
-            .predicate
-            .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env });
-        self.inspect.add_goal(self.infcx, self.max_input_universe, source, goal);
-        self.nested_goals.goals.push((source, goal));
-    }
-
     // Recursively evaluates all the goals added to this `EvalCtxt` to completion, returning
     // the certainty of all the goals.
     #[instrument(level = "trace", skip(self))]
@@ -600,27 +577,61 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
     pub(super) fn interner(&self) -> I {
         self.infcx.interner()
     }
-}
 
-impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
-    pub(super) fn next_ty_infer(&mut self) -> Ty<'tcx> {
-        let ty = self.infcx.next_ty_var(DUMMY_SP);
+    #[instrument(level = "trace", skip(self))]
+    pub(super) fn add_normalizes_to_goal(
+        &mut self,
+        mut goal: ir::solve::Goal<I, ir::NormalizesTo<I>>,
+    ) {
+        goal.predicate = goal
+            .predicate
+            .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env });
+        self.inspect.add_normalizes_to_goal(self.infcx, self.max_input_universe, goal);
+        self.nested_goals.normalizes_to_goals.push(goal);
+    }
+
+    #[instrument(level = "debug", skip(self))]
+    pub(super) fn add_goal(
+        &mut self,
+        source: GoalSource,
+        mut goal: ir::solve::Goal<I, I::Predicate>,
+    ) {
+        goal.predicate = goal
+            .predicate
+            .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env });
+        self.inspect.add_goal(self.infcx, self.max_input_universe, source, goal);
+        self.nested_goals.goals.push((source, goal));
+    }
+
+    #[instrument(level = "trace", skip(self, goals))]
+    pub(super) fn add_goals(
+        &mut self,
+        source: GoalSource,
+        goals: impl IntoIterator<Item = ir::solve::Goal<I, I::Predicate>>,
+    ) {
+        for goal in goals {
+            self.add_goal(source, goal);
+        }
+    }
+
+    pub(super) fn next_ty_infer(&mut self) -> I::Ty {
+        let ty = self.infcx.next_ty_infer();
         self.inspect.add_var_value(ty);
         ty
     }
 
-    pub(super) fn next_const_infer(&mut self) -> ty::Const<'tcx> {
-        let ct = self.infcx.next_const_var(DUMMY_SP);
+    pub(super) fn next_const_infer(&mut self) -> I::Const {
+        let ct = self.infcx.next_const_infer();
         self.inspect.add_var_value(ct);
         ct
     }
 
     /// Returns a ty infer or a const infer depending on whether `kind` is a `Ty` or `Const`.
     /// If `kind` is an integer inference variable this will still return a ty infer var.
-    pub(super) fn next_term_infer_of_kind(&mut self, kind: ty::Term<'tcx>) -> ty::Term<'tcx> {
-        match kind.unpack() {
-            ty::TermKind::Ty(_) => self.next_ty_infer().into(),
-            ty::TermKind::Const(_) => self.next_const_infer().into(),
+    pub(super) fn next_term_infer_of_kind(&mut self, kind: I::Term) -> I::Term {
+        match kind.kind() {
+            ir::TermKind::Ty(_) => self.next_ty_infer().into(),
+            ir::TermKind::Const(_) => self.next_const_infer().into(),
         }
     }
 
@@ -631,18 +642,18 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     #[instrument(level = "trace", skip(self), ret)]
     pub(super) fn term_is_fully_unconstrained(
         &self,
-        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
+        goal: ir::solve::Goal<I, ir::NormalizesTo<I>>,
     ) -> bool {
-        let universe_of_term = match goal.predicate.term.unpack() {
-            ty::TermKind::Ty(ty) => {
-                if let &ty::Infer(ty::TyVar(vid)) = ty.kind() {
+        let universe_of_term = match goal.predicate.term.kind() {
+            ir::TermKind::Ty(ty) => {
+                if let ir::Infer(ir::TyVar(vid)) = ty.kind() {
                     self.infcx.universe_of_ty(vid).unwrap()
                 } else {
                     return false;
                 }
             }
-            ty::TermKind::Const(ct) => {
-                if let ty::ConstKind::Infer(ty::InferConst::Var(vid)) = ct.kind() {
+            ir::TermKind::Const(ct) => {
+                if let ir::ConstKind::Infer(ir::InferConst::Var(vid)) = ct.kind() {
                     self.infcx.universe_of_ct(vid).unwrap()
                 } else {
                     return false;
@@ -650,14 +661,14 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             }
         };
 
-        struct ContainsTermOrNotNameable<'a, 'tcx> {
-            term: ty::Term<'tcx>,
-            universe_of_term: ty::UniverseIndex,
-            infcx: &'a InferCtxt<'tcx>,
+        struct ContainsTermOrNotNameable<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> {
+            term: I::Term,
+            universe_of_term: ir::UniverseIndex,
+            infcx: &'a Infcx,
         }
 
-        impl<'a, 'tcx> ContainsTermOrNotNameable<'a, 'tcx> {
-            fn check_nameable(&self, universe: ty::UniverseIndex) -> ControlFlow<()> {
+        impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> ContainsTermOrNotNameable<'_, Infcx, I> {
+            fn check_nameable(&self, universe: ir::UniverseIndex) -> ControlFlow<()> {
                 if self.universe_of_term.can_name(universe) {
                     ControlFlow::Continue(())
                 } else {
@@ -666,21 +677,23 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             }
         }
 
-        impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ContainsTermOrNotNameable<'_, 'tcx> {
+        impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeVisitor<I>
+            for ContainsTermOrNotNameable<'_, Infcx, I>
+        {
             type Result = ControlFlow<()>;
-            fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
-                match *t.kind() {
-                    ty::Infer(ty::TyVar(vid)) => {
-                        if let ty::TermKind::Ty(term) = self.term.unpack()
-                            && let Some(term_vid) = term.ty_vid()
-                            && self.infcx.root_var(vid) == self.infcx.root_var(term_vid)
+            fn visit_ty(&mut self, t: I::Ty) -> Self::Result {
+                match t.kind() {
+                    ir::Infer(ir::TyVar(vid)) => {
+                        if let ir::TermKind::Ty(term) = self.term.kind()
+                            && let ir::Infer(ir::TyVar(term_vid)) = term.kind()
+                            && self.infcx.root_ty_var(vid) == self.infcx.root_ty_var(term_vid)
                         {
                             ControlFlow::Break(())
                         } else {
                             self.check_nameable(self.infcx.universe_of_ty(vid).unwrap())
                         }
                     }
-                    ty::Placeholder(p) => self.check_nameable(p.universe),
+                    ir::Placeholder(p) => self.check_nameable(p.universe()),
                     _ => {
                         if t.has_non_region_infer() || t.has_placeholders() {
                             t.super_visit_with(self)
@@ -691,11 +704,11 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
                 }
             }
 
-            fn visit_const(&mut self, c: ty::Const<'tcx>) -> Self::Result {
+            fn visit_const(&mut self, c: I::Const) -> Self::Result {
                 match c.kind() {
-                    ty::ConstKind::Infer(ty::InferConst::Var(vid)) => {
-                        if let ty::TermKind::Const(term) = self.term.unpack()
-                            && let ty::ConstKind::Infer(ty::InferConst::Var(term_vid)) = term.kind()
+                    ir::ConstKind::Infer(ir::InferConst::Var(vid)) => {
+                        if let ir::TermKind::Const(term) = self.term.kind()
+                            && let ir::ConstKind::Infer(ir::InferConst::Var(term_vid)) = term.kind()
                             && self.infcx.root_const_var(vid) == self.infcx.root_const_var(term_vid)
                         {
                             ControlFlow::Break(())
@@ -703,7 +716,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
                             self.check_nameable(self.infcx.universe_of_ct(vid).unwrap())
                         }
                     }
-                    ty::ConstKind::Placeholder(p) => self.check_nameable(p.universe),
+                    ir::ConstKind::Placeholder(p) => self.check_nameable(p.universe()),
                     _ => {
                         if c.has_non_region_infer() || c.has_placeholders() {
                             c.super_visit_with(self)
@@ -725,23 +738,13 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     }
 
     #[instrument(level = "trace", skip(self, param_env), ret)]
-    pub(super) fn eq<T: ToTrace<'tcx>>(
+    pub(super) fn eq<T: Relate<I>>(
         &mut self,
-        param_env: ty::ParamEnv<'tcx>,
+        param_env: I::ParamEnv,
         lhs: T,
         rhs: T,
     ) -> Result<(), NoSolution> {
-        self.infcx
-            .at(&ObligationCause::dummy(), param_env)
-            // New solver ignores DefineOpaqueTypes, so choose Yes for consistency
-            .eq(DefineOpaqueTypes::Yes, lhs, rhs)
-            .map(|InferOk { value: (), obligations }| {
-                self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into()));
-            })
-            .map_err(|e| {
-                trace!(?e, "failed to equate");
-                NoSolution
-            })
+        self.relate(param_env, lhs, ir::Variance::Invariant, rhs)
     }
 
     /// This should be used when relating a rigid alias with another type.
@@ -752,10 +755,10 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     #[instrument(level = "trace", skip(self, param_env), ret)]
     pub(super) fn relate_rigid_alias_non_alias(
         &mut self,
-        param_env: ty::ParamEnv<'tcx>,
-        alias: ty::AliasTerm<'tcx>,
-        variance: ty::Variance,
-        term: ty::Term<'tcx>,
+        param_env: I::ParamEnv,
+        alias: ir::AliasTerm<I>,
+        variance: ir::Variance,
+        term: I::Term,
     ) -> Result<(), NoSolution> {
         // NOTE: this check is purely an optimization, the structural eq would
         // always fail if the term is not an inference variable.
@@ -770,12 +773,10 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             // Alternatively we could modify `Equate` for this case by adding another
             // variant to `StructurallyRelateAliases`.
             let identity_args = self.fresh_args_for_item(alias.def_id);
-            let rigid_ctor = ty::AliasTerm::new(tcx, alias.def_id, identity_args);
+            let rigid_ctor = ir::AliasTerm::new(tcx, alias.def_id, identity_args);
             let ctor_term = rigid_ctor.to_term(tcx);
-            let InferOk { value: (), obligations } = self
-                .infcx
-                .at(&ObligationCause::dummy(), param_env)
-                .eq_structurally_relating_aliases(term, ctor_term)?;
+            let obligations =
+                self.infcx.eq_structurally_relating_aliases(param_env, term, ctor_term)?;
             debug_assert!(obligations.is_empty());
             self.relate(param_env, alias, variance, rigid_ctor)
         } else {
@@ -787,58 +788,38 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     /// unconstrained "return value" or when we're sure that all aliases in
     /// the types are rigid.
     #[instrument(level = "trace", skip(self, param_env), ret)]
-    pub(super) fn eq_structurally_relating_aliases<T: ToTrace<'tcx>>(
+    pub(super) fn eq_structurally_relating_aliases<T: Relate<I>>(
         &mut self,
-        param_env: ty::ParamEnv<'tcx>,
+        param_env: I::ParamEnv,
         lhs: T,
         rhs: T,
     ) -> Result<(), NoSolution> {
-        let cause = ObligationCause::dummy();
-        let InferOk { value: (), obligations } =
-            self.infcx.at(&cause, param_env).eq_structurally_relating_aliases(lhs, rhs)?;
-        assert!(obligations.is_empty());
+        let result = self.infcx.eq_structurally_relating_aliases(param_env, lhs, rhs)?;
+        assert_eq!(result, vec![]);
         Ok(())
     }
 
     #[instrument(level = "trace", skip(self, param_env), ret)]
-    pub(super) fn sub<T: ToTrace<'tcx>>(
+    pub(super) fn sub<T: Relate<I>>(
         &mut self,
-        param_env: ty::ParamEnv<'tcx>,
+        param_env: I::ParamEnv,
         sub: T,
         sup: T,
     ) -> Result<(), NoSolution> {
-        self.infcx
-            .at(&ObligationCause::dummy(), param_env)
-            // New solver ignores DefineOpaqueTypes, so choose Yes for consistency
-            .sub(DefineOpaqueTypes::Yes, sub, sup)
-            .map(|InferOk { value: (), obligations }| {
-                self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into()));
-            })
-            .map_err(|e| {
-                trace!(?e, "failed to subtype");
-                NoSolution
-            })
+        self.relate(param_env, sub, ir::Variance::Covariant, sup)
     }
 
     #[instrument(level = "trace", skip(self, param_env), ret)]
-    pub(super) fn relate<T: ToTrace<'tcx>>(
+    pub(super) fn relate<T: Relate<I>>(
         &mut self,
-        param_env: ty::ParamEnv<'tcx>,
+        param_env: I::ParamEnv,
         lhs: T,
-        variance: ty::Variance,
+        variance: ir::Variance,
         rhs: T,
     ) -> Result<(), NoSolution> {
-        self.infcx
-            .at(&ObligationCause::dummy(), param_env)
-            // New solver ignores DefineOpaqueTypes, so choose Yes for consistency
-            .relate(DefineOpaqueTypes::Yes, lhs, variance, rhs)
-            .map(|InferOk { value: (), obligations }| {
-                self.add_goals(GoalSource::Misc, obligations.into_iter().map(|o| o.into()));
-            })
-            .map_err(|e| {
-                trace!(?e, "failed to relate");
-                NoSolution
-            })
+        let goals = self.infcx.relate(param_env, lhs, variance, rhs)?;
+        self.add_goals(GoalSource::Misc, goals);
+        Ok(())
     }
 
     /// Equates two values returning the nested goals without adding them
@@ -847,58 +828,47 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     /// If possible, try using `eq` instead which automatically handles nested
     /// goals correctly.
     #[instrument(level = "trace", skip(self, param_env), ret)]
-    pub(super) fn eq_and_get_goals<T: ToTrace<'tcx>>(
+    pub(super) fn eq_and_get_goals<T: Relate<I>>(
         &self,
-        param_env: ty::ParamEnv<'tcx>,
+        param_env: I::ParamEnv,
         lhs: T,
         rhs: T,
-    ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> {
-        self.infcx
-            .at(&ObligationCause::dummy(), param_env)
-            // New solver ignores DefineOpaqueTypes, so choose Yes for consistency
-            .eq(DefineOpaqueTypes::Yes, lhs, rhs)
-            .map(|InferOk { value: (), obligations }| {
-                obligations.into_iter().map(|o| o.into()).collect()
-            })
-            .map_err(|e| {
-                trace!(?e, "failed to equate");
-                NoSolution
-            })
+    ) -> Result<Vec<ir::solve::Goal<I, I::Predicate>>, NoSolution> {
+        self.infcx.relate(param_env, lhs, ir::Variance::Invariant, rhs)
     }
 
-    pub(super) fn instantiate_binder_with_infer<T: TypeFoldable<TyCtxt<'tcx>> + Copy>(
+    pub(super) fn instantiate_binder_with_infer<T: TypeFoldable<I> + Copy>(
         &self,
-        value: ty::Binder<'tcx, T>,
+        value: ir::Binder<I, T>,
     ) -> T {
-        self.infcx.instantiate_binder_with_fresh_vars(
-            DUMMY_SP,
-            BoundRegionConversionTime::HigherRankedType,
-            value,
-        )
+        self.infcx.instantiate_binder_with_infer(value)
     }
 
-    pub(super) fn enter_forall<T: TypeFoldable<TyCtxt<'tcx>> + Copy, U>(
+    pub(super) fn enter_forall<T: TypeFoldable<I> + Copy, U>(
         &self,
-        value: ty::Binder<'tcx, T>,
+        value: ir::Binder<I, T>,
         f: impl FnOnce(T) -> U,
     ) -> U {
         self.infcx.enter_forall(value, f)
     }
+
     pub(super) fn resolve_vars_if_possible<T>(&self, value: T) -> T
     where
-        T: TypeFoldable<TyCtxt<'tcx>>,
+        T: TypeFoldable<I>,
     {
         self.infcx.resolve_vars_if_possible(value)
     }
 
-    pub(super) fn fresh_args_for_item(&mut self, def_id: DefId) -> ty::GenericArgsRef<'tcx> {
-        let args = self.infcx.fresh_args_for_item(DUMMY_SP, def_id);
+    pub(super) fn fresh_args_for_item(&mut self, def_id: I::DefId) -> I::GenericArgs {
+        let args = self.infcx.fresh_args_for_item(def_id);
         for arg in args {
             self.inspect.add_var_value(arg);
         }
         args
     }
+}
 
+impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     pub(super) fn register_ty_outlives(&self, ty: Ty<'tcx>, lt: ty::Region<'tcx>) {
         self.infcx.register_region_obligation_with_cause(ty, lt, &ObligationCause::dummy());
     }
@@ -1096,28 +1066,36 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
 ///
 /// This is a performance optimization to more eagerly detect cycles during trait
 /// solving. See tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs.
-struct ReplaceAliasWithInfer<'me, 'a, 'tcx> {
-    ecx: &'me mut EvalCtxt<'a, InferCtxt<'tcx>>,
-    param_env: ty::ParamEnv<'tcx>,
+struct ReplaceAliasWithInfer<'me, 'a, Infcx, I>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
+    ecx: &'me mut EvalCtxt<'a, Infcx>,
+    param_env: I::ParamEnv,
 }
 
-impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceAliasWithInfer<'_, '_, 'tcx> {
-    fn interner(&self) -> TyCtxt<'tcx> {
+impl<Infcx, I> TypeFolder<I> for ReplaceAliasWithInfer<'_, '_, Infcx, I>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
+    fn interner(&self) -> I {
         self.ecx.interner()
     }
 
-    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        match *ty.kind() {
-            ty::Alias(..) if !ty.has_escaping_bound_vars() => {
+    fn fold_ty(&mut self, ty: I::Ty) -> I::Ty {
+        match ty.kind() {
+            ir::Alias(..) if !ty.has_escaping_bound_vars() => {
                 let infer_ty = self.ecx.next_ty_infer();
-                let normalizes_to = ty::PredicateKind::AliasRelate(
+                let normalizes_to = ir::PredicateKind::AliasRelate(
                     ty.into(),
                     infer_ty.into(),
                     AliasRelationDirection::Equate,
                 );
                 self.ecx.add_goal(
                     GoalSource::Misc,
-                    Goal::new(self.interner(), self.param_env, normalizes_to),
+                    ir::solve::Goal::new(self.interner(), self.param_env, normalizes_to),
                 );
                 infer_ty
             }
@@ -1125,18 +1103,18 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceAliasWithInfer<'_, '_, 'tcx> {
         }
     }
 
-    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+    fn fold_const(&mut self, ct: I::Const) -> I::Const {
         match ct.kind() {
-            ty::ConstKind::Unevaluated(..) if !ct.has_escaping_bound_vars() => {
+            ir::ConstKind::Unevaluated(..) if !ct.has_escaping_bound_vars() => {
                 let infer_ct = self.ecx.next_const_infer();
-                let normalizes_to = ty::PredicateKind::AliasRelate(
+                let normalizes_to = ir::PredicateKind::AliasRelate(
                     ct.into(),
                     infer_ct.into(),
                     AliasRelationDirection::Equate,
                 );
                 self.ecx.add_goal(
                     GoalSource::Misc,
-                    Goal::new(self.interner(), self.param_env, normalizes_to),
+                    ir::solve::Goal::new(self.interner(), self.param_env, normalizes_to),
                 );
                 infer_ct
             }
@@ -1144,7 +1122,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceAliasWithInfer<'_, '_, 'tcx> {
         }
     }
 
-    fn fold_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+    fn fold_predicate(&mut self, predicate: I::Predicate) -> I::Predicate {
         if predicate.allow_normalization() { predicate.super_fold_with(self) } else { predicate }
     }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
index 1748c9be927..ad6fdd2707d 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
@@ -1,27 +1,29 @@
 use crate::solve::assembly::Candidate;
 
 use super::EvalCtxt;
-use rustc_infer::infer::InferCtxt;
-use rustc_infer::traits::BuiltinImplSource;
-use rustc_middle::traits::query::NoSolution;
-use rustc_middle::traits::solve::{inspect, CandidateSource, QueryResult};
-use rustc_middle::ty::TyCtxt;
+use rustc_next_trait_solver::solve::{
+    inspect, BuiltinImplSource, CandidateSource, NoSolution, QueryResult,
+};
+use rustc_type_ir::{InferCtxtLike, Interner};
 use std::marker::PhantomData;
 
-pub(in crate::solve) struct ProbeCtxt<'me, 'a, 'tcx, F, T> {
-    ecx: &'me mut EvalCtxt<'a, InferCtxt<'tcx>>,
+pub(in crate::solve) struct ProbeCtxt<'me, 'a, Infcx, I, F, T>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
+    ecx: &'me mut EvalCtxt<'a, Infcx, I>,
     probe_kind: F,
     _result: PhantomData<T>,
 }
 
-impl<'tcx, F, T> ProbeCtxt<'_, '_, 'tcx, F, T>
+impl<Infcx, I, F, T> ProbeCtxt<'_, '_, Infcx, I, F, T>
 where
-    F: FnOnce(&T) -> inspect::ProbeKind<TyCtxt<'tcx>>,
+    F: FnOnce(&T) -> inspect::ProbeKind<I>,
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
 {
-    pub(in crate::solve) fn enter(
-        self,
-        f: impl FnOnce(&mut EvalCtxt<'_, InferCtxt<'tcx>>) -> T,
-    ) -> T {
+    pub(in crate::solve) fn enter(self, f: impl FnOnce(&mut EvalCtxt<'_, Infcx>) -> T) -> T {
         let ProbeCtxt { ecx: outer_ecx, probe_kind, _result } = self;
 
         let infcx = outer_ecx.infcx;
@@ -38,7 +40,7 @@ where
             tainted: outer_ecx.tainted,
             inspect: outer_ecx.inspect.take_and_enter_probe(),
         };
-        let r = nested_ecx.infcx.probe(|_| {
+        let r = nested_ecx.infcx.probe(|| {
             let r = f(&mut nested_ecx);
             nested_ecx.inspect.probe_final_state(infcx, max_input_universe);
             r
@@ -52,30 +54,43 @@ where
     }
 }
 
-pub(in crate::solve) struct TraitProbeCtxt<'me, 'a, 'tcx, F> {
-    cx: ProbeCtxt<'me, 'a, 'tcx, F, QueryResult<'tcx>>,
-    source: CandidateSource<'tcx>,
+pub(in crate::solve) struct TraitProbeCtxt<'me, 'a, Infcx, I, F>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
+    cx: ProbeCtxt<'me, 'a, Infcx, I, F, QueryResult<I>>,
+    source: CandidateSource<I>,
 }
 
-impl<'tcx, F> TraitProbeCtxt<'_, '_, 'tcx, F>
+impl<Infcx, I, F> TraitProbeCtxt<'_, '_, Infcx, I, F>
 where
-    F: FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<TyCtxt<'tcx>>,
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+    F: FnOnce(&QueryResult<I>) -> inspect::ProbeKind<I>,
 {
     #[instrument(level = "debug", skip_all, fields(source = ?self.source))]
     pub(in crate::solve) fn enter(
         self,
-        f: impl FnOnce(&mut EvalCtxt<'_, InferCtxt<'tcx>>) -> QueryResult<'tcx>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+        f: impl FnOnce(&mut EvalCtxt<'_, Infcx>) -> QueryResult<I>,
+    ) -> Result<Candidate<I>, NoSolution> {
         self.cx.enter(|ecx| f(ecx)).map(|result| Candidate { source: self.source, result })
     }
 }
 
-impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
+impl<'a, Infcx, I> EvalCtxt<'a, Infcx, I>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
     /// `probe_kind` is only called when proof tree building is enabled so it can be
     /// as expensive as necessary to output the desired information.
-    pub(in crate::solve) fn probe<F, T>(&mut self, probe_kind: F) -> ProbeCtxt<'_, 'a, 'tcx, F, T>
+    pub(in crate::solve) fn probe<F, T>(
+        &mut self,
+        probe_kind: F,
+    ) -> ProbeCtxt<'_, 'a, Infcx, I, F, T>
     where
-        F: FnOnce(&T) -> inspect::ProbeKind<TyCtxt<'tcx>>,
+        F: FnOnce(&T) -> inspect::ProbeKind<I>,
     {
         ProbeCtxt { ecx: self, probe_kind, _result: PhantomData }
     }
@@ -83,28 +98,20 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
     pub(in crate::solve) fn probe_builtin_trait_candidate(
         &mut self,
         source: BuiltinImplSource,
-    ) -> TraitProbeCtxt<
-        '_,
-        'a,
-        'tcx,
-        impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<TyCtxt<'tcx>>,
-    > {
+    ) -> TraitProbeCtxt<'_, 'a, Infcx, I, impl FnOnce(&QueryResult<I>) -> inspect::ProbeKind<I>>
+    {
         self.probe_trait_candidate(CandidateSource::BuiltinImpl(source))
     }
 
     pub(in crate::solve) fn probe_trait_candidate(
         &mut self,
-        source: CandidateSource<'tcx>,
-    ) -> TraitProbeCtxt<
-        '_,
-        'a,
-        'tcx,
-        impl FnOnce(&QueryResult<'tcx>) -> inspect::ProbeKind<TyCtxt<'tcx>>,
-    > {
+        source: CandidateSource<I>,
+    ) -> TraitProbeCtxt<'_, 'a, Infcx, I, impl FnOnce(&QueryResult<I>) -> inspect::ProbeKind<I>>
+    {
         TraitProbeCtxt {
             cx: ProbeCtxt {
                 ecx: self,
-                probe_kind: move |result: &QueryResult<'tcx>| inspect::ProbeKind::TraitCandidate {
+                probe_kind: move |result: &QueryResult<I>| inspect::ProbeKind::TraitCandidate {
                     source,
                     result: *result,
                 },
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index dc13941e5d7..3c01d1a65f5 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -460,9 +460,10 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
                     polarity: ty::PredicatePolarity::Positive,
                 }))
             }
-            ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => {
-                ChildMode::WellFormedObligation
-            }
+            ty::PredicateKind::Clause(
+                ty::ClauseKind::WellFormed(_) | ty::ClauseKind::Projection(..),
+            )
+            | ty::PredicateKind::AliasRelate(..) => ChildMode::PassThrough,
             _ => {
                 return ControlFlow::Break(self.obligation.clone());
             }
@@ -496,7 +497,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
                 (_, GoalSource::InstantiateHigherRanked) => {
                     obligation = self.obligation.clone();
                 }
-                (ChildMode::WellFormedObligation, _) => {
+                (ChildMode::PassThrough, _) => {
                     obligation = make_obligation(self.obligation.cause.clone());
                 }
             }
@@ -514,6 +515,32 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
             self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
         }
 
+        // alias-relate may fail because the lhs or rhs can't be normalized,
+        // and therefore is treated as rigid.
+        if let Some(ty::PredicateKind::AliasRelate(lhs, rhs, _)) = pred_kind.no_bound_vars() {
+            if let Some(obligation) = goal
+                .infcx()
+                .visit_proof_tree_at_depth(
+                    goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(lhs.into())),
+                    goal.depth() + 1,
+                    self,
+                )
+                .break_value()
+            {
+                return ControlFlow::Break(obligation);
+            } else if let Some(obligation) = goal
+                .infcx()
+                .visit_proof_tree_at_depth(
+                    goal.goal().with(goal.infcx().tcx, ty::ClauseKind::WellFormed(rhs.into())),
+                    goal.depth() + 1,
+                    self,
+                )
+                .break_value()
+            {
+                return ControlFlow::Break(obligation);
+            }
+        }
+
         ControlFlow::Break(self.obligation.clone())
     }
 }
@@ -527,7 +554,7 @@ enum ChildMode<'tcx> {
     // Skip trying to derive an `ObligationCause` from this obligation, and
     // report *all* sub-obligations as if they came directly from the parent
     // obligation.
-    WellFormedObligation,
+    PassThrough,
 }
 
 fn derive_cause<'tcx>(
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
index 19c95dad48c..464c188b6e3 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
@@ -15,7 +15,7 @@ use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
 use rustc_macros::extension;
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::traits::solve::{inspect, QueryResult};
-use rustc_middle::traits::solve::{Certainty, Goal};
+use rustc_middle::traits::solve::{Certainty, Goal, MaybeCause};
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::{TyCtxt, TypeFoldable};
 use rustc_middle::{bug, ty};
@@ -278,6 +278,10 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
         self.source
     }
 
+    pub fn depth(&self) -> usize {
+        self.depth
+    }
+
     fn candidates_recur(
         &'a self,
         candidates: &mut Vec<InspectCandidate<'a, 'tcx>>,
@@ -291,7 +295,10 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
                     steps.push(step)
                 }
                 inspect::ProbeStep::MakeCanonicalResponse { shallow_certainty: c } => {
-                    assert_eq!(shallow_certainty.replace(c), None);
+                    assert!(matches!(
+                        shallow_certainty.replace(c),
+                        None | Some(Certainty::Maybe(MaybeCause::Ambiguity))
+                    ));
                 }
                 inspect::ProbeStep::NestedProbe(ref probe) => {
                     match probe.kind {
@@ -433,8 +440,17 @@ impl<'tcx> InferCtxt<'tcx> {
         goal: Goal<'tcx, ty::Predicate<'tcx>>,
         visitor: &mut V,
     ) -> V::Result {
+        self.visit_proof_tree_at_depth(goal, 0, visitor)
+    }
+
+    fn visit_proof_tree_at_depth<V: ProofTreeVisitor<'tcx>>(
+        &self,
+        goal: Goal<'tcx, ty::Predicate<'tcx>>,
+        depth: usize,
+        visitor: &mut V,
+    ) -> V::Result {
         let (_, proof_tree) = self.evaluate_root_goal(goal, GenerateProofTree::Yes);
         let proof_tree = proof_tree.unwrap();
-        visitor.visit_goal(&InspectGoal::new(self, 0, proof_tree, None, GoalSource::Misc))
+        visitor.visit_goal(&InspectGoal::new(self, depth, proof_tree, None, GoalSource::Misc))
     }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/build.rs b/compiler/rustc_trait_selection/src/solve/inspect/build.rs
index 84c04900ae4..35750598bc7 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/build.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/build.rs
@@ -34,10 +34,11 @@ use rustc_type_ir::{self as ty, InferCtxtLike, Interner};
 /// trees. At the end of trait solving `ProofTreeBuilder::finalize`
 /// is called to recursively convert the whole structure to a
 /// finished proof tree.
-pub(in crate::solve) struct ProofTreeBuilder<
+pub(in crate::solve) struct ProofTreeBuilder<Infcx, I = <Infcx as InferCtxtLike>::Interner>
+where
     Infcx: InferCtxtLike<Interner = I>,
-    I: Interner = <Infcx as InferCtxtLike>::Interner,
-> {
+    I: Interner,
+{
     _infcx: PhantomData<Infcx>,
     state: Option<Box<DebugSolver<I>>>,
 }
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index c47b0194964..fdcf4ff11e4 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -235,17 +235,6 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
 }
 
 impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
-    #[instrument(level = "trace", skip(self, goals))]
-    fn add_goals(
-        &mut self,
-        source: GoalSource,
-        goals: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
-    ) {
-        for goal in goals {
-            self.add_goal(source, goal);
-        }
-    }
-
     /// Try to merge multiple possible ways to prove a goal, if that is not possible returns `None`.
     ///
     /// In this case we tend to flounder and return ambiguity by calling `[EvalCtxt::flounder]`.
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
index 787f08a084e..60a15392e0b 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
@@ -17,7 +17,7 @@ use rustc_middle::ty::NormalizesTo;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::ty::{TypeVisitableExt, Upcast};
 use rustc_middle::{bug, span_bug};
-use rustc_span::{sym, ErrorGuaranteed, DUMMY_SP};
+use rustc_span::{ErrorGuaranteed, DUMMY_SP};
 
 mod anon_const;
 mod inherent;
@@ -40,7 +40,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             Ok(res) => Ok(res),
             Err(NoSolution) => {
                 let Goal { param_env, predicate: NormalizesTo { alias, term } } = goal;
-                self.relate_rigid_alias_non_alias(param_env, alias, ty::Variance::Invariant, term)?;
+                self.relate_rigid_alias_non_alias(param_env, alias, ty::Invariant, term)?;
                 self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
             }
         }
@@ -103,7 +103,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
         goal: Goal<'tcx, Self>,
         assumption: ty::Clause<'tcx>,
         then: impl FnOnce(&mut EvalCtxt<'_, InferCtxt<'tcx>>) -> QueryResult<'tcx>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         if let Some(projection_pred) = assumption.as_projection_clause() {
             if projection_pred.projection_def_id() == goal.predicate.def_id() {
                 let tcx = ecx.interner();
@@ -140,7 +140,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, NormalizesTo<'tcx>>,
         impl_def_id: DefId,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         let tcx = ecx.interner();
 
         let goal_trait_ref = goal.predicate.alias.trait_ref(tcx);
@@ -267,14 +267,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     fn consider_error_guaranteed_candidate(
         _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         _guar: ErrorGuaranteed,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         Err(NoSolution)
     }
 
     fn consider_auto_trait_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         ecx.interner().dcx().span_delayed_bug(
             ecx.interner().def_span(goal.predicate.def_id()),
             "associated types not allowed on auto traits",
@@ -285,35 +285,35 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     fn consider_trait_alias_candidate(
         _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         bug!("trait aliases do not have associated types: {:?}", goal);
     }
 
     fn consider_builtin_sized_candidate(
         _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         bug!("`Sized` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_copy_clone_candidate(
         _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         bug!("`Copy`/`Clone` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_pointer_like_candidate(
         _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         bug!("`PointerLike` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_fn_ptr_trait_candidate(
         _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         bug!("`FnPtr` does not have an associated type: {:?}", goal);
     }
 
@@ -321,7 +321,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
         goal_kind: ty::ClosureKind,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         let tcx = ecx.interner();
         let tupled_inputs_and_output =
             match structural_traits::extract_tupled_inputs_and_output_from_callable(
@@ -364,7 +364,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
         goal_kind: ty::ClosureKind,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         let tcx = ecx.interner();
 
         let env_region = match goal_kind {
@@ -392,9 +392,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
                      output_coroutine_ty,
                      coroutine_return_ty,
                  }| {
-                    let lang_items = tcx.lang_items();
-                    let (projection_term, term) = if Some(goal.predicate.def_id())
-                        == lang_items.call_once_future()
+                    let (projection_term, term) = if tcx
+                        .is_lang_item(goal.predicate.def_id(), LangItem::CallOnceFuture)
                     {
                         (
                             ty::AliasTerm::new(
@@ -404,7 +403,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
                             ),
                             output_coroutine_ty.into(),
                         )
-                    } else if Some(goal.predicate.def_id()) == lang_items.call_ref_future() {
+                    } else if tcx.is_lang_item(goal.predicate.def_id(), LangItem::CallRefFuture) {
                         (
                             ty::AliasTerm::new(
                                 tcx,
@@ -417,7 +416,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
                             ),
                             output_coroutine_ty.into(),
                         )
-                    } else if Some(goal.predicate.def_id()) == lang_items.async_fn_once_output() {
+                    } else if tcx.is_lang_item(goal.predicate.def_id(), LangItem::AsyncFnOnceOutput)
+                    {
                         (
                             ty::AliasTerm::new(
                                 tcx,
@@ -454,7 +454,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     fn consider_builtin_async_fn_kind_helper_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         let [
             closure_fn_kind_ty,
             goal_kind_ty,
@@ -501,14 +501,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     fn consider_builtin_tuple_candidate(
         _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         bug!("`Tuple` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_pointee_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         let tcx = ecx.interner();
         let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None);
         assert_eq!(metadata_def_id, goal.predicate.def_id());
@@ -590,7 +590,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     fn consider_builtin_future_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         let self_ty = goal.predicate.self_ty();
         let ty::Coroutine(def_id, args) = *self_ty.kind() else {
             return Err(NoSolution);
@@ -626,7 +626,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     fn consider_builtin_iterator_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         let self_ty = goal.predicate.self_ty();
         let ty::Coroutine(def_id, args) = *self_ty.kind() else {
             return Err(NoSolution);
@@ -662,14 +662,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     fn consider_builtin_fused_iterator_candidate(
         _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         bug!("`FusedIterator` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_async_iterator_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         let self_ty = goal.predicate.self_ty();
         let ty::Coroutine(def_id, args) = *self_ty.kind() else {
             return Err(NoSolution);
@@ -705,7 +705,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     fn consider_builtin_coroutine_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         let self_ty = goal.predicate.self_ty();
         let ty::Coroutine(def_id, args) = *self_ty.kind() else {
             return Err(NoSolution);
@@ -719,13 +719,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
 
         let coroutine = args.as_coroutine();
 
-        let name = tcx.associated_item(goal.predicate.def_id()).name;
-        let term = if name == sym::Return {
+        let term = if tcx.is_lang_item(goal.predicate.def_id(), LangItem::CoroutineReturn) {
             coroutine.return_ty().into()
-        } else if name == sym::Yield {
+        } else if tcx.is_lang_item(goal.predicate.def_id(), LangItem::CoroutineYield) {
             coroutine.yield_ty().into()
         } else {
-            bug!("unexpected associated item `<{self_ty} as Coroutine>::{name}`")
+            bug!(
+                "unexpected associated item `<{self_ty} as Coroutine>::{}`",
+                tcx.item_name(goal.predicate.def_id())
+            )
         };
 
         Self::probe_and_consider_implied_clause(
@@ -750,14 +752,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     fn consider_structural_builtin_unsize_candidates(
         _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Vec<Candidate<'tcx>> {
+    ) -> Vec<Candidate<TyCtxt<'tcx>>> {
         bug!("`Unsize` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_discriminant_kind_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         let self_ty = goal.predicate.self_ty();
         let discriminant_ty = match *self_ty.kind() {
             ty::Bool
@@ -809,7 +811,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     fn consider_builtin_async_destruct_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         let self_ty = goal.predicate.self_ty();
         let async_destructor_ty = match *self_ty.kind() {
             ty::Bool
@@ -862,14 +864,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     fn consider_builtin_destruct_candidate(
         _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         bug!("`Destruct` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_transmute_candidate(
         _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         bug!("`BikeshedIntrinsicFrom` does not have an associated type: {:?}", goal)
     }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index a741f488901..3fdc11ade5f 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -39,7 +39,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, TraitPredicate<'tcx>>,
         impl_def_id: DefId,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         let tcx = ecx.interner();
 
         let impl_trait_header = tcx.impl_trait_header(impl_def_id).unwrap();
@@ -94,7 +94,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn consider_error_guaranteed_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         _guar: ErrorGuaranteed,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         // FIXME: don't need to enter a probe here.
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
             .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
@@ -106,7 +106,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         goal: Goal<'tcx, Self>,
         assumption: ty::Clause<'tcx>,
         then: impl FnOnce(&mut EvalCtxt<'_, InferCtxt<'tcx>>) -> QueryResult<'tcx>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         if let Some(trait_clause) = assumption.as_trait_clause() {
             if trait_clause.def_id() == goal.predicate.def_id()
                 && trait_clause.polarity() == goal.predicate.polarity
@@ -131,7 +131,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn consider_auto_trait_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -174,7 +174,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn consider_trait_alias_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -197,7 +197,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn consider_builtin_sized_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -212,7 +212,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn consider_builtin_copy_clone_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -227,7 +227,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn consider_builtin_pointer_like_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -257,7 +257,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn consider_builtin_fn_ptr_trait_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         let self_ty = goal.predicate.self_ty();
         match goal.predicate.polarity {
             // impl FnPtr for FnPtr {}
@@ -289,7 +289,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
         goal_kind: ty::ClosureKind,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -330,7 +330,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
         goal_kind: ty::ClosureKind,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -380,7 +380,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn consider_builtin_async_fn_kind_helper_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         let [closure_fn_kind_ty, goal_kind_ty] = **goal.predicate.trait_ref.args else {
             bug!();
         };
@@ -407,7 +407,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn consider_builtin_tuple_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -423,7 +423,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn consider_builtin_pointee_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -435,7 +435,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn consider_builtin_future_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -461,7 +461,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn consider_builtin_iterator_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -487,7 +487,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn consider_builtin_fused_iterator_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -511,7 +511,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn consider_builtin_async_iterator_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -537,7 +537,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn consider_builtin_coroutine_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -569,7 +569,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn consider_builtin_discriminant_kind_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -582,7 +582,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn consider_builtin_async_destruct_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -595,7 +595,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn consider_builtin_destruct_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -611,7 +611,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn consider_builtin_transmute_candidate(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -652,7 +652,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     fn consider_structural_builtin_unsize_candidates(
         ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
         goal: Goal<'tcx, Self>,
-    ) -> Vec<Candidate<'tcx>> {
+    ) -> Vec<Candidate<TyCtxt<'tcx>>> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return vec![];
         }
@@ -738,7 +738,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         a_region: ty::Region<'tcx>,
         b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
         b_region: ty::Region<'tcx>,
-    ) -> Vec<Candidate<'tcx>> {
+    ) -> Vec<Candidate<TyCtxt<'tcx>>> {
         let tcx = self.interner();
         let Goal { predicate: (a_ty, _b_ty), .. } = goal;
 
@@ -784,7 +784,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
         b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
         b_region: ty::Region<'tcx>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         let tcx = self.interner();
         let Goal { predicate: (a_ty, _), .. } = goal;
 
@@ -802,14 +802,13 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             );
 
             // The type must be `Sized` to be unsized.
-            if let Some(sized_def_id) = tcx.lang_items().sized_trait() {
-                ecx.add_goal(
-                    GoalSource::ImplWhereBound,
-                    goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty])),
-                );
-            } else {
-                return Err(NoSolution);
-            }
+            ecx.add_goal(
+                GoalSource::ImplWhereBound,
+                goal.with(
+                    tcx,
+                    ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, None), [a_ty]),
+                ),
+            );
 
             // The type must outlive the lifetime of the `dyn` we're unsizing into.
             ecx.add_goal(GoalSource::Misc, goal.with(tcx, ty::OutlivesPredicate(a_ty, b_region)));
@@ -826,7 +825,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
         b_region: ty::Region<'tcx>,
         upcast_principal: Option<ty::PolyExistentialTraitRef<'tcx>>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         let param_env = goal.param_env;
 
         // We may upcast to auto traits that are either explicitly listed in
@@ -929,7 +928,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
         a_elem_ty: Ty<'tcx>,
         b_elem_ty: Ty<'tcx>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         self.eq(goal.param_env, a_elem_ty, b_elem_ty)?;
         self.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
             .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
@@ -954,7 +953,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         def: ty::AdtDef<'tcx>,
         a_args: ty::GenericArgsRef<'tcx>,
         b_args: ty::GenericArgsRef<'tcx>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         let tcx = self.interner();
         let Goal { predicate: (_a_ty, b_ty), .. } = goal;
 
@@ -991,7 +990,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
                 tcx,
                 ty::TraitRef::new(
                     tcx,
-                    tcx.lang_items().unsize_trait().unwrap(),
+                    tcx.require_lang_item(LangItem::Unsize, None),
                     [a_tail_ty, b_tail_ty],
                 ),
             ),
@@ -1015,7 +1014,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
         a_tys: &'tcx ty::List<Ty<'tcx>>,
         b_tys: &'tcx ty::List<Ty<'tcx>>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         let tcx = self.interner();
         let Goal { predicate: (_a_ty, b_ty), .. } = goal;
 
@@ -1034,7 +1033,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
                 tcx,
                 ty::TraitRef::new(
                     tcx,
-                    tcx.lang_items().unsize_trait().unwrap(),
+                    tcx.require_lang_item(LangItem::Unsize, None),
                     [a_last_ty, b_last_ty],
                 ),
             ),
@@ -1050,7 +1049,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     fn disqualify_auto_trait_candidate_due_to_possible_impl(
         &mut self,
         goal: Goal<'tcx, TraitPredicate<'tcx>>,
-    ) -> Option<Result<Candidate<'tcx>, NoSolution>> {
+    ) -> Option<Result<Candidate<TyCtxt<'tcx>>, NoSolution>> {
         let self_ty = goal.predicate.self_ty();
         match *self_ty.kind() {
             // Stall int and float vars until they are resolved to a concrete
@@ -1076,7 +1075,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             // takes precedence over the structural auto trait candidate being
             // assembled.
             ty::Coroutine(def_id, _)
-                if Some(goal.predicate.def_id()) == self.interner().lang_items().unpin_trait() =>
+                if self.interner().is_lang_item(goal.predicate.def_id(), LangItem::Unpin) =>
             {
                 match self.interner().coroutine_movability(def_id) {
                     Movability::Static => Some(Err(NoSolution)),
@@ -1155,7 +1154,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             &EvalCtxt<'_, InferCtxt<'tcx>>,
             Ty<'tcx>,
         ) -> Result<Vec<ty::Binder<'tcx, Ty<'tcx>>>, NoSolution>,
-    ) -> Result<Candidate<'tcx>, NoSolution> {
+    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
         self.probe_trait_candidate(source).enter(|ecx| {
             ecx.add_goals(
                 GoalSource::ImplWhereBound,
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index fc5c71252e1..a4177d8a93f 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -769,8 +769,8 @@ pub struct UncoveredTyParams<'tcx, T> {
 ///    add "non-blanket" impls without breaking negative reasoning in dependent
 ///    crates. This is the "rebalancing coherence" (RFC 1023) restriction.
 ///
-///    For that, we only a allow crate to perform negative reasoning on
-///    non-local-non-`#[fundamental]` only if there's a local key parameter as per (2).
+///    For that, we only allow a crate to perform negative reasoning on
+///    non-local-non-`#[fundamental]` if there's a local key parameter as per (2).
 ///
 ///    Because we never perform negative reasoning generically (coherence does
 ///    not involve type parameters), this can be interpreted as doing the full
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 087f7fbea00..f632f1ad4f2 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -794,7 +794,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
         if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) =
             obligation.predicate.kind().skip_binder()
-            && Some(trait_pred.def_id()) == self.tcx.lang_items().sized_trait()
+            && self.tcx.is_lang_item(trait_pred.def_id(), LangItem::Sized)
         {
             // Don't suggest calling to turn an unsized type into a sized type
             return false;
@@ -1106,7 +1106,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                             .iter()
                             .find_map(|pred| {
                                 if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
-                        && Some(proj.projection_term.def_id) == self.tcx.lang_items().fn_once_output()
+                        && self.tcx.is_lang_item(proj.projection_term.def_id,LangItem::FnOnceOutput)
                         // args tuple will always be args[1]
                         && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()
                                 {
@@ -1123,7 +1123,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     ty::Dynamic(data, _, ty::Dyn) => {
                         data.iter().find_map(|pred| {
                             if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
-                        && Some(proj.def_id) == self.tcx.lang_items().fn_once_output()
+                        && self.tcx.is_lang_item(proj.def_id, LangItem::FnOnceOutput)
                         // for existential projection, args are shifted over by 1
                         && let ty::Tuple(args) = proj.args.type_at(0).kind()
                             {
@@ -1150,7 +1150,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         };
                         param_env.caller_bounds().iter().find_map(|pred| {
                             if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
-                        && Some(proj.projection_term.def_id) == self.tcx.lang_items().fn_once_output()
+                        && self.tcx.is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput)
                         && proj.projection_term.self_ty() == found
                         // args tuple will always be args[1]
                         && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()
@@ -1822,7 +1822,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 && box_path
                     .res
                     .opt_def_id()
-                    .is_some_and(|def_id| Some(def_id) == self.tcx.lang_items().owned_box())
+                    .is_some_and(|def_id| self.tcx.is_lang_item(def_id, LangItem::OwnedBox))
             {
                 // Don't box `Box::new`
                 vec![]
@@ -2737,7 +2737,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             | ObligationCauseCode::ObjectTypeBound(..) => {}
             ObligationCauseCode::RustCall => {
                 if let Some(pred) = predicate.as_trait_clause()
-                    && Some(pred.def_id()) == tcx.lang_items().sized_trait()
+                    && tcx.is_lang_item(pred.def_id(), LangItem::Sized)
                 {
                     err.note("argument required to be sized due to `extern \"rust-call\"` ABI");
                 }
@@ -2790,7 +2790,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         // Check for foreign traits being reachable.
                         tcx.visible_parent_map(()).get(&def_id).is_some()
                     };
-                    if Some(def_id) == tcx.lang_items().sized_trait() {
+                    if tcx.is_lang_item(def_id, LangItem::Sized) {
                         // Check if this is an implicit bound, even in foreign crates.
                         if tcx
                             .generics_of(item_def_id)
@@ -3597,7 +3597,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         &self,
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diag<'_>,
-        trait_ref: &ty::PolyTraitRef<'tcx>,
+        trait_ref: ty::PolyTraitRef<'tcx>,
     ) {
         let rhs_span = match obligation.cause.code() {
             ObligationCauseCode::BinOp { rhs_span: Some(span), rhs_is_lit, .. } if *rhs_is_lit => {
@@ -4592,7 +4592,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         &self,
         obligation: &PredicateObligation<'tcx>,
         err: &mut Diag<'_>,
-        trait_ref: ty::PolyTraitRef<'tcx>,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) {
         if ObligationCauseCode::QuestionMark != *obligation.cause.code().peel_derives() {
             return;
@@ -4602,10 +4602,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         if let hir::Node::Item(item) = node
             && let hir::ItemKind::Fn(sig, _, body_id) = item.kind
             && let hir::FnRetTy::DefaultReturn(ret_span) = sig.decl.output
-            && self.tcx.is_diagnostic_item(sym::FromResidual, trait_ref.def_id())
-            && let ty::Tuple(l) = trait_ref.skip_binder().args.type_at(0).kind()
-            && l.len() == 0
-            && let ty::Adt(def, _) = trait_ref.skip_binder().args.type_at(1).kind()
+            && self.tcx.is_diagnostic_item(sym::FromResidual, trait_pred.def_id())
+            && trait_pred.skip_binder().trait_ref.args.type_at(0).is_unit()
+            && let ty::Adt(def, _) = trait_pred.skip_binder().trait_ref.args.type_at(1).kind()
             && self.tcx.is_diagnostic_item(sym::Result, def.did())
         {
             let body = self.tcx.hir().body(body_id);
@@ -4863,14 +4862,13 @@ impl<'a, 'hir> hir::intravisit::Visitor<'hir> for ReplaceImplTraitVisitor<'a> {
 pub(super) fn get_explanation_based_on_obligation<'tcx>(
     tcx: TyCtxt<'tcx>,
     obligation: &PredicateObligation<'tcx>,
-    trait_ref: ty::PolyTraitRef<'tcx>,
-    trait_predicate: &ty::PolyTraitPredicate<'tcx>,
+    trait_predicate: ty::PolyTraitPredicate<'tcx>,
     pre_message: String,
 ) -> String {
     if let ObligationCauseCode::MainFunctionType = obligation.cause.code() {
         "consider using `()`, or a `Result`".to_owned()
     } else {
-        let ty_desc = match trait_ref.skip_binder().self_ty().kind() {
+        let ty_desc = match trait_predicate.self_ty().skip_binder().kind() {
             ty::FnDef(_, _) => Some("fn item"),
             ty::Closure(_, _) => Some("closure"),
             _ => None,
@@ -4895,7 +4893,7 @@ pub(super) fn get_explanation_based_on_obligation<'tcx>(
             format!(
                 "{pre_message}the trait `{}` is not implemented for{desc} `{}`{post}",
                 trait_predicate.print_modifiers_and_trait_path(),
-                tcx.short_ty_string(trait_ref.skip_binder().self_ty(), &mut None),
+                tcx.short_ty_string(trait_predicate.self_ty().skip_binder(), &mut None),
             )
         } else {
             // "the trait bound `T: !Send` is not satisfied" reads better than "`!Send` is
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
index 6b6438a7887..9cd69f54c12 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
@@ -24,10 +24,10 @@ use rustc_data_structures::unord::UnordSet;
 use rustc_errors::codes::*;
 use rustc_errors::{pluralize, struct_span_code_err, Applicability, MultiSpan, StringPart};
 use rustc_errors::{Diag, EmissionGuarantee, ErrorGuaranteed, FatalError, StashKey};
-use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Namespace, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::Visitor;
+use rustc_hir::{self as hir, LangItem};
 use rustc_hir::{GenericParam, Item, Node};
 use rustc_infer::infer::error_reporting::TypeErrCtxt;
 use rustc_infer::infer::{InferOk, TypeTrace};
@@ -118,7 +118,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         // with more relevant type information and hide redundant E0282 errors.
         errors.sort_by_key(|e| match e.obligation.predicate.kind().skip_binder() {
             ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred))
-                if Some(pred.def_id()) == self.tcx.lang_items().sized_trait() =>
+                if self.tcx.is_lang_item(pred.def_id(), LangItem::Sized) =>
             {
                 1
             }
@@ -412,8 +412,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 let bound_predicate = obligation.predicate.kind();
                 match bound_predicate.skip_binder() {
                     ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_predicate)) => {
-                        let trait_predicate = bound_predicate.rebind(trait_predicate);
-                        let trait_predicate = self.resolve_vars_if_possible(trait_predicate);
+                        let leaf_trait_predicate =
+                            self.resolve_vars_if_possible(bound_predicate.rebind(trait_predicate));
 
                         // Let's use the root obligation as the main message, when we care about the
                         // most general case ("X doesn't implement Pattern<'_>") over the case that
@@ -424,7 +424,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         let (main_trait_predicate, o) = if let ty::PredicateKind::Clause(
                             ty::ClauseKind::Trait(root_pred)
                         ) = root_obligation.predicate.kind().skip_binder()
-                            && !trait_predicate.self_ty().skip_binder().has_escaping_bound_vars()
+                            && !leaf_trait_predicate.self_ty().skip_binder().has_escaping_bound_vars()
                             && !root_pred.self_ty().has_escaping_bound_vars()
                             // The type of the leaf predicate is (roughly) the same as the type
                             // from the root predicate, as a proxy for "we care about the root"
@@ -434,20 +434,20 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                                 // `T: Trait` && `&&T: OtherTrait`, we want `OtherTrait`
                                 self.can_eq(
                                     obligation.param_env,
-                                    trait_predicate.self_ty().skip_binder(),
+                                    leaf_trait_predicate.self_ty().skip_binder(),
                                     root_pred.self_ty().peel_refs(),
                                 )
                                 // `&str: Iterator` && `&str: IntoIterator`, we want `IntoIterator`
                                 || self.can_eq(
                                     obligation.param_env,
-                                    trait_predicate.self_ty().skip_binder(),
+                                    leaf_trait_predicate.self_ty().skip_binder(),
                                     root_pred.self_ty(),
                                 )
                             )
                             // The leaf trait and the root trait are different, so as to avoid
                             // talking about `&mut T: Trait` and instead remain talking about
                             // `T: Trait` instead
-                            && trait_predicate.def_id() != root_pred.def_id()
+                            && leaf_trait_predicate.def_id() != root_pred.def_id()
                             // The root trait is not `Unsize`, as to avoid talking about it in
                             // `tests/ui/coercion/coerce-issue-49593-box-never.rs`.
                             && Some(root_pred.def_id()) != self.tcx.lang_items().unsize_trait()
@@ -459,13 +459,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                                 root_obligation,
                             )
                         } else {
-                            (trait_predicate, &obligation)
+                            (leaf_trait_predicate, &obligation)
                         };
-                        let trait_ref = main_trait_predicate.to_poly_trait_ref();
+                        let main_trait_ref = main_trait_predicate.to_poly_trait_ref();
+                        let leaf_trait_ref = leaf_trait_predicate.to_poly_trait_ref();
 
                         if let Some(guar) = self.emit_specialized_closure_kind_error(
                             &obligation,
-                            trait_ref,
+                            leaf_trait_ref,
                         ) {
                             return guar;
                         }
@@ -473,7 +474,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         // FIXME(effects)
                         let predicate_is_const = false;
 
-                        if let Err(guar) = trait_predicate.error_reported()
+                        if let Err(guar) = leaf_trait_predicate.error_reported()
                         {
                             return guar;
                         }
@@ -507,16 +508,17 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                             notes,
                             parent_label,
                             append_const_msg,
-                        } = self.on_unimplemented_note(trait_ref, o, &mut long_ty_file);
+                        } = self.on_unimplemented_note(main_trait_ref, o, &mut long_ty_file);
+
                         let have_alt_message = message.is_some() || label.is_some();
-                        let is_try_conversion = self.is_try_conversion(span, trait_ref.def_id());
+                        let is_try_conversion = self.is_try_conversion(span, main_trait_ref.def_id());
                         let is_unsize =
-                            Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait();
+                            self.tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Unsize);
                         let (message, notes, append_const_msg) = if is_try_conversion {
                             (
                                 Some(format!(
                                     "`?` couldn't convert the error to `{}`",
-                                    trait_ref.skip_binder().self_ty(),
+                                    main_trait_ref.skip_binder().self_ty(),
                                 )),
                                 vec![
                                     "the question mark operation (`?`) implicitly performs a \
@@ -530,20 +532,20 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         };
 
                         let err_msg = self.get_standard_error_message(
-                            &main_trait_predicate,
+                            main_trait_predicate,
                             message,
                             predicate_is_const,
                             append_const_msg,
                             post_message,
                         );
 
-                        let (err_msg, safe_transmute_explanation) = if Some(trait_ref.def_id())
+                        let (err_msg, safe_transmute_explanation) = if Some(main_trait_ref.def_id())
                             == self.tcx.lang_items().transmute_trait()
                         {
                             // Recompute the safe transmute reason and use that for the error reporting
                             match self.get_safe_transmute_error_and_reason(
                                 obligation.clone(),
-                                trait_ref,
+                                main_trait_ref,
                                 span,
                             ) {
                                 GetSafeTransmuteErrorAndReason::Silent => {
@@ -571,7 +573,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         }
                         let mut suggested = false;
                         if is_try_conversion {
-                            suggested = self.try_conversion_context(&obligation, trait_ref.skip_binder(), &mut err);
+                            suggested = self.try_conversion_context(&obligation, main_trait_ref.skip_binder(), &mut err);
                         }
 
                         if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) {
@@ -579,19 +581,19 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                                 ret_span,
                                 format!(
                                     "expected `{}` because of this",
-                                    trait_ref.skip_binder().self_ty()
+                                    main_trait_ref.skip_binder().self_ty()
                                 ),
                             );
                         }
 
-                        if Some(trait_ref.def_id()) == tcx.lang_items().tuple_trait() {
+                        if tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Tuple) {
                             self.add_tuple_trait_message(
                                 obligation.cause.code().peel_derives(),
                                 &mut err,
                             );
                         }
 
-                        if Some(trait_ref.def_id()) == tcx.lang_items().drop_trait()
+                        if tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Drop)
                             && predicate_is_const
                         {
                             err.note("`~const Drop` was renamed to `~const Destruct`");
@@ -601,24 +603,25 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         let explanation = get_explanation_based_on_obligation(
                             self.tcx,
                             &obligation,
-                            trait_ref,
-                            &trait_predicate,
+                            leaf_trait_predicate,
                             pre_message,
                         );
 
                         self.check_for_binding_assigned_block_without_tail_expression(
                             &obligation,
                             &mut err,
-                            trait_predicate,
+                            leaf_trait_predicate,
                         );
-                        self.suggest_add_result_as_return_type(&obligation,
+                        self.suggest_add_result_as_return_type(
+                            &obligation,
                             &mut err,
-                            trait_ref);
+                            leaf_trait_predicate,
+                        );
 
                         if self.suggest_add_reference_to_arg(
                             &obligation,
                             &mut err,
-                            trait_predicate,
+                            leaf_trait_predicate,
                             have_alt_message,
                         ) {
                             self.note_obligation_cause(&mut err, &obligation);
@@ -630,7 +633,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                             // If it has a custom `#[rustc_on_unimplemented]`
                             // error message, let's display it as the label!
                             err.span_label(span, s);
-                            if !matches!(trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) {
+                            if !matches!(leaf_trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) {
                                 // When the self type is a type param We don't need to "the trait
                                 // `std::marker::Sized` is not implemented for `T`" as we will point
                                 // at the type param with a label to suggest constraining it.
@@ -645,7 +648,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         if let ObligationCauseCode::Coercion { source, target } =
                             *obligation.cause.code().peel_derives()
                         {
-                            if Some(trait_ref.def_id()) == self.tcx.lang_items().sized_trait() {
+                            if self.tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Sized) {
                                 self.suggest_borrowing_for_object_cast(
                                     &mut err,
                                     root_obligation,
@@ -657,7 +660,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
                         let UnsatisfiedConst(unsatisfied_const) = self
                             .maybe_add_note_for_unsatisfied_const(
-                                &trait_predicate,
+                                leaf_trait_predicate,
                                 &mut err,
                                 span,
                             );
@@ -674,15 +677,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                             err.span_label(tcx.def_span(body), s);
                         }
 
-                        self.suggest_floating_point_literal(&obligation, &mut err, &trait_ref);
-                        self.suggest_dereferencing_index(&obligation, &mut err, trait_predicate);
-                        suggested |= self.suggest_dereferences(&obligation, &mut err, trait_predicate);
-                        suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate);
-                        let impl_candidates = self.find_similar_impl_candidates(trait_predicate);
+                        self.suggest_floating_point_literal(&obligation, &mut err, leaf_trait_ref);
+                        self.suggest_dereferencing_index(&obligation, &mut err, leaf_trait_predicate);
+                        suggested |= self.suggest_dereferences(&obligation, &mut err, leaf_trait_predicate);
+                        suggested |= self.suggest_fn_call(&obligation, &mut err, leaf_trait_predicate);
+                        let impl_candidates = self.find_similar_impl_candidates(leaf_trait_predicate);
                         suggested = if let &[cand] = &impl_candidates[..] {
                             let cand = cand.trait_ref;
                             if let (ty::FnPtr(_), ty::FnDef(..)) =
-                                (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind())
+                                (cand.self_ty().kind(), main_trait_ref.self_ty().skip_binder().kind())
                             {
                                 err.span_suggestion(
                                     span.shrink_to_hi(),
@@ -702,31 +705,31 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                             false
                         } || suggested;
                         suggested |=
-                            self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
+                            self.suggest_remove_reference(&obligation, &mut err, leaf_trait_predicate);
                         suggested |= self.suggest_semicolon_removal(
                             &obligation,
                             &mut err,
                             span,
-                            trait_predicate,
+                            leaf_trait_predicate,
                         );
-                        self.note_version_mismatch(&mut err, &trait_ref);
+                        self.note_version_mismatch(&mut err, leaf_trait_ref);
                         self.suggest_remove_await(&obligation, &mut err);
-                        self.suggest_derive(&obligation, &mut err, trait_predicate);
+                        self.suggest_derive(&obligation, &mut err, leaf_trait_predicate);
 
-                        if Some(trait_ref.def_id()) == tcx.lang_items().try_trait() {
+                        if tcx.is_lang_item(leaf_trait_ref.def_id(), LangItem::Try) {
                             self.suggest_await_before_try(
                                 &mut err,
                                 &obligation,
-                                trait_predicate,
+                                leaf_trait_predicate,
                                 span,
                             );
                         }
 
-                        if self.suggest_add_clone_to_arg(&obligation, &mut err, trait_predicate) {
+                        if self.suggest_add_clone_to_arg(&obligation, &mut err, leaf_trait_predicate) {
                             return err.emit();
                         }
 
-                        if self.suggest_impl_trait(&mut err, &obligation, trait_predicate) {
+                        if self.suggest_impl_trait(&mut err, &obligation, leaf_trait_predicate) {
                             return err.emit();
                         }
 
@@ -741,9 +744,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                             );
                         }
 
-                        let is_fn_trait = tcx.is_fn_trait(trait_ref.def_id());
+                        let is_fn_trait = tcx.is_fn_trait(leaf_trait_ref.def_id());
                         let is_target_feature_fn = if let ty::FnDef(def_id, _) =
-                            *trait_ref.skip_binder().self_ty().kind()
+                            *leaf_trait_ref.skip_binder().self_ty().kind()
                         {
                             !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty()
                         } else {
@@ -757,8 +760,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
                         self.try_to_add_help_message(
                             &obligation,
-                            trait_ref,
-                            &trait_predicate,
+                            leaf_trait_predicate,
                             &mut err,
                             span,
                             is_fn_trait,
@@ -769,17 +771,17 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         // Changing mutability doesn't make a difference to whether we have
                         // an `Unsize` impl (Fixes ICE in #71036)
                         if !is_unsize {
-                            self.suggest_change_mut(&obligation, &mut err, trait_predicate);
+                            self.suggest_change_mut(&obligation, &mut err, leaf_trait_predicate);
                         }
 
                         // If this error is due to `!: Trait` not implemented but `(): Trait` is
                         // implemented, and fallback has occurred, then it could be due to a
                         // variable that used to fallback to `()` now falling back to `!`. Issue a
                         // note informing about the change in behaviour.
-                        if trait_predicate.skip_binder().self_ty().is_never()
+                        if leaf_trait_predicate.skip_binder().self_ty().is_never()
                             && self.fallback_has_occurred
                         {
-                            let predicate = trait_predicate.map_bound(|trait_pred| {
+                            let predicate = leaf_trait_predicate.map_bound(|trait_pred| {
                                 trait_pred.with_self_ty(self.tcx, tcx.types.unit)
                             });
                             let unit_obligation = obligation.with(tcx, predicate);
@@ -794,8 +796,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                             }
                         }
 
-                        self.explain_hrtb_projection(&mut err, trait_predicate, obligation.param_env, &obligation.cause);
-                        self.suggest_desugaring_async_fn_in_trait(&mut err, trait_ref);
+                        self.explain_hrtb_projection(&mut err, leaf_trait_predicate, obligation.param_env, &obligation.cause);
+                        self.suggest_desugaring_async_fn_in_trait(&mut err, main_trait_ref);
 
                         // Return early if the trait is Debug or Display and the invocation
                         // originates within a standard library macro, because the output
@@ -813,15 +815,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
                         if in_std_macro
                             && matches!(
-                                self.tcx.get_diagnostic_name(trait_ref.def_id()),
+                                self.tcx.get_diagnostic_name(leaf_trait_ref.def_id()),
                                 Some(sym::Debug | sym::Display)
                             )
                         {
                             return err.emit();
                         }
 
-
-
                         err
                     }
 
@@ -1020,7 +1020,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         // doesn't extend the goal kind. This is worth reporting, but we can only do so
         // if we actually know which closure this goal comes from, so look at the cause
         // to see if we can extract that information.
-        if Some(trait_ref.def_id()) == self.tcx.lang_items().async_fn_kind_helper()
+        if self.tcx.is_lang_item(trait_ref.def_id(), LangItem::AsyncFnKindHelper)
             && let Some(found_kind) = trait_ref.skip_binder().args.type_at(0).to_opt_closure_kind()
             && let Some(expected_kind) =
                 trait_ref.skip_binder().args.type_at(1).to_opt_closure_kind()
@@ -1744,7 +1744,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         let self_ty = pred.projection_term.self_ty();
 
         with_forced_trimmed_paths! {
-            if Some(pred.projection_term.def_id) == self.tcx.lang_items().fn_once_output() {
+            if self.tcx.is_lang_item(pred.projection_term.def_id,LangItem::FnOnceOutput) {
                 let fn_kind = self_ty.prefix_string(self.tcx);
                 let item = match self_ty.kind() {
                     ty::FnDef(def, _) => self.tcx.item_name(*def).to_string(),
@@ -1754,7 +1754,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     "expected `{item}` to be a {fn_kind} that returns `{expected_ty}`, but it \
                      returns `{normalized_ty}`",
                 ))
-            } else if Some(trait_def_id) == self.tcx.lang_items().future_trait() {
+            } else if self.tcx.is_lang_item(trait_def_id, LangItem::Future) {
                 Some(format!(
                     "expected `{self_ty}` to be a future that resolves to `{expected_ty}`, but it \
                      resolves to `{normalized_ty}`"
@@ -1783,7 +1783,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 ty::Bool => Some(0),
                 ty::Char => Some(1),
                 ty::Str => Some(2),
-                ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().string() => Some(2),
+                ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::String) => Some(2),
                 ty::Int(..)
                 | ty::Uint(..)
                 | ty::Float(..)
@@ -2236,11 +2236,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
     /// with the same path as `trait_ref`, a help message about
     /// a probable version mismatch is added to `err`
-    fn note_version_mismatch(
-        &self,
-        err: &mut Diag<'_>,
-        trait_ref: &ty::PolyTraitRef<'tcx>,
-    ) -> bool {
+    fn note_version_mismatch(&self, err: &mut Diag<'_>, trait_ref: ty::PolyTraitRef<'tcx>) -> bool {
         let get_trait_impls = |trait_def_id| {
             let mut trait_impls = vec![];
             self.tcx.for_each_relevant_impl(
@@ -2346,7 +2342,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 // avoid inundating the user with unnecessary errors, but we now
                 // check upstream for type errors and don't add the obligations to
                 // begin with in those cases.
-                if self.tcx.lang_items().sized_trait() == Some(trait_ref.def_id()) {
+                if self.tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) {
                     match self.tainted_by_errors() {
                         None => {
                             let err = self.emit_inference_failure_err(
@@ -2705,6 +2701,22 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     ),
                 );
             }
+
+            ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })
+                if term.is_infer() =>
+            {
+                if let Some(e) = self.tainted_by_errors() {
+                    return e;
+                }
+                struct_span_code_err!(
+                    self.dcx(),
+                    span,
+                    E0284,
+                    "type annotations needed: cannot normalize `{alias}`",
+                )
+                .with_span_label(span, format!("cannot normalize `{alias}`"))
+            }
+
             _ => {
                 if let Some(e) = self.tainted_by_errors() {
                     return e;
@@ -2913,7 +2925,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
         let (Some(node), true) = (
             self.tcx.hir().get_if_local(item_def_id),
-            Some(pred.def_id()) == self.tcx.lang_items().sized_trait(),
+            self.tcx.is_lang_item(pred.def_id(), LangItem::Sized),
         ) else {
             return;
         };
@@ -3044,7 +3056,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
     fn get_standard_error_message(
         &self,
-        trait_predicate: &ty::PolyTraitPredicate<'tcx>,
+        trait_predicate: ty::PolyTraitPredicate<'tcx>,
         message: Option<String>,
         predicate_is_const: bool,
         append_const_msg: Option<AppendConstMessage>,
@@ -3215,8 +3227,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn try_to_add_help_message(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        trait_ref: ty::PolyTraitRef<'tcx>,
-        trait_predicate: &ty::PolyTraitPredicate<'tcx>,
+        trait_predicate: ty::PolyTraitPredicate<'tcx>,
         err: &mut Diag<'_>,
         span: Span,
         is_fn_trait: bool,
@@ -3233,16 +3244,22 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         };
 
         // Try to report a help message
+        let trait_def_id = trait_predicate.def_id();
         if is_fn_trait
             && let Ok((implemented_kind, params)) = self.type_implements_fn_trait(
                 obligation.param_env,
-                trait_ref.self_ty(),
+                trait_predicate.self_ty(),
                 trait_predicate.skip_binder().polarity,
             )
         {
-            self.add_help_message_for_fn_trait(trait_ref, err, implemented_kind, params);
-        } else if !trait_ref.has_non_region_infer()
-            && self.predicate_can_apply(obligation.param_env, *trait_predicate)
+            self.add_help_message_for_fn_trait(
+                trait_predicate.to_poly_trait_ref(),
+                err,
+                implemented_kind,
+                params,
+            );
+        } else if !trait_predicate.has_non_region_infer()
+            && self.predicate_can_apply(obligation.param_env, trait_predicate)
         {
             // If a where-clause may be useful, remind the
             // user that they can add it.
@@ -3253,25 +3270,25 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             // which is somewhat confusing.
             self.suggest_restricting_param_bound(
                 err,
-                *trait_predicate,
+                trait_predicate,
                 None,
                 obligation.cause.body_id,
             );
-        } else if trait_ref.def_id().is_local()
-            && self.tcx.trait_impls_of(trait_ref.def_id()).is_empty()
-            && !self.tcx.trait_is_auto(trait_ref.def_id())
-            && !self.tcx.trait_is_alias(trait_ref.def_id())
+        } else if trait_def_id.is_local()
+            && self.tcx.trait_impls_of(trait_def_id).is_empty()
+            && !self.tcx.trait_is_auto(trait_def_id)
+            && !self.tcx.trait_is_alias(trait_def_id)
         {
             err.span_help(
-                self.tcx.def_span(trait_ref.def_id()),
+                self.tcx.def_span(trait_def_id),
                 crate::fluent_generated::trait_selection_trait_has_no_impls,
             );
         } else if !suggested && !unsatisfied_const {
             // Can't show anything else useful, try to find similar impls.
-            let impl_candidates = self.find_similar_impl_candidates(*trait_predicate);
+            let impl_candidates = self.find_similar_impl_candidates(trait_predicate);
             if !self.report_similar_impl_candidates(
                 &impl_candidates,
-                trait_ref,
+                trait_predicate.to_poly_trait_ref(),
                 body_def_id,
                 err,
                 true,
@@ -3279,7 +3296,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             ) {
                 self.report_similar_impl_candidates_for_root_obligation(
                     obligation,
-                    *trait_predicate,
+                    trait_predicate,
                     body_def_id,
                     err,
                 );
@@ -3288,7 +3305,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             self.suggest_convert_to_slice(
                 err,
                 obligation,
-                trait_ref,
+                trait_predicate.to_poly_trait_ref(),
                 impl_candidates.as_slice(),
                 span,
             );
@@ -3353,7 +3370,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
     fn maybe_add_note_for_unsatisfied_const(
         &self,
-        _trait_predicate: &ty::PolyTraitPredicate<'tcx>,
+        _trait_predicate: ty::PolyTraitPredicate<'tcx>,
         _err: &mut Diag<'_>,
         _span: Span,
     ) -> UnsatisfiedConst {
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index 08355ef55c4..fc5a2875b67 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -194,7 +194,7 @@ fn predicates_reference_self(
     predicates
         .predicates
         .iter()
-        .map(|&(predicate, sp)| (predicate.instantiate_supertrait(tcx, &trait_ref), sp))
+        .map(|&(predicate, sp)| (predicate.instantiate_supertrait(tcx, trait_ref), sp))
         .filter_map(|predicate| predicate_references_self(tcx, predicate))
         .collect()
 }
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 2c9cb79664b..8ab324e6601 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -1015,6 +1015,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                 // not eligible.
                 let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
 
+                let tcx = selcx.tcx();
                 let lang_items = selcx.tcx().lang_items();
                 if [
                     lang_items.coroutine_trait(),
@@ -1031,7 +1032,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                 .contains(&Some(trait_ref.def_id))
                 {
                     true
-                } else if lang_items.async_fn_kind_helper() == Some(trait_ref.def_id) {
+                } else if tcx.is_lang_item(trait_ref.def_id, LangItem::AsyncFnKindHelper) {
                     // FIXME(async_closures): Validity constraints here could be cleaned up.
                     if obligation.predicate.args.type_at(0).is_ty_var()
                         || obligation.predicate.args.type_at(4).is_ty_var()
@@ -1043,7 +1044,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                         obligation.predicate.args.type_at(0).to_opt_closure_kind().is_some()
                             && obligation.predicate.args.type_at(1).to_opt_closure_kind().is_some()
                     }
-                } else if lang_items.discriminant_kind_trait() == Some(trait_ref.def_id) {
+                } else if tcx.is_lang_item(trait_ref.def_id, LangItem::DiscriminantKind) {
                     match self_ty.kind() {
                         ty::Bool
                         | ty::Char
@@ -1080,7 +1081,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                         | ty::Infer(..)
                         | ty::Error(_) => false,
                     }
-                } else if lang_items.async_destruct_trait() == Some(trait_ref.def_id) {
+                } else if tcx.is_lang_item(trait_ref.def_id, LangItem::AsyncDestruct) {
                     match self_ty.kind() {
                         ty::Bool
                         | ty::Char
@@ -1116,7 +1117,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                         | ty::Infer(_)
                         | ty::Error(_) => false,
                     }
-                } else if lang_items.pointee_trait() == Some(trait_ref.def_id) {
+                } else if tcx.is_lang_item(trait_ref.def_id, LangItem::PointeeTrait) {
                     let tail = selcx.tcx().struct_tail_with_normalize(
                         self_ty,
                         |ty| {
@@ -1300,15 +1301,15 @@ fn confirm_select_candidate<'cx, 'tcx>(
     match impl_source {
         ImplSource::UserDefined(data) => confirm_impl_candidate(selcx, obligation, data),
         ImplSource::Builtin(BuiltinImplSource::Misc, data) => {
-            let trait_def_id = obligation.predicate.trait_def_id(selcx.tcx());
-            let lang_items = selcx.tcx().lang_items();
-            if lang_items.coroutine_trait() == Some(trait_def_id) {
+            let tcx = selcx.tcx();
+            let trait_def_id = obligation.predicate.trait_def_id(tcx);
+            if tcx.is_lang_item(trait_def_id, LangItem::Coroutine) {
                 confirm_coroutine_candidate(selcx, obligation, data)
-            } else if lang_items.future_trait() == Some(trait_def_id) {
+            } else if tcx.is_lang_item(trait_def_id, LangItem::Future) {
                 confirm_future_candidate(selcx, obligation, data)
-            } else if lang_items.iterator_trait() == Some(trait_def_id) {
+            } else if tcx.is_lang_item(trait_def_id, LangItem::Iterator) {
                 confirm_iterator_candidate(selcx, obligation, data)
-            } else if lang_items.async_iterator_trait() == Some(trait_def_id) {
+            } else if tcx.is_lang_item(trait_def_id, LangItem::AsyncIterator) {
                 confirm_async_iterator_candidate(selcx, obligation, data)
             } else if selcx.tcx().fn_trait_kind_from_def_id(trait_def_id).is_some() {
                 if obligation.predicate.self_ty().is_closure()
@@ -1320,7 +1321,7 @@ fn confirm_select_candidate<'cx, 'tcx>(
                 }
             } else if selcx.tcx().async_fn_trait_kind_from_def_id(trait_def_id).is_some() {
                 confirm_async_closure_candidate(selcx, obligation, data)
-            } else if lang_items.async_fn_kind_helper() == Some(trait_def_id) {
+            } else if tcx.is_lang_item(trait_def_id, LangItem::AsyncFnKindHelper) {
                 confirm_async_fn_kind_helper_candidate(selcx, obligation, data)
             } else {
                 confirm_builtin_candidate(selcx, obligation, data)
@@ -1373,15 +1374,15 @@ fn confirm_coroutine_candidate<'cx, 'tcx>(
         coroutine_sig,
     );
 
-    let name = tcx.associated_item(obligation.predicate.def_id).name;
-    let ty = if name == sym::Return {
+    let ty = if tcx.is_lang_item(obligation.predicate.def_id, LangItem::CoroutineReturn) {
         return_ty
-    } else if name == sym::Yield {
+    } else if tcx.is_lang_item(obligation.predicate.def_id, LangItem::CoroutineYield) {
         yield_ty
     } else {
         span_bug!(
             tcx.def_span(obligation.predicate.def_id),
-            "unexpected associated type: `Coroutine::{name}`"
+            "unexpected associated type: `Coroutine::{}`",
+            tcx.item_name(obligation.predicate.def_id),
         );
     };
 
@@ -1538,21 +1539,20 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
 ) -> Progress<'tcx> {
     let tcx = selcx.tcx();
     let self_ty = obligation.predicate.self_ty();
-    let lang_items = tcx.lang_items();
     let item_def_id = obligation.predicate.def_id;
     let trait_def_id = tcx.trait_of_item(item_def_id).unwrap();
     let args = tcx.mk_args(&[self_ty.into()]);
-    let (term, obligations) = if lang_items.discriminant_kind_trait() == Some(trait_def_id) {
+    let (term, obligations) = if tcx.is_lang_item(trait_def_id, LangItem::DiscriminantKind) {
         let discriminant_def_id = tcx.require_lang_item(LangItem::Discriminant, None);
         assert_eq!(discriminant_def_id, item_def_id);
 
         (self_ty.discriminant_ty(tcx).into(), Vec::new())
-    } else if lang_items.async_destruct_trait() == Some(trait_def_id) {
+    } else if tcx.is_lang_item(trait_def_id, LangItem::AsyncDestruct) {
         let destructor_def_id = tcx.associated_item_def_ids(trait_def_id)[0];
         assert_eq!(destructor_def_id, item_def_id);
 
         (self_ty.async_destructor_ty(tcx).into(), Vec::new())
-    } else if lang_items.pointee_trait() == Some(trait_def_id) {
+    } else if tcx.is_lang_item(trait_def_id, LangItem::PointeeTrait) {
         let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None);
         assert_eq!(metadata_def_id, item_def_id);
 
diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
index 326c68e01db..7dc051e6fe9 100644
--- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
@@ -55,7 +55,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
         }
 
         ty::Adt(def, _) => {
-            if Some(def.did()) == tcx.lang_items().manually_drop() {
+            if def.is_manually_drop() {
                 // `ManuallyDrop` never has a dtor.
                 true
             } else {
diff --git a/compiler/rustc_infer/src/infer/relate/_match.rs b/compiler/rustc_trait_selection/src/traits/select/_match.rs
index 30a066a265a..50d8e96aaf9 100644
--- a/compiler/rustc_infer/src/infer/relate/_match.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/_match.rs
@@ -1,10 +1,10 @@
+use rustc_infer::infer::relate::{
+    self, structurally_relate_tys, Relate, RelateResult, TypeRelation,
+};
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::{self, InferConst, Ty, TyCtxt};
 use tracing::{debug, instrument};
 
-use super::{structurally_relate_tys, Relate, RelateResult, TypeRelation};
-use crate::infer::relate;
-
 /// A type "A" *matches* "B" if the fresh types in B could be
 /// instantiated with values so as to make it equal to A. Matching is
 /// intended to be used only on freshened types, and it basically
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index 6db5fa0e4e5..c53939bfe60 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -67,9 +67,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             // Other bounds. Consider both in-scope bounds from fn decl
             // and applicable impls. There is a certain set of precedence rules here.
             let def_id = obligation.predicate.def_id();
-            let lang_items = self.tcx().lang_items();
+            let tcx = self.tcx();
 
-            if lang_items.copy_trait() == Some(def_id) {
+            if tcx.is_lang_item(def_id, LangItem::Copy) {
                 debug!(obligation_self_ty = ?obligation.predicate.skip_binder().self_ty());
 
                 // User-defined copy impls are permitted, but only for
@@ -79,16 +79,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 // For other types, we'll use the builtin rules.
                 let copy_conditions = self.copy_clone_conditions(obligation);
                 self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates);
-            } else if lang_items.discriminant_kind_trait() == Some(def_id) {
+            } else if tcx.is_lang_item(def_id, LangItem::DiscriminantKind) {
                 // `DiscriminantKind` is automatically implemented for every type.
                 candidates.vec.push(BuiltinCandidate { has_nested: false });
-            } else if lang_items.async_destruct_trait() == Some(def_id) {
+            } else if tcx.is_lang_item(def_id, LangItem::AsyncDestruct) {
                 // `AsyncDestruct` is automatically implemented for every type.
                 candidates.vec.push(BuiltinCandidate { has_nested: false });
-            } else if lang_items.pointee_trait() == Some(def_id) {
+            } else if tcx.is_lang_item(def_id, LangItem::PointeeTrait) {
                 // `Pointee` is automatically implemented for every type.
                 candidates.vec.push(BuiltinCandidate { has_nested: false });
-            } else if lang_items.sized_trait() == Some(def_id) {
+            } else if tcx.is_lang_item(def_id, LangItem::Sized) {
                 // Sized is never implementable by end-users, it is
                 // always automatically computed.
 
@@ -101,22 +101,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
                 let sized_conditions = self.sized_conditions(obligation);
                 self.assemble_builtin_bound_candidates(sized_conditions, &mut candidates);
-            } else if lang_items.unsize_trait() == Some(def_id) {
+            } else if tcx.is_lang_item(def_id, LangItem::Unsize) {
                 self.assemble_candidates_for_unsizing(obligation, &mut candidates);
-            } else if lang_items.destruct_trait() == Some(def_id) {
+            } else if tcx.is_lang_item(def_id, LangItem::Destruct) {
                 self.assemble_const_destruct_candidates(obligation, &mut candidates);
-            } else if lang_items.transmute_trait() == Some(def_id) {
+            } else if tcx.is_lang_item(def_id, LangItem::TransmuteTrait) {
                 // User-defined transmutability impls are permitted.
                 self.assemble_candidates_from_impls(obligation, &mut candidates);
                 self.assemble_candidates_for_transmutability(obligation, &mut candidates);
-            } else if lang_items.tuple_trait() == Some(def_id) {
+            } else if tcx.is_lang_item(def_id, LangItem::Tuple) {
                 self.assemble_candidate_for_tuple(obligation, &mut candidates);
-            } else if lang_items.pointer_like() == Some(def_id) {
+            } else if tcx.is_lang_item(def_id, LangItem::PointerLike) {
                 self.assemble_candidate_for_pointer_like(obligation, &mut candidates);
-            } else if lang_items.fn_ptr_trait() == Some(def_id) {
+            } else if tcx.is_lang_item(def_id, LangItem::FnPtrTrait) {
                 self.assemble_candidates_for_fn_ptr_trait(obligation, &mut candidates);
             } else {
-                if lang_items.clone_trait() == Some(def_id) {
+                if tcx.is_lang_item(def_id, LangItem::Clone) {
                     // Same builtin conditions as `Copy`, i.e., every type which has builtin support
                     // for `Copy` also has builtin support for `Clone`, and tuples/arrays of `Clone`
                     // types have builtin support for `Clone`.
@@ -124,17 +124,17 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates);
                 }
 
-                if lang_items.coroutine_trait() == Some(def_id) {
+                if tcx.is_lang_item(def_id, LangItem::Coroutine) {
                     self.assemble_coroutine_candidates(obligation, &mut candidates);
-                } else if lang_items.future_trait() == Some(def_id) {
+                } else if tcx.is_lang_item(def_id, LangItem::Future) {
                     self.assemble_future_candidates(obligation, &mut candidates);
-                } else if lang_items.iterator_trait() == Some(def_id) {
+                } else if tcx.is_lang_item(def_id, LangItem::Iterator) {
                     self.assemble_iterator_candidates(obligation, &mut candidates);
-                } else if lang_items.fused_iterator_trait() == Some(def_id) {
+                } else if tcx.is_lang_item(def_id, LangItem::FusedIterator) {
                     self.assemble_fused_iterator_candidates(obligation, &mut candidates);
-                } else if lang_items.async_iterator_trait() == Some(def_id) {
+                } else if tcx.is_lang_item(def_id, LangItem::AsyncIterator) {
                     self.assemble_async_iterator_candidates(obligation, &mut candidates);
-                } else if lang_items.async_fn_kind_helper() == Some(def_id) {
+                } else if tcx.is_lang_item(def_id, LangItem::AsyncFnKindHelper) {
                     self.assemble_async_fn_kind_helper_candidates(obligation, &mut candidates);
                 }
 
@@ -239,24 +239,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             return Ok(());
         }
 
-        let all_bounds = stack
+        let bounds = stack
             .obligation
             .param_env
             .caller_bounds()
             .iter()
             .filter(|p| !p.references_error())
-            .filter_map(|p| p.as_trait_clause());
-
-        // Micro-optimization: filter out predicates relating to different traits.
-        let matching_bounds =
-            all_bounds.filter(|p| p.def_id() == stack.obligation.predicate.def_id());
+            .filter_map(|p| p.as_trait_clause())
+            // Micro-optimization: filter out predicates relating to different traits.
+            .filter(|p| p.def_id() == stack.obligation.predicate.def_id())
+            .filter(|p| p.polarity() == stack.obligation.predicate.polarity());
 
         // Keep only those bounds which may apply, and propagate overflow if it occurs.
-        for bound in matching_bounds {
-            if bound.skip_binder().polarity != stack.obligation.predicate.skip_binder().polarity {
-                continue;
-            }
-
+        for bound in bounds {
             // FIXME(oli-obk): it is suspicious that we are dropping the constness and
             // polarity here.
             let wc = self.where_clause_may_apply(stack, bound.map_bound(|t| t.trait_ref))?;
@@ -755,7 +750,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     candidates.ambiguous = true;
                 }
                 ty::Coroutine(coroutine_def_id, _)
-                    if self.tcx().lang_items().unpin_trait() == Some(def_id) =>
+                    if self.tcx().is_lang_item(def_id, LangItem::Unpin) =>
                 {
                     match self.tcx().coroutine_movability(coroutine_def_id) {
                         hir::Movability::Static => {
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 749081006f3..074fd487888 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -258,16 +258,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     ) -> Vec<PredicateObligation<'tcx>> {
         debug!(?obligation, ?has_nested, "confirm_builtin_candidate");
 
-        let lang_items = self.tcx().lang_items();
+        let tcx = self.tcx();
         let obligations = if has_nested {
             let trait_def = obligation.predicate.def_id();
-            let conditions = if Some(trait_def) == lang_items.sized_trait() {
+            let conditions = if tcx.is_lang_item(trait_def, LangItem::Sized) {
                 self.sized_conditions(obligation)
-            } else if Some(trait_def) == lang_items.copy_trait() {
+            } else if tcx.is_lang_item(trait_def, LangItem::Copy) {
                 self.copy_clone_conditions(obligation)
-            } else if Some(trait_def) == lang_items.clone_trait() {
+            } else if tcx.is_lang_item(trait_def, LangItem::Clone) {
                 self.copy_clone_conditions(obligation)
-            } else if Some(trait_def) == lang_items.fused_iterator_trait() {
+            } else if tcx.is_lang_item(trait_def, LangItem::FusedIterator) {
                 self.fused_iterator_conditions(obligation)
             } else {
                 bug!("unexpected builtin trait {:?}", trait_def)
@@ -338,7 +338,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     let make_freeze_obl = |ty| {
                         let trait_ref = ty::TraitRef::new(
                             tcx,
-                            tcx.lang_items().freeze_trait().unwrap(),
+                            tcx.require_lang_item(LangItem::Freeze, None),
                             [ty::GenericArg::from(ty)],
                         );
                         Obligation::with_depth(
@@ -1444,7 +1444,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 | ty::Foreign(_) => {}
 
                 // `ManuallyDrop` is trivially drop
-                ty::Adt(def, _) if Some(def.did()) == tcx.lang_items().manually_drop() => {}
+                ty::Adt(def, _) if def.is_manually_drop() => {}
 
                 // These types are built-in, so we can fast-track by registering
                 // nested predicates for their constituent type(s)
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 4306a803524..b4019585771 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -32,7 +32,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::{Diag, EmissionGuarantee};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
-use rustc_infer::infer::relate::MatchAgainstFreshVars;
+use rustc_hir::LangItem;
 use rustc_infer::infer::relate::TypeRelation;
 use rustc_infer::infer::BoundRegionConversionTime;
 use rustc_infer::infer::BoundRegionConversionTime::HigherRankedType;
@@ -60,6 +60,7 @@ use std::ops::ControlFlow;
 pub use rustc_middle::traits::select::*;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 
+mod _match;
 mod candidate_assembly;
 mod confirmation;
 
@@ -1866,7 +1867,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
         // the param_env so that it can be given the lowest priority. See
         // #50825 for the motivation for this.
         let is_global =
-            |cand: &ty::PolyTraitPredicate<'tcx>| cand.is_global() && !cand.has_bound_vars();
+            |cand: ty::PolyTraitPredicate<'tcx>| cand.is_global() && !cand.has_bound_vars();
 
         // (*) Prefer `BuiltinCandidate { has_nested: false }`, `PointeeCandidate`,
         // `DiscriminantKindCandidate`, `ConstDestructCandidate`
@@ -1909,7 +1910,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             }
 
             (
-                ParamCandidate(ref other_cand),
+                ParamCandidate(other_cand),
                 ImplCandidate(..)
                 | AutoImplCandidate
                 | ClosureCandidate { .. }
@@ -1934,12 +1935,12 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 //
                 // Global bounds from the where clause should be ignored
                 // here (see issue #50825).
-                DropVictim::drop_if(!is_global(other_cand))
+                DropVictim::drop_if(!is_global(*other_cand))
             }
-            (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(ref victim_cand)) => {
+            (ObjectCandidate(_) | ProjectionCandidate(_), ParamCandidate(victim_cand)) => {
                 // Prefer these to a global where-clause bound
                 // (see issue #50825).
-                if is_global(victim_cand) { DropVictim::Yes } else { DropVictim::No }
+                if is_global(*victim_cand) { DropVictim::Yes } else { DropVictim::No }
             }
             (
                 ImplCandidate(_)
@@ -1957,12 +1958,12 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 | TraitUpcastingUnsizeCandidate(_)
                 | BuiltinCandidate { has_nested: true }
                 | TraitAliasCandidate,
-                ParamCandidate(ref victim_cand),
+                ParamCandidate(victim_cand),
             ) => {
                 // Prefer these to a global where-clause bound
                 // (see issue #50825).
                 DropVictim::drop_if(
-                    is_global(victim_cand) && other.evaluation.must_apply_modulo_regions(),
+                    is_global(*victim_cand) && other.evaluation.must_apply_modulo_regions(),
                 )
             }
 
@@ -2719,7 +2720,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
         previous: ty::PolyTraitPredicate<'tcx>,
         current: ty::PolyTraitPredicate<'tcx>,
     ) -> bool {
-        let mut matcher = MatchAgainstFreshVars::new(self.tcx());
+        let mut matcher = _match::MatchAgainstFreshVars::new(self.tcx());
         matcher.relate(previous, current).is_ok()
     }
 
@@ -2800,19 +2801,18 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
         let predicates = predicates.instantiate_own(tcx, args);
         let mut obligations = Vec::with_capacity(predicates.len());
         for (index, (predicate, span)) in predicates.into_iter().enumerate() {
-            let cause =
-                if Some(parent_trait_pred.def_id()) == tcx.lang_items().coerce_unsized_trait() {
-                    cause.clone()
-                } else {
-                    cause.clone().derived_cause(parent_trait_pred, |derived| {
-                        ObligationCauseCode::ImplDerived(Box::new(ImplDerivedCause {
-                            derived,
-                            impl_or_alias_def_id: def_id,
-                            impl_def_predicate_index: Some(index),
-                            span,
-                        }))
-                    })
-                };
+            let cause = if tcx.is_lang_item(parent_trait_pred.def_id(), LangItem::CoerceUnsized) {
+                cause.clone()
+            } else {
+                cause.clone().derived_cause(parent_trait_pred, |derived| {
+                    ObligationCauseCode::ImplDerived(Box::new(ImplDerivedCause {
+                        derived,
+                        impl_or_alias_def_id: def_id,
+                        impl_def_predicate_index: Some(index),
+                        span,
+                    }))
+                })
+            };
             let clause = normalize_with_depth_to(
                 self,
                 param_env,
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index 960c27b636e..ce7245d93a4 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -132,7 +132,7 @@ impl<'tcx> TraitAliasExpander<'tcx> {
         debug!(?predicates);
 
         let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| {
-            pred.instantiate_supertrait(tcx, &trait_ref)
+            pred.instantiate_supertrait(tcx, trait_ref)
                 .as_trait_clause()
                 .map(|trait_ref| item.clone_and_push(trait_ref.map_bound(|t| t.trait_ref), *span))
         });
diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs
index c93ec43944a..9bd4a9aab0a 100644
--- a/compiler/rustc_trait_selection/src/traits/vtable.rs
+++ b/compiler/rustc_trait_selection/src/traits/vtable.rs
@@ -125,7 +125,7 @@ fn prepare_vtable_segments_inner<'tcx, T>(
                 .predicates
                 .into_iter()
                 .filter_map(move |(pred, _)| {
-                    pred.instantiate_supertrait(tcx, &inner_most_trait_ref).as_trait_clause()
+                    pred.instantiate_supertrait(tcx, inner_most_trait_ref).as_trait_clause()
                 });
 
             // Find an unvisited supertrait
diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs
index eae1a9dfaa2..241381f5875 100644
--- a/compiler/rustc_transmute/src/layout/tree.rs
+++ b/compiler/rustc_transmute/src/layout/tree.rs
@@ -331,30 +331,63 @@ pub(crate) mod rustc {
             assert!(def.is_enum());
             let layout = ty_and_layout.layout;
 
-            if let Variants::Multiple { tag_field, .. } = layout.variants() {
-                // For enums (but not coroutines), the tag field is
-                // currently always the first field of the layout.
-                assert_eq!(*tag_field, 0);
-            }
+            // Computes the variant of a given index.
+            let layout_of_variant = |index| {
+                let tag = cx.tcx.tag_for_variant((ty_and_layout.ty, index));
+                let variant_def = Def::Variant(def.variant(index));
+                let variant_ty_and_layout = ty_and_layout.for_variant(&cx, index);
+                Self::from_variant(variant_def, tag, variant_ty_and_layout, layout.size, cx)
+            };
 
-            let variants = def.discriminants(cx.tcx()).try_fold(
-                Self::uninhabited(),
-                |variants, (idx, ref discriminant)| {
-                    let tag = cx.tcx.tag_for_variant((ty_and_layout.ty, idx));
-                    let variant_def = Def::Variant(def.variant(idx));
-                    let variant_ty_and_layout = ty_and_layout.for_variant(&cx, idx);
-                    let variant = Self::from_variant(
-                        variant_def,
-                        tag,
-                        variant_ty_and_layout,
-                        layout.size,
-                        cx,
+            // We consider three kinds of enums, each demanding a different
+            // treatment of their layout computation:
+            // 1. enums that are uninhabited
+            // 2. enums for which all but one variant is uninhabited
+            // 3. enums with multiple inhabited variants
+            match layout.variants() {
+                _ if layout.abi.is_uninhabited() => {
+                    // Uninhabited enums are usually (always?) zero-sized. In
+                    // the (unlikely?) event that an uninhabited enum is
+                    // non-zero-sized, this assert will trigger an ICE, and this
+                    // code should be modified such that a `layout.size` amount
+                    // of uninhabited bytes is returned instead.
+                    //
+                    // Uninhabited enums are currently implemented such that
+                    // their layout is described with `Variants::Single`, even
+                    // though they don't necessarily have a 'single' variant to
+                    // defer to. That said, we don't bother specifically
+                    // matching on `Variants::Single` in this arm because the
+                    // behavioral principles here remain true even if, for
+                    // whatever reason, the compiler describes an uninhabited
+                    // enum with `Variants::Multiple`.
+                    assert_eq!(layout.size, Size::ZERO);
+                    Ok(Self::uninhabited())
+                }
+                Variants::Single { index } => {
+                    // `Variants::Single` on non-uninhabited enums denotes that
+                    // the enum delegates its layout to the variant at `index`.
+                    layout_of_variant(*index)
+                }
+                Variants::Multiple { tag_field, .. } => {
+                    // `Variants::Multiple` denotes an enum with multiple
+                    // inhabited variants. The layout of such an enum is the
+                    // disjunction of the layouts of its tagged variants.
+
+                    // For enums (but not coroutines), the tag field is
+                    // currently always the first field of the layout.
+                    assert_eq!(*tag_field, 0);
+
+                    let variants = def.discriminants(cx.tcx()).try_fold(
+                        Self::uninhabited(),
+                        |variants, (idx, ref discriminant)| {
+                            let variant = layout_of_variant(idx)?;
+                            Result::<Self, Err>::Ok(variants.or(variant))
+                        },
                     )?;
-                    Result::<Self, Err>::Ok(variants.or(variant))
-                },
-            )?;
 
-            return Ok(Self::def(Def::Adt(def)).then(variants));
+                    return Ok(Self::def(Def::Adt(def)).then(variants));
+                }
+            }
         }
 
         /// Constructs a `Tree` from a 'variant-like' layout.
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index e4dcea785d4..094903f61d4 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -1,5 +1,6 @@
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def_id::DefId;
+use rustc_hir::LangItem;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::bug;
 use rustc_middle::query::Providers;
@@ -34,7 +35,7 @@ fn resolve_instance<'tcx>(
         let def = if tcx.intrinsic(def_id).is_some() {
             debug!(" => intrinsic");
             ty::InstanceDef::Intrinsic(def_id)
-        } else if Some(def_id) == tcx.lang_items().drop_in_place_fn() {
+        } else if tcx.is_lang_item(def_id, LangItem::DropInPlace) {
             let ty = args.type_at(0);
 
             if ty.needs_drop(tcx, param_env) {
@@ -57,7 +58,7 @@ fn resolve_instance<'tcx>(
                 debug!(" => trivial drop glue");
                 ty::InstanceDef::DropGlue(def_id, None)
             }
-        } else if Some(def_id) == tcx.lang_items().async_drop_in_place_fn() {
+        } else if tcx.is_lang_item(def_id, LangItem::AsyncDropInPlace) {
             let ty = args.type_at(0);
 
             if ty.async_drop_glue_morphology(tcx) != AsyncDropGlueMorphology::Noop {
@@ -221,8 +222,7 @@ fn resolve_associated_item<'tcx>(
             )
         }
         traits::ImplSource::Builtin(BuiltinImplSource::Misc, _) => {
-            let lang_items = tcx.lang_items();
-            if Some(trait_ref.def_id) == lang_items.clone_trait() {
+            if tcx.is_lang_item(trait_ref.def_id, LangItem::Clone) {
                 // FIXME(eddyb) use lang items for methods instead of names.
                 let name = tcx.item_name(trait_item_id);
                 if name == sym::clone {
@@ -248,8 +248,8 @@ fn resolve_associated_item<'tcx>(
                     let args = tcx.erase_regions(rcvr_args);
                     Some(ty::Instance::new(trait_item_id, args))
                 }
-            } else if Some(trait_ref.def_id) == lang_items.fn_ptr_trait() {
-                if lang_items.fn_ptr_addr() == Some(trait_item_id) {
+            } else if tcx.is_lang_item(trait_ref.def_id, LangItem::FnPtrTrait) {
+                if tcx.is_lang_item(trait_item_id, LangItem::FnPtrAddr) {
                     let self_ty = trait_ref.self_ty();
                     if !matches!(self_ty.kind(), ty::FnPtr(..)) {
                         return Ok(None);
diff --git a/compiler/rustc_type_ir/src/infcx.rs b/compiler/rustc_type_ir/src/infcx.rs
index 24e10722448..4a5f34e3542 100644
--- a/compiler/rustc_type_ir/src/infcx.rs
+++ b/compiler/rustc_type_ir/src/infcx.rs
@@ -1,23 +1,77 @@
-use crate::{ConstVid, EffectVid, FloatVid, IntVid, Interner, RegionVid, TyVid, UniverseIndex};
+use crate::fold::TypeFoldable;
+use crate::relate::Relate;
+use crate::solve::{Goal, NoSolution};
+use crate::{self as ty, Interner};
 
-pub trait InferCtxtLike {
+pub trait InferCtxtLike: Sized {
     type Interner: Interner;
 
     fn interner(&self) -> Self::Interner;
 
-    fn universe_of_ty(&self, ty: TyVid) -> Option<UniverseIndex>;
-    fn universe_of_lt(&self, lt: RegionVid) -> Option<UniverseIndex>;
-    fn universe_of_ct(&self, ct: ConstVid) -> Option<UniverseIndex>;
+    fn universe_of_ty(&self, ty: ty::TyVid) -> Option<ty::UniverseIndex>;
+    fn universe_of_lt(&self, lt: ty::RegionVid) -> Option<ty::UniverseIndex>;
+    fn universe_of_ct(&self, ct: ty::ConstVid) -> Option<ty::UniverseIndex>;
 
-    fn opportunistic_resolve_ty_var(&self, vid: TyVid) -> <Self::Interner as Interner>::Ty;
-    fn opportunistic_resolve_int_var(&self, vid: IntVid) -> <Self::Interner as Interner>::Ty;
-    fn opportunistic_resolve_float_var(&self, vid: FloatVid) -> <Self::Interner as Interner>::Ty;
-    fn opportunistic_resolve_ct_var(&self, vid: ConstVid) -> <Self::Interner as Interner>::Const;
+    fn root_ty_var(&self, var: ty::TyVid) -> ty::TyVid;
+    fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid;
+
+    fn opportunistic_resolve_ty_var(&self, vid: ty::TyVid) -> <Self::Interner as Interner>::Ty;
+    fn opportunistic_resolve_int_var(&self, vid: ty::IntVid) -> <Self::Interner as Interner>::Ty;
+    fn opportunistic_resolve_float_var(
+        &self,
+        vid: ty::FloatVid,
+    ) -> <Self::Interner as Interner>::Ty;
+    fn opportunistic_resolve_ct_var(
+        &self,
+        vid: ty::ConstVid,
+    ) -> <Self::Interner as Interner>::Const;
     fn opportunistic_resolve_effect_var(
         &self,
-        vid: EffectVid,
+        vid: ty::EffectVid,
     ) -> <Self::Interner as Interner>::Const;
-    fn opportunistic_resolve_lt_var(&self, vid: RegionVid) -> <Self::Interner as Interner>::Region;
+    fn opportunistic_resolve_lt_var(
+        &self,
+        vid: ty::RegionVid,
+    ) -> <Self::Interner as Interner>::Region;
 
     fn defining_opaque_types(&self) -> <Self::Interner as Interner>::DefiningOpaqueTypes;
+
+    fn next_ty_infer(&self) -> <Self::Interner as Interner>::Ty;
+    fn next_const_infer(&self) -> <Self::Interner as Interner>::Const;
+    fn fresh_args_for_item(
+        &self,
+        def_id: <Self::Interner as Interner>::DefId,
+    ) -> <Self::Interner as Interner>::GenericArgs;
+
+    fn instantiate_binder_with_infer<T: TypeFoldable<Self::Interner> + Copy>(
+        &self,
+        value: ty::Binder<Self::Interner, T>,
+    ) -> T;
+
+    fn enter_forall<T: TypeFoldable<Self::Interner> + Copy, U>(
+        &self,
+        value: ty::Binder<Self::Interner, T>,
+        f: impl FnOnce(T) -> U,
+    ) -> U;
+
+    fn relate<T: Relate<Self::Interner>>(
+        &self,
+        param_env: <Self::Interner as Interner>::ParamEnv,
+        lhs: T,
+        variance: ty::Variance,
+        rhs: T,
+    ) -> Result<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>, NoSolution>;
+
+    fn eq_structurally_relating_aliases<T: Relate<Self::Interner>>(
+        &self,
+        param_env: <Self::Interner as Interner>::ParamEnv,
+        lhs: T,
+        rhs: T,
+    ) -> Result<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>, NoSolution>;
+
+    fn resolve_vars_if_possible<T>(&self, value: T) -> T
+    where
+        T: TypeFoldable<Self::Interner>;
+
+    fn probe<T>(&self, probe: impl FnOnce() -> T) -> T;
 }
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index 2fc765f1c8f..6b84592978a 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -29,6 +29,8 @@ pub trait Ty<I: Interner<Ty = Self>>:
 {
     fn new_bool(interner: I) -> Self;
 
+    fn new_u8(interner: I) -> Self;
+
     fn new_infer(interner: I, var: ty::InferTy) -> Self;
 
     fn new_var(interner: I, var: ty::TyVid) -> Self;
@@ -39,6 +41,18 @@ pub trait Ty<I: Interner<Ty = Self>>:
 
     fn new_alias(interner: I, kind: ty::AliasTyKind, alias_ty: ty::AliasTy<I>) -> Self;
 
+    fn new_projection(
+        interner: I,
+        def_id: I::DefId,
+        args: impl IntoIterator<Item: Into<I::GenericArg>>,
+    ) -> Self {
+        Ty::new_alias(
+            interner,
+            ty::AliasTyKind::Projection,
+            ty::AliasTy::new(interner, def_id, args),
+        )
+    }
+
     fn new_error(interner: I, guar: I::ErrorGuaranteed) -> Self;
 
     fn new_adt(interner: I, adt_def: I::AdtDef, args: I::GenericArgs) -> Self;
@@ -75,6 +89,12 @@ pub trait Ty<I: Interner<Ty = Self>>:
         It: Iterator<Item = T>,
         T: CollectAndApply<Self, Self>;
 
+    fn new_fn_def(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self;
+
+    fn new_fn_ptr(interner: I, sig: ty::Binder<I, ty::FnSig<I>>) -> Self;
+
+    fn new_pat(interner: I, ty: Self, pat: I::Pat) -> Self;
+
     fn tuple_fields(self) -> I::Tys;
 
     fn to_opt_closure_kind(self) -> Option<ty::ClosureKind>;
@@ -83,11 +103,29 @@ pub trait Ty<I: Interner<Ty = Self>>:
 
     fn from_coroutine_closure_kind(interner: I, kind: ty::ClosureKind) -> Self;
 
-    fn new_fn_def(interner: I, def_id: I::DefId, args: I::GenericArgs) -> Self;
-
-    fn new_fn_ptr(interner: I, sig: ty::Binder<I, ty::FnSig<I>>) -> Self;
-
-    fn new_pat(interner: I, ty: Self, pat: I::Pat) -> Self;
+    fn is_ty_var(self) -> bool {
+        matches!(self.kind(), ty::Infer(ty::TyVar(_)))
+    }
+
+    fn fn_sig(self, interner: I) -> ty::Binder<I, ty::FnSig<I>> {
+        match self.kind() {
+            ty::FnPtr(sig) => sig,
+            ty::FnDef(def_id, args) => interner.fn_sig(def_id).instantiate(interner, &args),
+            ty::Error(_) => {
+                // ignore errors (#54954)
+                ty::Binder::dummy(ty::FnSig {
+                    inputs_and_output: Default::default(),
+                    c_variadic: false,
+                    safety: I::Safety::safe(),
+                    abi: I::Abi::rust(),
+                })
+            }
+            ty::Closure(..) => panic!(
+                "to get the signature of a closure, use `args.as_closure().sig()` not `fn_sig()`",
+            ),
+            _ => panic!("Ty::fn_sig() called on non-fn type: {:?}", self),
+        }
+    }
 }
 
 pub trait Tys<I: Interner<Tys = Self>>:
@@ -103,12 +141,16 @@ pub trait Tys<I: Interner<Tys = Self>>:
     fn split_inputs_and_output(self) -> (I::FnInputTys, I::Ty);
 }
 
-pub trait Abi<I: Interner<Abi = Self>>: Copy + Debug + Hash + Eq + TypeVisitable<I> {
+pub trait Abi<I: Interner<Abi = Self>>: Copy + Debug + Hash + Eq + Relate<I> {
+    fn rust() -> Self;
+
     /// Whether this ABI is `extern "Rust"`.
     fn is_rust(self) -> bool;
 }
 
-pub trait Safety<I: Interner<Safety = Self>>: Copy + Debug + Hash + Eq + TypeVisitable<I> {
+pub trait Safety<I: Interner<Safety = Self>>: Copy + Debug + Hash + Eq + Relate<I> {
+    fn safe() -> Self;
+
     fn is_safe(self) -> bool;
 
     fn prefix_str(self) -> &'static str;
@@ -122,7 +164,6 @@ pub trait Region<I: Interner<Region = Self>>:
     + Into<I::GenericArg>
     + IntoKind<Kind = ty::RegionKind<I>>
     + Flags
-    + TypeVisitable<I>
     + Relate<I>
 {
     fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: I::BoundRegion) -> Self;
@@ -158,12 +199,57 @@ pub trait Const<I: Interner<Const = Self>>:
     fn new_unevaluated(interner: I, uv: ty::UnevaluatedConst<I>) -> Self;
 
     fn new_expr(interner: I, expr: I::ExprConst) -> Self;
+
+    fn is_ct_var(self) -> bool {
+        matches!(self.kind(), ty::ConstKind::Infer(ty::InferConst::Var(_)))
+    }
 }
 
 pub trait GenericsOf<I: Interner<GenericsOf = Self>> {
     fn count(&self) -> usize;
 }
 
+pub trait GenericArg<I: Interner<GenericArg = Self>>:
+    Copy
+    + Debug
+    + Hash
+    + Eq
+    + IntoKind<Kind = ty::GenericArgKind<I>>
+    + TypeVisitable<I>
+    + Relate<I>
+    + From<I::Ty>
+    + From<I::Region>
+    + From<I::Const>
+{
+}
+
+pub trait Term<I: Interner<Term = Self>>:
+    Copy + Debug + Hash + Eq + IntoKind<Kind = ty::TermKind<I>> + TypeFoldable<I> + Relate<I>
+{
+    fn as_type(&self) -> Option<I::Ty> {
+        if let ty::TermKind::Ty(ty) = self.kind() { Some(ty) } else { None }
+    }
+
+    fn expect_type(&self) -> I::Ty {
+        self.as_type().expect("expected a type, but found a const")
+    }
+
+    fn as_const(&self) -> Option<I::Const> {
+        if let ty::TermKind::Const(c) = self.kind() { Some(c) } else { None }
+    }
+
+    fn expect_const(&self) -> I::Const {
+        self.as_const().expect("expected a const, but found a type")
+    }
+
+    fn is_infer(self) -> bool {
+        match self.kind() {
+            ty::TermKind::Ty(ty) => ty.is_ty_var(),
+            ty::TermKind::Const(ct) => ct.is_ct_var(),
+        }
+    }
+}
+
 pub trait GenericArgs<I: Interner<GenericArgs = Self>>:
     Copy
     + Debug
@@ -172,7 +258,6 @@ pub trait GenericArgs<I: Interner<GenericArgs = Self>>:
     + IntoIterator<Item = I::GenericArg>
     + Deref<Target: Deref<Target = [I::GenericArg]>>
     + Default
-    + TypeFoldable<I>
     + Relate<I>
 {
     fn type_at(self, i: usize) -> I::Ty;
@@ -188,6 +273,16 @@ pub trait GenericArgs<I: Interner<GenericArgs = Self>>:
     fn split_closure_args(self) -> ty::ClosureArgsParts<I>;
     fn split_coroutine_closure_args(self) -> ty::CoroutineClosureArgsParts<I>;
     fn split_coroutine_args(self) -> ty::CoroutineArgsParts<I>;
+
+    fn as_closure(self) -> ty::ClosureArgs<I> {
+        ty::ClosureArgs { args: self }
+    }
+    fn as_coroutine_closure(self) -> ty::CoroutineClosureArgs<I> {
+        ty::CoroutineClosureArgs { args: self }
+    }
+    fn as_coroutine(self) -> ty::CoroutineArgs<I> {
+        ty::CoroutineArgs { args: self }
+    }
 }
 
 pub trait Predicate<I: Interner<Predicate = Self>>:
@@ -198,9 +293,20 @@ pub trait Predicate<I: Interner<Predicate = Self>>:
     + TypeSuperVisitable<I>
     + TypeSuperFoldable<I>
     + Flags
+    + UpcastFrom<I, ty::PredicateKind<I>>
+    + UpcastFrom<I, ty::Binder<I, ty::PredicateKind<I>>>
+    + UpcastFrom<I, ty::ClauseKind<I>>
+    + UpcastFrom<I, ty::Binder<I, ty::ClauseKind<I>>>
+    + UpcastFrom<I, I::Clause>
     + UpcastFrom<I, ty::NormalizesTo<I>>
+    + UpcastFrom<I, ty::TraitRef<I>>
+    + UpcastFrom<I, ty::Binder<I, ty::TraitRef<I>>>
+    + IntoKind<Kind = ty::Binder<I, ty::PredicateKind<I>>>
 {
     fn is_coinductive(self, interner: I) -> bool;
+
+    // FIXME: Eventually uplift the impl out of rustc and make this defaulted.
+    fn allow_normalization(self) -> bool;
 }
 
 pub trait Clause<I: Interner<Clause = Self>>:
@@ -208,6 +314,7 @@ pub trait Clause<I: Interner<Clause = Self>>:
     + Debug
     + Hash
     + Eq
+    + TypeFoldable<I>
     // FIXME: Remove these, uplift the `Upcast` impls.
     + UpcastFrom<I, ty::Binder<I, ty::TraitRef<I>>>
     + UpcastFrom<I, ty::Binder<I, ty::ProjectionPredicate<I>>>
@@ -242,8 +349,17 @@ pub trait ParamLike {
 
 pub trait AdtDef<I: Interner>: Copy + Debug + Hash + Eq {
     fn def_id(self) -> I::DefId;
+
+    fn is_phantom_data(self) -> bool;
+
+    // FIXME: perhaps use `all_fields` and expose `FieldDef`.
+    fn all_field_tys(self, interner: I) -> ty::EarlyBinder<I, impl Iterator<Item = I::Ty>>;
+
+    fn sized_constraint(self, interner: I) -> Option<ty::EarlyBinder<I, I::Ty>>;
 }
 
 pub trait Features<I: Interner>: Copy {
     fn generic_const_exprs(self) -> bool;
+
+    fn coroutine_clone(self) -> bool;
 }
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index b7f412ecb8e..a2b71e1fc25 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -1,3 +1,4 @@
+use rustc_ast_ir::Movability;
 use smallvec::SmallVec;
 use std::fmt::Debug;
 use std::hash::Hash;
@@ -6,6 +7,7 @@ use std::ops::Deref;
 use crate::fold::TypeFoldable;
 use crate::inherent::*;
 use crate::ir_print::IrPrint;
+use crate::lang_items::TraitSolverLangItem;
 use crate::relate::Relate;
 use crate::solve::inspect::CanonicalGoalEvaluationStep;
 use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
@@ -31,20 +33,8 @@ pub trait Interner:
 
     type GenericArgs: GenericArgs<Self>;
     type GenericArgsSlice: Copy + Debug + Hash + Eq + Deref<Target = [Self::GenericArg]>;
-    type GenericArg: Copy
-        + Debug
-        + Hash
-        + Eq
-        + IntoKind<Kind = ty::GenericArgKind<Self>>
-        + TypeVisitable<Self>
-        + Relate<Self>;
-    type Term: Copy
-        + Debug
-        + Hash
-        + Eq
-        + IntoKind<Kind = ty::TermKind<Self>>
-        + TypeFoldable<Self>
-        + Relate<Self>;
+    type GenericArg: GenericArg<Self>;
+    type Term: Term<Self>;
 
     type BoundVarKinds: Copy
         + Debug
@@ -74,11 +64,16 @@ pub trait Interner:
 
     // Things stored inside of tys
     type ErrorGuaranteed: Copy + Debug + Hash + Eq;
-    type BoundExistentialPredicates: Copy + Debug + Hash + Eq + Relate<Self>;
+    type BoundExistentialPredicates: Copy
+        + Debug
+        + Hash
+        + Eq
+        + Relate<Self>
+        + IntoIterator<Item = ty::Binder<Self, ty::ExistentialPredicate<Self>>>;
     type AllocId: Copy + Debug + Hash + Eq;
     type Pat: Copy + Debug + Hash + Eq + Debug + Relate<Self>;
-    type Safety: Safety<Self> + TypeFoldable<Self> + Relate<Self>;
-    type Abi: Abi<Self> + TypeFoldable<Self> + Relate<Self>;
+    type Safety: Safety<Self>;
+    type Abi: Abi<Self>;
 
     // Kinds of consts
     type Const: Const<Self>;
@@ -153,6 +148,38 @@ pub trait Interner:
 
     type Features: Features<Self>;
     fn features(self) -> Self::Features;
+
+    fn bound_coroutine_hidden_types(
+        self,
+        def_id: Self::DefId,
+    ) -> impl IntoIterator<Item = ty::EarlyBinder<Self, ty::Binder<Self, Self::Ty>>>;
+
+    fn fn_sig(
+        self,
+        def_id: Self::DefId,
+    ) -> ty::EarlyBinder<Self, ty::Binder<Self, ty::FnSig<Self>>>;
+
+    fn coroutine_movability(self, def_id: Self::DefId) -> Movability;
+
+    fn coroutine_for_closure(self, def_id: Self::DefId) -> Self::DefId;
+
+    fn generics_require_sized_self(self, def_id: Self::DefId) -> bool;
+
+    fn item_bounds(
+        self,
+        def_id: Self::DefId,
+    ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>>;
+
+    fn super_predicates_of(
+        self,
+        def_id: Self::DefId,
+    ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>>;
+
+    fn has_target_features(self, def_id: Self::DefId) -> bool;
+
+    fn require_lang_item(self, lang_item: TraitSolverLangItem) -> Self::DefId;
+
+    fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator<Item = Self::DefId>;
 }
 
 /// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter`
diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs
new file mode 100644
index 00000000000..9a3b324fcd7
--- /dev/null
+++ b/compiler/rustc_type_ir/src/lang_items.rs
@@ -0,0 +1,8 @@
+/// Lang items used by the new trait solver. This can be mapped to whatever internal
+/// representation of `LangItem`s used in the underlying compiler implementation.
+pub enum TraitSolverLangItem {
+    Future,
+    FutureOutput,
+    AsyncFnKindHelper,
+    AsyncFnKindUpvars,
+}
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index a76e278cc05..4775a0f8cbb 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -32,6 +32,7 @@ pub mod error;
 pub mod fold;
 pub mod inherent;
 pub mod ir_print;
+pub mod lang_items;
 pub mod lift;
 pub mod relate;
 pub mod solve;
@@ -73,6 +74,7 @@ pub use DynKind::*;
 pub use InferTy::*;
 pub use RegionKind::*;
 pub use TyKind::*;
+pub use Variance::*;
 
 rustc_index::newtype_index! {
     /// A [De Bruijn index][dbi] is a standard means of representing
diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs
index cae1d13020d..8a6ba87b60e 100644
--- a/compiler/rustc_type_ir/src/relate.rs
+++ b/compiler/rustc_type_ir/src/relate.rs
@@ -128,7 +128,7 @@ pub fn relate_args_invariantly<I: Interner, R: TypeRelation<I>>(
     b_arg: I::GenericArgs,
 ) -> RelateResult<I, I::GenericArgs> {
     relation.tcx().mk_args_from_iter(iter::zip(a_arg, b_arg).map(|(a, b)| {
-        relation.relate_with_variance(ty::Variance::Invariant, VarianceDiagInfo::default(), a, b)
+        relation.relate_with_variance(ty::Invariant, VarianceDiagInfo::default(), a, b)
     }))
 }
 
@@ -145,7 +145,7 @@ pub fn relate_args_with_variances<I: Interner, R: TypeRelation<I>>(
     let mut cached_ty = None;
     let params = iter::zip(a_arg, b_arg).enumerate().map(|(i, (a, b))| {
         let variance = variances[i];
-        let variance_info = if variance == ty::Variance::Invariant && fetch_ty_for_diag {
+        let variance_info = if variance == ty::Invariant && fetch_ty_for_diag {
             let ty =
                 *cached_ty.get_or_insert_with(|| tcx.type_of(ty_def_id).instantiate(tcx, &a_arg));
             VarianceDiagInfo::Invariant { ty, param_index: i.try_into().unwrap() }
@@ -191,7 +191,7 @@ impl<I: Interner> Relate<I> for ty::FnSig<I> {
                     relation.relate(a, b)
                 } else {
                     relation.relate_with_variance(
-                        ty::Variance::Contravariant,
+                        ty::Contravariant,
                         VarianceDiagInfo::default(),
                         a,
                         b,
@@ -311,13 +311,13 @@ impl<I: Interner> Relate<I> for ty::ExistentialProjection<I> {
             }))
         } else {
             let term = relation.relate_with_variance(
-                ty::Variance::Invariant,
+                ty::Invariant,
                 VarianceDiagInfo::default(),
                 a.term,
                 b.term,
             )?;
             let args = relation.relate_with_variance(
-                ty::Variance::Invariant,
+                ty::Invariant,
                 VarianceDiagInfo::default(),
                 a.args,
                 b.args,
@@ -466,9 +466,9 @@ pub fn structurally_relate_tys<I: Interner, R: TypeRelation<I>>(
             }
 
             let (variance, info) = match a_mutbl {
-                Mutability::Not => (ty::Variance::Covariant, VarianceDiagInfo::None),
+                Mutability::Not => (ty::Covariant, VarianceDiagInfo::None),
                 Mutability::Mut => {
-                    (ty::Variance::Invariant, VarianceDiagInfo::Invariant { ty: a, param_index: 0 })
+                    (ty::Invariant, VarianceDiagInfo::Invariant { ty: a, param_index: 0 })
                 }
             };
 
@@ -483,9 +483,9 @@ pub fn structurally_relate_tys<I: Interner, R: TypeRelation<I>>(
             }
 
             let (variance, info) = match a_mutbl {
-                Mutability::Not => (ty::Variance::Covariant, VarianceDiagInfo::None),
+                Mutability::Not => (ty::Covariant, VarianceDiagInfo::None),
                 Mutability::Mut => {
-                    (ty::Variance::Invariant, VarianceDiagInfo::Invariant { ty: a, param_index: 0 })
+                    (ty::Invariant, VarianceDiagInfo::Invariant { ty: a, param_index: 0 })
                 }
             };
 
@@ -612,7 +612,7 @@ pub fn structurally_relate_consts<I: Interner, R: TypeRelation<I>>(
             }
 
             let args = relation.relate_with_variance(
-                ty::Variance::Invariant,
+                ty::Invariant,
                 VarianceDiagInfo::default(),
                 au.args,
                 bu.args,
diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs
index 085dfd9ea89..3e138e3c2e0 100644
--- a/compiler/stable_mir/src/compiler_interface.rs
+++ b/compiler/stable_mir/src/compiler_interface.rs
@@ -94,10 +94,6 @@ pub trait Context {
     /// Retrieve the plain function name of an intrinsic.
     fn intrinsic_name(&self, def: IntrinsicDef) -> Symbol;
 
-    /// Returns whether the intrinsic has no meaningful body and all backends
-    /// need to shim all calls to it.
-    fn intrinsic_must_be_overridden(&self, def: IntrinsicDef) -> bool;
-
     /// Retrieve the closure signature for the given generic arguments.
     fn closure_sig(&self, args: &GenericArgs) -> PolyFnSig;
 
diff --git a/compiler/stable_mir/src/crate_def.rs b/compiler/stable_mir/src/crate_def.rs
index 70ca9e6825e..67752a5e629 100644
--- a/compiler/stable_mir/src/crate_def.rs
+++ b/compiler/stable_mir/src/crate_def.rs
@@ -1,7 +1,7 @@
 //! Module that define a common trait for things that represent a crate definition,
 //! such as, a function, a trait, an enum, and any other definitions.
 
-use crate::ty::Span;
+use crate::ty::{GenericArgs, Span, Ty};
 use crate::{with, Crate, Symbol};
 
 /// A unique identification number for each item accessible for the current compilation unit.
@@ -52,6 +52,23 @@ pub trait CrateDef {
     }
 }
 
+/// A trait that can be used to retrieve a definition's type.
+///
+/// Note that not every CrateDef has a type `Ty`. They should not implement this trait.
+pub trait CrateDefType: CrateDef {
+    /// Returns the type of this crate item.
+    fn ty(&self) -> Ty {
+        with(|cx| cx.def_ty(self.def_id()))
+    }
+
+    /// Retrieve the type of this definition by instantiating and normalizing it with `args`.
+    ///
+    /// This will panic if instantiation fails.
+    fn ty_with_args(&self, args: &GenericArgs) -> Ty {
+        with(|cx| cx.def_ty_with_args(self.def_id(), args))
+    }
+}
+
 macro_rules! crate_def {
     ( $(#[$attr:meta])*
       $vis:vis $name:ident $(;)?
@@ -67,3 +84,21 @@ macro_rules! crate_def {
         }
     };
 }
+
+macro_rules! crate_def_with_ty {
+    ( $(#[$attr:meta])*
+      $vis:vis $name:ident $(;)?
+    ) => {
+        $(#[$attr])*
+        #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
+        $vis struct $name(pub DefId);
+
+        impl CrateDef for $name {
+            fn def_id(&self) -> DefId {
+                self.0
+            }
+        }
+
+        impl CrateDefType for $name {}
+    };
+}
diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs
index d9f988935ab..8385856ae53 100644
--- a/compiler/stable_mir/src/lib.rs
+++ b/compiler/stable_mir/src/lib.rs
@@ -22,8 +22,7 @@ use std::fmt::Debug;
 use std::io;
 
 use crate::compiler_interface::with;
-pub use crate::crate_def::CrateDef;
-pub use crate::crate_def::DefId;
+pub use crate::crate_def::{CrateDef, CrateDefType, DefId};
 pub use crate::error::*;
 use crate::mir::Body;
 use crate::mir::Mutability;
@@ -115,12 +114,15 @@ pub enum CtorKind {
 
 pub type Filename = String;
 
-crate_def! {
+crate_def_with_ty! {
     /// Holds information about an item in a crate.
     pub CrateItem;
 }
 
 impl CrateItem {
+    /// This will return the body of an item.
+    ///
+    /// This will panic if no body is available.
     pub fn body(&self) -> mir::Body {
         with(|cx| cx.mir_body(self.0))
     }
diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs
index 43e4682dc10..e0f9e7ae67a 100644
--- a/compiler/stable_mir/src/mir/body.rs
+++ b/compiler/stable_mir/src/mir/body.rs
@@ -637,7 +637,7 @@ pub enum AggregateKind {
 pub enum Operand {
     Copy(Place),
     Move(Place),
-    Constant(Constant),
+    Constant(ConstOperand),
 }
 
 #[derive(Clone, Eq, PartialEq)]
@@ -653,6 +653,13 @@ impl From<Local> for Place {
     }
 }
 
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct ConstOperand {
+    pub span: Span,
+    pub user_ty: Option<UserTypeAnnotationIndex>,
+    pub const_: MirConst,
+}
+
 /// Debug information pertaining to a user variable.
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct VarDebugInfo {
@@ -714,13 +721,6 @@ pub enum VarDebugInfoContents {
     Const(ConstOperand),
 }
 
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct ConstOperand {
-    pub span: Span,
-    pub user_ty: Option<UserTypeAnnotationIndex>,
-    pub const_: MirConst,
-}
-
 // In MIR ProjectionElem is parameterized on the second Field argument and the Index argument. This
 // is so it can be used for both Places (for which the projection elements are of type
 // ProjectionElem<Local, Ty>) and user-provided type annotations (for which the projection elements
@@ -829,13 +829,6 @@ pub type FieldIdx = usize;
 
 type UserTypeAnnotationIndex = usize;
 
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct Constant {
-    pub span: Span,
-    pub user_ty: Option<UserTypeAnnotationIndex>,
-    pub literal: MirConst,
-}
-
 /// The possible branch sites of a [TerminatorKind::SwitchInt].
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct SwitchTargets {
@@ -1001,9 +994,9 @@ impl Operand {
     }
 }
 
-impl Constant {
+impl ConstOperand {
     pub fn ty(&self) -> Ty {
-        self.literal.ty()
+        self.const_.ty()
     }
 }
 
diff --git a/compiler/stable_mir/src/mir/pretty.rs b/compiler/stable_mir/src/mir/pretty.rs
index 2fb180b84c7..83734a0d138 100644
--- a/compiler/stable_mir/src/mir/pretty.rs
+++ b/compiler/stable_mir/src/mir/pretty.rs
@@ -310,7 +310,7 @@ fn pretty_operand(operand: &Operand) -> String {
         Operand::Move(mv) => {
             format!("move {:?}", mv)
         }
-        Operand::Constant(cnst) => pretty_mir_const(&cnst.literal),
+        Operand::Constant(cnst) => pretty_mir_const(&cnst.const_),
     }
 }
 
diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs
index 10f30083dc2..50d7bae21db 100644
--- a/compiler/stable_mir/src/mir/visit.rs
+++ b/compiler/stable_mir/src/mir/visit.rs
@@ -108,8 +108,8 @@ pub trait MirVisitor {
         self.super_ty(ty)
     }
 
-    fn visit_constant(&mut self, constant: &Constant, location: Location) {
-        self.super_constant(constant, location)
+    fn visit_const_operand(&mut self, constant: &ConstOperand, location: Location) {
+        self.super_const_operand(constant, location)
     }
 
     fn visit_mir_const(&mut self, constant: &MirConst, location: Location) {
@@ -366,7 +366,7 @@ pub trait MirVisitor {
                 self.visit_place(place, PlaceContext::NON_MUTATING, location)
             }
             Operand::Constant(constant) => {
-                self.visit_constant(constant, location);
+                self.visit_const_operand(constant, location);
             }
         }
     }
@@ -380,10 +380,10 @@ pub trait MirVisitor {
         let _ = ty;
     }
 
-    fn super_constant(&mut self, constant: &Constant, location: Location) {
-        let Constant { span, user_ty: _, literal } = constant;
+    fn super_const_operand(&mut self, constant: &ConstOperand, location: Location) {
+        let ConstOperand { span, user_ty: _, const_ } = constant;
         self.visit_span(span);
-        self.visit_mir_const(literal, location);
+        self.visit_mir_const(const_, location);
     }
 
     fn super_mir_const(&mut self, constant: &MirConst, location: Location) {
diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs
index bcbe87f7303..35927237281 100644
--- a/compiler/stable_mir/src/ty.rs
+++ b/compiler/stable_mir/src/ty.rs
@@ -3,9 +3,10 @@ use super::{
     with, DefId, Error, Symbol,
 };
 use crate::abi::Layout;
+use crate::crate_def::{CrateDef, CrateDefType};
 use crate::mir::alloc::{read_target_int, read_target_uint, AllocId};
+use crate::mir::mono::StaticDef;
 use crate::target::MachineInfo;
-use crate::{crate_def::CrateDef, mir::mono::StaticDef};
 use crate::{Filename, Opaque};
 use std::fmt::{self, Debug, Display, Formatter};
 use std::ops::Range;
@@ -122,7 +123,7 @@ impl TyConst {
     }
 
     /// Creates an interned usize constant.
-    fn try_from_target_usize(val: u64) -> Result<Self, Error> {
+    pub fn try_from_target_usize(val: u64) -> Result<Self, Error> {
         with(|cx| cx.try_new_ty_const_uint(val.into(), UintTy::Usize))
     }
 
@@ -504,6 +505,15 @@ impl TyKind {
     pub fn discriminant_ty(&self) -> Option<Ty> {
         self.rigid().map(|ty| with(|cx| cx.rigid_ty_discriminant_ty(ty)))
     }
+
+    /// Deconstruct a function type if this is one.
+    pub fn fn_def(&self) -> Option<(FnDef, &GenericArgs)> {
+        if let TyKind::RigidTy(RigidTy::FnDef(def, args)) = self {
+            Some((*def, args))
+        } else {
+            None
+        }
+    }
 }
 
 pub struct TypeAndMut {
@@ -629,7 +639,7 @@ impl ForeignModule {
     }
 }
 
-crate_def! {
+crate_def_with_ty! {
     /// Hold information about a ForeignItem in a crate.
     pub ForeignDef;
 }
@@ -647,7 +657,7 @@ pub enum ForeignItemKind {
     Type(Ty),
 }
 
-crate_def! {
+crate_def_with_ty! {
     /// Hold information about a function definition in a crate.
     pub FnDef;
 }
@@ -658,6 +668,11 @@ impl FnDef {
         with(|ctx| ctx.has_body(self.0).then(|| ctx.mir_body(self.0)))
     }
 
+    // Check if the function body is available.
+    pub fn has_body(&self) -> bool {
+        with(|ctx| ctx.has_body(self.0))
+    }
+
     /// Get the information of the intrinsic if this function is a definition of one.
     pub fn as_intrinsic(&self) -> Option<IntrinsicDef> {
         with(|cx| cx.intrinsic(self.def_id()))
@@ -668,9 +683,15 @@ impl FnDef {
     pub fn is_intrinsic(&self) -> bool {
         self.as_intrinsic().is_some()
     }
+
+    /// Get the function signature for this function definition.
+    pub fn fn_sig(&self) -> PolyFnSig {
+        let kind = self.ty().kind();
+        kind.fn_sig().unwrap()
+    }
 }
 
-crate_def! {
+crate_def_with_ty! {
     pub IntrinsicDef;
 }
 
@@ -684,7 +705,7 @@ impl IntrinsicDef {
     /// Returns whether the intrinsic has no meaningful body and all backends
     /// need to shim all calls to it.
     pub fn must_be_overridden(&self) -> bool {
-        with(|cx| cx.intrinsic_must_be_overridden(*self))
+        with(|cx| !cx.has_body(self.0))
     }
 }
 
@@ -710,7 +731,7 @@ crate_def! {
     pub BrNamedDef;
 }
 
-crate_def! {
+crate_def_with_ty! {
     pub AdtDef;
 }
 
@@ -866,7 +887,7 @@ crate_def! {
     pub GenericDef;
 }
 
-crate_def! {
+crate_def_with_ty! {
     pub ConstDef;
 }