about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast/src/lib.rs2
-rw-r--r--compiler/rustc_ast/src/visit.rs4
-rw-r--r--compiler/rustc_ast_lowering/src/format.rs3
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs8
-rw-r--r--compiler/rustc_borrowck/src/lib.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/constant.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs13
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs2
-rw-r--r--compiler/rustc_codegen_gcc/src/lib.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/context.rs11
-rw-r--r--compiler/rustc_codegen_llvm/src/intrinsic.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/back/symbol_export.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs3
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs1
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/constant.rs8
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/intrinsic.rs9
-rw-r--r--compiler/rustc_const_eval/src/interpret/eval_context.rs12
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs5
-rw-r--r--compiler/rustc_const_eval/src/interpret/machine.rs4
-rw-r--r--compiler/rustc_const_eval/src/interpret/operand.rs2
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/ops.rs12
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs4
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0719.md4
-rw-r--r--compiler/rustc_expand/src/lib.rs2
-rw-r--r--compiler/rustc_feature/src/accepted.rs4
-rw-r--r--compiler/rustc_feature/src/unstable.rs4
-rw-r--r--compiler/rustc_hir/src/hir.rs35
-rw-r--r--compiler/rustc_hir/src/intravisit.rs2
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl23
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/bounds.rs183
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/generics.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsic.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs11
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs26
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs83
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs38
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs24
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs16
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs2
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs11
-rw-r--r--compiler/rustc_infer/src/lib.rs2
-rw-r--r--compiler/rustc_lint/src/levels.rs1
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp40
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp16
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs20
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs5
-rw-r--r--compiler/rustc_middle/src/hir/map/mod.rs54
-rw-r--r--compiler/rustc_middle/src/lib.rs3
-rw-r--r--compiler/rustc_middle/src/mir/consts.rs10
-rw-r--r--compiler/rustc_middle/src/mir/interpret/queries.rs16
-rw-r--r--compiler/rustc_middle/src/query/mod.rs4
-rw-r--r--compiler/rustc_middle/src/traits/solve.rs29
-rw-r--r--compiler/rustc_middle/src/traits/solve/inspect.rs6
-rw-r--r--compiler/rustc_middle/src/traits/solve/inspect/format.rs5
-rw-r--r--compiler/rustc_middle/src/ty/adt.rs10
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs10
-rw-r--r--compiler/rustc_middle/src/ty/context.rs43
-rw-r--r--compiler/rustc_middle/src/ty/context/tls.rs7
-rw-r--r--compiler/rustc_middle/src/ty/inhabitedness/mod.rs2
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs154
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs11
-rw-r--r--compiler/rustc_middle/src/util/bug.rs19
-rw-r--r--compiler/rustc_middle/src/values.rs8
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs9
-rw-r--r--compiler/rustc_mir_build/src/lib.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs74
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs9
-rw-r--r--compiler/rustc_mir_transform/src/add_retag.rs36
-rw-r--r--compiler/rustc_mir_transform/src/coroutine.rs14
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mod.rs4
-rw-r--r--compiler/rustc_mir_transform/src/cross_crate_inline.rs4
-rw-r--r--compiler/rustc_mir_transform/src/dataflow_const_prop.rs2
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs2
-rw-r--r--compiler/rustc_mir_transform/src/jump_threading.rs2
-rw-r--r--compiler/rustc_mir_transform/src/known_panics_lint.rs2
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs10
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs7
-rw-r--r--compiler/rustc_parse/src/parser/path.rs2
-rw-r--r--compiler/rustc_passes/src/check_attr.rs8
-rw-r--r--compiler/rustc_passes/src/check_const.rs15
-rw-r--r--compiler/rustc_passes/src/entry.rs11
-rw-r--r--compiler/rustc_pattern_analysis/src/constructor.rs39
-rw-r--r--compiler/rustc_pattern_analysis/src/pat.rs21
-rw-r--r--compiler/rustc_pattern_analysis/src/rustc.rs7
-rw-r--r--compiler/rustc_serialize/src/lib.rs2
-rw-r--r--compiler/rustc_session/messages.ftl3
-rw-r--r--compiler/rustc_session/src/errors.rs12
-rw-r--r--compiler/rustc_session/src/parse.rs13
-rw-r--r--compiler/rustc_smir/src/rustc_smir/builder.rs2
-rw-r--r--compiler/rustc_smir/src/rustc_smir/context.rs2
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/alias_relate.rs33
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs24
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs50
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs135
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs1
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs13
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/analyse.rs14
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/build.rs23
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs14
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs9
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs34
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs7
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/auto_trait.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs11
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs11
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs17
-rw-r--r--compiler/rustc_transmute/src/lib.rs3
-rw-r--r--compiler/rustc_ty_utils/src/representability.rs2
-rw-r--r--compiler/rustc_ty_utils/src/ty.rs140
-rw-r--r--compiler/rustc_type_ir/src/predicate_kind.rs15
120 files changed, 1148 insertions, 831 deletions
diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs
index 6e42cf37b86..ef60a1c9b5b 100644
--- a/compiler/rustc_ast/src/lib.rs
+++ b/compiler/rustc_ast/src/lib.rs
@@ -11,7 +11,7 @@
 #![doc(rust_logo)]
 #![allow(internal_features)]
 #![feature(rustdoc_internals)]
-#![feature(associated_type_bounds)]
+#![cfg_attr(bootstrap, feature(associated_type_bounds))]
 #![feature(associated_type_defaults)]
 #![feature(box_patterns)]
 #![feature(if_let_guard)]
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index d75ff4565e6..825f8dad8e4 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -755,7 +755,7 @@ pub fn walk_assoc_item<'a, V: Visitor<'a>>(
         }
         AssocItemKind::Delegation(box Delegation { id, qself, path, body }) => {
             if let Some(qself) = qself {
-                visitor.visit_ty(&qself.ty);
+                try_visit!(visitor.visit_ty(&qself.ty));
             }
             try_visit!(visitor.visit_path(path, *id));
             visit_opt!(visitor, visit_block, body);
@@ -994,7 +994,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
         ExprKind::InlineAsm(asm) => try_visit!(visitor.visit_inline_asm(asm)),
         ExprKind::FormatArgs(f) => try_visit!(visitor.visit_format_args(f)),
         ExprKind::OffsetOf(container, fields) => {
-            visitor.visit_ty(container);
+            try_visit!(visitor.visit_ty(container));
             walk_list!(visitor, visit_ident, fields.iter().copied());
         }
         ExprKind::Yield(optional_expression) => {
diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs
index 3f84e6b100d..d2c3b8b2d56 100644
--- a/compiler/rustc_ast_lowering/src/format.rs
+++ b/compiler/rustc_ast_lowering/src/format.rs
@@ -604,8 +604,7 @@ fn may_contain_yield_point(e: &ast::Expr) -> bool {
             if let ast::ExprKind::Await(_, _) | ast::ExprKind::Yield(_) = e.kind {
                 ControlFlow::Break(())
             } else {
-                visit::walk_expr(self, e);
-                ControlFlow::Continue(())
+                visit::walk_expr(self, e)
             }
         }
 
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 4db8ffd3e54..2c396a64789 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -463,13 +463,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                     constraint.span,
                     "return type notation is experimental"
                 );
-            } else {
-                gate!(
-                    &self,
-                    associated_type_bounds,
-                    constraint.span,
-                    "associated type bounds are unstable"
-                );
             }
         }
         visit::walk_assoc_constraint(self, constraint)
@@ -615,7 +608,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
     }
 
     gate_all_legacy_dont_use!(trait_alias, "trait aliases are experimental");
-    gate_all_legacy_dont_use!(associated_type_bounds, "associated type bounds are unstable");
     // Despite being a new feature, `where T: Trait<Assoc(): Sized>`, which is RTN syntax now,
     // used to be gated under associated_type_bounds, which are right above, so RTN needs to
     // be too.
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 415ba095a1b..9f4f88b2b93 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -4,7 +4,7 @@
 #![feature(rustdoc_internals)]
 #![doc(rust_logo)]
 #![feature(assert_matches)]
-#![feature(associated_type_bounds)]
+#![cfg_attr(bootstrap, feature(associated_type_bounds))]
 #![feature(box_patterns)]
 #![feature(control_flow_enum)]
 #![feature(let_chains)]
diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs
index 39fa277fedc..fc9b0f6ef02 100644
--- a/compiler/rustc_codegen_cranelift/src/constant.rs
+++ b/compiler/rustc_codegen_cranelift/src/constant.rs
@@ -74,7 +74,7 @@ pub(crate) fn eval_mir_constant<'tcx>(
     let cv = fx.monomorphize(constant.const_);
     // This cannot fail because we checked all required_consts in advance.
     let val = cv
-        .eval(fx.tcx, ty::ParamEnv::reveal_all(), Some(constant.span))
+        .eval(fx.tcx, ty::ParamEnv::reveal_all(), constant.span)
         .expect("erroneous constant missed by mono item collection");
     (val, cv.ty())
 }
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
index 8b86a116df1..25694af78f1 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
@@ -728,8 +728,10 @@ fn codegen_regular_intrinsic_call<'tcx>(
         | sym::variant_count => {
             intrinsic_args!(fx, args => (); intrinsic);
 
-            let const_val =
-                fx.tcx.const_eval_instance(ParamEnv::reveal_all(), instance, None).unwrap();
+            let const_val = fx
+                .tcx
+                .const_eval_instance(ParamEnv::reveal_all(), instance, source_info.span)
+                .unwrap();
             let val = crate::constant::codegen_const_value(fx, const_val, ret.layout().ty);
             ret.write_cvalue(fx, val);
         }
@@ -755,13 +757,6 @@ fn codegen_regular_intrinsic_call<'tcx>(
             ret.write_cvalue(fx, val);
         }
 
-        sym::ptr_guaranteed_cmp => {
-            intrinsic_args!(fx, args => (a, b); intrinsic);
-
-            let val = crate::num::codegen_ptr_binop(fx, BinOp::Eq, a, b).load_scalar(fx);
-            ret.write_cvalue(fx, CValue::by_val(val, fx.layout_of(fx.tcx.types.u8)));
-        }
-
         sym::caller_location => {
             intrinsic_args!(fx, args => (); intrinsic);
 
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
index 79da970a58e..4d55a95aa9d 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/simd.rs
@@ -131,7 +131,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
 
             let idx = generic_args[2]
                 .expect_const()
-                .eval(fx.tcx, ty::ParamEnv::reveal_all(), Some(span))
+                .eval(fx.tcx, ty::ParamEnv::reveal_all(), span)
                 .unwrap()
                 .unwrap_branch();
 
diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs
index a4ee3015b8d..345c22394f2 100644
--- a/compiler/rustc_codegen_gcc/src/lib.rs
+++ b/compiler/rustc_codegen_gcc/src/lib.rs
@@ -19,11 +19,11 @@
 #![feature(
     rustc_private,
     decl_macro,
-    associated_type_bounds,
     never_type,
     trusted_len,
     hash_raw_entry
 )]
+#![cfg_attr(bootstrap, feature(associated_type_bounds))]
 #![allow(broken_intra_doc_links)]
 #![recursion_limit = "256"]
 #![warn(rust_2018_idioms)]
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index f89c8c9f836..c3f17563b0a 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -126,17 +126,6 @@ pub unsafe fn create_module<'ll>(
 
     let mut target_data_layout = sess.target.data_layout.to_string();
     let llvm_version = llvm_util::get_version();
-    if llvm_version < (17, 0, 0) {
-        if sess.target.arch.starts_with("powerpc") {
-            // LLVM 17 specifies function pointer alignment for ppc:
-            // https://reviews.llvm.org/D147016
-            target_data_layout = target_data_layout
-                .replace("-Fn32", "")
-                .replace("-Fi32", "")
-                .replace("-Fn64", "")
-                .replace("-Fi64", "");
-        }
-    }
     if llvm_version < (18, 0, 0) {
         if sess.target.arch == "x86" || sess.target.arch == "x86_64" {
             // LLVM 18 adjusts i128 to be 128-bit aligned on x86 variants.
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index 467e02d55e3..71b69a94e99 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -1129,7 +1129,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
     if name == sym::simd_shuffle_generic {
         let idx = fn_args[2]
             .expect_const()
-            .eval(tcx, ty::ParamEnv::reveal_all(), Some(span))
+            .eval(tcx, ty::ParamEnv::reveal_all(), span)
             .unwrap()
             .unwrap_branch();
         let n = idx.len() as u64;
diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
index 87b6f0e914c..b19f52182b6 100644
--- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
+++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
@@ -81,10 +81,6 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap<S
                 return library.kind.is_statically_included().then_some(def_id);
             }
 
-            if tcx.intrinsic(def_id).is_some_and(|i| i.must_be_overridden) {
-                return None;
-            }
-
             // Only consider nodes that actually have exported symbols.
             match tcx.def_kind(def_id) {
                 DefKind::Fn | DefKind::Static { .. } => {}
diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
index 5bd7442822a..fcd7fa9247b 100644
--- a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
+++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
@@ -19,6 +19,7 @@ use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Mutability}
 use rustc_middle::ty::layout::{IntegerExt, TyAndLayout};
 use rustc_middle::ty::{self, ExistentialProjection, ParamEnv, Ty, TyCtxt};
 use rustc_middle::ty::{GenericArgKind, GenericArgsRef};
+use rustc_span::DUMMY_SP;
 use rustc_target::abi::Integer;
 use smallvec::SmallVec;
 
@@ -704,7 +705,7 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S
                 // avoiding collisions and will make the emitted type names shorter.
                 let hash_short = tcx.with_stable_hashing_context(|mut hcx| {
                     let mut hasher = StableHasher::new();
-                    let ct = ct.eval(tcx, ty::ParamEnv::reveal_all(), None).unwrap();
+                    let ct = ct.eval(tcx, ty::ParamEnv::reveal_all(), DUMMY_SP).unwrap();
                     hcx.while_hashing_spans(false, |hcx| ct.hash_stable(hcx, &mut hasher));
                     hasher.finish::<Hash64>()
                 });
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index 92f0be541c0..9be8dcf166d 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -4,7 +4,7 @@
 #![allow(internal_features)]
 #![allow(rustc::diagnostic_outside_of_impl)]
 #![allow(rustc::untranslatable_diagnostic)]
-#![feature(associated_type_bounds)]
+#![cfg_attr(bootstrap, feature(associated_type_bounds))]
 #![feature(box_patterns)]
 #![feature(if_let_guard)]
 #![feature(let_chains)]
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index 9bb2a528265..02e7bb05b77 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -1688,7 +1688,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     pub fn try_llbb(&mut self, bb: mir::BasicBlock) -> Option<Bx::BasicBlock> {
         match self.cached_llbbs[bb] {
             CachedLlbb::None => {
-                // FIXME(eddyb) only name the block if `fewer_names` is `false`.
                 let llbb = Bx::append_block(self.cx, self.llfn, &format!("{bb:?}"));
                 self.cached_llbbs[bb] = CachedLlbb::Some(llbb);
                 Some(llbb)
diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs
index ff899c50b3d..c6260d35916 100644
--- a/compiler/rustc_codegen_ssa/src/mir/constant.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs
@@ -24,7 +24,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         // `MirUsedCollector` visited all required_consts before codegen began, so if we got here
         // there can be no more constants that fail to evaluate.
         self.monomorphize(constant.const_)
-            .eval(self.cx.tcx(), ty::ParamEnv::reveal_all(), Some(constant.span))
+            .eval(self.cx.tcx(), ty::ParamEnv::reveal_all(), constant.span)
             .expect("erroneous constant missed by mono item collection")
     }
 
@@ -56,11 +56,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             other => span_bug!(constant.span, "{other:#?}"),
         };
         let uv = self.monomorphize(uv);
-        self.cx.tcx().const_eval_resolve_for_typeck(
-            ty::ParamEnv::reveal_all(),
-            uv,
-            Some(constant.span),
-        )
+        self.cx.tcx().const_eval_resolve_for_typeck(ty::ParamEnv::reveal_all(), uv, constant.span)
     }
 
     /// process constant containing SIMD shuffle indices
diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
index 1d1826d9844..5532ff6e6a5 100644
--- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
@@ -1,7 +1,6 @@
 use super::operand::{OperandRef, OperandValue};
 use super::place::PlaceRef;
 use super::FunctionCx;
-use crate::common::IntPredicate;
 use crate::errors;
 use crate::errors::InvalidMonomorphization;
 use crate::meth;
@@ -130,7 +129,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             | sym::variant_count => {
                 let value = bx
                     .tcx()
-                    .const_eval_instance(ty::ParamEnv::reveal_all(), instance, None)
+                    .const_eval_instance(ty::ParamEnv::reveal_all(), instance, span)
                     .unwrap();
                 OperandRef::from_const(bx, value, ret_ty).immediate_or_packed_pair(bx)
             }
@@ -456,12 +455,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 return Ok(());
             }
 
-            sym::ptr_guaranteed_cmp => {
-                let a = args[0].immediate();
-                let b = args[1].immediate();
-                bx.icmp(IntPredicate::IntEQ, a, b)
-            }
-
             sym::ptr_offset_from | sym::ptr_offset_from_unsigned => {
                 let ty = fn_args.type_at(0);
                 let pointee_size = bx.layout_of(ty).size;
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index 09e9cc4b35d..3283bcc4c45 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -826,7 +826,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             for &const_ in &body.required_consts {
                 let c = self
                     .instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?;
-                c.eval(*self.tcx, self.param_env, Some(const_.span)).map_err(|err| {
+                c.eval(*self.tcx, self.param_env, const_.span).map_err(|err| {
                     err.emit_note(*self.tcx);
                     err
                 })?;
@@ -1034,8 +1034,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     ) -> InterpResult<'tcx> {
         trace!("{:?} is now live", local);
 
-        // We avoid `ty.is_trivially_sized` since that (a) cannot assume WF, so it recurses through
-        // all fields of a tuple, and (b) does something expensive for ADTs.
+        // We avoid `ty.is_trivially_sized` since that does something expensive for ADTs.
         fn is_very_trivially_sized(ty: Ty<'_>) -> bool {
             match ty.kind() {
                 ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
@@ -1054,9 +1053,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 | ty::Closure(..)
                 | ty::CoroutineClosure(..)
                 | ty::Never
-                | ty::Error(_) => true,
+                | ty::Error(_)
+                | ty::Dynamic(_, _, ty::DynStar) => true,
 
-                ty::Str | ty::Slice(_) | ty::Dynamic(..) | ty::Foreign(..) => false,
+                ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false,
 
                 ty::Tuple(tys) => tys.last().iter().all(|ty| is_very_trivially_sized(**ty)),
 
@@ -1174,7 +1174,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     pub fn eval_mir_constant(
         &self,
         val: &mir::Const<'tcx>,
-        span: Option<Span>,
+        span: Span,
         layout: Option<TyAndLayout<'tcx>>,
     ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
         M::eval_mir_constant(self, *val, span, layout, |ecx, val, span, layout| {
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index b68bfb4211d..748c7dce5a3 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -153,9 +153,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     sym::type_name => Ty::new_static_str(self.tcx.tcx),
                     _ => bug!(),
                 };
-                let val = self.ctfe_query(|tcx| {
-                    tcx.const_eval_global_id(self.param_env, gid, Some(tcx.span))
-                })?;
+                let val =
+                    self.ctfe_query(|tcx| tcx.const_eval_global_id(self.param_env, gid, tcx.span))?;
                 let val = self.const_val_to_op(val, ty, Some(dest.layout))?;
                 self.copy_op(&val, dest)?;
             }
diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs
index afc4a80c283..827d8fd9417 100644
--- a/compiler/rustc_const_eval/src/interpret/machine.rs
+++ b/compiler/rustc_const_eval/src/interpret/machine.rs
@@ -525,7 +525,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
     fn eval_mir_constant<F>(
         ecx: &InterpCx<'mir, 'tcx, Self>,
         val: mir::Const<'tcx>,
-        span: Option<Span>,
+        span: Span,
         layout: Option<TyAndLayout<'tcx>>,
         eval: F,
     ) -> InterpResult<'tcx, OpTy<'tcx, Self::Provenance>>
@@ -533,7 +533,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
         F: Fn(
             &InterpCx<'mir, 'tcx, Self>,
             mir::Const<'tcx>,
-            Option<Span>,
+            Span,
             Option<TyAndLayout<'tcx>>,
         ) -> InterpResult<'tcx, OpTy<'tcx, Self::Provenance>>,
     {
diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs
index e49067a2f00..dbc6a317640 100644
--- a/compiler/rustc_const_eval/src/interpret/operand.rs
+++ b/compiler/rustc_const_eval/src/interpret/operand.rs
@@ -741,7 +741,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // * During ConstProp, with `TooGeneric` or since the `required_consts` were not all
                 //   checked yet.
                 // * During CTFE, since promoteds in `const`/`static` initializer bodies can fail.
-                self.eval_mir_constant(&c, Some(constant.span), layout)?
+                self.eval_mir_constant(&c, constant.span, layout)?
             }
         };
         trace!("{:?}: {:?}", mir_op, op);
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
index 15720f25c5c..e87e60f62dc 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
@@ -99,7 +99,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
     #[allow(rustc::untranslatable_diagnostic)]
     fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> Diag<'tcx> {
         let FnCallNonConst { caller, callee, args, span, call_source, feature } = *self;
-        let ConstCx { tcx, param_env, .. } = *ccx;
+        let ConstCx { tcx, param_env, body, .. } = *ccx;
 
         let diag_trait = |err, self_ty: Ty<'_>, trait_id| {
             let trait_ref = TraitRef::from_method(tcx, trait_id, args);
@@ -297,10 +297,12 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
             ccx.const_kind(),
         ));
 
-        if let Some(feature) = feature
-            && ccx.tcx.sess.is_nightly_build()
-        {
-            err.help(format!("add `#![feature({feature})]` to the crate attributes to enable",));
+        if let Some(feature) = feature {
+            ccx.tcx.disabled_nightly_features(
+                &mut err,
+                body.source.def_id().as_local().map(|local| ccx.tcx.local_def_id_to_hir_id(local)),
+                [(String::new(), feature)],
+            );
         }
 
         if let ConstContext::Static(_) = ccx.const_kind() {
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index f26c8f8592d..68270dab284 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -341,7 +341,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
                 // FIXME(JakobDegen) The validator should check that `self.mir_phase <
                 // DropsLowered`. However, this causes ICEs with generation of drop shims, which
                 // seem to fail to set their `MirPhase` correctly.
-                if matches!(kind, RetagKind::Raw | RetagKind::TwoPhase) {
+                if matches!(kind, RetagKind::TwoPhase) {
                     self.fail(location, format!("explicit `{kind:?}` is forbidden"));
                 }
             }
@@ -1272,7 +1272,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                 // FIXME(JakobDegen) The validator should check that `self.mir_phase <
                 // DropsLowered`. However, this causes ICEs with generation of drop shims, which
                 // seem to fail to set their `MirPhase` correctly.
-                if matches!(kind, RetagKind::Raw | RetagKind::TwoPhase) {
+                if matches!(kind, RetagKind::TwoPhase) {
                     self.fail(location, format!("explicit `{kind:?}` is forbidden"));
                 }
             }
diff --git a/compiler/rustc_error_codes/src/error_codes/E0719.md b/compiler/rustc_error_codes/src/error_codes/E0719.md
index 057a0b1645c..cd981db1058 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0719.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0719.md
@@ -3,8 +3,6 @@ An associated type value was specified more than once.
 Erroneous code example:
 
 ```compile_fail,E0719
-#![feature(associated_type_bounds)]
-
 trait FooTrait {}
 trait BarTrait {}
 
@@ -19,8 +17,6 @@ specify the associated type with the new trait.
 Corrected example:
 
 ```
-#![feature(associated_type_bounds)]
-
 trait FooTrait {}
 trait BarTrait {}
 trait FooBarTrait: FooTrait + BarTrait {}
diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs
index e550f7242c3..0b8f75bc2ca 100644
--- a/compiler/rustc_expand/src/lib.rs
+++ b/compiler/rustc_expand/src/lib.rs
@@ -1,7 +1,7 @@
 #![doc(rust_logo)]
 #![feature(rustdoc_internals)]
 #![feature(array_windows)]
-#![feature(associated_type_bounds)]
+#![cfg_attr(bootstrap, feature(associated_type_bounds))]
 #![feature(associated_type_defaults)]
 #![feature(if_let_guard)]
 #![feature(let_chains)]
diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs
index 7e5197f16f9..5957d03853d 100644
--- a/compiler/rustc_feature/src/accepted.rs
+++ b/compiler/rustc_feature/src/accepted.rs
@@ -59,6 +59,8 @@ declare_features! (
     (accepted, asm_sym, "1.66.0", Some(93333)),
     /// Allows the definition of associated constants in `trait` or `impl` blocks.
     (accepted, associated_consts, "1.20.0", Some(29646)),
+    /// Allows the user of associated type bounds.
+    (accepted, associated_type_bounds, "CURRENT_RUSTC_VERSION", Some(52662)),
     /// Allows using associated `type`s in `trait`s.
     (accepted, associated_types, "1.0.0", None),
     /// Allows free and inherent `async fn`s, `async` blocks, and `<expr>.await` expressions.
@@ -203,6 +205,8 @@ declare_features! (
     (accepted, impl_header_lifetime_elision, "1.31.0", Some(15872)),
     /// Allows referencing `Self` and projections in impl-trait.
     (accepted, impl_trait_projections, "1.74.0", Some(103532)),
+    /// Allows using imported `main` function
+    (accepted, imported_main, "CURRENT_RUSTC_VERSION", Some(28937)),
     /// Allows using `a..=b` and `..=b` as inclusive range syntaxes.
     (accepted, inclusive_range_syntax, "1.26.0", Some(28237)),
     /// Allows inferring outlives requirements (RFC 2093).
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index c3f216936df..528fabc8d9c 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -351,8 +351,6 @@ declare_features! (
     (unstable, asm_unwind, "1.58.0", Some(93334)),
     /// Allows users to enforce equality of associated constants `TraitImpl<AssocConst=3>`.
     (unstable, associated_const_equality, "1.58.0", Some(92827)),
-    /// Allows the user of associated type bounds.
-    (unstable, associated_type_bounds, "1.34.0", Some(52662)),
     /// Allows associated type defaults.
     (unstable, associated_type_defaults, "1.2.0", Some(29661)),
     /// Allows `async || body` closures.
@@ -495,8 +493,6 @@ declare_features! (
     (unstable, impl_trait_in_assoc_type, "1.70.0", Some(63063)),
     /// Allows `impl Trait` as output type in `Fn` traits in return position of functions.
     (unstable, impl_trait_in_fn_trait_return, "1.64.0", Some(99697)),
-    /// Allows using imported `main` function
-    (unstable, imported_main, "1.53.0", Some(28937)),
     /// Allows associated types in inherent impls.
     (incomplete, inherent_associated_types, "1.52.0", Some(8995)),
     /// Allow anonymous constants from an inline `const` block
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 186b8716d9a..bcd1e547d3d 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -3640,35 +3640,42 @@ impl<'hir> Node<'hir> {
         }
     }
 
-    pub fn body_id(&self) -> Option<BodyId> {
+    #[inline]
+    pub fn associated_body(&self) -> Option<(LocalDefId, BodyId)> {
         match self {
             Node::Item(Item {
+                owner_id,
                 kind:
-                    ItemKind::Static(_, _, body)
-                    | ItemKind::Const(_, _, body)
-                    | ItemKind::Fn(_, _, body),
+                    ItemKind::Const(_, _, body) | ItemKind::Static(.., body) | ItemKind::Fn(.., body),
                 ..
             })
             | Node::TraitItem(TraitItem {
+                owner_id,
                 kind:
-                    TraitItemKind::Fn(_, TraitFn::Provided(body)) | TraitItemKind::Const(_, Some(body)),
+                    TraitItemKind::Const(_, Some(body)) | TraitItemKind::Fn(_, TraitFn::Provided(body)),
                 ..
             })
             | Node::ImplItem(ImplItem {
-                kind: ImplItemKind::Fn(_, body) | ImplItemKind::Const(_, body),
-                ..
-            })
-            | Node::Expr(Expr {
-                kind:
-                    ExprKind::ConstBlock(ConstBlock { body, .. })
-                    | ExprKind::Closure(Closure { body, .. })
-                    | ExprKind::Repeat(_, ArrayLen::Body(AnonConst { body, .. })),
+                owner_id,
+                kind: ImplItemKind::Const(_, body) | ImplItemKind::Fn(_, body),
                 ..
-            }) => Some(*body),
+            }) => Some((owner_id.def_id, *body)),
+
+            Node::Expr(Expr { kind: ExprKind::Closure(Closure { def_id, body, .. }), .. }) => {
+                Some((*def_id, *body))
+            }
+
+            Node::AnonConst(constant) => Some((constant.def_id, constant.body)),
+            Node::ConstBlock(constant) => Some((constant.def_id, constant.body)),
+
             _ => None,
         }
     }
 
+    pub fn body_id(&self) -> Option<BodyId> {
+        Some(self.associated_body()?.1)
+    }
+
     pub fn generics(self) -> Option<&'hir Generics<'hir>> {
         match self {
             Node::ForeignItem(ForeignItem {
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index fbbad38d17f..186bb234a45 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -899,7 +899,7 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>(
         GenericParamKind::Const { ref ty, ref default, is_host_effect: _ } => {
             try_visit!(visitor.visit_ty(ty));
             if let Some(ref default) = default {
-                visitor.visit_const_param_default(param.hir_id, default);
+                try_visit!(visitor.visit_const_param_default(param.hir_id, default));
             }
         }
     }
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 1c68de51ba6..9bf4d63267a 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -118,6 +118,11 @@ hir_analysis_enum_discriminant_overflowed = enum discriminant overflowed
     .label = overflowed on value after {$discr}
     .note = explicitly set `{$item_name} = {$wrapped_discr}` if that is desired outcome
 
+hir_analysis_escaping_bound_var_in_ty_of_assoc_const_binding =
+    the type of the associated constant `{$assoc_const}` cannot capture late-bound generic parameters
+    .label = its type cannot capture the late-bound {$var_def_kind} `{$var_name}`
+    .var_defined_here_label = the late-bound {$var_def_kind} `{$var_name}` is defined here
+
 hir_analysis_field_already_declared =
     field `{$field_name}` is already declared
     .label = field already declared
@@ -316,6 +321,22 @@ hir_analysis_opaque_captures_higher_ranked_lifetime = `impl Trait` cannot captur
     .label = `impl Trait` implicitly captures all lifetimes in scope
     .note = lifetime declared here
 
+hir_analysis_param_in_ty_of_assoc_const_binding =
+    the type of the associated constant `{$assoc_const}` must not depend on {$param_category ->
+        [self] `Self`
+        [synthetic] `impl Trait`
+        *[normal] generic parameters
+    }
+    .label = its type must not depend on {$param_category ->
+        [self] `Self`
+        [synthetic] `impl Trait`
+        *[normal] the {$param_def_kind} `{$param_name}`
+    }
+    .param_defined_here_label = {$param_category ->
+        [synthetic] the `impl Trait` is specified here
+        *[normal] the {$param_def_kind} `{$param_name}` is defined here
+    }
+
 hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a temporary means of controlling which traits can use parenthetical notation
     .help = add `#![feature(unboxed_closures)]` to the crate attributes to use it
 
@@ -432,6 +453,8 @@ hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$de
     .label = needs at most one field with non-trivial size or alignment, but has {$field_count}
     .labels = this field has non-zero size or requires alignment
 
+hir_analysis_ty_of_assoc_const_binding_note = `{$assoc_const}` has type `{$ty}`
+
 hir_analysis_ty_param_first_local = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`)
     .label = type parameter `{$param_ty}` must be covered by another type when it appears before the first local type (`{$local_type}`)
     .note = implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs
index c6942b0f456..3067e2d0b71 100644
--- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs
@@ -1,12 +1,15 @@
-use rustc_data_structures::fx::FxIndexMap;
+use std::ops::ControlFlow;
+
+use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_errors::{codes::*, struct_span_code_err};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_middle::ty::{self as ty, Ty};
+use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt};
 use rustc_span::symbol::Ident;
-use rustc_span::{ErrorGuaranteed, Span};
+use rustc_span::{ErrorGuaranteed, Span, Symbol};
 use rustc_trait_selection::traits;
+use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
 use smallvec::SmallVec;
 
 use crate::astconv::{AstConv, OnlySelfBounds, PredicateFilter};
@@ -433,14 +436,8 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
                 binding.kind
             {
                 let ty = alias_ty.map_bound(|ty| tcx.type_of(ty.def_id).instantiate(tcx, ty.args));
-                // Since the arguments passed to the alias type above may contain early-bound
-                // generic parameters, the instantiated type may contain some as well.
-                // Therefore wrap it in `EarlyBinder`.
-                // FIXME(fmease): Reject escaping late-bound vars.
-                tcx.feed_anon_const_type(
-                    anon_const.def_id,
-                    ty::EarlyBinder::bind(ty.skip_binder()),
-                );
+                let ty = check_assoc_const_binding_type(tcx, assoc_ident, ty, binding.hir_id);
+                tcx.feed_anon_const_type(anon_const.def_id, ty::EarlyBinder::bind(ty));
             }
 
             alias_ty
@@ -530,3 +527,167 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
         Ok(())
     }
 }
+
+/// Detect and reject early-bound & escaping late-bound generic params in the type of assoc const bindings.
+///
+/// FIXME(const_generics): This is a temporary and semi-artifical restriction until the
+/// arrival of *generic const generics*[^1].
+///
+/// It might actually be possible that we can already support early-bound generic params
+/// in such types if we just lifted some more checks in other places, too, for example
+/// inside [`ty::Const::from_anon_const`]. However, even if that were the case, we should
+/// probably gate this behind another feature flag.
+///
+/// [^1]: <https://github.com/rust-lang/project-const-generics/issues/28>.
+fn check_assoc_const_binding_type<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    assoc_const: Ident,
+    ty: ty::Binder<'tcx, Ty<'tcx>>,
+    hir_id: hir::HirId,
+) -> Ty<'tcx> {
+    // We can't perform the checks for early-bound params during name resolution unlike E0770
+    // because this information depends on *type* resolution.
+    // We can't perform these checks in `resolve_bound_vars` either for the same reason.
+    // Consider the trait ref `for<'a> Trait<'a, C = { &0 }>`. We need to know the fully
+    // resolved type of `Trait::C` in order to know if it references `'a` or not.
+
+    let ty = ty.skip_binder();
+    if !ty.has_param() && !ty.has_escaping_bound_vars() {
+        return ty;
+    }
+
+    let mut collector = GenericParamAndBoundVarCollector {
+        tcx,
+        params: Default::default(),
+        vars: Default::default(),
+        depth: ty::INNERMOST,
+    };
+    let mut guar = ty.visit_with(&mut collector).break_value();
+
+    let ty_note = ty
+        .make_suggestable(tcx, false)
+        .map(|ty| crate::errors::TyOfAssocConstBindingNote { assoc_const, ty });
+
+    let enclosing_item_owner_id = tcx
+        .hir()
+        .parent_owner_iter(hir_id)
+        .find_map(|(owner_id, parent)| parent.generics().map(|_| owner_id))
+        .unwrap();
+    let generics = tcx.generics_of(enclosing_item_owner_id);
+    for index in collector.params {
+        let param = generics.param_at(index as _, tcx);
+        let is_self_param = param.name == rustc_span::symbol::kw::SelfUpper;
+        guar.get_or_insert(tcx.dcx().emit_err(crate::errors::ParamInTyOfAssocConstBinding {
+            span: assoc_const.span,
+            assoc_const,
+            param_name: param.name,
+            param_def_kind: tcx.def_descr(param.def_id),
+            param_category: if is_self_param {
+                "self"
+            } else if param.kind.is_synthetic() {
+                "synthetic"
+            } else {
+                "normal"
+            },
+            param_defined_here_label:
+                (!is_self_param).then(|| tcx.def_ident_span(param.def_id).unwrap()),
+            ty_note,
+        }));
+    }
+    for (var_def_id, var_name) in collector.vars {
+        guar.get_or_insert(tcx.dcx().emit_err(
+            crate::errors::EscapingBoundVarInTyOfAssocConstBinding {
+                span: assoc_const.span,
+                assoc_const,
+                var_name,
+                var_def_kind: tcx.def_descr(var_def_id),
+                var_defined_here_label: tcx.def_ident_span(var_def_id).unwrap(),
+                ty_note,
+            },
+        ));
+    }
+
+    let guar = guar.unwrap_or_else(|| bug!("failed to find gen params or bound vars in ty"));
+    Ty::new_error(tcx, guar)
+}
+
+struct GenericParamAndBoundVarCollector<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    params: FxIndexSet<u32>,
+    vars: FxIndexSet<(DefId, Symbol)>,
+    depth: ty::DebruijnIndex,
+}
+
+impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'tcx> {
+    type Result = ControlFlow<ErrorGuaranteed>;
+
+    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
+        &mut self,
+        binder: &ty::Binder<'tcx, T>,
+    ) -> Self::Result {
+        self.depth.shift_in(1);
+        let result = binder.super_visit_with(self);
+        self.depth.shift_out(1);
+        result
+    }
+
+    fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
+        match ty.kind() {
+            ty::Param(param) => {
+                self.params.insert(param.index);
+            }
+            ty::Bound(db, bt) if *db >= self.depth => {
+                self.vars.insert(match bt.kind {
+                    ty::BoundTyKind::Param(def_id, name) => (def_id, name),
+                    ty::BoundTyKind::Anon => {
+                        let reported = self
+                            .tcx
+                            .dcx()
+                            .delayed_bug(format!("unexpected anon bound ty: {:?}", bt.var));
+                        return ControlFlow::Break(reported);
+                    }
+                });
+            }
+            _ if ty.has_param() || ty.has_bound_vars() => return ty.super_visit_with(self),
+            _ => {}
+        }
+        ControlFlow::Continue(())
+    }
+
+    fn visit_region(&mut self, re: ty::Region<'tcx>) -> Self::Result {
+        match re.kind() {
+            ty::ReEarlyParam(param) => {
+                self.params.insert(param.index);
+            }
+            ty::ReBound(db, br) if db >= self.depth => {
+                self.vars.insert(match br.kind {
+                    ty::BrNamed(def_id, name) => (def_id, name),
+                    ty::BrAnon | ty::BrEnv => {
+                        let guar = self
+                            .tcx
+                            .dcx()
+                            .delayed_bug(format!("unexpected bound region kind: {:?}", br.kind));
+                        return ControlFlow::Break(guar);
+                    }
+                });
+            }
+            _ => {}
+        }
+        ControlFlow::Continue(())
+    }
+
+    fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result {
+        match ct.kind() {
+            ty::ConstKind::Param(param) => {
+                self.params.insert(param.index);
+            }
+            ty::ConstKind::Bound(db, ty::BoundVar { .. }) if db >= self.depth => {
+                let guar = self.tcx.dcx().delayed_bug("unexpected escaping late-bound const var");
+                return ControlFlow::Break(guar);
+            }
+            _ if ct.has_param() || ct.has_bound_vars() => return ct.super_visit_with(self),
+            _ => {}
+        }
+        ControlFlow::Continue(())
+    }
+}
diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs
index 428eea2f686..42e303c10ea 100644
--- a/compiler/rustc_hir_analysis/src/astconv/generics.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs
@@ -16,7 +16,7 @@ use rustc_middle::ty::{
     self, GenericArgsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, TyCtxt,
 };
 use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS;
-use rustc_span::symbol::kw;
+use rustc_span::symbol::{kw, sym};
 use smallvec::SmallVec;
 
 /// Report an error that a generic argument did not match the generic parameter that was
@@ -41,9 +41,11 @@ fn generic_arg_mismatch_err(
     if let GenericParamDefKind::Const { .. } = param.kind {
         if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. })) {
             err.help("const arguments cannot yet be inferred with `_`");
-            if sess.is_nightly_build() {
-                err.help("add `#![feature(generic_arg_infer)]` to the crate attributes to enable");
-            }
+            tcx.disabled_nightly_features(
+                &mut err,
+                param.def_id.as_local().map(|local| tcx.local_def_id_to_hir_id(local)),
+                [(String::new(), sym::generic_arg_infer)],
+            );
         }
     }
 
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index bf5838143d9..07054c184f4 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -441,7 +441,7 @@ pub fn check_intrinsic_type(
 
             sym::ptr_guaranteed_cmp => (
                 1,
-                0,
+                1,
                 vec![Ty::new_imm_ptr(tcx, param(0)), Ty::new_imm_ptr(tcx, param(0))],
                 tcx.types.u8,
             ),
@@ -579,7 +579,7 @@ pub fn check_intrinsic_type(
 
             sym::is_val_statically_known => (1, 1, vec![param(0)], tcx.types.bool),
 
-            sym::const_eval_select => (4, 0, vec![param(0), param(1), param(2)], param(3)),
+            sym::const_eval_select => (4, 1, vec![param(0), param(1), param(2)], param(3)),
 
             sym::vtable_size | sym::vtable_align => {
                 (0, 0, vec![Ty::new_imm_ptr(tcx, Ty::new_unit(tcx))], tcx.types.usize)
@@ -658,10 +658,6 @@ pub fn check_intrinsic_type(
             sym::simd_shuffle => (3, 0, vec![param(0), param(0), param(1)], param(2)),
             sym::simd_shuffle_generic => (2, 1, vec![param(0), param(0)], param(1)),
 
-            sym::retag_box_to_raw => {
-                (2, 0, vec![Ty::new_mut_ptr(tcx, param(0))], Ty::new_mut_ptr(tcx, param(0)))
-            }
-
             other => {
                 tcx.dcx().emit_err(UnrecognizedIntrinsicFunction { span, name: other });
                 return;
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index 22afddad633..4c4ff28808e 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -291,12 +291,16 @@ fn default_body_is_unstable(
         reason: reason_str,
     });
 
+    let inject_span = item_did
+        .as_local()
+        .and_then(|id| tcx.crate_level_attribute_injection_span(tcx.local_def_id_to_hir_id(id)));
     rustc_session::parse::add_feature_diagnostics_for_issue(
         &mut err,
         &tcx.sess,
         feature,
         rustc_feature::GateIssue::Library(issue),
         false,
+        inject_span,
     );
 
     err.emit();
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index f5250045344..41b03f0b66e 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -999,9 +999,14 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
                     // Implments `ConstParamTy`, suggest adding the feature to enable.
                     Ok(..) => true,
                 };
-                if may_suggest_feature && tcx.sess.is_nightly_build() {
-                    diag.help(
-                        "add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types",
+                if may_suggest_feature {
+                    tcx.disabled_nightly_features(
+                        &mut diag,
+                        Some(param.hir_id),
+                        [(
+                            " more complex and user defined types".to_string(),
+                            sym::adt_const_params,
+                        )],
                     );
                 }
 
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index c86788db988..bc6abc53cad 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -508,29 +508,9 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S
         visitor.has_late_bound_regions
     }
 
-    match node {
-        Node::TraitItem(item) => match &item.kind {
-            hir::TraitItemKind::Fn(sig, _) => has_late_bound_regions(tcx, item.generics, sig.decl),
-            _ => None,
-        },
-        Node::ImplItem(item) => match &item.kind {
-            hir::ImplItemKind::Fn(sig, _) => has_late_bound_regions(tcx, item.generics, sig.decl),
-            _ => None,
-        },
-        Node::ForeignItem(item) => match item.kind {
-            hir::ForeignItemKind::Fn(fn_decl, _, generics) => {
-                has_late_bound_regions(tcx, generics, fn_decl)
-            }
-            _ => None,
-        },
-        Node::Item(item) => match &item.kind {
-            hir::ItemKind::Fn(sig, .., generics, _) => {
-                has_late_bound_regions(tcx, generics, sig.decl)
-            }
-            _ => None,
-        },
-        _ => None,
-    }
+    let decl = node.fn_decl()?;
+    let generics = node.generics()?;
+    has_late_bound_regions(tcx, generics, decl)
 }
 
 struct AnonConstInParamTyDetector {
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index 6aae4aa21b8..66c9fb93500 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -123,43 +123,22 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
     // Preserving the order of insertion is important here so as not to break UI tests.
     let mut predicates: FxIndexSet<(ty::Clause<'_>, Span)> = FxIndexSet::default();
 
-    let ast_generics = match node {
-        Node::TraitItem(item) => item.generics,
-
-        Node::ImplItem(item) => item.generics,
-
-        Node::Item(item) => match item.kind {
+    let ast_generics = node.generics().unwrap_or(NO_GENERICS);
+    if let Node::Item(item) = node {
+        match item.kind {
             ItemKind::Impl(impl_) => {
                 if impl_.defaultness.is_default() {
                     is_default_impl_trait = tcx
                         .impl_trait_ref(def_id)
                         .map(|t| ty::Binder::dummy(t.instantiate_identity()));
                 }
-                impl_.generics
             }
-            ItemKind::Fn(.., generics, _)
-            | ItemKind::TyAlias(_, generics)
-            | ItemKind::Const(_, generics, _)
-            | ItemKind::Enum(_, generics)
-            | ItemKind::Struct(_, generics)
-            | ItemKind::Union(_, generics) => generics,
-
-            ItemKind::Trait(_, _, generics, self_bounds, ..)
-            | ItemKind::TraitAlias(generics, self_bounds) => {
+
+            ItemKind::Trait(_, _, _, self_bounds, ..) | ItemKind::TraitAlias(_, self_bounds) => {
                 is_trait = Some(self_bounds);
-                generics
             }
-            ItemKind::OpaqueTy(OpaqueTy { generics, .. }) => generics,
-            _ => NO_GENERICS,
-        },
-
-        Node::ForeignItem(item) => match item.kind {
-            ForeignItemKind::Static(..) => NO_GENERICS,
-            ForeignItemKind::Fn(_, _, generics) => generics,
-            ForeignItemKind::Type => NO_GENERICS,
-        },
-
-        _ => NO_GENERICS,
+            _ => {}
+        }
     };
 
     let generics = tcx.generics_of(def_id);
@@ -703,45 +682,17 @@ pub(super) fn type_param_predicates(
     let mut extend = None;
 
     let item_hir_id = tcx.local_def_id_to_hir_id(item_def_id);
-    let ast_generics = match tcx.hir_node(item_hir_id) {
-        Node::TraitItem(item) => item.generics,
-
-        Node::ImplItem(item) => item.generics,
-
-        Node::Item(item) => {
-            match item.kind {
-                ItemKind::Fn(.., generics, _)
-                | ItemKind::Impl(&hir::Impl { generics, .. })
-                | ItemKind::TyAlias(_, generics)
-                | ItemKind::Const(_, generics, _)
-                | ItemKind::OpaqueTy(&OpaqueTy {
-                    generics,
-                    origin: hir::OpaqueTyOrigin::TyAlias { .. },
-                    ..
-                })
-                | ItemKind::Enum(_, generics)
-                | ItemKind::Struct(_, generics)
-                | ItemKind::Union(_, generics) => generics,
-                ItemKind::Trait(_, _, generics, ..) => {
-                    // Implied `Self: Trait` and supertrait bounds.
-                    if param_id == item_hir_id {
-                        let identity_trait_ref =
-                            ty::TraitRef::identity(tcx, item_def_id.to_def_id());
-                        extend = Some((identity_trait_ref.to_predicate(tcx), item.span));
-                    }
-                    generics
-                }
-                _ => return result,
-            }
-        }
-
-        Node::ForeignItem(item) => match item.kind {
-            ForeignItemKind::Fn(_, _, generics) => generics,
-            _ => return result,
-        },
 
-        _ => return result,
-    };
+    let hir_node = tcx.hir_node(item_hir_id);
+    let Some(ast_generics) = hir_node.generics() else { return result };
+    if let Node::Item(item) = hir_node
+        && let ItemKind::Trait(..) = item.kind
+        // Implied `Self: Trait` and supertrait bounds.
+        && param_id == item_hir_id
+    {
+        let identity_trait_ref = ty::TraitRef::identity(tcx, item_def_id.to_def_id());
+        extend = Some((identity_trait_ref.to_predicate(tcx), item.span));
+    }
 
     let icx = ItemCtxt::new(tcx, item_def_id);
     let extra_predicates = extend.into_iter().chain(
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 7bf87f444db..fb919714afd 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -295,6 +295,44 @@ pub struct AssocTypeBindingNotAllowed {
     pub fn_trait_expansion: Option<ParenthesizedFnTraitExpansion>,
 }
 
+#[derive(Diagnostic)]
+#[diag(hir_analysis_param_in_ty_of_assoc_const_binding)]
+pub(crate) struct ParamInTyOfAssocConstBinding<'tcx> {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub assoc_const: Ident,
+    pub param_name: Symbol,
+    pub param_def_kind: &'static str,
+    pub param_category: &'static str,
+    #[label(hir_analysis_param_defined_here_label)]
+    pub param_defined_here_label: Option<Span>,
+    #[subdiagnostic]
+    pub ty_note: Option<TyOfAssocConstBindingNote<'tcx>>,
+}
+
+#[derive(Subdiagnostic, Clone, Copy)]
+#[note(hir_analysis_ty_of_assoc_const_binding_note)]
+pub(crate) struct TyOfAssocConstBindingNote<'tcx> {
+    pub assoc_const: Ident,
+    pub ty: Ty<'tcx>,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_escaping_bound_var_in_ty_of_assoc_const_binding)]
+pub(crate) struct EscapingBoundVarInTyOfAssocConstBinding<'tcx> {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub assoc_const: Ident,
+    pub var_name: Symbol,
+    pub var_def_kind: &'static str,
+    #[label(hir_analysis_var_defined_here_label)]
+    pub var_defined_here_label: Span,
+    #[subdiagnostic]
+    pub ty_note: Option<TyOfAssocConstBindingNote<'tcx>>,
+}
+
 #[derive(Subdiagnostic)]
 #[help(hir_analysis_parenthesized_fn_trait_expansion)]
 pub struct ParenthesizedFnTraitExpansion {
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index d86b4912c89..bc6e09addce 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -95,29 +95,7 @@ macro_rules! type_error_struct {
 fn primary_body_of(
     node: Node<'_>,
 ) -> Option<(hir::BodyId, Option<&hir::Ty<'_>>, Option<&hir::FnSig<'_>>)> {
-    match node {
-        Node::Item(item) => match item.kind {
-            hir::ItemKind::Const(ty, _, body) | hir::ItemKind::Static(ty, _, body) => {
-                Some((body, Some(ty), None))
-            }
-            hir::ItemKind::Fn(ref sig, .., body) => Some((body, None, Some(sig))),
-            _ => None,
-        },
-        Node::TraitItem(item) => match item.kind {
-            hir::TraitItemKind::Const(ty, Some(body)) => Some((body, Some(ty), None)),
-            hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => {
-                Some((body, None, Some(sig)))
-            }
-            _ => None,
-        },
-        Node::ImplItem(item) => match item.kind {
-            hir::ImplItemKind::Const(ty, body) => Some((body, Some(ty), None)),
-            hir::ImplItemKind::Fn(ref sig, body) => Some((body, None, Some(sig))),
-            _ => None,
-        },
-        Node::AnonConst(constant) => Some((constant.body, None, None)),
-        _ => None,
-    }
+    Some((node.body_id()?, node.ty(), node.fn_sig()))
 }
 
 fn has_typeck_results(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 3d24dcb95f7..eada5a0bc30 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -1417,15 +1417,13 @@ impl<'tcx> Pick<'tcx> {
                     }
                     _ => {}
                 }
-                if tcx.sess.is_nightly_build() {
-                    for (candidate, feature) in &self.unstable_candidates {
-                        lint.help(format!(
-                            "add `#![feature({})]` to the crate attributes to enable `{}`",
-                            feature,
-                            tcx.def_path_str(candidate.item.def_id),
-                        ));
-                    }
-                }
+                tcx.disabled_nightly_features(
+                    lint,
+                    Some(scope_expr_id),
+                    self.unstable_candidates.iter().map(|(candidate, feature)| {
+                        (format!(" `{}`", tcx.def_path_str(candidate.item.def_id)), *feature)
+                    }),
+                );
             },
         );
     }
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 55372950368..491da7eb2c2 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -2178,7 +2178,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         len: ty::Const<'tcx>,
         min_len: u64,
     ) -> (Option<Ty<'tcx>>, Ty<'tcx>) {
-        let len = match len.eval(self.tcx, self.param_env, None) {
+        let len = match len.eval(self.tcx, self.param_env, span) {
             Ok(val) => val
                 .try_to_scalar()
                 .and_then(|scalar| scalar.try_to_int().ok())
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 5bf44f60d95..a3ff655b609 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -1475,7 +1475,7 @@ impl<'tcx> InferCtxt<'tcx> {
         param_env: ty::ParamEnv<'tcx>,
         unevaluated: ty::UnevaluatedConst<'tcx>,
         ty: Ty<'tcx>,
-        span: Option<Span>,
+        span: Span,
     ) -> Result<ty::Const<'tcx>, ErrorHandled> {
         match self.const_eval_resolve(param_env, unevaluated, span) {
             Ok(Some(val)) => Ok(ty::Const::new_value(self.tcx, val, ty)),
@@ -1509,7 +1509,7 @@ impl<'tcx> InferCtxt<'tcx> {
         &self,
         mut param_env: ty::ParamEnv<'tcx>,
         unevaluated: ty::UnevaluatedConst<'tcx>,
-        span: Option<Span>,
+        span: Span,
     ) -> EvalToValTreeResult<'tcx> {
         let mut args = self.resolve_vars_if_possible(unevaluated.args);
         debug!(?args);
@@ -1521,12 +1521,9 @@ impl<'tcx> InferCtxt<'tcx> {
             if let Some(ct) = tcx.thir_abstract_const(unevaluated.def)? {
                 let ct = tcx.expand_abstract_consts(ct.instantiate(tcx, args));
                 if let Err(e) = ct.error_reported() {
-                    return Err(ErrorHandled::Reported(
-                        e.into(),
-                        span.unwrap_or(rustc_span::DUMMY_SP),
-                    ));
+                    return Err(ErrorHandled::Reported(e.into(), span));
                 } else if ct.has_non_region_infer() || ct.has_non_region_param() {
-                    return Err(ErrorHandled::TooGeneric(span.unwrap_or(rustc_span::DUMMY_SP)));
+                    return Err(ErrorHandled::TooGeneric(span));
                 } else {
                     args = replace_param_and_infer_args_with_placeholder(tcx, args);
                 }
diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs
index 029bddda1e1..3c2071be04e 100644
--- a/compiler/rustc_infer/src/lib.rs
+++ b/compiler/rustc_infer/src/lib.rs
@@ -18,7 +18,7 @@
 #![allow(internal_features)]
 #![allow(rustc::diagnostic_outside_of_impl)]
 #![allow(rustc::untranslatable_diagnostic)]
-#![feature(associated_type_bounds)]
+#![cfg_attr(bootstrap, feature(associated_type_bounds))]
 #![feature(box_patterns)]
 #![feature(control_flow_enum)]
 #![feature(extend_one)]
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index 95f312a31b3..21af6a182a9 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -1078,6 +1078,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
                         feature,
                         GateIssue::Language,
                         lint_from_cli,
+                        None,
                     );
                 },
             );
diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
index f6253068eaa..067374c0261 100644
--- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
@@ -24,9 +24,7 @@
 #include "llvm/Passes/StandardInstrumentations.h"
 #include "llvm/Support/CBindingWrapping.h"
 #include "llvm/Support/FileSystem.h"
-#if LLVM_VERSION_GE(17, 0)
 #include "llvm/Support/VirtualFileSystem.h"
-#endif
 #include "llvm/Target/TargetMachine.h"
 #include "llvm/Transforms/IPO/AlwaysInliner.h"
 #include "llvm/Transforms/IPO/FunctionImport.h"
@@ -334,14 +332,8 @@ extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM,
 
   std::ostringstream Buf;
 
-#if LLVM_VERSION_GE(17, 0)
   const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
   const ArrayRef<SubtargetSubTypeKV> CPUTable = MCInfo->getAllProcessorDescriptions();
-#else
-  Buf << "Full target CPU help is not supported by this LLVM version.\n\n";
-  SubtargetSubTypeKV TargetCPUKV = { TargetCPU, {{}}, {{}} };
-  const ArrayRef<SubtargetSubTypeKV> CPUTable = TargetCPUKV;
-#endif
   unsigned MaxCPULen = getLongestEntryLength(CPUTable);
 
   Buf << "Available CPUs for this target:\n";
@@ -476,10 +468,6 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
   Options.RelaxELFRelocations = RelaxELFRelocations;
 #endif
   Options.UseInitArray = UseInitArray;
-
-#if LLVM_VERSION_LT(17, 0)
-  Options.ExplicitEmulatedTLS = true;
-#endif
   Options.EmulatedTLS = UseEmulatedTls;
 
   if (TrapUnreachable) {
@@ -761,16 +749,10 @@ LLVMRustOptimize(
   }
 
   std::optional<PGOOptions> PGOOpt;
-#if LLVM_VERSION_GE(17, 0)
   auto FS = vfs::getRealFileSystem();
-#endif
   if (PGOGenPath) {
     assert(!PGOUsePath && !PGOSampleUsePath);
-    PGOOpt = PGOOptions(PGOGenPath, "", "",
-#if LLVM_VERSION_GE(17, 0)
-                        "",
-                        FS,
-#endif
+    PGOOpt = PGOOptions(PGOGenPath, "", "", "", FS,
                         PGOOptions::IRInstr, PGOOptions::NoCSAction,
 #if LLVM_VERSION_GE(19, 0)
                         PGOOptions::ColdFuncOpt::Default,
@@ -778,33 +760,21 @@ LLVMRustOptimize(
                         DebugInfoForProfiling);
   } else if (PGOUsePath) {
     assert(!PGOSampleUsePath);
-    PGOOpt = PGOOptions(PGOUsePath, "", "",
-#if LLVM_VERSION_GE(17, 0)
-                        "",
-                        FS,
-#endif
+    PGOOpt = PGOOptions(PGOUsePath, "", "", "", FS,
                         PGOOptions::IRUse, PGOOptions::NoCSAction,
 #if LLVM_VERSION_GE(19, 0)
                         PGOOptions::ColdFuncOpt::Default,
 #endif
                         DebugInfoForProfiling);
   } else if (PGOSampleUsePath) {
-    PGOOpt = PGOOptions(PGOSampleUsePath, "", "",
-#if LLVM_VERSION_GE(17, 0)
-                        "",
-                        FS,
-#endif
+    PGOOpt = PGOOptions(PGOSampleUsePath, "", "", "", FS,
                         PGOOptions::SampleUse, PGOOptions::NoCSAction,
 #if LLVM_VERSION_GE(19, 0)
                         PGOOptions::ColdFuncOpt::Default,
 #endif
                         DebugInfoForProfiling);
   } else if (DebugInfoForProfiling) {
-    PGOOpt = PGOOptions("", "", "",
-#if LLVM_VERSION_GE(17, 0)
-                        "",
-                        FS,
-#endif
+    PGOOpt = PGOOptions("", "", "", "", FS,
                         PGOOptions::NoAction, PGOOptions::NoCSAction,
 #if LLVM_VERSION_GE(19, 0)
                         PGOOptions::ColdFuncOpt::Default,
@@ -1353,9 +1323,7 @@ LLVMRustCreateThinLTOData(LLVMRustThinLTOModule *modules,
   ComputeCrossModuleImport(
     Ret->Index,
     Ret->ModuleToDefinedGVSummaries,
-#if LLVM_VERSION_GE(17, 0)
     isPrevailing,
-#endif
     Ret->ImportLists,
     Ret->ExportLists
   );
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 91f54da5c12..f6ec410bfac 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -2152,19 +2152,3 @@ extern "C" LLVMValueRef LLVMConstStringInContext2(LLVMContextRef C,
   return wrap(ConstantDataArray::getString(*unwrap(C), StringRef(Str, Length), !DontNullTerminate));
 }
 #endif
-
-// FIXME: Remove when Rust's minimum supported LLVM version reaches 17.
-// https://github.com/llvm/llvm-project/commit/35276f16e5a2cae0dfb49c0fbf874d4d2f177acc
-#if LLVM_VERSION_LT(17, 0)
-extern "C" LLVMValueRef LLVMConstArray2(LLVMTypeRef ElementTy,
-                                        LLVMValueRef *ConstantVals,
-                                        uint64_t Length) {
-  ArrayRef<Constant *> V(unwrap<Constant>(ConstantVals, Length), Length);
-  return wrap(ConstantArray::get(ArrayType::get(unwrap(ElementTy), Length), V));
-}
-
-extern "C" LLVMTypeRef LLVMArrayType2(LLVMTypeRef ElementTy,
-                                      uint64_t ElementCount) {
-  return wrap(ArrayType::get(unwrap(ElementTy), ElementCount));
-}
-#endif
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index 0467cf2969f..03783fa9798 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -1590,17 +1590,17 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
             })
         }
 
-        // Translate the virtual `/rustc/$hash` prefix back to a real directory
-        // that should hold actual sources, where possible.
-        //
-        // NOTE: if you update this, you might need to also update bootstrap's code for generating
-        // the `rust-src` component in `Src::run` in `src/bootstrap/dist.rs`.
-        let virtual_rust_source_base_dir = [
-            filter(sess, option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(Path::new)),
-            filter(sess, sess.opts.unstable_opts.simulate_remapped_rust_src_base.as_deref()),
-        ];
-
         let try_to_translate_virtual_to_real = |name: &mut rustc_span::FileName| {
+            // Translate the virtual `/rustc/$hash` prefix back to a real directory
+            // that should hold actual sources, where possible.
+            //
+            // NOTE: if you update this, you might need to also update bootstrap's code for generating
+            // the `rust-src` component in `Src::run` in `src/bootstrap/dist.rs`.
+            let virtual_rust_source_base_dir = [
+                filter(sess, option_env!("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR").map(Path::new)),
+                filter(sess, sess.opts.unstable_opts.simulate_remapped_rust_src_base.as_deref()),
+            ];
+
             debug!(
                 "try_to_translate_virtual_to_real(name={:?}): \
                  virtual_rust_source_base_dir={:?}, real_rust_source_base_dir={:?}",
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index d8cfceab460..0a9659745db 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1067,14 +1067,11 @@ fn should_encode_mir(
         // Full-fledged functions + closures
         DefKind::AssocFn | DefKind::Fn | DefKind::Closure => {
             let generics = tcx.generics_of(def_id);
-            let mut opt = tcx.sess.opts.unstable_opts.always_encode_mir
+            let opt = tcx.sess.opts.unstable_opts.always_encode_mir
                 || (tcx.sess.opts.output_types.should_codegen()
                     && reachable_set.contains(&def_id)
                     && (generics.requires_monomorphization(tcx)
                         || tcx.cross_crate_inlinable(def_id)));
-            if let Some(intrinsic) = tcx.intrinsic(def_id) {
-                opt &= !intrinsic.must_be_overridden;
-            }
             // The function has a `const` modifier or is in a `#[const_trait]`.
             let is_const_fn = tcx.is_const_fn_raw(def_id.to_def_id())
                 || tcx.is_const_default_method(def_id.to_def_id());
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index 5043bd855cc..ed05ffef205 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -20,44 +20,6 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{ErrorGuaranteed, Span};
 use rustc_target::spec::abi::Abi;
 
-#[inline]
-pub fn associated_body(node: Node<'_>) -> Option<(LocalDefId, BodyId)> {
-    match node {
-        Node::Item(Item {
-            owner_id,
-            kind: ItemKind::Const(_, _, body) | ItemKind::Static(.., body) | ItemKind::Fn(.., body),
-            ..
-        })
-        | Node::TraitItem(TraitItem {
-            owner_id,
-            kind:
-                TraitItemKind::Const(_, Some(body)) | TraitItemKind::Fn(_, TraitFn::Provided(body)),
-            ..
-        })
-        | Node::ImplItem(ImplItem {
-            owner_id,
-            kind: ImplItemKind::Const(_, body) | ImplItemKind::Fn(_, body),
-            ..
-        }) => Some((owner_id.def_id, *body)),
-
-        Node::Expr(Expr { kind: ExprKind::Closure(Closure { def_id, body, .. }), .. }) => {
-            Some((*def_id, *body))
-        }
-
-        Node::AnonConst(constant) => Some((constant.def_id, constant.body)),
-        Node::ConstBlock(constant) => Some((constant.def_id, constant.body)),
-
-        _ => None,
-    }
-}
-
-fn is_body_owner(node: Node<'_>, hir_id: HirId) -> bool {
-    match associated_body(node) {
-        Some((_, b)) => b.hir_id == hir_id,
-        None => false,
-    }
-}
-
 // FIXME: the structure was necessary in the past but now it
 // only serves as "namespace" for HIR-related methods, and can be
 // removed if all the methods are reasonably renamed and moved to tcx
@@ -273,7 +235,7 @@ impl<'hir> Map<'hir> {
     #[track_caller]
     pub fn enclosing_body_owner(self, hir_id: HirId) -> LocalDefId {
         for (_, node) in self.parent_iter(hir_id) {
-            if let Some((def_id, _)) = associated_body(node) {
+            if let Some((def_id, _)) = node.associated_body() {
                 return def_id;
             }
         }
@@ -286,20 +248,18 @@ impl<'hir> Map<'hir> {
     /// item (possibly associated), a closure, or a `hir::AnonConst`.
     pub fn body_owner(self, BodyId { hir_id }: BodyId) -> HirId {
         let parent = self.tcx.parent_hir_id(hir_id);
-        assert!(is_body_owner(self.tcx.hir_node(parent), hir_id), "{hir_id:?}");
+        assert_eq!(self.tcx.hir_node(parent).body_id().unwrap().hir_id, hir_id, "{hir_id:?}");
         parent
     }
 
     pub fn body_owner_def_id(self, BodyId { hir_id }: BodyId) -> LocalDefId {
-        associated_body(self.tcx.parent_hir_node(hir_id)).unwrap().0
+        self.tcx.parent_hir_node(hir_id).associated_body().unwrap().0
     }
 
     /// Given a `LocalDefId`, returns the `BodyId` associated with it,
     /// if the node is a body owner, otherwise returns `None`.
     pub fn maybe_body_owned_by(self, id: LocalDefId) -> Option<BodyId> {
-        let node = self.tcx.hir_node_by_def_id(id);
-        let (_, body_id) = associated_body(node)?;
-        Some(body_id)
+        self.tcx.hir_node_by_def_id(id).body_id()
     }
 
     /// Given a body owner's id, returns the `BodyId` associated with it.
@@ -1314,7 +1274,7 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> {
     }
 
     fn visit_item(&mut self, item: &'hir Item<'hir>) {
-        if associated_body(Node::Item(item)).is_some() {
+        if Node::Item(item).associated_body().is_some() {
             self.body_owners.push(item.owner_id.def_id);
         }
 
@@ -1355,7 +1315,7 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> {
     }
 
     fn visit_trait_item(&mut self, item: &'hir TraitItem<'hir>) {
-        if associated_body(Node::TraitItem(item)).is_some() {
+        if Node::TraitItem(item).associated_body().is_some() {
             self.body_owners.push(item.owner_id.def_id);
         }
 
@@ -1364,7 +1324,7 @@ impl<'hir> Visitor<'hir> for ItemCollector<'hir> {
     }
 
     fn visit_impl_item(&mut self, item: &'hir ImplItem<'hir>) {
-        if associated_body(Node::ImplItem(item)).is_some() {
+        if Node::ImplItem(item).associated_body().is_some() {
             self.body_owners.push(item.owner_id.def_id);
         }
 
diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs
index c3e4a03ad16..d910a15e262 100644
--- a/compiler/rustc_middle/src/lib.rs
+++ b/compiler/rustc_middle/src/lib.rs
@@ -31,6 +31,7 @@
 #![feature(array_windows)]
 #![feature(assert_matches)]
 #![feature(box_patterns)]
+#![feature(closure_track_caller)]
 #![feature(core_intrinsics)]
 #![feature(const_type_name)]
 #![feature(discriminant_kind)]
@@ -48,7 +49,7 @@
 #![feature(trusted_len)]
 #![feature(type_alias_impl_trait)]
 #![feature(strict_provenance)]
-#![feature(associated_type_bounds)]
+#![cfg_attr(bootstrap, feature(associated_type_bounds))]
 #![feature(rustc_attrs)]
 #![feature(control_flow_enum)]
 #![feature(trait_upcasting)]
diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs
index 5feee3c3ba1..214297b9869 100644
--- a/compiler/rustc_middle/src/mir/consts.rs
+++ b/compiler/rustc_middle/src/mir/consts.rs
@@ -2,7 +2,7 @@ use std::fmt::{self, Debug, Display, Formatter};
 
 use rustc_hir::def_id::DefId;
 use rustc_session::RemapFileNameExt;
-use rustc_span::Span;
+use rustc_span::{Span, DUMMY_SP};
 use rustc_target::abi::{HasDataLayout, Size};
 
 use crate::mir::interpret::{alloc_range, AllocId, ConstAllocation, ErrorHandled, Scalar};
@@ -273,7 +273,7 @@ impl<'tcx> Const<'tcx> {
         self,
         tcx: TyCtxt<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
-        span: Option<Span>,
+        span: Span,
     ) -> Result<ConstValue<'tcx>, ErrorHandled> {
         match self {
             Const::Ty(c) => {
@@ -293,7 +293,7 @@ impl<'tcx> Const<'tcx> {
     /// Normalizes the constant to a value or an error if possible.
     #[inline]
     pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
-        match self.eval(tcx, param_env, None) {
+        match self.eval(tcx, param_env, DUMMY_SP) {
             Ok(val) => Self::Val(val, self.ty()),
             Err(ErrorHandled::Reported(guar, _span)) => {
                 Self::Ty(ty::Const::new_error(tcx, guar.into(), self.ty()))
@@ -313,10 +313,10 @@ impl<'tcx> Const<'tcx> {
                 // Avoid the `valtree_to_const_val` query. Can only be done on primitive types that
                 // are valtree leaves, and *not* on references. (References should return the
                 // pointer here, which valtrees don't represent.)
-                let val = c.eval(tcx, param_env, None).ok()?;
+                let val = c.eval(tcx, param_env, DUMMY_SP).ok()?;
                 Some(val.unwrap_leaf().into())
             }
-            _ => self.eval(tcx, param_env, None).ok()?.try_to_scalar(),
+            _ => self.eval(tcx, param_env, DUMMY_SP).ok()?.try_to_scalar(),
         }
     }
 
diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs
index 643b61c1de3..04d6301116e 100644
--- a/compiler/rustc_middle/src/mir/interpret/queries.rs
+++ b/compiler/rustc_middle/src/mir/interpret/queries.rs
@@ -24,7 +24,7 @@ impl<'tcx> TyCtxt<'tcx> {
         let instance = ty::Instance::new(def_id, args);
         let cid = GlobalId { instance, promoted: None };
         let param_env = self.param_env(def_id).with_reveal_all_normalized(self);
-        self.const_eval_global_id(param_env, cid, None)
+        self.const_eval_global_id(param_env, cid, DUMMY_SP)
     }
     /// Resolves and evaluates a constant.
     ///
@@ -40,7 +40,7 @@ impl<'tcx> TyCtxt<'tcx> {
         self,
         param_env: ty::ParamEnv<'tcx>,
         ct: mir::UnevaluatedConst<'tcx>,
-        span: Option<Span>,
+        span: Span,
     ) -> EvalToConstValueResult<'tcx> {
         // Cannot resolve `Unevaluated` constants that contain inference
         // variables. We reject those here since `resolve`
@@ -73,7 +73,7 @@ impl<'tcx> TyCtxt<'tcx> {
         self,
         param_env: ty::ParamEnv<'tcx>,
         ct: ty::UnevaluatedConst<'tcx>,
-        span: Option<Span>,
+        span: Span,
     ) -> EvalToValTreeResult<'tcx> {
         // Cannot resolve `Unevaluated` constants that contain inference
         // variables. We reject those here since `resolve`
@@ -130,7 +130,7 @@ impl<'tcx> TyCtxt<'tcx> {
         self,
         param_env: ty::ParamEnv<'tcx>,
         instance: ty::Instance<'tcx>,
-        span: Option<Span>,
+        span: Span,
     ) -> EvalToConstValueResult<'tcx> {
         self.const_eval_global_id(param_env, GlobalId { instance, promoted: None }, span)
     }
@@ -141,12 +141,12 @@ impl<'tcx> TyCtxt<'tcx> {
         self,
         param_env: ty::ParamEnv<'tcx>,
         cid: GlobalId<'tcx>,
-        span: Option<Span>,
+        span: Span,
     ) -> EvalToConstValueResult<'tcx> {
         // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should
         // improve caching of queries.
         let inputs = self.erase_regions(param_env.with_reveal_all_normalized(self).and(cid));
-        if let Some(span) = span {
+        if !span.is_dummy() {
             // The query doesn't know where it is being invoked, so we need to fix the span.
             self.at(span).eval_to_const_value_raw(inputs).map_err(|e| e.with_span(span))
         } else {
@@ -160,13 +160,13 @@ impl<'tcx> TyCtxt<'tcx> {
         self,
         param_env: ty::ParamEnv<'tcx>,
         cid: GlobalId<'tcx>,
-        span: Option<Span>,
+        span: Span,
     ) -> EvalToValTreeResult<'tcx> {
         // Const-eval shouldn't depend on lifetimes at all, so we can erase them, which should
         // improve caching of queries.
         let inputs = self.erase_regions(param_env.with_reveal_all_normalized(self).and(cid));
         debug!(?inputs);
-        if let Some(span) = span {
+        if !span.is_dummy() {
             // The query doesn't know where it is being invoked, so we need to fix the span.
             self.at(span).eval_to_valtree(inputs).map_err(|e| e.with_span(span))
         } else {
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 865299e15c8..9f0d2a89e6d 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -703,8 +703,8 @@ rustc_queries! {
         separate_provide_extern
     }
 
-    query adt_sized_constraint(key: DefId) -> ty::EarlyBinder<&'tcx ty::List<Ty<'tcx>>> {
-        desc { |tcx| "computing `Sized` constraints for `{}`", tcx.def_path_str(key) }
+    query adt_sized_constraint(key: DefId) -> Option<ty::EarlyBinder<Ty<'tcx>>> {
+        desc { |tcx| "computing the `Sized` constraint for `{}`", tcx.def_path_str(key) }
     }
 
     query adt_dtorck_constraint(
diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs
index dc4cd203415..6dcea2aaff1 100644
--- a/compiler/rustc_middle/src/traits/solve.rs
+++ b/compiler/rustc_middle/src/traits/solve.rs
@@ -164,6 +164,19 @@ pub struct ExternalConstraintsData<'tcx> {
     // FIXME: implement this.
     pub region_constraints: QueryRegionConstraints<'tcx>,
     pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>,
+    pub normalization_nested_goals: NestedNormalizationGoals<'tcx>,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default, TypeVisitable, TypeFoldable)]
+pub struct NestedNormalizationGoals<'tcx>(pub Vec<(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>);
+impl<'tcx> NestedNormalizationGoals<'tcx> {
+    pub fn empty() -> Self {
+        NestedNormalizationGoals(vec![])
+    }
+
+    pub fn is_empty(&self) -> bool {
+        self.0.is_empty()
+    }
 }
 
 // FIXME: Having to clone `region_constraints` for folding feels bad and
@@ -183,6 +196,10 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> {
                 .iter()
                 .map(|opaque| opaque.try_fold_with(folder))
                 .collect::<Result<_, F::Error>>()?,
+            normalization_nested_goals: self
+                .normalization_nested_goals
+                .clone()
+                .try_fold_with(folder)?,
         }))
     }
 
@@ -190,6 +207,7 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> {
         TypeFolder::interner(folder).mk_external_constraints(ExternalConstraintsData {
             region_constraints: self.region_constraints.clone().fold_with(folder),
             opaque_types: self.opaque_types.iter().map(|opaque| opaque.fold_with(folder)).collect(),
+            normalization_nested_goals: self.normalization_nested_goals.clone().fold_with(folder),
         })
     }
 }
@@ -197,7 +215,8 @@ impl<'tcx> TypeFoldable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> {
 impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for ExternalConstraints<'tcx> {
     fn visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(&self, visitor: &mut V) -> V::Result {
         try_visit!(self.region_constraints.visit_with(visitor));
-        self.opaque_types.visit_with(visitor)
+        try_visit!(self.opaque_types.visit_with(visitor));
+        self.normalization_nested_goals.visit_with(visitor)
     }
 }
 
@@ -239,7 +258,7 @@ impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for PredefinedOpaques<'tcx> {
 ///
 /// This is necessary as we treat nested goals different depending on
 /// their source.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TypeVisitable, TypeFoldable)]
 pub enum GoalSource {
     Misc,
     /// We're proving a where-bound of an impl.
@@ -256,12 +275,6 @@ pub enum GoalSource {
     ImplWhereBound,
 }
 
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable)]
-pub enum IsNormalizesToHack {
-    Yes,
-    No,
-}
-
 /// Possible ways the given goal can be proven.
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum CandidateSource {
diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs
index 90180af3b16..16842c8208f 100644
--- a/compiler/rustc_middle/src/traits/solve/inspect.rs
+++ b/compiler/rustc_middle/src/traits/solve/inspect.rs
@@ -19,8 +19,8 @@
 //! [canonicalized]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
 
 use super::{
-    CandidateSource, Canonical, CanonicalInput, Certainty, Goal, GoalSource, IsNormalizesToHack,
-    NoSolution, QueryInput, QueryResult,
+    CandidateSource, Canonical, CanonicalInput, Certainty, Goal, GoalSource, NoSolution,
+    QueryInput, QueryResult,
 };
 use crate::{infer::canonical::CanonicalVarValues, ty};
 use format::ProofTreeFormatter;
@@ -50,7 +50,7 @@ pub type CanonicalState<'tcx, T> = Canonical<'tcx, State<'tcx, T>>;
 #[derive(Eq, PartialEq)]
 pub enum GoalEvaluationKind<'tcx> {
     Root { orig_values: Vec<ty::GenericArg<'tcx>> },
-    Nested { is_normalizes_to_hack: IsNormalizesToHack },
+    Nested,
 }
 
 #[derive(Eq, PartialEq)]
diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs
index 8d3109a7b28..43931205017 100644
--- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs
+++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs
@@ -55,10 +55,7 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
     pub(super) fn format_goal_evaluation(&mut self, eval: &GoalEvaluation<'_>) -> std::fmt::Result {
         let goal_text = match eval.kind {
             GoalEvaluationKind::Root { orig_values: _ } => "ROOT GOAL",
-            GoalEvaluationKind::Nested { is_normalizes_to_hack } => match is_normalizes_to_hack {
-                IsNormalizesToHack::No => "GOAL",
-                IsNormalizesToHack::Yes => "NORMALIZES-TO HACK GOAL",
-            },
+            GoalEvaluationKind::Nested => "GOAL",
         };
         write!(self.f, "{}: {:?}", goal_text, eval.uncanonicalized_goal)?;
         self.nested(|this| this.format_canonical_goal_evaluation(&eval.evaluation))
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index 2e1c7df6454..a7144316769 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -590,10 +590,10 @@ impl<'tcx> AdtDef<'tcx> {
         tcx.adt_destructor(self.did())
     }
 
-    /// Returns a list of types such that `Self: Sized` if and only if that
-    /// type is `Sized`, or `ty::Error` if this type has a recursive layout.
-    pub fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> ty::EarlyBinder<&'tcx ty::List<Ty<'tcx>>> {
-        tcx.adt_sized_constraint(self.did())
+    /// Returns a type such that `Self: Sized` if and only if that type is `Sized`,
+    /// or `None` if the type is always sized.
+    pub fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> Option<ty::EarlyBinder<Ty<'tcx>>> {
+        if self.is_struct() { tcx.adt_sized_constraint(self.did()) } else { None }
     }
 }
 
@@ -601,5 +601,5 @@ impl<'tcx> AdtDef<'tcx> {
 #[derive(HashStable)]
 pub enum Representability {
     Representable,
-    Infinite,
+    Infinite(ErrorGuaranteed),
 }
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index 5b62c0bf931..0d621cd1cfd 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -335,7 +335,7 @@ impl<'tcx> Const<'tcx> {
         self,
         tcx: TyCtxt<'tcx>,
         param_env: ParamEnv<'tcx>,
-        span: Option<Span>,
+        span: Span,
     ) -> Result<ValTree<'tcx>, ErrorHandled> {
         assert!(!self.has_escaping_bound_vars(), "escaping vars in {self:?}");
         match self.kind() {
@@ -349,7 +349,7 @@ impl<'tcx> Const<'tcx> {
                 else {
                     // This can happen when we run on ill-typed code.
                     let e = tcx.dcx().span_delayed_bug(
-                        span.unwrap_or(DUMMY_SP),
+                        span,
                         "`ty::Const::eval` called on a non-valtree-compatible type",
                     );
                     return Err(e.into());
@@ -362,14 +362,14 @@ impl<'tcx> Const<'tcx> {
             | ConstKind::Infer(_)
             | ConstKind::Bound(_, _)
             | ConstKind::Placeholder(_)
-            | ConstKind::Expr(_) => Err(ErrorHandled::TooGeneric(span.unwrap_or(DUMMY_SP))),
+            | ConstKind::Expr(_) => Err(ErrorHandled::TooGeneric(span)),
         }
     }
 
     /// Normalizes the constant to a value or an error if possible.
     #[inline]
     pub fn normalize(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self {
-        match self.eval(tcx, param_env, None) {
+        match self.eval(tcx, param_env, DUMMY_SP) {
             Ok(val) => Self::new_value(tcx, val, self.ty()),
             Err(ErrorHandled::Reported(r, _span)) => Self::new_error(tcx, r.into(), self.ty()),
             Err(ErrorHandled::TooGeneric(_span)) => self,
@@ -382,7 +382,7 @@ impl<'tcx> Const<'tcx> {
         tcx: TyCtxt<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
     ) -> Option<Scalar> {
-        self.eval(tcx, param_env, None).ok()?.try_to_scalar()
+        self.eval(tcx, param_env, DUMMY_SP).ok()?.try_to_scalar()
     }
 
     #[inline]
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 17ba97c5fd3..10a4da40429 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -43,7 +43,9 @@ use rustc_data_structures::sync::{self, FreezeReadGuard, Lock, Lrc, WorkerLocal}
 #[cfg(parallel_compiler)]
 use rustc_data_structures::sync::{DynSend, DynSync};
 use rustc_data_structures::unord::UnordSet;
-use rustc_errors::{Diag, DiagCtxt, DiagMessage, ErrorGuaranteed, LintDiagnostic, MultiSpan};
+use rustc_errors::{
+    Applicability, Diag, DiagCtxt, DiagMessage, ErrorGuaranteed, LintDiagnostic, MultiSpan,
+};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
@@ -2174,6 +2176,45 @@ impl<'tcx> TyCtxt<'tcx> {
         lint_level(self.sess, lint, level, src, Some(span.into()), msg, decorate);
     }
 
+    /// Find the crate root and the appropriate span where `use` and outer attributes can be
+    /// inserted at.
+    pub fn crate_level_attribute_injection_span(self, hir_id: HirId) -> Option<Span> {
+        for (_hir_id, node) in self.hir().parent_iter(hir_id) {
+            if let hir::Node::Crate(m) = node {
+                return Some(m.spans.inject_use_span.shrink_to_lo());
+            }
+        }
+        None
+    }
+
+    pub fn disabled_nightly_features<E: rustc_errors::EmissionGuarantee>(
+        self,
+        diag: &mut Diag<'_, E>,
+        hir_id: Option<HirId>,
+        features: impl IntoIterator<Item = (String, Symbol)>,
+    ) {
+        if !self.sess.is_nightly_build() {
+            return;
+        }
+
+        let span = hir_id.and_then(|id| self.crate_level_attribute_injection_span(id));
+        for (desc, feature) in features {
+            // FIXME: make this string translatable
+            let msg =
+                format!("add `#![feature({feature})]` to the crate attributes to enable{desc}");
+            if let Some(span) = span {
+                diag.span_suggestion_verbose(
+                    span,
+                    msg,
+                    format!("#![feature({feature})]\n"),
+                    Applicability::MachineApplicable,
+                );
+            } else {
+                diag.help(msg);
+            }
+        }
+    }
+
     /// Emit a lint from a lint struct (some type that implements `LintDiagnostic`, typically
     /// generated by `#[derive(LintDiagnostic)]`).
     #[track_caller]
diff --git a/compiler/rustc_middle/src/ty/context/tls.rs b/compiler/rustc_middle/src/ty/context/tls.rs
index 788ccd5dbbd..5e256dc8d26 100644
--- a/compiler/rustc_middle/src/ty/context/tls.rs
+++ b/compiler/rustc_middle/src/ty/context/tls.rs
@@ -85,6 +85,7 @@ where
 
 /// Allows access to the current `ImplicitCtxt` in a closure if one is available.
 #[inline]
+#[track_caller]
 pub fn with_context_opt<F, R>(f: F) -> R
 where
     F: for<'a, 'tcx> FnOnce(Option<&ImplicitCtxt<'a, 'tcx>>) -> R,
@@ -147,9 +148,13 @@ where
 /// Allows access to the `TyCtxt` in the current `ImplicitCtxt`.
 /// The closure is passed None if there is no `ImplicitCtxt` available.
 #[inline]
+#[track_caller]
 pub fn with_opt<F, R>(f: F) -> R
 where
     F: for<'tcx> FnOnce(Option<TyCtxt<'tcx>>) -> R,
 {
-    with_context_opt(|opt_context| f(opt_context.map(|context| context.tcx)))
+    with_context_opt(
+        #[track_caller]
+        |opt_context| f(opt_context.map(|context| context.tcx)),
+    )
 }
diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs
index bbcc244cb26..da5d57db5be 100644
--- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs
+++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs
@@ -61,7 +61,7 @@ pub(crate) fn provide(providers: &mut Providers) {
 /// requires calling [`InhabitedPredicate::instantiate`]
 fn inhabited_predicate_adt(tcx: TyCtxt<'_>, def_id: DefId) -> InhabitedPredicate<'_> {
     if let Some(def_id) = def_id.as_local() {
-        if matches!(tcx.representability(def_id), ty::Representability::Infinite) {
+        if matches!(tcx.representability(def_id), ty::Representability::Infinite(_)) {
             return InhabitedPredicate::True;
         }
     }
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 802953867ac..995b439d10a 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -4,7 +4,7 @@ use crate::query::Providers;
 use crate::traits::util::{super_predicates_for_pretty_printing, supertraits_for_pretty_printing};
 use crate::ty::GenericArgKind;
 use crate::ty::{
-    ConstInt, ParamConst, ScalarInt, Term, TermKind, TypeFoldable, TypeSuperFoldable,
+    ConstInt, Expr, ParamConst, ScalarInt, Term, TermKind, TypeFoldable, TypeSuperFoldable,
     TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
 };
 use rustc_apfloat::ieee::{Double, Single};
@@ -270,6 +270,31 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
         Ok(())
     }
 
+    /// Prints `(...)` around what `f` prints.
+    fn parenthesized(
+        &mut self,
+        f: impl FnOnce(&mut Self) -> Result<(), PrintError>,
+    ) -> Result<(), PrintError> {
+        self.write_str("(")?;
+        f(self)?;
+        self.write_str(")")?;
+        Ok(())
+    }
+
+    /// Prints `(...)` around what `f` prints if `parenthesized` is true, otherwise just prints `f`.
+    fn maybe_parenthesized(
+        &mut self,
+        f: impl FnOnce(&mut Self) -> Result<(), PrintError>,
+        parenthesized: bool,
+    ) -> Result<(), PrintError> {
+        if parenthesized {
+            self.parenthesized(f)?;
+        } else {
+            f(self)?;
+        }
+        Ok(())
+    }
+
     /// Prints `<...>` around what `f` prints.
     fn generic_delimiters(
         &mut self,
@@ -1490,12 +1515,137 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
             ty::ConstKind::Placeholder(placeholder) => p!(write("{placeholder:?}")),
             // FIXME(generic_const_exprs):
             // write out some legible representation of an abstract const?
-            ty::ConstKind::Expr(_) => p!("{{const expr}}"),
+            ty::ConstKind::Expr(expr) => self.pretty_print_const_expr(expr, print_ty)?,
             ty::ConstKind::Error(_) => p!("{{const error}}"),
         };
         Ok(())
     }
 
+    fn pretty_print_const_expr(
+        &mut self,
+        expr: Expr<'tcx>,
+        print_ty: bool,
+    ) -> Result<(), PrintError> {
+        define_scoped_cx!(self);
+        match expr {
+            Expr::Binop(op, c1, c2) => {
+                let precedence = |binop: rustc_middle::mir::BinOp| {
+                    use rustc_ast::util::parser::AssocOp;
+                    AssocOp::from_ast_binop(binop.to_hir_binop().into()).precedence()
+                };
+                let op_precedence = precedence(op);
+                let formatted_op = op.to_hir_binop().as_str();
+                let (lhs_parenthesized, rhs_parenthesized) = match (c1.kind(), c2.kind()) {
+                    (
+                        ty::ConstKind::Expr(Expr::Binop(lhs_op, _, _)),
+                        ty::ConstKind::Expr(Expr::Binop(rhs_op, _, _)),
+                    ) => (precedence(lhs_op) < op_precedence, precedence(rhs_op) < op_precedence),
+                    (ty::ConstKind::Expr(Expr::Binop(lhs_op, ..)), ty::ConstKind::Expr(_)) => {
+                        (precedence(lhs_op) < op_precedence, true)
+                    }
+                    (ty::ConstKind::Expr(_), ty::ConstKind::Expr(Expr::Binop(rhs_op, ..))) => {
+                        (true, precedence(rhs_op) < op_precedence)
+                    }
+                    (ty::ConstKind::Expr(_), ty::ConstKind::Expr(_)) => (true, true),
+                    (ty::ConstKind::Expr(Expr::Binop(lhs_op, ..)), _) => {
+                        (precedence(lhs_op) < op_precedence, false)
+                    }
+                    (_, ty::ConstKind::Expr(Expr::Binop(rhs_op, ..))) => {
+                        (false, precedence(rhs_op) < op_precedence)
+                    }
+                    (ty::ConstKind::Expr(_), _) => (true, false),
+                    (_, ty::ConstKind::Expr(_)) => (false, true),
+                    _ => (false, false),
+                };
+
+                self.maybe_parenthesized(
+                    |this| this.pretty_print_const(c1, print_ty),
+                    lhs_parenthesized,
+                )?;
+                p!(write(" {formatted_op} "));
+                self.maybe_parenthesized(
+                    |this| this.pretty_print_const(c2, print_ty),
+                    rhs_parenthesized,
+                )?;
+            }
+            Expr::UnOp(op, ct) => {
+                use rustc_middle::mir::UnOp;
+                let formatted_op = match op {
+                    UnOp::Not => "!",
+                    UnOp::Neg => "-",
+                };
+                let parenthesized = match ct.kind() {
+                    ty::ConstKind::Expr(Expr::UnOp(c_op, ..)) => c_op != op,
+                    ty::ConstKind::Expr(_) => true,
+                    _ => false,
+                };
+                p!(write("{formatted_op}"));
+                self.maybe_parenthesized(
+                    |this| this.pretty_print_const(ct, print_ty),
+                    parenthesized,
+                )?
+            }
+            Expr::FunctionCall(fn_def, fn_args) => {
+                use ty::TyKind;
+                match fn_def.ty().kind() {
+                    TyKind::FnDef(def_id, gen_args) => {
+                        p!(print_value_path(*def_id, gen_args), "(");
+                        if print_ty {
+                            let tcx = self.tcx();
+                            let sig = tcx.fn_sig(def_id).instantiate(tcx, gen_args).skip_binder();
+
+                            let mut args_with_ty = fn_args.iter().map(|ct| (ct, ct.ty()));
+                            let output_ty = sig.output();
+
+                            if let Some((ct, ty)) = args_with_ty.next() {
+                                self.typed_value(
+                                    |this| this.pretty_print_const(ct, print_ty),
+                                    |this| this.pretty_print_type(ty),
+                                    ": ",
+                                )?;
+                                for (ct, ty) in args_with_ty {
+                                    p!(", ");
+                                    self.typed_value(
+                                        |this| this.pretty_print_const(ct, print_ty),
+                                        |this| this.pretty_print_type(ty),
+                                        ": ",
+                                    )?;
+                                }
+                            }
+                            p!(write(") -> {output_ty}"));
+                        } else {
+                            p!(comma_sep(fn_args.iter()), ")");
+                        }
+                    }
+                    _ => bug!("unexpected type of fn def"),
+                }
+            }
+            Expr::Cast(kind, ct, ty) => {
+                use ty::abstract_const::CastKind;
+                if kind == CastKind::As || (kind == CastKind::Use && self.should_print_verbose()) {
+                    let parenthesized = match ct.kind() {
+                        ty::ConstKind::Expr(Expr::Cast(_, _, _)) => false,
+                        ty::ConstKind::Expr(_) => true,
+                        _ => false,
+                    };
+                    self.maybe_parenthesized(
+                        |this| {
+                            this.typed_value(
+                                |this| this.pretty_print_const(ct, print_ty),
+                                |this| this.pretty_print_type(ty),
+                                " as ",
+                            )
+                        },
+                        parenthesized,
+                    )?;
+                } else {
+                    self.pretty_print_const(ct, print_ty)?
+                }
+            }
+        }
+        Ok(())
+    }
+
     fn pretty_print_const_scalar(
         &mut self,
         scalar: Scalar,
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 11065b2a382..540936d7d8a 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -2484,13 +2484,16 @@ impl<'tcx> Ty<'tcx> {
             | ty::Closure(..)
             | ty::CoroutineClosure(..)
             | ty::Never
-            | ty::Error(_) => true,
+            | ty::Error(_)
+            | ty::Dynamic(_, _, ty::DynStar) => true,
 
-            ty::Str | ty::Slice(_) | ty::Dynamic(..) | ty::Foreign(..) => false,
+            ty::Str | ty::Slice(_) | ty::Dynamic(_, _, ty::Dyn) | ty::Foreign(..) => false,
 
-            ty::Tuple(tys) => tys.iter().all(|ty| ty.is_trivially_sized(tcx)),
+            ty::Tuple(tys) => tys.last().map_or(true, |ty| ty.is_trivially_sized(tcx)),
 
-            ty::Adt(def, _args) => def.sized_constraint(tcx).skip_binder().is_empty(),
+            ty::Adt(def, args) => def
+                .sized_constraint(tcx)
+                .map_or(true, |ty| ty.instantiate(tcx, args).is_trivially_sized(tcx)),
 
             ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) | ty::Bound(..) => false,
 
diff --git a/compiler/rustc_middle/src/util/bug.rs b/compiler/rustc_middle/src/util/bug.rs
index a67ec991582..43853a10896 100644
--- a/compiler/rustc_middle/src/util/bug.rs
+++ b/compiler/rustc_middle/src/util/bug.rs
@@ -28,14 +28,17 @@ fn opt_span_bug_fmt<S: Into<MultiSpan>>(
     args: fmt::Arguments<'_>,
     location: &Location<'_>,
 ) -> ! {
-    tls::with_opt(move |tcx| {
-        let msg = format!("{location}: {args}");
-        match (tcx, span) {
-            (Some(tcx), Some(span)) => tcx.dcx().span_bug(span, msg),
-            (Some(tcx), None) => tcx.dcx().bug(msg),
-            (None, _) => panic_any(msg),
-        }
-    })
+    tls::with_opt(
+        #[track_caller]
+        move |tcx| {
+            let msg = format!("{location}: {args}");
+            match (tcx, span) {
+                (Some(tcx), Some(span)) => tcx.dcx().span_bug(span, msg),
+                (Some(tcx), None) => tcx.dcx().bug(msg),
+                (None, _) => panic_any(msg),
+            }
+        },
+    )
 }
 
 /// A query to trigger a delayed bug. Clearly, if one has a `tcx` one can already trigger a
diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs
index f7a3879a7d4..5c17c0b3088 100644
--- a/compiler/rustc_middle/src/values.rs
+++ b/compiler/rustc_middle/src/values.rs
@@ -106,8 +106,8 @@ impl<'tcx> Value<TyCtxt<'tcx>> for Representability {
                 representable_ids.insert(def_id);
             }
         }
-        recursive_type_error(tcx, item_and_field_ids, &representable_ids);
-        Representability::Infinite
+        let guar = recursive_type_error(tcx, item_and_field_ids, &representable_ids);
+        Representability::Infinite(guar)
     }
 }
 
@@ -268,7 +268,7 @@ pub fn recursive_type_error(
     tcx: TyCtxt<'_>,
     mut item_and_field_ids: Vec<(LocalDefId, LocalDefId)>,
     representable_ids: &FxHashSet<LocalDefId>,
-) {
+) -> ErrorGuaranteed {
     const ITEM_LIMIT: usize = 5;
 
     // Rotate the cycle so that the item with the lowest span is first
@@ -344,7 +344,7 @@ pub fn recursive_type_error(
         suggestion,
         Applicability::HasPlaceholders,
     )
-    .emit();
+    .emit()
 }
 
 fn find_item_ty_spans(
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index 411119b521b..acadfe7b35e 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -1013,8 +1013,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         if let Some(source_scope) = scope {
             self.source_scope = source_scope;
         }
-
-        self.expr_into_dest(Place::return_place(), block, expr_id)
+        if self.tcx.intrinsic(self.def_id).is_some_and(|i| i.must_be_overridden) {
+            let source_info = self.source_info(rustc_span::DUMMY_SP);
+            self.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
+            self.cfg.start_new_block().unit()
+        } else {
+            self.expr_into_dest(Place::return_place(), block, expr_id)
+        }
     }
 
     fn set_correct_source_scope_for_arg(
diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs
index e3f202b7f18..b9a20cb21e9 100644
--- a/compiler/rustc_mir_build/src/lib.rs
+++ b/compiler/rustc_mir_build/src/lib.rs
@@ -5,7 +5,7 @@
 #![allow(rustc::diagnostic_outside_of_impl)]
 #![allow(rustc::untranslatable_diagnostic)]
 #![feature(assert_matches)]
-#![feature(associated_type_bounds)]
+#![cfg_attr(bootstrap, feature(associated_type_bounds))]
 #![feature(box_patterns)]
 #![feature(if_let_guard)]
 #![feature(let_chains)]
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 20d3b3f98ce..cfa853417ca 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -938,9 +938,6 @@ fn report_non_exhaustive_match<'p, 'tcx>(
     };
     // In the case of an empty match, replace the '`_` not covered' diagnostic with something more
     // informative.
-    let mut err;
-    let pattern;
-    let patterns_len;
     if is_empty_match && !non_empty_enum {
         return cx.tcx.dcx().emit_err(NonExhaustivePatternsTypeNotEmpty {
             cx,
@@ -948,33 +945,23 @@ fn report_non_exhaustive_match<'p, 'tcx>(
             span: sp,
             ty: scrut_ty,
         });
-    } else {
-        // FIXME: migration of this diagnostic will require list support
-        let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
-        err = create_e0004(
-            cx.tcx.sess,
-            sp,
-            format!("non-exhaustive patterns: {joined_patterns} not covered"),
-        );
-        err.span_label(
-            sp,
-            format!(
-                "pattern{} {} not covered",
-                rustc_errors::pluralize!(witnesses.len()),
-                joined_patterns
-            ),
-        );
-        patterns_len = witnesses.len();
-        pattern = if witnesses.len() < 4 {
-            witnesses
-                .iter()
-                .map(|witness| cx.hoist_witness_pat(witness).to_string())
-                .collect::<Vec<String>>()
-                .join(" | ")
-        } else {
-            "_".to_string()
-        };
-    };
+    }
+
+    // FIXME: migration of this diagnostic will require list support
+    let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
+    let mut err = create_e0004(
+        cx.tcx.sess,
+        sp,
+        format!("non-exhaustive patterns: {joined_patterns} not covered"),
+    );
+    err.span_label(
+        sp,
+        format!(
+            "pattern{} {} not covered",
+            rustc_errors::pluralize!(witnesses.len()),
+            joined_patterns
+        ),
+    );
 
     // Point at the definition of non-covered `enum` variants.
     if let Some(AdtDefinedHere { adt_def_span, ty, variants }) =
@@ -1021,6 +1008,23 @@ fn report_non_exhaustive_match<'p, 'tcx>(
         }
     }
 
+    // Whether we suggest the actual missing patterns or `_`.
+    let suggest_the_witnesses = witnesses.len() < 4;
+    let suggested_arm = if suggest_the_witnesses {
+        let pattern = witnesses
+            .iter()
+            .map(|witness| cx.hoist_witness_pat(witness).to_string())
+            .collect::<Vec<String>>()
+            .join(" | ");
+        if witnesses.iter().all(|p| p.is_never_pattern()) && cx.tcx.features().never_patterns {
+            // Arms with a never pattern don't take a body.
+            pattern
+        } else {
+            format!("{pattern} => todo!()")
+        }
+    } else {
+        format!("_ => todo!()")
+    };
     let mut suggestion = None;
     let sm = cx.tcx.sess.source_map();
     match arms {
@@ -1033,7 +1037,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
             };
             suggestion = Some((
                 sp.shrink_to_hi().with_hi(expr_span.hi()),
-                format!(" {{{indentation}{more}{pattern} => todo!(),{indentation}}}",),
+                format!(" {{{indentation}{more}{suggested_arm},{indentation}}}",),
             ));
         }
         [only] => {
@@ -1059,7 +1063,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
             };
             suggestion = Some((
                 only.span.shrink_to_hi(),
-                format!("{comma}{pre_indentation}{pattern} => todo!()"),
+                format!("{comma}{pre_indentation}{suggested_arm}"),
             ));
         }
         [.., prev, last] => {
@@ -1082,7 +1086,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
                 if let Some(spacing) = spacing {
                     suggestion = Some((
                         last.span.shrink_to_hi(),
-                        format!("{comma}{spacing}{pattern} => todo!()"),
+                        format!("{comma}{spacing}{suggested_arm}"),
                     ));
                 }
             }
@@ -1093,13 +1097,13 @@ fn report_non_exhaustive_match<'p, 'tcx>(
     let msg = format!(
         "ensure that all possible cases are being handled by adding a match arm with a wildcard \
          pattern{}{}",
-        if patterns_len > 1 && patterns_len < 4 && suggestion.is_some() {
+        if witnesses.len() > 1 && suggest_the_witnesses && suggestion.is_some() {
             ", a match arm with multiple or-patterns"
         } else {
             // we are either not suggesting anything, or suggesting `_`
             ""
         },
-        match patterns_len {
+        match witnesses.len() {
             // non-exhaustive enum case
             0 if suggestion.is_some() => " as shown",
             0 => "",
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index ac3043afcff..03eda9a9322 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -524,12 +524,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         // Prefer valtrees over opaque constants.
         let const_value = self
             .tcx
-            .const_eval_global_id_for_typeck(param_env_reveal_all, cid, Some(span))
+            .const_eval_global_id_for_typeck(param_env_reveal_all, cid, span)
             .map(|val| match val {
                 Some(valtree) => mir::Const::Ty(ty::Const::new_value(self.tcx, valtree, ty)),
                 None => mir::Const::Val(
                     self.tcx
-                        .const_eval_global_id(param_env_reveal_all, cid, Some(span))
+                        .const_eval_global_id(param_env_reveal_all, cid, span)
                         .expect("const_eval_global_id_for_typeck should have already failed"),
                     ty,
                 ),
@@ -627,15 +627,14 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         // First try using a valtree in order to destructure the constant into a pattern.
         // FIXME: replace "try to do a thing, then fall back to another thing"
         // but something more principled, like a trait query checking whether this can be turned into a valtree.
-        if let Ok(Some(valtree)) =
-            self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, Some(span))
+        if let Ok(Some(valtree)) = self.tcx.const_eval_resolve_for_typeck(self.param_env, ct, span)
         {
             let subpattern =
                 self.const_to_pat(Const::Ty(ty::Const::new_value(self.tcx, valtree, ty)), id, span);
             PatKind::InlineConstant { subpattern, def: def_id }
         } else {
             // If that fails, convert it to an opaque constant pattern.
-            match tcx.const_eval_resolve(self.param_env, uneval, Some(span)) {
+            match tcx.const_eval_resolve(self.param_env, uneval, span) {
                 Ok(val) => self.const_to_pat(mir::Const::Val(val, ty), id, span).kind,
                 Err(ErrorHandled::TooGeneric(_)) => {
                     // If we land here it means the const can't be evaluated because it's `TooGeneric`.
diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs
index 6f668aa4ce8..f880476cec2 100644
--- a/compiler/rustc_mir_transform/src/add_retag.rs
+++ b/compiler/rustc_mir_transform/src/add_retag.rs
@@ -24,7 +24,7 @@ fn may_contain_reference<'tcx>(ty: Ty<'tcx>, depth: u32, tcx: TyCtxt<'tcx>) -> b
         | ty::Str
         | ty::FnDef(..)
         | ty::Never => false,
-        // References
+        // 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,
@@ -125,15 +125,39 @@ impl<'tcx> MirPass<'tcx> for AddRetag {
             for i in (0..block_data.statements.len()).rev() {
                 let (retag_kind, place) = match block_data.statements[i].kind {
                     // Retag after assignments of reference type.
-                    StatementKind::Assign(box (ref place, ref rvalue)) if needs_retag(place) => {
+                    StatementKind::Assign(box (ref place, ref rvalue)) => {
                         let add_retag = match rvalue {
                             // Ptr-creating operations already do their own internal retagging, no
                             // need to also add a retag statement.
-                            Rvalue::Ref(..) | Rvalue::AddressOf(..) => false,
-                            _ => true,
+                            // *Except* if we are deref'ing a Box, because those get desugared to directly working
+                            // with the inner raw pointer! That's relevant for `AddressOf` as Miri otherwise makes it
+                            // a NOP when the original pointer is already raw.
+                            Rvalue::AddressOf(_mutbl, place) => {
+                                // Using `is_box_global` here is a bit sketchy: if this code is
+                                // generic over the allocator, we'll not add a retag! This is a hack
+                                // to make Stacked Borrows compatible with custom allocator code.
+                                // Long-term, we'll want to move to an aliasing model where "cast to
+                                // raw pointer" is a complete NOP, and then this will no longer be
+                                // an issue.
+                                if place.is_indirect_first_projection()
+                                    && body.local_decls[place.local].ty.is_box_global(tcx)
+                                {
+                                    Some(RetagKind::Raw)
+                                } else {
+                                    None
+                                }
+                            }
+                            Rvalue::Ref(..) => None,
+                            _ => {
+                                if needs_retag(place) {
+                                    Some(RetagKind::Default)
+                                } else {
+                                    None
+                                }
+                            }
                         };
-                        if add_retag {
-                            (RetagKind::Default, *place)
+                        if let Some(kind) = add_retag {
+                            (kind, *place)
                         } else {
                             continue;
                         }
diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index 54b13a40e92..0d18d4fd69e 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -1964,15 +1964,21 @@ fn check_must_not_suspend_ty<'tcx>(
     debug!("Checking must_not_suspend for {}", ty);
 
     match *ty.kind() {
-        ty::Adt(..) if ty.is_box() => {
-            let boxed_ty = ty.boxed_ty();
-            let descr_pre = &format!("{}boxed ", data.descr_pre);
+        ty::Adt(_, args) if ty.is_box() => {
+            let boxed_ty = args.type_at(0);
+            let allocator_ty = args.type_at(1);
             check_must_not_suspend_ty(
                 tcx,
                 boxed_ty,
                 hir_id,
                 param_env,
-                SuspendCheckData { descr_pre, ..data },
+                SuspendCheckData { descr_pre: &format!("{}boxed ", data.descr_pre), ..data },
+            ) || check_must_not_suspend_ty(
+                tcx,
+                allocator_ty,
+                hir_id,
+                param_env,
+                SuspendCheckData { descr_pre: &format!("{}allocator ", data.descr_pre), ..data },
             )
         }
         ty::Adt(def, _) => check_must_not_suspend_def(tcx, def.did(), hir_id, data),
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index b2407c54507..83189c6a50a 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -13,7 +13,6 @@ use self::spans::{BcbMapping, BcbMappingKind, CoverageSpans};
 
 use crate::MirPass;
 
-use rustc_middle::hir;
 use rustc_middle::mir::coverage::*;
 use rustc_middle::mir::{
     self, BasicBlock, BasicBlockData, Coverage, SourceInfo, Statement, StatementKind, Terminator,
@@ -368,8 +367,7 @@ fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHir
     // to HIR for it.
 
     let hir_node = tcx.hir_node_by_def_id(def_id);
-    let (_, fn_body_id) =
-        hir::map::associated_body(hir_node).expect("HIR node is a function with body");
+    let fn_body_id = hir_node.body_id().expect("HIR node is a function with body");
     let hir_body = tcx.hir().body(fn_body_id);
 
     let maybe_fn_sig = hir_node.fn_sig();
diff --git a/compiler/rustc_mir_transform/src/cross_crate_inline.rs b/compiler/rustc_mir_transform/src/cross_crate_inline.rs
index 07e6ecccaa4..483fd753e70 100644
--- a/compiler/rustc_mir_transform/src/cross_crate_inline.rs
+++ b/compiler/rustc_mir_transform/src/cross_crate_inline.rs
@@ -23,10 +23,6 @@ fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
         return false;
     }
 
-    if tcx.intrinsic(def_id).is_some_and(|i| i.must_be_overridden) {
-        return false;
-    }
-
     // This just reproduces the logic from Instance::requires_inline.
     match tcx.def_kind(def_id) {
         DefKind::Ctor(..) | DefKind::Closure => return true,
diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
index f456196b282..3389305e7ee 100644
--- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
+++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
@@ -394,7 +394,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
             }
             Operand::Constant(box constant) => {
                 if let Ok(constant) =
-                    self.ecx.eval_mir_constant(&constant.const_, Some(constant.span), None)
+                    self.ecx.eval_mir_constant(&constant.const_, constant.span, None)
                 {
                     self.assign_constant(state, place, constant, &[]);
                 }
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index a3a2108787a..87dff49e0be 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -367,7 +367,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             Repeat(..) => return None,
 
             Constant { ref value, disambiguator: _ } => {
-                self.ecx.eval_mir_constant(value, None, None).ok()?
+                self.ecx.eval_mir_constant(value, DUMMY_SP, None).ok()?
             }
             Aggregate(kind, variant, ref fields) => {
                 let fields = fields
diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs
index 6629face940..116d6f48456 100644
--- a/compiler/rustc_mir_transform/src/jump_threading.rs
+++ b/compiler/rustc_mir_transform/src/jump_threading.rs
@@ -417,7 +417,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
             // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`.
             Operand::Constant(constant) => {
                 let constant =
-                    self.ecx.eval_mir_constant(&constant.const_, Some(constant.span), None).ok()?;
+                    self.ecx.eval_mir_constant(&constant.const_, constant.span, None).ok()?;
                 self.process_constant(bb, lhs, constant, state);
             }
             // Transfer the conditions on the copied rhs.
diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs
index 4bca437ea6f..f19b78a3a5c 100644
--- a/compiler/rustc_mir_transform/src/known_panics_lint.rs
+++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs
@@ -261,7 +261,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         // manually normalized.
         let val = self.tcx.try_normalize_erasing_regions(self.param_env, c.const_).ok()?;
 
-        self.use_ecx(|this| this.ecx.eval_mir_constant(&val, Some(c.span), None))?
+        self.use_ecx(|this| this.ecx.eval_mir_constant(&val, c.span, None))?
             .as_mplace_or_imm()
             .right()
     }
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 3e5ec323d27..c63bdff9a85 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -529,11 +529,11 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         // AddMovesForPackedDrops needs to run after drop
         // elaboration.
         &add_moves_for_packed_drops::AddMovesForPackedDrops,
-        // `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late,
+        // `AddRetag` needs to run after `ElaborateDrops` but before `ElaborateBoxDerefs`. Otherwise it should run fairly late,
         // but before optimizations begin.
+        &add_retag::AddRetag,
         &elaborate_box_derefs::ElaborateBoxDerefs,
         &coroutine::StateTransform,
-        &add_retag::AddRetag,
         &Lint(known_panics_lint::KnownPanicsLint),
     ];
     pm::run_passes_no_validate(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::Initial)));
@@ -633,12 +633,6 @@ fn optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> &Body<'_> {
 }
 
 fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> {
-    if tcx.intrinsic(did).is_some_and(|i| i.must_be_overridden) {
-        span_bug!(
-            tcx.def_span(did),
-            "this intrinsic must be overridden by the codegen backend, it has no meaningful body",
-        )
-    }
     if tcx.is_constructor(did.to_def_id()) {
         // There's no reason to run all of the MIR passes on constructors when
         // we can just output the MIR we want directly. This also saves const
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index dd8cb6127be..abe691ba0d8 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -828,7 +828,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
         // a codegen-time error). rustc stops after collection if there was an error, so this
         // ensures codegen never has to worry about failing consts.
         // (codegen relies on this and ICEs will happen if this is violated.)
-        let val = match const_.eval(self.tcx, param_env, Some(constant.span)) {
+        let val = match const_.eval(self.tcx, param_env, constant.span) {
             Ok(v) => v,
             Err(ErrorHandled::TooGeneric(..)) => span_bug!(
                 self.body.source_info(location).span,
@@ -1030,11 +1030,6 @@ fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) ->
         return false;
     }
 
-    if tcx.intrinsic(def_id).is_some_and(|i| i.must_be_overridden) {
-        // These are implemented by backends directly and have no meaningful body.
-        return false;
-    }
-
     if def_id.is_local() {
         // Local items cannot be referred to locally without monomorphizing them locally.
         return true;
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index 163d10d03f0..a9fcc134c13 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -731,8 +731,6 @@ impl<'a> Parser<'a> {
                             && matches!(args.output, ast::FnRetTy::Default(..))
                         {
                             self.psess.gated_spans.gate(sym::return_type_notation, span);
-                        } else {
-                            self.psess.gated_spans.gate(sym::associated_type_bounds, span);
                         }
                     }
                     let constraint =
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index eb2399f7a64..1254ae8cfc8 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -609,13 +609,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                     && !self.tcx.sess.target.is_like_wasm
                     && !self.tcx.sess.opts.actually_rustdoc
                 {
-                    let hir::Node::Item(item) = self.tcx.hir_node(hir_id) else {
-                        unreachable!();
-                    };
-                    let hir::ItemKind::Fn(sig, _, _) = item.kind else {
-                        // target is `Fn`
-                        unreachable!();
-                    };
+                    let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap();
 
                     self.dcx().emit_err(errors::LangItemWithTargetFeature {
                         attr_span: attr.span,
diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs
index 30a65d69c4e..8080216a252 100644
--- a/compiler/rustc_passes/src/check_const.rs
+++ b/compiler/rustc_passes/src/check_const.rs
@@ -155,16 +155,11 @@ impl<'tcx> CheckConstVisitor<'tcx> {
                 //
                 // FIXME(ecstaticmorse): Maybe this could be incorporated into `feature_err`? This
                 // is a pretty narrow case, however.
-                if tcx.sess.is_nightly_build() {
-                    for gate in missing_secondary {
-                        // FIXME: make this translatable
-                        #[allow(rustc::diagnostic_outside_of_impl)]
-                        #[allow(rustc::untranslatable_diagnostic)]
-                        err.help(format!(
-                            "add `#![feature({gate})]` to the crate attributes to enable"
-                        ));
-                    }
-                }
+                tcx.disabled_nightly_features(
+                    &mut err,
+                    def_id.map(|id| tcx.local_def_id_to_hir_id(id)),
+                    missing_secondary.into_iter().map(|gate| (String::new(), *gate)),
+                );
 
                 err.emit();
             }
diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs
index 2af5a54a0ca..f34e8d96f05 100644
--- a/compiler/rustc_passes/src/entry.rs
+++ b/compiler/rustc_passes/src/entry.rs
@@ -7,7 +7,6 @@ use rustc_hir::{ItemId, Node, CRATE_HIR_ID};
 use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::config::{sigpipe, CrateType, EntryFnType};
-use rustc_session::parse::feature_err;
 use rustc_span::symbol::sym;
 use rustc_span::{Span, Symbol};
 
@@ -133,16 +132,6 @@ fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId,
                 return None;
             }
 
-            if main_def.is_import && !tcx.features().imported_main {
-                let span = main_def.span;
-                feature_err(
-                    &tcx.sess,
-                    sym::imported_main,
-                    span,
-                    "using an imported function as entry point `main` is experimental",
-                )
-                .emit();
-            }
             return Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx, def_id) }));
         }
         no_main_err(tcx, visitor);
diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs
index bbb89576ef7..5b58d7f43b2 100644
--- a/compiler/rustc_pattern_analysis/src/constructor.rs
+++ b/compiler/rustc_pattern_analysis/src/constructor.rs
@@ -678,15 +678,19 @@ pub enum Constructor<Cx: PatCx> {
     Or,
     /// Wildcard pattern.
     Wildcard,
+    /// Never pattern. Only used in `WitnessPat`. An actual never pattern should be lowered as
+    /// `Wildcard`.
+    Never,
     /// Fake extra constructor for enums that aren't allowed to be matched exhaustively. Also used
-    /// for those types for which we cannot list constructors explicitly, like `f64` and `str`.
+    /// for those types for which we cannot list constructors explicitly, like `f64` and `str`. Only
+    /// used in `WitnessPat`.
     NonExhaustive,
-    /// Fake extra constructor for variants that should not be mentioned in diagnostics.
-    /// We use this for variants behind an unstable gate as well as
-    /// `#[doc(hidden)]` ones.
+    /// Fake extra constructor for variants that should not be mentioned in diagnostics. We use this
+    /// for variants behind an unstable gate as well as `#[doc(hidden)]` ones. Only used in
+    /// `WitnessPat`.
     Hidden,
     /// Fake extra constructor for constructors that are not seen in the matrix, as explained at the
-    /// top of the file.
+    /// top of the file. Only used for specialization.
     Missing,
     /// Fake extra constructor that indicates and empty field that is private. When we encounter one
     /// we skip the column entirely so we don't observe its emptiness. Only used for specialization.
@@ -708,6 +712,7 @@ impl<Cx: PatCx> Clone for Constructor<Cx> {
             Constructor::Str(value) => Constructor::Str(value.clone()),
             Constructor::Opaque(inner) => Constructor::Opaque(inner.clone()),
             Constructor::Or => Constructor::Or,
+            Constructor::Never => Constructor::Never,
             Constructor::Wildcard => Constructor::Wildcard,
             Constructor::NonExhaustive => Constructor::NonExhaustive,
             Constructor::Hidden => Constructor::Hidden,
@@ -1040,10 +1045,32 @@ impl<Cx: PatCx> ConstructorSet<Cx> {
                 // In a `MaybeInvalid` place even an empty pattern may be reachable. We therefore
                 // add a dummy empty constructor here, which will be ignored if the place is
                 // `ValidOnly`.
-                missing_empty.push(NonExhaustive);
+                missing_empty.push(Never);
             }
         }
 
         SplitConstructorSet { present, missing, missing_empty }
     }
+
+    /// Whether this set only contains empty constructors.
+    pub(crate) fn all_empty(&self) -> bool {
+        match self {
+            ConstructorSet::Bool
+            | ConstructorSet::Integers { .. }
+            | ConstructorSet::Ref
+            | ConstructorSet::Union
+            | ConstructorSet::Unlistable => false,
+            ConstructorSet::NoConstructors => true,
+            ConstructorSet::Struct { empty } => *empty,
+            ConstructorSet::Variants { variants, non_exhaustive } => {
+                !*non_exhaustive
+                    && variants
+                        .iter()
+                        .all(|visibility| matches!(visibility, VariantVisibility::Empty))
+            }
+            ConstructorSet::Slice { array_len, subtype_is_empty } => {
+                *subtype_is_empty && matches!(array_len, Some(1..))
+            }
+        }
+    }
 }
diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs
index e3667d44bc9..e7eed673c94 100644
--- a/compiler/rustc_pattern_analysis/src/pat.rs
+++ b/compiler/rustc_pattern_analysis/src/pat.rs
@@ -208,6 +208,7 @@ impl<Cx: PatCx> fmt::Debug for DeconstructedPat<Cx> {
                 }
                 Ok(())
             }
+            Never => write!(f, "!"),
             Wildcard | Missing | NonExhaustive | Hidden | PrivateUninhabited => {
                 write!(f, "_ : {:?}", pat.ty())
             }
@@ -311,18 +312,24 @@ impl<Cx: PatCx> WitnessPat<Cx> {
     pub(crate) fn new(ctor: Constructor<Cx>, fields: Vec<Self>, ty: Cx::Ty) -> Self {
         Self { ctor, fields, ty }
     }
-    pub(crate) fn wildcard(ty: Cx::Ty) -> Self {
-        Self::new(Wildcard, Vec::new(), ty)
+    /// Create a wildcard pattern for this type. If the type is empty, we create a `!` pattern.
+    pub(crate) fn wildcard(cx: &Cx, ty: Cx::Ty) -> Self {
+        let is_empty = cx.ctors_for_ty(&ty).is_ok_and(|ctors| ctors.all_empty());
+        let ctor = if is_empty { Never } else { Wildcard };
+        Self::new(ctor, Vec::new(), ty)
     }
 
     /// Construct a pattern that matches everything that starts with this constructor.
     /// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern
     /// `Some(_)`.
     pub(crate) fn wild_from_ctor(cx: &Cx, ctor: Constructor<Cx>, ty: Cx::Ty) -> Self {
+        if matches!(ctor, Wildcard) {
+            return Self::wildcard(cx, ty);
+        }
         let fields = cx
             .ctor_sub_tys(&ctor, &ty)
             .filter(|(_, PrivateUninhabitedField(skip))| !skip)
-            .map(|(ty, _)| Self::wildcard(ty))
+            .map(|(ty, _)| Self::wildcard(cx, ty))
             .collect();
         Self::new(ctor, fields, ty)
     }
@@ -334,6 +341,14 @@ impl<Cx: PatCx> WitnessPat<Cx> {
         &self.ty
     }
 
+    pub fn is_never_pattern(&self) -> bool {
+        match self.ctor() {
+            Never => true,
+            Or => self.fields.iter().all(|p| p.is_never_pattern()),
+            _ => self.fields.iter().any(|p| p.is_never_pattern()),
+        }
+    }
+
     pub fn iter_fields(&self) -> impl Iterator<Item = &WitnessPat<Cx>> {
         self.fields.iter()
     }
diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs
index fd51fbedeef..eedc00a5613 100644
--- a/compiler/rustc_pattern_analysis/src/rustc.rs
+++ b/compiler/rustc_pattern_analysis/src/rustc.rs
@@ -247,7 +247,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
                 _ => bug!("bad slice pattern {:?} {:?}", ctor, ty),
             },
             Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..)
-            | NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => &[],
+            | Never | NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => &[],
             Or => {
                 bug!("called `Fields::wildcards` on an `Or` ctor")
             }
@@ -275,7 +275,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
             Ref => 1,
             Slice(slice) => slice.arity(),
             Bool(..) | IntRange(..) | F32Range(..) | F64Range(..) | Str(..) | Opaque(..)
-            | NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => 0,
+            | Never | NonExhaustive | Hidden | Missing | PrivateUninhabited | Wildcard => 0,
             Or => bug!("The `Or` constructor doesn't have a fixed arity"),
         }
     }
@@ -824,7 +824,8 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
                 }
             }
             &Str(value) => PatKind::Constant { value },
-            Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild,
+            Never if self.tcx.features().never_patterns => PatKind::Never,
+            Never | Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild,
             Missing { .. } => bug!(
                 "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
                 `Missing` should have been processed in `apply_constructors`"
diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs
index bb822c611a1..b67b7d79d97 100644
--- a/compiler/rustc_serialize/src/lib.rs
+++ b/compiler/rustc_serialize/src/lib.rs
@@ -8,7 +8,7 @@
 #![doc(rust_logo)]
 #![allow(internal_features)]
 #![feature(rustdoc_internals)]
-#![feature(associated_type_bounds)]
+#![cfg_attr(bootstrap, feature(associated_type_bounds))]
 #![feature(const_option)]
 #![feature(core_intrinsics)]
 #![feature(generic_nonzero)]
diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl
index 42c681e4961..179fd79bef7 100644
--- a/compiler/rustc_session/messages.ftl
+++ b/compiler/rustc_session/messages.ftl
@@ -24,6 +24,9 @@ session_feature_diagnostic_for_issue =
 session_feature_diagnostic_help =
     add `#![feature({$feature})]` to the crate attributes to enable
 
+session_feature_diagnostic_suggestion =
+    add `#![feature({$feature})]` to the crate attributes to enable
+
 session_feature_suggest_upgrade_compiler =
     this compiler was built on {$date}; consider upgrading it if it is out of date
 
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index d523da1ad7e..cfbeac79e50 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -55,6 +55,18 @@ pub struct FeatureDiagnosticHelp {
 }
 
 #[derive(Subdiagnostic)]
+#[suggestion(
+    session_feature_diagnostic_suggestion,
+    applicability = "maybe-incorrect",
+    code = "#![feature({feature})]\n"
+)]
+pub struct FeatureDiagnosticSuggestion {
+    pub feature: Symbol,
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Subdiagnostic)]
 #[help(session_cli_feature_diagnostic_help)]
 pub struct CliFeatureDiagnosticHelp {
     pub feature: Symbol,
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index 398138d7e1f..5434bbe0b98 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -3,8 +3,8 @@
 
 use crate::config::{Cfg, CheckCfg};
 use crate::errors::{
-    CliFeatureDiagnosticHelp, FeatureDiagnosticForIssue, FeatureDiagnosticHelp, FeatureGateError,
-    SuggestUpgradeCompiler,
+    CliFeatureDiagnosticHelp, FeatureDiagnosticForIssue, FeatureDiagnosticHelp,
+    FeatureDiagnosticSuggestion, FeatureGateError, SuggestUpgradeCompiler,
 };
 use crate::lint::{
     builtin::UNSTABLE_SYNTAX_PRE_EXPANSION, BufferedEarlyLint, BuiltinLintDiag, Lint, LintId,
@@ -112,7 +112,7 @@ pub fn feature_err_issue(
     }
 
     let mut err = sess.psess.dcx.create_err(FeatureGateError { span, explain: explain.into() });
-    add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false);
+    add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None);
     err
 }
 
@@ -141,7 +141,7 @@ pub fn feature_warn_issue(
     explain: &'static str,
 ) {
     let mut err = sess.psess.dcx.struct_span_warn(span, explain);
-    add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false);
+    add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None);
 
     // Decorate this as a future-incompatibility lint as in rustc_middle::lint::lint_level
     let lint = UNSTABLE_SYNTAX_PRE_EXPANSION;
@@ -160,7 +160,7 @@ pub fn add_feature_diagnostics<G: EmissionGuarantee>(
     sess: &Session,
     feature: Symbol,
 ) {
-    add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language, false);
+    add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language, false, None);
 }
 
 /// Adds the diagnostics for a feature to an existing error.
@@ -175,6 +175,7 @@ pub fn add_feature_diagnostics_for_issue<G: EmissionGuarantee>(
     feature: Symbol,
     issue: GateIssue,
     feature_from_cli: bool,
+    inject_span: Option<Span>,
 ) {
     if let Some(n) = find_feature_issue(feature, issue) {
         err.subdiagnostic(sess.dcx(), FeatureDiagnosticForIssue { n });
@@ -184,6 +185,8 @@ pub fn add_feature_diagnostics_for_issue<G: EmissionGuarantee>(
     if sess.psess.unstable_features.is_nightly_build() {
         if feature_from_cli {
             err.subdiagnostic(sess.dcx(), CliFeatureDiagnosticHelp { feature });
+        } else if let Some(span) = inject_span {
+            err.subdiagnostic(sess.dcx(), FeatureDiagnosticSuggestion { feature, span });
         } else {
             err.subdiagnostic(sess.dcx(), FeatureDiagnosticHelp { feature });
         }
diff --git a/compiler/rustc_smir/src/rustc_smir/builder.rs b/compiler/rustc_smir/src/rustc_smir/builder.rs
index a13262cdcc4..0762016ef75 100644
--- a/compiler/rustc_smir/src/rustc_smir/builder.rs
+++ b/compiler/rustc_smir/src/rustc_smir/builder.rs
@@ -56,7 +56,7 @@ impl<'tcx> MutVisitor<'tcx> for BodyBuilder<'tcx> {
 
     fn visit_constant(&mut self, constant: &mut mir::ConstOperand<'tcx>, location: mir::Location) {
         let const_ = self.monomorphize(constant.const_);
-        let val = match const_.eval(self.tcx, ty::ParamEnv::reveal_all(), Some(constant.span)) {
+        let val = match const_.eval(self.tcx, ty::ParamEnv::reveal_all(), constant.span) {
             Ok(v) => v,
             Err(mir::interpret::ErrorHandled::Reported(..)) => return,
             Err(mir::interpret::ErrorHandled::TooGeneric(..)) => {
diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs
index 509f0def256..d427e5fb88d 100644
--- a/compiler/rustc_smir/src/rustc_smir/context.rs
+++ b/compiler/rustc_smir/src/rustc_smir/context.rs
@@ -566,7 +566,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
         let result = tcx.const_eval_instance(
             ParamEnv::reveal_all(),
             instance,
-            Some(tcx.def_span(instance.def_id())),
+            tcx.def_span(instance.def_id()),
         );
         result
             .map(|const_val| {
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index e43c9533382..63b950a2803 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1463,7 +1463,6 @@ symbols! {
         residual,
         result,
         resume,
-        retag_box_to_raw,
         return_position_impl_trait_in_trait,
         return_type_notation,
         rhs,
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
index fd3d62d1321..9fac0ea7715 100644
--- a/compiler/rustc_trait_selection/src/lib.rs
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -17,7 +17,7 @@
 #![allow(rustc::diagnostic_outside_of_impl)]
 #![allow(rustc::untranslatable_diagnostic)]
 #![feature(assert_matches)]
-#![feature(associated_type_bounds)]
+#![cfg_attr(bootstrap, feature(associated_type_bounds))]
 #![feature(associated_type_defaults)]
 #![feature(box_patterns)]
 #![feature(control_flow_enum)]
diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
index 67657c81cf6..e081a9100e2 100644
--- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs
+++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
@@ -21,8 +21,9 @@
 //! However, if `?fresh_var` ends up geteting equated to another type, we retry the
 //! `NormalizesTo` goal, at which point the opaque is actually defined.
 
-use super::{EvalCtxt, GoalSource};
+use super::EvalCtxt;
 use rustc_infer::traits::query::NoSolution;
+use rustc_infer::traits::solve::GoalSource;
 use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
 use rustc_middle::ty::{self, Ty};
 
@@ -121,10 +122,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             ty::TermKind::Const(_) => {
                 if let Some(alias) = term.to_alias_ty(self.tcx()) {
                     let term = self.next_term_infer_of_kind(term);
-                    self.add_goal(
-                        GoalSource::Misc,
-                        Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias, term }),
-                    );
+                    self.add_normalizes_to_goal(Goal::new(
+                        self.tcx(),
+                        param_env,
+                        ty::NormalizesTo { alias, term },
+                    ));
                     self.try_evaluate_added_goals()?;
                     Ok(Some(self.resolve_vars_if_possible(term)))
                 } else {
@@ -145,18 +147,25 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             return None;
         }
 
-        let ty::Alias(_, alias) = *ty.kind() else {
+        let ty::Alias(kind, alias) = *ty.kind() else {
             return Some(ty);
         };
 
         match self.commit_if_ok(|this| {
+            let tcx = this.tcx();
             let normalized_ty = this.next_ty_infer();
-            let normalizes_to_goal = Goal::new(
-                this.tcx(),
-                param_env,
-                ty::NormalizesTo { alias, term: normalized_ty.into() },
-            );
-            this.add_goal(GoalSource::Misc, normalizes_to_goal);
+            let normalizes_to = ty::NormalizesTo { alias, term: normalized_ty.into() };
+            match kind {
+                ty::AliasKind::Opaque => {
+                    // HACK: Unlike for associated types, `normalizes-to` for opaques
+                    // is currently not treated as a function. We do not erase the
+                    // expected term.
+                    this.add_goal(GoalSource::Misc, Goal::new(tcx, param_env, normalizes_to));
+                }
+                ty::AliasKind::Projection | ty::AliasKind::Inherent | ty::AliasKind::Weak => {
+                    this.add_normalizes_to_goal(Goal::new(tcx, param_env, normalizes_to))
+                }
+            }
             this.try_evaluate_added_goals()?;
             Ok(this.resolve_vars_if_possible(normalized_ty))
         }) {
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index 9c7fa5216d7..9f33dce2a6d 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -128,7 +128,7 @@ pub(super) trait GoalKind<'tcx>:
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx>;
 
-    /// A type is `Copy` or `Clone` if its components are `Sized`.
+    /// A type is `Sized` if its tail component is `Sized`.
     ///
     /// These components are given by built-in rules from
     /// [`structural_traits::instantiate_constituent_tys_for_sized_trait`].
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 2bfb86b592b..faf8c55c475 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
@@ -154,13 +154,25 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
             bug!("unexpected type `{ty}`")
         }
 
-        // impl Sized for (T1, T2, .., Tn) where T1: Sized, T2: Sized, .. Tn: Sized
-        ty::Tuple(tys) => Ok(tys.iter().map(ty::Binder::dummy).collect()),
-
-        // impl Sized for Adt where T: Sized forall T in field types
+        // impl Sized for ()
+        // impl Sized for (T1, T2, .., Tn) where Tn: Sized if n >= 1
+        ty::Tuple(tys) => Ok(tys.last().map_or_else(Vec::new, |&ty| vec![ty::Binder::dummy(ty)])),
+
+        // impl Sized for Adt<Args...> where sized_constraint(Adt)<Args...>: Sized
+        //   `sized_constraint(Adt)` is the deepest struct trail that can be determined
+        //   by the definition of `Adt`, independent of the generic args.
+        // impl Sized for Adt<Args...> if sized_constraint(Adt) == None
+        //   As a performance optimization, `sized_constraint(Adt)` can return `None`
+        //   if the ADTs definition implies that it is sized by for all possible args.
+        //   In this case, the builtin impl will have no nested subgoals. This is a
+        //   "best effort" optimization and `sized_constraint` may return `Some`, even
+        //   if the ADT is sized for all possible args.
         ty::Adt(def, args) => {
-            let sized_crit = def.sized_constraint(ecx.tcx());
-            Ok(sized_crit.iter_instantiated(ecx.tcx(), args).map(ty::Binder::dummy).collect())
+            if let Some(sized_crit) = def.sized_constraint(ecx.tcx()) {
+                Ok(vec![ty::Binder::dummy(sized_crit.instantiate(ecx.tcx(), args))])
+            } else {
+                Ok(vec![])
+            }
         }
     }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
index 251b0a193f1..619435d2e8d 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
@@ -9,6 +9,7 @@
 //!
 //! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
 use super::{CanonicalInput, Certainty, EvalCtxt, Goal};
+use crate::solve::eval_ctxt::NestedGoals;
 use crate::solve::{
     inspect, response_no_constraints_raw, CanonicalResponse, QueryResult, Response,
 };
@@ -19,6 +20,7 @@ use rustc_infer::infer::canonical::CanonicalVarValues;
 use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints};
 use rustc_infer::infer::resolve::EagerResolver;
 use rustc_infer::infer::{InferCtxt, InferOk};
+use rustc_infer::traits::solve::NestedNormalizationGoals;
 use rustc_middle::infer::canonical::Canonical;
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::traits::solve::{
@@ -28,6 +30,7 @@ use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable};
 use rustc_next_trait_solver::canonicalizer::{CanonicalizeMode, Canonicalizer};
 use rustc_span::DUMMY_SP;
+use std::assert_matches::assert_matches;
 use std::iter;
 use std::ops::Deref;
 
@@ -93,13 +96,31 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             previous call to `try_evaluate_added_goals!`"
         );
 
-        let certainty = certainty.unify_with(goals_certainty);
-
-        let var_values = self.var_values;
-        let external_constraints = self.compute_external_query_constraints()?;
-
+        // When normalizing, we've replaced the expected term with an unconstrained
+        // inference variable. This means that we dropped information which could
+        // have been important. We handle this by instead returning the nested goals
+        // to the caller, where they are then handled.
+        //
+        // As we return all ambiguous nested goals, we can ignore the certainty returned
+        // by `try_evaluate_added_goals()`.
+        let (certainty, normalization_nested_goals) = if self.is_normalizes_to_goal {
+            let NestedGoals { normalizes_to_goals, goals } = std::mem::take(&mut self.nested_goals);
+            if cfg!(debug_assertions) {
+                assert!(normalizes_to_goals.is_empty());
+                if goals.is_empty() {
+                    assert_matches!(goals_certainty, Certainty::Yes);
+                }
+            }
+            (certainty, NestedNormalizationGoals(goals))
+        } else {
+            let certainty = certainty.unify_with(goals_certainty);
+            (certainty, NestedNormalizationGoals::empty())
+        };
+
+        let external_constraints =
+            self.compute_external_query_constraints(normalization_nested_goals)?;
         let (var_values, mut external_constraints) =
-            (var_values, external_constraints).fold_with(&mut EagerResolver::new(self.infcx));
+            (self.var_values, external_constraints).fold_with(&mut EagerResolver::new(self.infcx));
         // Remove any trivial region constraints once we've resolved regions
         external_constraints
             .region_constraints
@@ -146,6 +167,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
     #[instrument(level = "debug", skip(self), ret)]
     fn compute_external_query_constraints(
         &self,
+        normalization_nested_goals: NestedNormalizationGoals<'tcx>,
     ) -> Result<ExternalConstraintsData<'tcx>, NoSolution> {
         // We only check for leaks from universes which were entered inside
         // of the query.
@@ -176,7 +198,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a)
         });
 
-        Ok(ExternalConstraintsData { region_constraints, opaque_types })
+        Ok(ExternalConstraintsData { region_constraints, opaque_types, normalization_nested_goals })
     }
 
     /// After calling a canonical query, we apply the constraints returned
@@ -185,13 +207,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
     /// This happens in three steps:
     /// - we instantiate the bound variables of the query response
     /// - we unify the `var_values` of the response with the `original_values`
-    /// - we apply the `external_constraints` returned by the query
+    /// - we apply the `external_constraints` returned by the query, returning
+    ///   the `normalization_nested_goals`
     pub(super) fn instantiate_and_apply_query_response(
         &mut self,
         param_env: ty::ParamEnv<'tcx>,
         original_values: Vec<ty::GenericArg<'tcx>>,
         response: CanonicalResponse<'tcx>,
-    ) -> Certainty {
+    ) -> (NestedNormalizationGoals<'tcx>, Certainty) {
         let instantiation = Self::compute_query_response_instantiation_values(
             self.infcx,
             &original_values,
@@ -203,11 +226,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
 
         Self::unify_query_var_values(self.infcx, param_env, &original_values, var_values);
 
-        let ExternalConstraintsData { region_constraints, opaque_types } =
-            external_constraints.deref();
+        let ExternalConstraintsData {
+            region_constraints,
+            opaque_types,
+            normalization_nested_goals,
+        } = external_constraints.deref();
         self.register_region_constraints(region_constraints);
         self.register_new_opaque_types(param_env, opaque_types);
-        certainty
+        (normalization_nested_goals.clone(), certainty)
     }
 
     /// This returns the canoncial variable values to instantiate the bound variables of
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs
index 67b6801059a..c8f9a461adf 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/commit_if_ok.rs
@@ -11,6 +11,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
             infcx: self.infcx,
             variables: self.variables,
             var_values: self.var_values,
+            is_normalizes_to_goal: self.is_normalizes_to_goal,
             predefined_opaques_in_body: self.predefined_opaques_in_body,
             max_input_universe: self.max_input_universe,
             search_graph: self.search_graph,
@@ -25,6 +26,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
                 infcx: _,
                 variables: _,
                 var_values: _,
+                is_normalizes_to_goal: _,
                 predefined_opaques_in_body: _,
                 max_input_universe: _,
                 search_graph: _,
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 3b858cb449f..a0fe6eca0fc 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
@@ -7,14 +7,14 @@ use rustc_infer::infer::{
     BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt,
 };
 use rustc_infer::traits::query::NoSolution;
-use rustc_infer::traits::solve::MaybeCause;
+use rustc_infer::traits::solve::{MaybeCause, NestedNormalizationGoals};
 use rustc_infer::traits::ObligationCause;
 use rustc_middle::infer::canonical::CanonicalVarInfos;
 use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
 use rustc_middle::traits::solve::inspect;
 use rustc_middle::traits::solve::{
-    CanonicalInput, CanonicalResponse, Certainty, IsNormalizesToHack, PredefinedOpaques,
-    PredefinedOpaquesData, QueryResult,
+    CanonicalInput, CanonicalResponse, Certainty, PredefinedOpaques, PredefinedOpaquesData,
+    QueryResult,
 };
 use rustc_middle::traits::specialization_graph;
 use rustc_middle::ty::{
@@ -61,6 +61,14 @@ pub struct EvalCtxt<'a, 'tcx> {
     /// The variable info for the `var_values`, only used to make an ambiguous response
     /// with no constraints.
     variables: CanonicalVarInfos<'tcx>,
+    /// Whether we're currently computing a `NormalizesTo` goal. Unlike other goals,
+    /// `NormalizesTo` goals act like functions with the expected term always being
+    /// fully unconstrained. This would weaken inference however, as the nested goals
+    /// never get the inference constraints from the actual normalized-to type. Because
+    /// of this we return any ambiguous nested goals from `NormalizesTo` to the caller
+    /// when then adds these to its own context. The caller is always an `AliasRelate`
+    /// goal so this never leaks out of the solver.
+    is_normalizes_to_goal: bool,
     pub(super) var_values: CanonicalVarValues<'tcx>,
 
     predefined_opaques_in_body: PredefinedOpaques<'tcx>,
@@ -91,9 +99,9 @@ pub struct EvalCtxt<'a, 'tcx> {
     pub(super) inspect: ProofTreeBuilder<'tcx>,
 }
 
-#[derive(Debug, Clone)]
+#[derive(Default, Debug, Clone)]
 pub(super) struct NestedGoals<'tcx> {
-    /// This normalizes-to goal that is treated specially during the evaluation
+    /// These normalizes-to goals are treated specially during the evaluation
     /// loop. In each iteration we take the RHS of the projection, replace it with
     /// a fresh inference variable, and only after evaluating that goal do we
     /// equate the fresh inference variable with the actual RHS of the predicate.
@@ -101,26 +109,24 @@ pub(super) struct NestedGoals<'tcx> {
     /// This is both to improve caching, and to avoid using the RHS of the
     /// projection predicate to influence the normalizes-to candidate we select.
     ///
-    /// This is not a 'real' nested goal. We must not forget to replace the RHS
-    /// with a fresh inference variable when we evaluate this goal. That can result
-    /// in a trait solver cycle. This would currently result in overflow but can be
-    /// can be unsound with more powerful coinduction in the future.
-    pub(super) normalizes_to_hack_goal: Option<Goal<'tcx, ty::NormalizesTo<'tcx>>>,
+    /// Forgetting to replace the RHS with a fresh inference variable when we evaluate
+    /// this goal results in an ICE..
+    pub(super) normalizes_to_goals: Vec<Goal<'tcx, ty::NormalizesTo<'tcx>>>,
     /// The rest of the goals which have not yet processed or remain ambiguous.
     pub(super) goals: Vec<(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>,
 }
 
 impl<'tcx> NestedGoals<'tcx> {
     pub(super) fn new() -> Self {
-        Self { normalizes_to_hack_goal: None, goals: Vec::new() }
+        Self { normalizes_to_goals: Vec::new(), goals: Vec::new() }
     }
 
     pub(super) fn is_empty(&self) -> bool {
-        self.normalizes_to_hack_goal.is_none() && self.goals.is_empty()
+        self.normalizes_to_goals.is_empty() && self.goals.is_empty()
     }
 
     pub(super) fn extend(&mut self, other: NestedGoals<'tcx>) {
-        assert_eq!(other.normalizes_to_hack_goal, None);
+        self.normalizes_to_goals.extend(other.normalizes_to_goals);
         self.goals.extend(other.goals)
     }
 }
@@ -155,6 +161,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
         self.search_graph.solver_mode()
     }
 
+    pub(super) fn set_is_normalizes_to_goal(&mut self) {
+        self.is_normalizes_to_goal = true;
+    }
+
     /// Creates a root evaluation context and search graph. This should only be
     /// used from outside of any evaluation, and other methods should be preferred
     /// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]).
@@ -167,8 +177,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
         let mut search_graph = search_graph::SearchGraph::new(mode);
 
         let mut ecx = EvalCtxt {
-            search_graph: &mut search_graph,
             infcx,
+            search_graph: &mut search_graph,
             nested_goals: NestedGoals::new(),
             inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree),
 
@@ -180,6 +190,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
             max_input_universe: ty::UniverseIndex::ROOT,
             variables: ty::List::empty(),
             var_values: CanonicalVarValues::dummy(),
+            is_normalizes_to_goal: false,
             tainted: Ok(()),
         };
         let result = f(&mut ecx);
@@ -233,6 +244,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
             infcx,
             variables: canonical_input.variables,
             var_values,
+            is_normalizes_to_goal: false,
             predefined_opaques_in_body: input.predefined_opaques_in_body,
             max_input_universe: canonical_input.max_universe,
             search_graph,
@@ -319,6 +331,27 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
         source: GoalSource,
         goal: Goal<'tcx, ty::Predicate<'tcx>>,
     ) -> Result<(bool, Certainty), NoSolution> {
+        let (normalization_nested_goals, has_changed, certainty) =
+            self.evaluate_goal_raw(goal_evaluation_kind, source, goal)?;
+        assert!(normalization_nested_goals.is_empty());
+        Ok((has_changed, certainty))
+    }
+
+    /// Recursively evaluates `goal`, returning the nested goals in case
+    /// the nested goal is a `NormalizesTo` goal.
+    ///
+    /// As all other goal kinds do not return any nested goals and
+    /// `NormalizesTo` is only used by `AliasRelate`, all other callsites
+    /// should use [`EvalCtxt::evaluate_goal`] which discards that empty
+    /// storage.
+    // FIXME(-Znext-solver=coinduction): `_source` is currently unused but will
+    // be necessary once we implement the new coinduction approach.
+    fn evaluate_goal_raw(
+        &mut self,
+        goal_evaluation_kind: GoalEvaluationKind,
+        _source: GoalSource,
+        goal: Goal<'tcx, ty::Predicate<'tcx>>,
+    ) -> Result<(NestedNormalizationGoals<'tcx>, bool, Certainty), NoSolution> {
         let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
         let mut goal_evaluation =
             self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind);
@@ -336,12 +369,12 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
             Ok(response) => response,
         };
 
-        let (certainty, has_changed) = self.instantiate_response_discarding_overflow(
-            goal.param_env,
-            source,
-            orig_values,
-            canonical_response,
-        );
+        let (normalization_nested_goals, certainty, has_changed) = self
+            .instantiate_response_discarding_overflow(
+                goal.param_env,
+                orig_values,
+                canonical_response,
+            );
         self.inspect.goal_evaluation(goal_evaluation);
         // FIXME: We previously had an assert here that checked that recomputing
         // a goal after applying its constraints did not change its response.
@@ -353,47 +386,25 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
         // Once we have decided on how to handle trait-system-refactor-initiative#75,
         // we should re-add an assert here.
 
-        Ok((has_changed, certainty))
+        Ok((normalization_nested_goals, has_changed, certainty))
     }
 
     fn instantiate_response_discarding_overflow(
         &mut self,
         param_env: ty::ParamEnv<'tcx>,
-        source: GoalSource,
         original_values: Vec<ty::GenericArg<'tcx>>,
         response: CanonicalResponse<'tcx>,
-    ) -> (Certainty, bool) {
-        // The old solver did not evaluate nested goals when normalizing.
-        // It returned the selection constraints allowing a `Projection`
-        // obligation to not hold in coherence while avoiding the fatal error
-        // from overflow.
-        //
-        // We match this behavior here by considering all constraints
-        // from nested goals which are not from where-bounds. We will already
-        // need to track which nested goals are required by impl where-bounds
-        // for coinductive cycles, so we simply reuse that here.
-        //
-        // While we could consider overflow constraints in more cases, this should
-        // not be necessary for backcompat and results in better perf. It also
-        // avoids a potential inconsistency which would otherwise require some
-        // tracking for root goals as well. See #119071 for an example.
-        let keep_overflow_constraints = || {
-            self.search_graph.current_goal_is_normalizes_to()
-                && source != GoalSource::ImplWhereBound
-        };
-
-        if let Certainty::Maybe(MaybeCause::Overflow { .. }) = response.value.certainty
-            && !keep_overflow_constraints()
-        {
-            return (response.value.certainty, false);
+    ) -> (NestedNormalizationGoals<'tcx>, Certainty, bool) {
+        if let Certainty::Maybe(MaybeCause::Overflow { .. }) = response.value.certainty {
+            return (NestedNormalizationGoals::empty(), response.value.certainty, false);
         }
 
         let has_changed = !response.value.var_values.is_identity_modulo_regions()
             || !response.value.external_constraints.opaque_types.is_empty();
 
-        let certainty =
+        let (normalization_nested_goals, certainty) =
             self.instantiate_and_apply_query_response(param_env, original_values, response);
-        (certainty, has_changed)
+        (normalization_nested_goals, certainty, has_changed)
     }
 
     fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> {
@@ -496,7 +507,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
     /// Goals for the next step get directly added to the nested goals of the `EvalCtxt`.
     fn evaluate_added_goals_step(&mut self) -> Result<Option<Certainty>, NoSolution> {
         let tcx = self.tcx();
-        let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new());
+        let mut goals = core::mem::take(&mut self.nested_goals);
 
         self.inspect.evaluate_added_goals_loop_start();
 
@@ -508,7 +519,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
 
         // If this loop did not result in any progress, what's our final certainty.
         let mut unchanged_certainty = Some(Certainty::Yes);
-        if let Some(goal) = goals.normalizes_to_hack_goal.take() {
+        for goal in goals.normalizes_to_goals {
             // Replace the goal with an unconstrained infer var, so the
             // RHS does not affect projection candidate assembly.
             let unconstrained_rhs = self.next_term_infer_of_kind(goal.predicate.term);
@@ -517,11 +528,13 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
                 ty::NormalizesTo { alias: goal.predicate.alias, term: unconstrained_rhs },
             );
 
-            let (_, certainty) = self.evaluate_goal(
-                GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::Yes },
+            let (NestedNormalizationGoals(nested_goals), _, certainty) = self.evaluate_goal_raw(
+                GoalEvaluationKind::Nested,
                 GoalSource::Misc,
                 unconstrained_goal,
             )?;
+            // Add the nested goals from normalization to our own nested goals.
+            goals.goals.extend(nested_goals);
 
             // Finally, equate the goal's RHS with the unconstrained var.
             // We put the nested goals from this into goals instead of
@@ -536,27 +549,23 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
             // looking at the "has changed" return from evaluate_goal,
             // because we expect the `unconstrained_rhs` part of the predicate
             // to have changed -- that means we actually normalized successfully!
-            if goal.predicate.alias != self.resolve_vars_if_possible(goal.predicate.alias) {
+            let with_resolved_vars = self.resolve_vars_if_possible(goal);
+            if goal.predicate.alias != with_resolved_vars.predicate.alias {
                 unchanged_certainty = None;
             }
 
             match certainty {
                 Certainty::Yes => {}
                 Certainty::Maybe(_) => {
-                    // We need to resolve vars here so that we correctly
-                    // deal with `has_changed` in the next iteration.
-                    self.set_normalizes_to_hack_goal(self.resolve_vars_if_possible(goal));
+                    self.nested_goals.normalizes_to_goals.push(with_resolved_vars);
                     unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty));
                 }
             }
         }
 
-        for (source, goal) in goals.goals.drain(..) {
-            let (has_changed, certainty) = self.evaluate_goal(
-                GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::No },
-                source,
-                goal,
-            )?;
+        for (source, goal) in goals.goals {
+            let (has_changed, certainty) =
+                self.evaluate_goal(GoalEvaluationKind::Nested, source, goal)?;
             if has_changed {
                 unchanged_certainty = None;
             }
@@ -968,7 +977,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         ty: Ty<'tcx>,
     ) -> Option<ty::Const<'tcx>> {
         use rustc_middle::mir::interpret::ErrorHandled;
-        match self.infcx.try_const_eval_resolve(param_env, unevaluated, ty, None) {
+        match self.infcx.try_const_eval_resolve(param_env, unevaluated, ty, DUMMY_SP) {
             Ok(ct) => Some(ct),
             Err(ErrorHandled::Reported(e, _)) => {
                 Some(ty::Const::new_error(self.tcx(), e.into(), ty))
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 91fd48807a4..5b1124e8b9f 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
@@ -24,6 +24,7 @@ where
             infcx: outer_ecx.infcx,
             variables: outer_ecx.variables,
             var_values: outer_ecx.var_values,
+            is_normalizes_to_goal: outer_ecx.is_normalizes_to_goal,
             predefined_opaques_in_body: outer_ecx.predefined_opaques_in_body,
             max_input_universe: outer_ecx.max_input_universe,
             search_graph: outer_ecx.search_graph,
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
index 3262d64cb7d..e0c7804b6db 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs
@@ -58,12 +58,13 @@ impl<'tcx> InferCtxt<'tcx> {
                 }
 
                 let candidate = candidates.pop().unwrap();
-                let certainty = ecx.instantiate_and_apply_query_response(
-                    trait_goal.param_env,
-                    orig_values,
-                    candidate.result,
-                );
-
+                let (normalization_nested_goals, certainty) = ecx
+                    .instantiate_and_apply_query_response(
+                        trait_goal.param_env,
+                        orig_values,
+                        candidate.result,
+                    );
+                assert!(normalization_nested_goals.is_empty());
                 Ok(Some((candidate, certainty)))
             });
 
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
index 9e3e6a4676e..cfec2e9bbf3 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
@@ -70,7 +70,19 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
                     instantiated_goals.push(goal);
                 }
 
-                for &goal in &instantiated_goals {
+                for goal in instantiated_goals.iter().copied() {
+                    // We need to be careful with `NormalizesTo` goals as the
+                    // expected term has to be replaced with an unconstrained
+                    // inference variable.
+                    if let Some(kind) = goal.predicate.kind().no_bound_vars()
+                        && let ty::PredicateKind::NormalizesTo(predicate) = kind
+                        && !predicate.alias.is_opaque(infcx.tcx)
+                    {
+                        // FIXME: We currently skip these goals as
+                        // `fn evaluate_root_goal` ICEs if there are any
+                        // `NestedNormalizationGoals`.
+                        continue;
+                    };
                     let (_, proof_tree) = infcx.evaluate_root_goal(goal, GenerateProofTree::Yes);
                     let proof_tree = proof_tree.unwrap();
                     try_visit!(visitor.visit_goal(&InspectGoal::new(
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/build.rs b/compiler/rustc_trait_selection/src/solve/inspect/build.rs
index f7b310a7abe..4da999f2406 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/build.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/build.rs
@@ -7,7 +7,7 @@ use std::mem;
 
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::traits::solve::{
-    CanonicalInput, Certainty, Goal, GoalSource, IsNormalizesToHack, QueryInput, QueryResult,
+    CanonicalInput, Certainty, Goal, GoalSource, QueryInput, QueryResult,
 };
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_session::config::DumpSolverProofTree;
@@ -97,9 +97,7 @@ impl<'tcx> WipGoalEvaluation<'tcx> {
                 WipGoalEvaluationKind::Root { orig_values } => {
                     inspect::GoalEvaluationKind::Root { orig_values }
                 }
-                WipGoalEvaluationKind::Nested { is_normalizes_to_hack } => {
-                    inspect::GoalEvaluationKind::Nested { is_normalizes_to_hack }
-                }
+                WipGoalEvaluationKind::Nested => inspect::GoalEvaluationKind::Nested,
             },
             evaluation: self.evaluation.unwrap().finalize(),
         }
@@ -109,7 +107,7 @@ impl<'tcx> WipGoalEvaluation<'tcx> {
 #[derive(Eq, PartialEq, Debug)]
 pub(in crate::solve) enum WipGoalEvaluationKind<'tcx> {
     Root { orig_values: Vec<ty::GenericArg<'tcx>> },
-    Nested { is_normalizes_to_hack: IsNormalizesToHack },
+    Nested,
 }
 
 #[derive(Eq, PartialEq)]
@@ -305,9 +303,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
                 solve::GoalEvaluationKind::Root => {
                     WipGoalEvaluationKind::Root { orig_values: orig_values.to_vec() }
                 }
-                solve::GoalEvaluationKind::Nested { is_normalizes_to_hack } => {
-                    WipGoalEvaluationKind::Nested { is_normalizes_to_hack }
-                }
+                solve::GoalEvaluationKind::Nested => WipGoalEvaluationKind::Nested,
             },
             evaluation: None,
         })
@@ -419,6 +415,17 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
         }
     }
 
+    pub fn add_normalizes_to_goal(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
+    ) {
+        if ecx.inspect.is_noop() {
+            return;
+        }
+
+        Self::add_goal(ecx, GoalSource::Misc, goal.with(ecx.tcx(), goal.predicate));
+    }
+
     pub fn add_goal(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         source: GoalSource,
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index 0bf28f520a4..8294a8a67b1 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -18,8 +18,7 @@ use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues};
 use rustc_infer::traits::query::NoSolution;
 use rustc_middle::infer::canonical::CanonicalVarInfos;
 use rustc_middle::traits::solve::{
-    CanonicalResponse, Certainty, ExternalConstraintsData, Goal, GoalSource, IsNormalizesToHack,
-    QueryResult, Response,
+    CanonicalResponse, Certainty, ExternalConstraintsData, Goal, GoalSource, QueryResult, Response,
 };
 use rustc_middle::ty::{self, AliasRelationDirection, Ty, TyCtxt, UniverseIndex};
 use rustc_middle::ty::{
@@ -69,7 +68,7 @@ enum SolverMode {
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
 enum GoalEvaluationKind {
     Root,
-    Nested { is_normalizes_to_hack: IsNormalizesToHack },
+    Nested,
 }
 
 #[extension(trait CanonicalResponseExt)]
@@ -202,12 +201,9 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
 
 impl<'tcx> EvalCtxt<'_, 'tcx> {
     #[instrument(level = "debug", skip(self))]
-    fn set_normalizes_to_hack_goal(&mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) {
-        assert!(
-            self.nested_goals.normalizes_to_hack_goal.is_none(),
-            "attempted to set the projection eq hack goal when one already exists"
-        );
-        self.nested_goals.normalizes_to_hack_goal = Some(goal);
+    fn add_normalizes_to_goal(&mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) {
+        inspect::ProofTreeBuilder::add_normalizes_to_goal(self, goal);
+        self.nested_goals.normalizes_to_goals.push(goal);
     }
 
     #[instrument(level = "debug", skip(self))]
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs
index 911462f4b9a..37d56452893 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs
@@ -16,7 +16,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                 .no_bound_vars()
                 .expect("const ty should not rely on other generics"),
         ) {
-            self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?;
+            self.instantiate_normalizes_to_term(goal, normalized_const.into());
             self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
         } else {
             self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs
index 52d2fe1e3ec..d60490bce44 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs
@@ -16,7 +16,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
     ) -> QueryResult<'tcx> {
         let tcx = self.tcx();
         let inherent = goal.predicate.alias;
-        let expected = goal.predicate.term.ty().expect("inherent consts are treated separately");
 
         let impl_def_id = tcx.parent(inherent.def_id);
         let impl_args = self.fresh_args_for_item(impl_def_id);
@@ -30,12 +29,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
 
         // Equate IAT with the RHS of the project goal
         let inherent_args = inherent.rebase_inherent_args_onto_impl(impl_args, tcx);
-        self.eq(
-            goal.param_env,
-            expected,
-            tcx.type_of(inherent.def_id).instantiate(tcx, inherent_args),
-        )
-        .expect("expected goal term to be fully unconstrained");
 
         // Check both where clauses on the impl and IAT
         //
@@ -51,6 +44,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                 .map(|(pred, _)| goal.with(tcx, pred)),
         );
 
+        let normalized = tcx.type_of(inherent.def_id).instantiate(tcx, inherent_args);
+        self.instantiate_normalizes_to_term(goal, normalized.into());
         self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
     }
 }
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 a45c1c34410..d24bec5a766 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
@@ -31,32 +31,19 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         goal: Goal<'tcx, NormalizesTo<'tcx>>,
     ) -> QueryResult<'tcx> {
         let def_id = goal.predicate.def_id();
+        let def_kind = self.tcx().def_kind(def_id);
+        match def_kind {
+            DefKind::OpaqueTy => return self.normalize_opaque_type(goal),
+            _ => self.set_is_normalizes_to_goal(),
+        }
+
+        debug_assert!(self.term_is_fully_unconstrained(goal));
         match self.tcx().def_kind(def_id) {
             DefKind::AssocTy | DefKind::AssocConst => {
                 match self.tcx().associated_item(def_id).container {
                     ty::AssocItemContainer::TraitContainer => {
-                        // To only compute normalization once for each projection we only
-                        // assemble normalization candidates if the expected term is an
-                        // unconstrained inference variable.
-                        //
-                        // Why: For better cache hits, since if we have an unconstrained RHS then
-                        // there are only as many cache keys as there are (canonicalized) alias
-                        // types in each normalizes-to goal. This also weakens inference in a
-                        // forwards-compatible way so we don't use the value of the RHS term to
-                        // affect candidate assembly for projections.
-                        //
-                        // E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal
-                        // `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for
-                        // `U` and equate it with `u32`. This means that we don't need a separate
-                        // projection cache in the solver, since we're piggybacking off of regular
-                        // goal caching.
-                        if self.term_is_fully_unconstrained(goal) {
-                            let candidates = self.assemble_and_evaluate_candidates(goal);
-                            self.merge_candidates(candidates)
-                        } else {
-                            self.set_normalizes_to_hack_goal(goal);
-                            self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-                        }
+                        let candidates = self.assemble_and_evaluate_candidates(goal);
+                        self.merge_candidates(candidates)
                     }
                     ty::AssocItemContainer::ImplContainer => {
                         self.normalize_inherent_associated_type(goal)
@@ -64,9 +51,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                 }
             }
             DefKind::AnonConst => self.normalize_anon_const(goal),
-            DefKind::OpaqueTy => self.normalize_opaque_type(goal),
             DefKind::TyAlias => self.normalize_weak_type(goal),
-            kind => bug!("unknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)),
+            kind => bug!("unknown DefKind {} in normalizes-to goal: {goal:#?}", kind.descr(def_id)),
         }
     }
 
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs
index 9f91c02c1ab..13af5068b6c 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs
@@ -15,10 +15,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
     ) -> QueryResult<'tcx> {
         let tcx = self.tcx();
         let weak_ty = goal.predicate.alias;
-        let expected = goal.predicate.term.ty().expect("no such thing as a const alias");
-
-        let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, weak_ty.args);
-        self.eq(goal.param_env, expected, actual)?;
 
         // Check where clauses
         self.add_goals(
@@ -30,6 +26,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                 .map(|pred| goal.with(tcx, pred)),
         );
 
+        let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, weak_ty.args);
+        self.instantiate_normalizes_to_term(goal, actual.into());
+
         self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
     }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/search_graph.rs b/compiler/rustc_trait_selection/src/solve/search_graph.rs
index 07a8aca85a0..a48b2f2478b 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph.rs
@@ -10,7 +10,6 @@ use rustc_index::IndexVec;
 use rustc_middle::dep_graph::dep_kinds;
 use rustc_middle::traits::solve::CacheData;
 use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult};
-use rustc_middle::ty;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Limit;
 use std::mem;
@@ -175,15 +174,6 @@ impl<'tcx> SearchGraph<'tcx> {
         }
     }
 
-    pub(super) fn current_goal_is_normalizes_to(&self) -> bool {
-        self.stack.raw.last().map_or(false, |e| {
-            matches!(
-                e.input.value.goal.predicate.kind().skip_binder(),
-                ty::PredicateKind::NormalizesTo(..)
-            )
-        })
-    }
-
     /// Returns the remaining depth allowed for nested goals.
     ///
     /// This is generally simply one less than the current depth.
diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
index fbf96833187..0796ffcbc45 100644
--- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs
+++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -786,7 +786,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
                             match selcx.infcx.const_eval_resolve(
                                 obligation.param_env,
                                 unevaluated,
-                                Some(obligation.cause.span),
+                                obligation.cause.span,
                             ) {
                                 Ok(Some(valtree)) => Ok(ty::Const::new_value(selcx.tcx(),valtree, c.ty())),
                                 Ok(None) => {
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index 7f0f9a12d6a..9ca1dd4557d 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -68,7 +68,7 @@ pub fn is_const_evaluatable<'tcx>(
                 tcx.dcx().span_bug(span, "evaluating `ConstKind::Expr` is not currently supported");
             }
             ty::ConstKind::Unevaluated(uv) => {
-                let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
+                let concrete = infcx.const_eval_resolve(param_env, uv, span);
                 match concrete {
                     Err(ErrorHandled::TooGeneric(_)) => {
                         Err(NotConstEvaluatable::Error(infcx.dcx().span_delayed_bug(
@@ -99,7 +99,7 @@ pub fn is_const_evaluatable<'tcx>(
         // and hopefully soon change this to an error.
         //
         // See #74595 for more details about this.
-        let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
+        let concrete = infcx.const_eval_resolve(param_env, uv, span);
         match concrete {
             // If we're evaluating a generic foreign constant, under a nightly compiler while
             // the current crate does not enable `feature(generic_const_exprs)`, abort
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 067ca883bd8..ee390d9c9f0 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -194,8 +194,7 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>(
         sugg.extend(ty_spans.into_iter().map(|s| (s, type_param_name.to_string())));
 
         // Suggest `fn foo<T: Trait>(t: T) where <T as Trait>::A: Bound`.
-        // FIXME: once `#![feature(associated_type_bounds)]` is stabilized, we should suggest
-        // `fn foo(t: impl Trait<A: Bound>)` instead.
+        // FIXME: we should suggest `fn foo(t: impl Trait<A: Bound>)` instead.
         err.multipart_suggestion(
             "introduce a type parameter with a trait bound instead of using `impl Trait`",
             sugg,
@@ -3510,9 +3509,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             }
             ObligationCauseCode::TrivialBound => {
                 err.help("see issue #48214");
-                if tcx.sess.opts.unstable_features.is_nightly_build() {
-                    err.help("add `#![feature(trivial_bounds)]` to the crate attributes to enable");
-                }
+                tcx.disabled_nightly_features(
+                    err,
+                    Some(tcx.local_def_id_to_hir_id(body_id)),
+                    [(String::new(), sym::trivial_bounds)],
+                );
             }
             ObligationCauseCode::OpaqueReturnType(expr_info) => {
                 if let Some((expr_ty, expr_span)) = expr_info {
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 2fd64f474d5..34c891d400e 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -600,7 +600,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                                 obligation.param_env,
                                 unevaluated,
                                 c.ty(),
-                                Some(obligation.cause.span),
+                                obligation.cause.span,
                             ) {
                                 Ok(val) => Ok(val),
                                 Err(e) => {
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs
index 14f14ae6e2e..63289746f5e 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs
@@ -20,14 +20,11 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> {
         // such cases.
         if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_ref)) =
             key.value.predicate.kind().skip_binder()
+            && let Some(sized_def_id) = tcx.lang_items().sized_trait()
+            && trait_ref.def_id() == sized_def_id
+            && trait_ref.self_ty().is_trivially_sized(tcx)
         {
-            if let Some(sized_def_id) = tcx.lang_items().sized_trait() {
-                if trait_ref.def_id() == sized_def_id {
-                    if trait_ref.self_ty().is_trivially_sized(tcx) {
-                        return Some(());
-                    }
-                }
-            }
+            return Some(());
         }
 
         if let ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) =
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index d10fe6a9490..6460613d7d5 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -949,7 +949,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                                 obligation.param_env,
                                 unevaluated,
                                 c.ty(),
-                                Some(obligation.cause.span),
+                                obligation.cause.span,
                             ) {
                                 Ok(val) => Ok(val),
                                 Err(e) => Err(e),
@@ -2123,13 +2123,14 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
             ),
 
             ty::Adt(def, args) => {
-                let sized_crit = def.sized_constraint(self.tcx());
-                // (*) binder moved here
-                Where(
-                    obligation
-                        .predicate
-                        .rebind(sized_crit.iter_instantiated(self.tcx(), args).collect()),
-                )
+                if let Some(sized_crit) = def.sized_constraint(self.tcx()) {
+                    // (*) binder moved here
+                    Where(
+                        obligation.predicate.rebind(vec![sized_crit.instantiate(self.tcx(), args)]),
+                    )
+                } else {
+                    Where(ty::Binder::dummy(Vec::new()))
+                }
             }
 
             ty::Alias(..) | ty::Param(_) | ty::Placeholder(..) => None,
diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs
index e871c4659b4..b6f937ebe47 100644
--- a/compiler/rustc_transmute/src/lib.rs
+++ b/compiler/rustc_transmute/src/lib.rs
@@ -89,6 +89,7 @@ mod rustc {
     use rustc_middle::ty::Ty;
     use rustc_middle::ty::TyCtxt;
     use rustc_middle::ty::ValTree;
+    use rustc_span::DUMMY_SP;
 
     /// The source and destination types of a transmutation.
     #[derive(TypeVisitable, Debug, Clone, Copy)]
@@ -135,7 +136,7 @@ mod rustc {
             use rustc_middle::ty::ScalarInt;
             use rustc_span::symbol::sym;
 
-            let Ok(cv) = c.eval(tcx, param_env, None) else {
+            let Ok(cv) = c.eval(tcx, param_env, DUMMY_SP) else {
                 return Some(Self {
                     alignment: true,
                     lifetimes: true,
diff --git a/compiler/rustc_ty_utils/src/representability.rs b/compiler/rustc_ty_utils/src/representability.rs
index bb546cee2dd..a5ffa553c2a 100644
--- a/compiler/rustc_ty_utils/src/representability.rs
+++ b/compiler/rustc_ty_utils/src/representability.rs
@@ -12,7 +12,7 @@ pub(crate) fn provide(providers: &mut Providers) {
 macro_rules! rtry {
     ($e:expr) => {
         match $e {
-            e @ Representability::Infinite => return e,
+            e @ Representability::Infinite(_) => return e,
             Representability::Representable => {}
         }
     };
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
index 2b6b91672c3..547a3cb5a8c 100644
--- a/compiler/rustc_ty_utils/src/ty.rs
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -1,78 +1,56 @@
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
+use rustc_hir::LangItem;
 use rustc_index::bit_set::BitSet;
 use rustc_middle::query::Providers;
-use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt, TypeVisitor};
+use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt, TypeVisitableExt, TypeVisitor};
 use rustc_middle::ty::{ToPredicate, TypeSuperVisitable, TypeVisitable};
 use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
 use rustc_span::DUMMY_SP;
 use rustc_trait_selection::traits;
 
-fn sized_constraint_for_ty<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    adtdef: ty::AdtDef<'tcx>,
-    ty: Ty<'tcx>,
-) -> Vec<Ty<'tcx>> {
+#[instrument(level = "debug", skip(tcx), ret)]
+fn sized_constraint_for_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
     use rustc_type_ir::TyKind::*;
 
-    let result = match ty.kind() {
-        Bool | Char | Int(..) | Uint(..) | Float(..) | RawPtr(..) | Ref(..) | FnDef(..)
-        | FnPtr(_) | Array(..) | Closure(..) | CoroutineClosure(..) | Coroutine(..) | Never => {
-            vec![]
-        }
-
-        Str | Dynamic(..) | Slice(_) | Foreign(..) | Error(_) | CoroutineWitness(..) => {
-            // these are never sized - return the target type
-            vec![ty]
-        }
-
-        Tuple(tys) => match tys.last() {
-            None => vec![],
-            Some(&ty) => sized_constraint_for_ty(tcx, adtdef, ty),
-        },
-
-        Adt(adt, args) => {
-            // recursive case
-            let adt_tys = adt.sized_constraint(tcx);
-            debug!("sized_constraint_for_ty({:?}) intermediate = {:?}", ty, adt_tys);
-            adt_tys
-                .iter_instantiated(tcx, args)
-                .flat_map(|ty| sized_constraint_for_ty(tcx, adtdef, ty))
-                .collect()
-        }
-
-        Alias(..) => {
-            // must calculate explicitly.
-            // FIXME: consider special-casing always-Sized projections
-            vec![ty]
-        }
-
-        Param(..) => {
-            // perf hack: if there is a `T: Sized` bound, then
-            // we know that `T` is Sized and do not need to check
-            // it on the impl.
-
-            let Some(sized_trait_def_id) = tcx.lang_items().sized_trait() else { return vec![ty] };
-            let predicates = tcx.predicates_of(adtdef.did()).predicates;
-            if predicates.iter().any(|(p, _)| {
-                p.as_trait_clause().is_some_and(|trait_pred| {
-                    trait_pred.def_id() == sized_trait_def_id
-                        && trait_pred.self_ty().skip_binder() == ty
-                })
-            }) {
-                vec![]
-            } else {
-                vec![ty]
-            }
-        }
+    match ty.kind() {
+        // these are always sized
+        Bool
+        | Char
+        | Int(..)
+        | Uint(..)
+        | Float(..)
+        | RawPtr(..)
+        | Ref(..)
+        | FnDef(..)
+        | FnPtr(..)
+        | Array(..)
+        | Closure(..)
+        | CoroutineClosure(..)
+        | Coroutine(..)
+        | CoroutineWitness(..)
+        | Never
+        | Dynamic(_, _, ty::DynStar) => None,
+
+        // these are never sized
+        Str | Slice(..) | Dynamic(_, _, ty::Dyn) | Foreign(..) => Some(ty),
+
+        Tuple(tys) => tys.last().and_then(|&ty| sized_constraint_for_ty(tcx, ty)),
+
+        // recursive case
+        Adt(adt, args) => adt.sized_constraint(tcx).and_then(|intermediate| {
+            let ty = intermediate.instantiate(tcx, args);
+            sized_constraint_for_ty(tcx, ty)
+        }),
+
+        // these can be sized or unsized
+        Param(..) | Alias(..) | Error(_) => Some(ty),
 
         Placeholder(..) | Bound(..) | Infer(..) => {
-            bug!("unexpected type `{:?}` in sized_constraint_for_ty", ty)
+            bug!("unexpected type `{ty:?}` in sized_constraint_for_ty")
         }
-    };
-    debug!("sized_constraint_for_ty({:?}) = {:?}", ty, result);
-    result
+    }
 }
 
 fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness {
@@ -90,29 +68,45 @@ fn defaultness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Defaultness {
 ///
 /// In fact, there are only a few options for the types in the constraint:
 ///     - an obviously-unsized type
-///     - a type parameter or projection whose Sizedness can't be known
-///     - a tuple of type parameters or projections, if there are multiple
-///       such.
-///     - an Error, if a type is infinitely sized
+///     - a type parameter or projection whose sizedness can't be known
+#[instrument(level = "debug", skip(tcx), ret)]
 fn adt_sized_constraint<'tcx>(
     tcx: TyCtxt<'tcx>,
     def_id: DefId,
-) -> ty::EarlyBinder<&'tcx ty::List<Ty<'tcx>>> {
+) -> Option<ty::EarlyBinder<Ty<'tcx>>> {
     if let Some(def_id) = def_id.as_local() {
-        if matches!(tcx.representability(def_id), ty::Representability::Infinite) {
-            return ty::EarlyBinder::bind(tcx.mk_type_list(&[Ty::new_misc_error(tcx)]));
+        if let ty::Representability::Infinite(_) = tcx.representability(def_id) {
+            return None;
         }
     }
     let def = tcx.adt_def(def_id);
 
-    let result =
-        tcx.mk_type_list_from_iter(def.variants().iter().filter_map(|v| v.tail_opt()).flat_map(
-            |f| sized_constraint_for_ty(tcx, def, tcx.type_of(f.did).instantiate_identity()),
-        ));
+    if !def.is_struct() {
+        bug!("`adt_sized_constraint` called on non-struct type: {def:?}");
+    }
+
+    let tail_def = def.non_enum_variant().tail_opt()?;
+    let tail_ty = tcx.type_of(tail_def.did).instantiate_identity();
 
-    debug!("adt_sized_constraint: {:?} => {:?}", def, result);
+    let constraint_ty = sized_constraint_for_ty(tcx, tail_ty)?;
+    if constraint_ty.references_error() {
+        return None;
+    }
+
+    // perf hack: if there is a `constraint_ty: Sized` bound, then we know
+    // that the type is sized and do not need to check it on the impl.
+    let sized_trait_def_id = tcx.require_lang_item(LangItem::Sized, None);
+    let predicates = tcx.predicates_of(def.did()).predicates;
+    if predicates.iter().any(|(p, _)| {
+        p.as_trait_clause().is_some_and(|trait_pred| {
+            trait_pred.def_id() == sized_trait_def_id
+                && trait_pred.self_ty().skip_binder() == constraint_ty
+        })
+    }) {
+        return None;
+    }
 
-    ty::EarlyBinder::bind(result)
+    Some(ty::EarlyBinder::bind(constraint_ty))
 }
 
 /// See `ParamEnv` struct definition for details.
diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs
index a0759f7df7f..112f617fe16 100644
--- a/compiler/rustc_type_ir/src/predicate_kind.rs
+++ b/compiler/rustc_type_ir/src/predicate_kind.rs
@@ -148,19 +148,20 @@ pub enum PredicateKind<I: Interner> {
     /// Used for coherence to mark opaque types as possibly equal to each other but ambiguous.
     Ambiguous,
 
-    /// The alias normalizes to `term`. Unlike `Projection`, this always fails if the alias
-    /// cannot be normalized in the current context.
+    /// This should only be used inside of the new solver for `AliasRelate` and expects
+    /// the `term` to be an unconstrained inference variable.
     ///
-    /// `Projection(<T as Trait>::Assoc, ?x)` results in `?x == <T as Trait>::Assoc` while
-    /// `NormalizesTo(<T as Trait>::Assoc, ?x)` results in `NoSolution`.
-    ///
-    /// Only used in the new solver.
+    /// The alias normalizes to `term`. Unlike `Projection`, this always fails if the
+    /// alias cannot be normalized in the current context. For the rigid alias
+    /// `T as Trait>::Assoc`, `Projection(<T as Trait>::Assoc, ?x)` constrains `?x`
+    /// to `<T as Trait>::Assoc` while `NormalizesTo(<T as Trait>::Assoc, ?x)`
+    /// results in `NoSolution`.
     NormalizesTo(I::NormalizesTo),
 
     /// Separate from `ClauseKind::Projection` which is used for normalization in new solver.
     /// This predicate requires two terms to be equal to eachother.
     ///
-    /// Only used for new solver
+    /// Only used for new solver.
     AliasRelate(I::Term, I::Term, AliasRelationDirection),
 }