about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-08-14 14:13:48 +0000
committerbors <bors@rust-lang.org>2024-08-14 14:13:48 +0000
commit355a307a874077eff12bd99c6fd3eb6bfda79993 (patch)
tree459cf7ef097c14f40256cb7e0bcbceafd74b9f8d
parent0f442e265c165c0a78633bef98de18517815150c (diff)
parent4d8c0b3b5dc6c4476946ab713e1d01a1d40a8bef (diff)
downloadrust-355a307a874077eff12bd99c6fd3eb6bfda79993.tar.gz
rust-355a307a874077eff12bd99c6fd3eb6bfda79993.zip
Auto merge of #129092 - jieyouxu:rollup-z2522nm, r=jieyouxu
Rollup of 6 pull requests

Successful merges:

 - #128570 (Stabilize `asm_const`)
 - #128828 (`-Znext-solver` caching)
 - #128954 (Explicitly specify type parameter on FromResidual for Option and ControlFlow.)
 - #129059 (Record the correct target type when coercing fn items/closures to pointers)
 - #129071 (Port `run-make/sysroot-crates-are-unstable` to rmake)
 - #129088 (Make the rendered html doc for rustc better)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_ast_lowering/messages.ftl2
-rw-r--r--compiler/rustc_ast_lowering/src/asm.rs17
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs8
-rw-r--r--compiler/rustc_codegen_gcc/tests/run/asm.rs15
-rw-r--r--compiler/rustc_errors/src/lib.rs32
-rw-r--r--compiler/rustc_feature/src/accepted.rs2
-rw-r--r--compiler/rustc_feature/src/unstable.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs2
-rw-r--r--compiler/rustc_middle/src/arena.rs4
-rw-r--r--compiler/rustc_middle/src/ty/context.rs9
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/inspect/build.rs106
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/search_graph.rs66
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/analyse.rs10
-rw-r--r--compiler/rustc_type_ir/src/binder.rs24
-rw-r--r--compiler/rustc_type_ir/src/fold.rs11
-rw-r--r--compiler/rustc_type_ir/src/interner.rs12
-rw-r--r--compiler/rustc_type_ir/src/search_graph/global_cache.rs88
-rw-r--r--compiler/rustc_type_ir/src/search_graph/mod.rs1106
-rw-r--r--compiler/rustc_type_ir/src/search_graph/validate.rs75
-rw-r--r--compiler/rustc_type_ir/src/solve/inspect.rs4
-rw-r--r--compiler/rustc_type_ir/src/solve/mod.rs8
-rw-r--r--library/core/src/lib.rs2
-rw-r--r--library/core/src/ops/control_flow.rs4
-rw-r--r--library/core/src/option.rs4
-rw-r--r--library/core/tests/ops.rs1
-rw-r--r--library/core/tests/ops/from_residual.rs26
-rw-r--r--src/doc/unstable-book/src/language-features/asm-const.md11
-rw-r--r--src/tools/tidy/src/allowed_run_make_makefiles.txt1
-rw-r--r--tests/assembly/asm/global_asm.rs1
-rw-r--r--tests/assembly/asm/msp430-types.rs2
-rw-r--r--tests/mir-opt/build_correct_coerce.main.built.after.mir18
-rw-r--r--tests/mir-opt/build_correct_coerce.rs12
-rw-r--r--tests/run-make/sysroot-crates-are-unstable/Makefile2
-rw-r--r--tests/run-make/sysroot-crates-are-unstable/rmake.rs5
-rw-r--r--tests/ui/asm/aarch64/bad-reg.rs2
-rw-r--r--tests/ui/asm/aarch64/bad-reg.stderr44
-rw-r--r--tests/ui/asm/aarch64/const.rs2
-rw-r--r--tests/ui/asm/aarch64/parse-error.rs2
-rw-r--r--tests/ui/asm/aarch64/parse-error.stderr118
-rw-r--r--tests/ui/asm/aarch64/type-check-3.rs2
-rw-r--r--tests/ui/asm/aarch64/type-check-4.rs5
-rw-r--r--tests/ui/asm/aarch64/type-check-4.stderr6
-rw-r--r--tests/ui/asm/bad-template.rs2
-rw-r--r--tests/ui/asm/const-error.rs8
-rw-r--r--tests/ui/asm/const-error.stderr6
-rw-r--r--tests/ui/asm/fail-const-eval-issue-121099.rs1
-rw-r--r--tests/ui/asm/fail-const-eval-issue-121099.stderr4
-rw-r--r--tests/ui/asm/generic-const.rs2
-rw-r--r--tests/ui/asm/invalid-const-operand.rs2
-rw-r--r--tests/ui/asm/invalid-const-operand.stderr16
-rw-r--r--tests/ui/asm/naked-functions.rs2
-rw-r--r--tests/ui/asm/named-asm-labels.rs6
-rw-r--r--tests/ui/asm/named-asm-labels.stderr44
-rw-r--r--tests/ui/asm/parse-error.rs2
-rw-r--r--tests/ui/asm/parse-error.stderr144
-rw-r--r--tests/ui/asm/type-check-1.rs2
-rw-r--r--tests/ui/asm/type-check-1.stderr16
-rw-r--r--tests/ui/asm/x86_64/bad-reg.rs2
-rw-r--r--tests/ui/asm/x86_64/bad-reg.stderr60
-rw-r--r--tests/ui/asm/x86_64/const.rs2
-rw-r--r--tests/ui/asm/x86_64/type-check-3.rs2
-rw-r--r--tests/ui/asm/x86_64/type-check-3.stderr26
-rw-r--r--tests/ui/asm/x86_64/type-check-4.rs2
-rw-r--r--tests/ui/asm/x86_64/type-check-4.stderr6
-rw-r--r--tests/ui/asm/x86_64/x86_64_parse_error.rs2
-rw-r--r--tests/ui/asm/x86_64/x86_64_parse_error.stderr10
-rw-r--r--tests/ui/feature-gates/feature-gate-asm_const.rs16
-rw-r--r--tests/ui/feature-gates/feature-gate-asm_const.stderr23
-rw-r--r--tests/ui/higher-ranked/subtyping-fn-ptr-coercion.rs10
-rw-r--r--tests/ui/impl-trait/recursive-ice-101862.stderr4
-rw-r--r--tests/ui/traits/next-solver/alias-bound-unsound.rs1
-rw-r--r--tests/ui/traits/next-solver/alias-bound-unsound.stderr8
-rw-r--r--tests/ui/try-trait/bad-interconversion.stderr4
-rw-r--r--tests/ui/try-trait/option-to-result.stderr2
74 files changed, 1280 insertions, 1030 deletions
diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl
index 0a7f75039f6..a5ee6713be8 100644
--- a/compiler/rustc_ast_lowering/messages.ftl
+++ b/compiler/rustc_ast_lowering/messages.ftl
@@ -175,8 +175,6 @@ ast_lowering_underscore_expr_lhs_assign =
     .label = `_` not allowed here
 
 ast_lowering_unstable_inline_assembly = inline assembly is not stable yet on this architecture
-ast_lowering_unstable_inline_assembly_const_operands =
-    const operands for inline assembly are unstable
 ast_lowering_unstable_inline_assembly_label_operands =
     label operands for inline assembly are unstable
 ast_lowering_unstable_may_unwind = the `may_unwind` option is unstable
diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs
index 8acca78379b..7d9d689e6d7 100644
--- a/compiler/rustc_ast_lowering/src/asm.rs
+++ b/compiler/rustc_ast_lowering/src/asm.rs
@@ -183,20 +183,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                             out_expr: out_expr.as_ref().map(|expr| self.lower_expr(expr)),
                         }
                     }
-                    InlineAsmOperand::Const { anon_const } => {
-                        if !self.tcx.features().asm_const {
-                            feature_err(
-                                sess,
-                                sym::asm_const,
-                                *op_sp,
-                                fluent::ast_lowering_unstable_inline_assembly_const_operands,
-                            )
-                            .emit();
-                        }
-                        hir::InlineAsmOperand::Const {
-                            anon_const: self.lower_anon_const_to_anon_const(anon_const),
-                        }
-                    }
+                    InlineAsmOperand::Const { anon_const } => hir::InlineAsmOperand::Const {
+                        anon_const: self.lower_anon_const_to_anon_const(anon_const),
+                    },
                     InlineAsmOperand::Sym { sym } => {
                         let static_def_id = self
                             .resolver
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 9e0724b0948..a2669da1b04 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -1989,9 +1989,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
 
                         let ty_fn_ptr_from = Ty::new_fn_ptr(tcx, fn_sig);
 
-                        if let Err(terr) = self.eq_types(
-                            *ty,
+                        if let Err(terr) = self.sub_types(
                             ty_fn_ptr_from,
+                            *ty,
                             location.to_locations(),
                             ConstraintCategory::Cast { unsize_to: None },
                         ) {
@@ -2014,9 +2014,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         let ty_fn_ptr_from =
                             Ty::new_fn_ptr(tcx, tcx.signature_unclosure(sig, *safety));
 
-                        if let Err(terr) = self.eq_types(
-                            *ty,
+                        if let Err(terr) = self.sub_types(
                             ty_fn_ptr_from,
+                            *ty,
                             location.to_locations(),
                             ConstraintCategory::Cast { unsize_to: None },
                         ) {
diff --git a/compiler/rustc_codegen_gcc/tests/run/asm.rs b/compiler/rustc_codegen_gcc/tests/run/asm.rs
index 56f2aac3d0a..4e05d026868 100644
--- a/compiler/rustc_codegen_gcc/tests/run/asm.rs
+++ b/compiler/rustc_codegen_gcc/tests/run/asm.rs
@@ -3,12 +3,10 @@
 // Run-time:
 //   status: 0
 
-#![feature(asm_const)]
-
-#[cfg(target_arch="x86_64")]
+#[cfg(target_arch = "x86_64")]
 use std::arch::{asm, global_asm};
 
-#[cfg(target_arch="x86_64")]
+#[cfg(target_arch = "x86_64")]
 global_asm!(
     "
     .global add_asm
@@ -22,7 +20,7 @@ extern "C" {
     fn add_asm(a: i64, b: i64) -> i64;
 }
 
-#[cfg(target_arch="x86_64")]
+#[cfg(target_arch = "x86_64")]
 pub unsafe fn mem_cpy(dst: *mut u8, src: *const u8, len: usize) {
     asm!(
         "rep movsb",
@@ -33,7 +31,7 @@ pub unsafe fn mem_cpy(dst: *mut u8, src: *const u8, len: usize) {
     );
 }
 
-#[cfg(target_arch="x86_64")]
+#[cfg(target_arch = "x86_64")]
 fn asm() {
     unsafe {
         asm!("nop");
@@ -178,9 +176,8 @@ fn asm() {
     assert_eq!(array1, array2);
 }
 
-#[cfg(not(target_arch="x86_64"))]
-fn asm() {
-}
+#[cfg(not(target_arch = "x86_64"))]
+fn asm() {}
 
 fn main() {
     asm();
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 3bc03a1e516..fd203c38318 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -1837,23 +1837,23 @@ impl DelayedDiagInner {
     }
 }
 
-/// Level              is_error  EmissionGuarantee         Top-level  Sub   Used in lints?
-/// -----              --------  -----------------         ---------  ---   --------------
-/// Bug                yes       BugAbort                  yes        -     -
-/// Fatal              yes       FatalAbort/FatalError(*)  yes        -     -
-/// Error              yes       ErrorGuaranteed           yes        -     yes
-/// DelayedBug         yes       ErrorGuaranteed           yes        -     -
-/// ForceWarning       -         ()                        yes        -     lint-only
-/// Warning            -         ()                        yes        yes   yes
-/// Note               -         ()                        rare       yes   -
-/// OnceNote           -         ()                        -          yes   lint-only
-/// Help               -         ()                        rare       yes   -
-/// OnceHelp           -         ()                        -          yes   lint-only
-/// FailureNote        -         ()                        rare       -     -
-/// Allow              -         ()                        yes        -     lint-only
-/// Expect             -         ()                        yes        -     lint-only
+/// | Level        | is_error | EmissionGuarantee            | Top-level | Sub | Used in lints?
+/// | -----        | -------- | -----------------            | --------- | --- | --------------
+/// | Bug          | yes      | BugAbort                     | yes       | -   | -
+/// | Fatal        | yes      | FatalAbort/FatalError[^star] | yes       | -   | -
+/// | Error        | yes      | ErrorGuaranteed              | yes       | -   | yes
+/// | DelayedBug   | yes      | ErrorGuaranteed              | yes       | -   | -
+/// | ForceWarning | -        | ()                           | yes       | -   | lint-only
+/// | Warning      | -        | ()                           | yes       | yes | yes
+/// | Note         | -        | ()                           | rare      | yes | -
+/// | OnceNote     | -        | ()                           | -         | yes | lint-only
+/// | Help         | -        | ()                           | rare      | yes | -
+/// | OnceHelp     | -        | ()                           | -         | yes | lint-only
+/// | FailureNote  | -        | ()                           | rare      | -   | -
+/// | Allow        | -        | ()                           | yes       | -   | lint-only
+/// | Expect       | -        | ()                           | yes       | -   | lint-only
 ///
-/// (*) `FatalAbort` normally, `FatalError` in the non-aborting "almost fatal" case that is
+/// [^star]: `FatalAbort` normally, `FatalError` in the non-aborting "almost fatal" case that is
 ///     occasionally used.
 ///
 #[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)]
diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs
index 44286cfeeef..03b40e28f8b 100644
--- a/compiler/rustc_feature/src/accepted.rs
+++ b/compiler/rustc_feature/src/accepted.rs
@@ -60,6 +60,8 @@ declare_features! (
     (accepted, adx_target_feature, "1.61.0", Some(44839)),
     /// Allows explicit discriminants on non-unit enum variants.
     (accepted, arbitrary_enum_discriminant, "1.66.0", Some(60553)),
+    /// Allows using `const` operands in inline assembly.
+    (accepted, asm_const, "CURRENT_RUSTC_VERSION", Some(93332)),
     /// Allows using `sym` operands in inline assembly.
     (accepted, asm_sym, "1.66.0", Some(93333)),
     /// Allows the definition of associated constants in `trait` or `impl` blocks.
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 47810bc9165..24f691ea7fa 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -348,8 +348,6 @@ declare_features! (
     (unstable, alloc_error_handler, "1.29.0", Some(51540)),
     /// Allows trait methods with arbitrary self types.
     (unstable, arbitrary_self_types, "1.23.0", Some(44874)),
-    /// Allows using `const` operands in inline assembly.
-    (unstable, asm_const, "1.58.0", Some(93332)),
     /// Enables experimental inline assembly support for additional architectures.
     (unstable, asm_experimental_arch, "1.58.0", Some(93335)),
     /// Allows using `label` operands in inline assembly.
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 0c83d50ad12..d53df251a15 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -137,7 +137,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
                 at.lub(DefineOpaqueTypes::Yes, b, a)
             } else {
                 at.sup(DefineOpaqueTypes::Yes, b, a)
-                    .map(|InferOk { value: (), obligations }| InferOk { value: a, obligations })
+                    .map(|InferOk { value: (), obligations }| InferOk { value: b, obligations })
             };
 
             // In the new solver, lazy norm may allow us to shallowly equate
diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs
index e3d7dff3c66..37c10b14054 100644
--- a/compiler/rustc_middle/src/arena.rs
+++ b/compiler/rustc_middle/src/arena.rs
@@ -61,10 +61,6 @@ macro_rules! arena_types {
             [] dtorck_constraint: rustc_middle::traits::query::DropckConstraint<'tcx>,
             [] candidate_step: rustc_middle::traits::query::CandidateStep<'tcx>,
             [] autoderef_bad_ty: rustc_middle::traits::query::MethodAutoderefBadTy<'tcx>,
-            [] canonical_goal_evaluation:
-                rustc_type_ir::solve::inspect::CanonicalGoalEvaluationStep<
-                    rustc_middle::ty::TyCtxt<'tcx>
-                >,
             [] query_region_constraints: rustc_middle::infer::canonical::QueryRegionConstraints<'tcx>,
             [] type_op_subtype:
                 rustc_middle::infer::canonical::Canonical<'tcx,
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 83799e40868..9b39b849704 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -107,8 +107,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.mk_predefined_opaques_in_body(data)
     }
     type DefiningOpaqueTypes = &'tcx ty::List<LocalDefId>;
-    type CanonicalGoalEvaluationStepRef =
-        &'tcx solve::inspect::CanonicalGoalEvaluationStep<TyCtxt<'tcx>>;
     type CanonicalVars = CanonicalVarInfos<'tcx>;
     fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo<Self>]) -> Self::CanonicalVars {
         self.mk_canonical_var_infos(infos)
@@ -277,13 +275,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.debug_assert_args_compatible(def_id, args);
     }
 
-    fn intern_canonical_goal_evaluation_step(
-        self,
-        step: solve::inspect::CanonicalGoalEvaluationStep<TyCtxt<'tcx>>,
-    ) -> &'tcx solve::inspect::CanonicalGoalEvaluationStep<TyCtxt<'tcx>> {
-        self.arena.alloc(step)
-    }
-
     fn mk_type_list_from_iter<I, T>(self, args: I) -> T::Output
     where
         I: Iterator<Item = T>,
diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs
index a3c21666bd6..86fb036cd3d 100644
--- a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs
@@ -5,11 +5,10 @@
 //! see the comment on [ProofTreeBuilder].
 
 use std::marker::PhantomData;
-use std::mem;
 
 use derive_where::derive_where;
 use rustc_type_ir::inherent::*;
-use rustc_type_ir::{self as ty, search_graph, Interner};
+use rustc_type_ir::{self as ty, Interner};
 
 use crate::delegate::SolverDelegate;
 use crate::solve::eval_ctxt::canonical;
@@ -94,31 +93,10 @@ impl<I: Interner> WipGoalEvaluation<I> {
     }
 }
 
-#[derive_where(PartialEq, Eq; I: Interner)]
-pub(in crate::solve) enum WipCanonicalGoalEvaluationKind<I: Interner> {
-    Overflow,
-    CycleInStack,
-    ProvisionalCacheHit,
-    Interned { final_revision: I::CanonicalGoalEvaluationStepRef },
-}
-
-impl<I: Interner> std::fmt::Debug for WipCanonicalGoalEvaluationKind<I> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            Self::Overflow => write!(f, "Overflow"),
-            Self::CycleInStack => write!(f, "CycleInStack"),
-            Self::ProvisionalCacheHit => write!(f, "ProvisionalCacheHit"),
-            Self::Interned { final_revision: _ } => {
-                f.debug_struct("Interned").finish_non_exhaustive()
-            }
-        }
-    }
-}
-
 #[derive_where(PartialEq, Eq, Debug; I: Interner)]
 struct WipCanonicalGoalEvaluation<I: Interner> {
     goal: CanonicalInput<I>,
-    kind: Option<WipCanonicalGoalEvaluationKind<I>>,
+    encountered_overflow: bool,
     /// Only used for uncached goals. After we finished evaluating
     /// the goal, this is interned and moved into `kind`.
     final_revision: Option<WipCanonicalGoalEvaluationStep<I>>,
@@ -127,25 +105,17 @@ struct WipCanonicalGoalEvaluation<I: Interner> {
 
 impl<I: Interner> WipCanonicalGoalEvaluation<I> {
     fn finalize(self) -> inspect::CanonicalGoalEvaluation<I> {
-        // We've already interned the final revision in
-        // `fn finalize_canonical_goal_evaluation`.
-        assert!(self.final_revision.is_none());
-        let kind = match self.kind.unwrap() {
-            WipCanonicalGoalEvaluationKind::Overflow => {
+        inspect::CanonicalGoalEvaluation {
+            goal: self.goal,
+            kind: if self.encountered_overflow {
+                assert!(self.final_revision.is_none());
                 inspect::CanonicalGoalEvaluationKind::Overflow
-            }
-            WipCanonicalGoalEvaluationKind::CycleInStack => {
-                inspect::CanonicalGoalEvaluationKind::CycleInStack
-            }
-            WipCanonicalGoalEvaluationKind::ProvisionalCacheHit => {
-                inspect::CanonicalGoalEvaluationKind::ProvisionalCacheHit
-            }
-            WipCanonicalGoalEvaluationKind::Interned { final_revision } => {
+            } else {
+                let final_revision = self.final_revision.unwrap().finalize();
                 inspect::CanonicalGoalEvaluationKind::Evaluation { final_revision }
-            }
-        };
-
-        inspect::CanonicalGoalEvaluation { goal: self.goal, kind, result: self.result.unwrap() }
+            },
+            result: self.result.unwrap(),
+        }
     }
 }
 
@@ -308,7 +278,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> {
     ) -> ProofTreeBuilder<D> {
         self.nested(|| WipCanonicalGoalEvaluation {
             goal,
-            kind: None,
+            encountered_overflow: false,
             final_revision: None,
             result: None,
         })
@@ -329,11 +299,11 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> {
         }
     }
 
-    pub fn canonical_goal_evaluation_kind(&mut self, kind: WipCanonicalGoalEvaluationKind<I>) {
+    pub fn canonical_goal_evaluation_overflow(&mut self) {
         if let Some(this) = self.as_mut() {
             match this {
                 DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => {
-                    assert_eq!(canonical_goal_evaluation.kind.replace(kind), None);
+                    canonical_goal_evaluation.encountered_overflow = true;
                 }
                 _ => unreachable!(),
             };
@@ -547,51 +517,3 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> {
         }
     }
 }
-
-impl<D, I> search_graph::ProofTreeBuilder<I> for ProofTreeBuilder<D>
-where
-    D: SolverDelegate<Interner = I>,
-    I: Interner,
-{
-    fn try_apply_proof_tree(
-        &mut self,
-        proof_tree: Option<I::CanonicalGoalEvaluationStepRef>,
-    ) -> bool {
-        if !self.is_noop() {
-            if let Some(final_revision) = proof_tree {
-                let kind = WipCanonicalGoalEvaluationKind::Interned { final_revision };
-                self.canonical_goal_evaluation_kind(kind);
-                true
-            } else {
-                false
-            }
-        } else {
-            true
-        }
-    }
-
-    fn on_provisional_cache_hit(&mut self) {
-        self.canonical_goal_evaluation_kind(WipCanonicalGoalEvaluationKind::ProvisionalCacheHit);
-    }
-
-    fn on_cycle_in_stack(&mut self) {
-        self.canonical_goal_evaluation_kind(WipCanonicalGoalEvaluationKind::CycleInStack);
-    }
-
-    fn finalize_canonical_goal_evaluation(
-        &mut self,
-        tcx: I,
-    ) -> Option<I::CanonicalGoalEvaluationStepRef> {
-        self.as_mut().map(|this| match this {
-            DebugSolver::CanonicalGoalEvaluation(evaluation) => {
-                let final_revision = mem::take(&mut evaluation.final_revision).unwrap();
-                let final_revision =
-                    tcx.intern_canonical_goal_evaluation_step(final_revision.finalize());
-                let kind = WipCanonicalGoalEvaluationKind::Interned { final_revision };
-                assert_eq!(evaluation.kind.replace(kind), None);
-                final_revision
-            }
-            _ => unreachable!(),
-        })
-    }
-}
diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs
index fe053a506e7..81c89fad8e8 100644
--- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs
@@ -1,12 +1,13 @@
+use std::convert::Infallible;
 use std::marker::PhantomData;
 
 use rustc_type_ir::inherent::*;
-use rustc_type_ir::search_graph::{self, CycleKind, UsageKind};
+use rustc_type_ir::search_graph::{self, PathKind};
 use rustc_type_ir::solve::{CanonicalInput, Certainty, QueryResult};
 use rustc_type_ir::Interner;
 
-use super::inspect::{self, ProofTreeBuilder};
-use super::FIXPOINT_STEP_LIMIT;
+use super::inspect::ProofTreeBuilder;
+use super::{has_no_inference_or_external_constraints, FIXPOINT_STEP_LIMIT};
 use crate::delegate::SolverDelegate;
 
 /// This type is never constructed. We only use it to implement `search_graph::Delegate`
@@ -22,43 +23,48 @@ where
 {
     type Cx = D::Interner;
 
+    const ENABLE_PROVISIONAL_CACHE: bool = true;
+    type ValidationScope = Infallible;
+    fn enter_validation_scope(
+        _cx: Self::Cx,
+        _input: CanonicalInput<I>,
+    ) -> Option<Self::ValidationScope> {
+        None
+    }
+
     const FIXPOINT_STEP_LIMIT: usize = FIXPOINT_STEP_LIMIT;
 
     type ProofTreeBuilder = ProofTreeBuilder<D>;
+    fn inspect_is_noop(inspect: &mut Self::ProofTreeBuilder) -> bool {
+        inspect.is_noop()
+    }
 
+    const DIVIDE_AVAILABLE_DEPTH_ON_OVERFLOW: usize = 4;
     fn recursion_limit(cx: I) -> usize {
         cx.recursion_limit()
     }
 
     fn initial_provisional_result(
         cx: I,
-        kind: CycleKind,
+        kind: PathKind,
         input: CanonicalInput<I>,
     ) -> QueryResult<I> {
         match kind {
-            CycleKind::Coinductive => response_no_constraints(cx, input, Certainty::Yes),
-            CycleKind::Inductive => response_no_constraints(cx, input, Certainty::overflow(false)),
+            PathKind::Coinductive => response_no_constraints(cx, input, Certainty::Yes),
+            PathKind::Inductive => response_no_constraints(cx, input, Certainty::overflow(false)),
         }
     }
 
-    fn reached_fixpoint(
-        cx: I,
-        kind: UsageKind,
+    fn is_initial_provisional_result(
+        cx: Self::Cx,
+        kind: PathKind,
         input: CanonicalInput<I>,
-        provisional_result: Option<QueryResult<I>>,
         result: QueryResult<I>,
     ) -> bool {
-        if let Some(r) = provisional_result {
-            r == result
-        } else {
-            match kind {
-                UsageKind::Single(CycleKind::Coinductive) => {
-                    response_no_constraints(cx, input, Certainty::Yes) == result
-                }
-                UsageKind::Single(CycleKind::Inductive) => {
-                    response_no_constraints(cx, input, Certainty::overflow(false)) == result
-                }
-                UsageKind::Mixed => false,
+        match kind {
+            PathKind::Coinductive => response_no_constraints(cx, input, Certainty::Yes) == result,
+            PathKind::Inductive => {
+                response_no_constraints(cx, input, Certainty::overflow(false)) == result
             }
         }
     }
@@ -68,7 +74,7 @@ where
         inspect: &mut ProofTreeBuilder<D>,
         input: CanonicalInput<I>,
     ) -> QueryResult<I> {
-        inspect.canonical_goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::Overflow);
+        inspect.canonical_goal_evaluation_overflow();
         response_no_constraints(cx, input, Certainty::overflow(true))
     }
 
@@ -76,6 +82,22 @@ where
         response_no_constraints(cx, input, Certainty::overflow(false))
     }
 
+    fn is_ambiguous_result(result: QueryResult<I>) -> bool {
+        result.is_ok_and(|response| {
+            has_no_inference_or_external_constraints(response)
+                && matches!(response.value.certainty, Certainty::Maybe(_))
+        })
+    }
+
+    fn propagate_ambiguity(
+        cx: I,
+        for_input: CanonicalInput<I>,
+        from_result: QueryResult<I>,
+    ) -> QueryResult<I> {
+        let certainty = from_result.unwrap().value.certainty;
+        response_no_constraints(cx, for_input, certainty)
+    }
+
     fn step_is_coinductive(cx: I, input: CanonicalInput<I>) -> bool {
         input.value.goal.predicate.is_coinductive(cx)
     }
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
index 1a459aa484f..51dda25d8ad 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
@@ -334,13 +334,9 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
 
     pub fn candidates(&'a self) -> Vec<InspectCandidate<'a, 'tcx>> {
         let mut candidates = vec![];
-        let last_eval_step = match self.evaluation_kind {
-            inspect::CanonicalGoalEvaluationKind::Overflow
-            | inspect::CanonicalGoalEvaluationKind::CycleInStack
-            | inspect::CanonicalGoalEvaluationKind::ProvisionalCacheHit => {
-                warn!("unexpected root evaluation: {:?}", self.evaluation_kind);
-                return vec![];
-            }
+        let last_eval_step = match &self.evaluation_kind {
+            // An annoying edge case in case the recursion limit is 0.
+            inspect::CanonicalGoalEvaluationKind::Overflow => return vec![],
             inspect::CanonicalGoalEvaluationKind::Evaluation { final_revision } => final_revision,
         };
 
diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs
index 652201f11e3..d42efbc91e1 100644
--- a/compiler/rustc_type_ir/src/binder.rs
+++ b/compiler/rustc_type_ir/src/binder.rs
@@ -8,7 +8,7 @@ use derive_where::derive_where;
 use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable};
 #[cfg(feature = "nightly")]
 use rustc_serialize::Decodable;
-use tracing::debug;
+use tracing::instrument;
 
 use crate::data_structures::SsoHashSet;
 use crate::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable};
@@ -817,28 +817,20 @@ impl<'a, I: Interner> ArgFolder<'a, I> {
     /// As indicated in the diagram, here the same type `&'a i32` is instantiated once, but in the
     /// first case we do not increase the De Bruijn index and in the second case we do. The reason
     /// is that only in the second case have we passed through a fn binder.
+    #[instrument(level = "trace", skip(self), fields(binders_passed = self.binders_passed), ret)]
     fn shift_vars_through_binders<T: TypeFoldable<I>>(&self, val: T) -> T {
-        debug!(
-            "shift_vars(val={:?}, binders_passed={:?}, has_escaping_bound_vars={:?})",
-            val,
-            self.binders_passed,
-            val.has_escaping_bound_vars()
-        );
-
         if self.binders_passed == 0 || !val.has_escaping_bound_vars() {
-            return val;
+            val
+        } else {
+            ty::fold::shift_vars(self.cx, val, self.binders_passed)
         }
-
-        let result = ty::fold::shift_vars(TypeFolder::cx(self), val, self.binders_passed);
-        debug!("shift_vars: shifted result = {:?}", result);
-
-        result
     }
 
     fn shift_region_through_binders(&self, region: I::Region) -> I::Region {
         if self.binders_passed == 0 || !region.has_escaping_bound_vars() {
-            return region;
+            region
+        } else {
+            ty::fold::shift_region(self.cx, region, self.binders_passed)
         }
-        ty::fold::shift_region(self.cx, region, self.binders_passed)
     }
 }
diff --git a/compiler/rustc_type_ir/src/fold.rs b/compiler/rustc_type_ir/src/fold.rs
index d37bacc7d35..8e3534b0e9e 100644
--- a/compiler/rustc_type_ir/src/fold.rs
+++ b/compiler/rustc_type_ir/src/fold.rs
@@ -48,7 +48,7 @@
 use std::mem;
 
 use rustc_index::{Idx, IndexVec};
-use tracing::debug;
+use tracing::instrument;
 
 use crate::data_structures::Lrc;
 use crate::inherent::*;
@@ -417,15 +417,14 @@ pub fn shift_region<I: Interner>(cx: I, region: I::Region, amount: u32) -> I::Re
     }
 }
 
+#[instrument(level = "trace", skip(cx), ret)]
 pub fn shift_vars<I: Interner, T>(cx: I, value: T, amount: u32) -> T
 where
     T: TypeFoldable<I>,
 {
-    debug!("shift_vars(value={:?}, amount={})", value, amount);
-
     if amount == 0 || !value.has_escaping_bound_vars() {
-        return value;
+        value
+    } else {
+        value.fold_with(&mut Shifter::new(cx, amount))
     }
-
-    value.fold_with(&mut Shifter::new(cx, amount))
 }
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index c251540c0fc..f2492ede4f5 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -11,7 +11,6 @@ use crate::inherent::*;
 use crate::ir_print::IrPrint;
 use crate::lang_items::TraitSolverLangItem;
 use crate::relate::Relate;
-use crate::solve::inspect::CanonicalGoalEvaluationStep;
 use crate::solve::{
     CanonicalInput, ExternalConstraintsData, PredefinedOpaquesData, QueryResult, SolverMode,
 };
@@ -65,11 +64,6 @@ pub trait Interner:
         + Eq
         + TypeVisitable<Self>
         + SliceLike<Item = Self::LocalDefId>;
-    type CanonicalGoalEvaluationStepRef: Copy
-        + Debug
-        + Hash
-        + Eq
-        + Deref<Target = CanonicalGoalEvaluationStep<Self>>;
 
     type CanonicalVars: Copy
         + Debug
@@ -177,11 +171,6 @@ pub trait Interner:
 
     fn debug_assert_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs);
 
-    fn intern_canonical_goal_evaluation_step(
-        self,
-        step: CanonicalGoalEvaluationStep<Self>,
-    ) -> Self::CanonicalGoalEvaluationStepRef;
-
     fn mk_type_list_from_iter<I, T>(self, args: I) -> T::Output
     where
         I: Iterator<Item = T>,
@@ -390,7 +379,6 @@ impl<T, R, E> CollectAndApply<T, R> for Result<T, E> {
 }
 
 impl<I: Interner> search_graph::Cx for I {
-    type ProofTree = Option<I::CanonicalGoalEvaluationStepRef>;
     type Input = CanonicalInput<I>;
     type Result = QueryResult<I>;
 
diff --git a/compiler/rustc_type_ir/src/search_graph/global_cache.rs b/compiler/rustc_type_ir/src/search_graph/global_cache.rs
index be4f1069cd1..47f7cefac6a 100644
--- a/compiler/rustc_type_ir/src/search_graph/global_cache.rs
+++ b/compiler/rustc_type_ir/src/search_graph/global_cache.rs
@@ -1,18 +1,17 @@
 use derive_where::derive_where;
-use rustc_index::IndexVec;
 
-use super::{AvailableDepth, Cx, StackDepth, StackEntry};
-use crate::data_structures::{HashMap, HashSet};
-
-#[derive_where(Debug, Clone, Copy; X: Cx)]
-struct QueryData<X: Cx> {
-    result: X::Result,
-    proof_tree: X::ProofTree,
-}
+use super::{AvailableDepth, Cx, NestedGoals};
+use crate::data_structures::HashMap;
 
 struct Success<X: Cx> {
-    data: X::Tracked<QueryData<X>>,
     additional_depth: usize,
+    nested_goals: NestedGoals<X>,
+    result: X::Tracked<X::Result>,
+}
+
+struct WithOverflow<X: Cx> {
+    nested_goals: NestedGoals<X>,
+    result: X::Tracked<X::Result>,
 }
 
 /// The cache entry for a given input.
@@ -23,24 +22,15 @@ struct Success<X: Cx> {
 #[derive_where(Default; X: Cx)]
 struct CacheEntry<X: Cx> {
     success: Option<Success<X>>,
-    /// We have to be careful when caching roots of cycles.
-    ///
-    /// See the doc comment of `StackEntry::cycle_participants` for more
-    /// details.
-    nested_goals: HashSet<X::Input>,
-    with_overflow: HashMap<usize, X::Tracked<QueryData<X>>>,
+    with_overflow: HashMap<usize, WithOverflow<X>>,
 }
 
 #[derive_where(Debug; X: Cx)]
 pub(super) struct CacheData<'a, X: Cx> {
     pub(super) result: X::Result,
-    pub(super) proof_tree: X::ProofTree,
     pub(super) additional_depth: usize,
     pub(super) encountered_overflow: bool,
-    // FIXME: This is currently unused, but impacts the design
-    // by requiring a closure for `Cx::with_global_cache`.
-    #[allow(dead_code)]
-    pub(super) nested_goals: &'a HashSet<X::Input>,
+    pub(super) nested_goals: &'a NestedGoals<X>,
 }
 #[derive_where(Default; X: Cx)]
 pub struct GlobalCache<X: Cx> {
@@ -55,20 +45,21 @@ impl<X: Cx> GlobalCache<X> {
         input: X::Input,
 
         result: X::Result,
-        proof_tree: X::ProofTree,
         dep_node: X::DepNodeIndex,
 
         additional_depth: usize,
         encountered_overflow: bool,
-        nested_goals: &HashSet<X::Input>,
+        nested_goals: NestedGoals<X>,
     ) {
-        let data = cx.mk_tracked(QueryData { result, proof_tree }, dep_node);
+        let result = cx.mk_tracked(result, dep_node);
         let entry = self.map.entry(input).or_default();
-        entry.nested_goals.extend(nested_goals);
         if encountered_overflow {
-            entry.with_overflow.insert(additional_depth, data);
+            let with_overflow = WithOverflow { nested_goals, result };
+            let prev = entry.with_overflow.insert(additional_depth, with_overflow);
+            assert!(prev.is_none());
         } else {
-            entry.success = Some(Success { data, additional_depth });
+            let prev = entry.success.replace(Success { additional_depth, nested_goals, result });
+            assert!(prev.is_none());
         }
     }
 
@@ -80,36 +71,37 @@ impl<X: Cx> GlobalCache<X> {
         &'a self,
         cx: X,
         input: X::Input,
-        stack: &IndexVec<StackDepth, StackEntry<X>>,
         available_depth: AvailableDepth,
+        mut candidate_is_applicable: impl FnMut(&NestedGoals<X>) -> bool,
     ) -> Option<CacheData<'a, X>> {
         let entry = self.map.get(&input)?;
-        if stack.iter().any(|e| entry.nested_goals.contains(&e.input)) {
-            return None;
-        }
-
-        if let Some(ref success) = entry.success {
-            if available_depth.cache_entry_is_applicable(success.additional_depth) {
-                let QueryData { result, proof_tree } = cx.get_tracked(&success.data);
+        if let Some(Success { additional_depth, ref nested_goals, ref result }) = entry.success {
+            if available_depth.cache_entry_is_applicable(additional_depth)
+                && candidate_is_applicable(nested_goals)
+            {
                 return Some(CacheData {
-                    result,
-                    proof_tree,
-                    additional_depth: success.additional_depth,
+                    result: cx.get_tracked(&result),
+                    additional_depth,
                     encountered_overflow: false,
-                    nested_goals: &entry.nested_goals,
+                    nested_goals,
                 });
             }
         }
 
-        entry.with_overflow.get(&available_depth.0).map(|e| {
-            let QueryData { result, proof_tree } = cx.get_tracked(e);
-            CacheData {
-                result,
-                proof_tree,
-                additional_depth: available_depth.0,
-                encountered_overflow: true,
-                nested_goals: &entry.nested_goals,
+        let additional_depth = available_depth.0;
+        if let Some(WithOverflow { nested_goals, result }) =
+            entry.with_overflow.get(&additional_depth)
+        {
+            if candidate_is_applicable(nested_goals) {
+                return Some(CacheData {
+                    result: cx.get_tracked(result),
+                    additional_depth,
+                    encountered_overflow: true,
+                    nested_goals,
+                });
             }
-        })
+        }
+
+        None
     }
 }
diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs
index 4abf99b1ded..d47c9e725f3 100644
--- a/compiler/rustc_type_ir/src/search_graph/mod.rs
+++ b/compiler/rustc_type_ir/src/search_graph/mod.rs
@@ -1,19 +1,32 @@
+/// The search graph is responsible for caching and cycle detection in the trait
+/// solver. Making sure that caching doesn't result in soundness bugs or unstable
+/// query results is very challenging and makes this one of the most-involved
+/// self-contained components of the compiler.
+///
+/// We added fuzzing support to test its correctness. The fuzzers used to verify
+/// the current implementation can be found in https://github.com/lcnr/search_graph_fuzz.
+///
+/// This is just a quick overview of the general design, please check out the relevant
+/// [rustc-dev-guide chapter](https://rustc-dev-guide.rust-lang.org/solve/caching.html) for
+/// more details. Caching is split between a global cache and the per-cycle `provisional_cache`.
+/// The global cache has to be completely unobservable, while the per-cycle cache may impact
+/// behavior as long as the resulting behavior is still correct.
+use std::cmp::Ordering;
+use std::collections::BTreeSet;
 use std::fmt::Debug;
 use std::hash::Hash;
 use std::marker::PhantomData;
-use std::mem;
 
 use derive_where::derive_where;
 use rustc_index::{Idx, IndexVec};
 use tracing::debug;
 
-use crate::data_structures::{HashMap, HashSet};
+use crate::data_structures::HashMap;
 use crate::solve::SolverMode;
 
 mod global_cache;
 use global_cache::CacheData;
 pub use global_cache::GlobalCache;
-mod validate;
 
 /// The search graph does not simply use `Interner` directly
 /// to enable its fuzzing without having to stub the rest of
@@ -22,7 +35,6 @@ mod validate;
 /// about `Input` and `Result` as they are implementation details
 /// of the search graph.
 pub trait Cx: Copy {
-    type ProofTree: Debug + Copy;
     type Input: Debug + Eq + Hash + Copy;
     type Result: Debug + Eq + Hash + Copy;
 
@@ -43,30 +55,41 @@ pub trait Cx: Copy {
     ) -> R;
 }
 
-pub trait ProofTreeBuilder<X: Cx> {
-    fn try_apply_proof_tree(&mut self, proof_tree: X::ProofTree) -> bool;
-    fn on_provisional_cache_hit(&mut self);
-    fn on_cycle_in_stack(&mut self);
-    fn finalize_canonical_goal_evaluation(&mut self, cx: X) -> X::ProofTree;
-}
-
 pub trait Delegate {
     type Cx: Cx;
+    /// Whether to use the provisional cache. Set to `false` by a fuzzer when
+    /// validating the search graph.
+    const ENABLE_PROVISIONAL_CACHE: bool;
+    type ValidationScope;
+    /// Returning `Some` disables the global cache for the current goal.
+    ///
+    /// The `ValidationScope` is used when fuzzing the search graph to track
+    /// for which goals the global cache has been disabled. This is necessary
+    /// as we may otherwise ignore the global cache entry for some goal `G`
+    /// only to later use it, failing to detect a cycle goal and potentially
+    /// changing the result.
+    fn enter_validation_scope(
+        cx: Self::Cx,
+        input: <Self::Cx as Cx>::Input,
+    ) -> Option<Self::ValidationScope>;
+
     const FIXPOINT_STEP_LIMIT: usize;
-    type ProofTreeBuilder: ProofTreeBuilder<Self::Cx>;
 
+    type ProofTreeBuilder;
+    fn inspect_is_noop(inspect: &mut Self::ProofTreeBuilder) -> bool;
+
+    const DIVIDE_AVAILABLE_DEPTH_ON_OVERFLOW: usize;
     fn recursion_limit(cx: Self::Cx) -> usize;
 
     fn initial_provisional_result(
         cx: Self::Cx,
-        kind: CycleKind,
+        kind: PathKind,
         input: <Self::Cx as Cx>::Input,
     ) -> <Self::Cx as Cx>::Result;
-    fn reached_fixpoint(
+    fn is_initial_provisional_result(
         cx: Self::Cx,
-        kind: UsageKind,
+        kind: PathKind,
         input: <Self::Cx as Cx>::Input,
-        provisional_result: Option<<Self::Cx as Cx>::Result>,
         result: <Self::Cx as Cx>::Result,
     ) -> bool;
     fn on_stack_overflow(
@@ -79,6 +102,13 @@ pub trait Delegate {
         input: <Self::Cx as Cx>::Input,
     ) -> <Self::Cx as Cx>::Result;
 
+    fn is_ambiguous_result(result: <Self::Cx as Cx>::Result) -> bool;
+    fn propagate_ambiguity(
+        cx: Self::Cx,
+        for_input: <Self::Cx as Cx>::Input,
+        from_result: <Self::Cx as Cx>::Result,
+    ) -> <Self::Cx as Cx>::Result;
+
     fn step_is_coinductive(cx: Self::Cx, input: <Self::Cx as Cx>::Input) -> bool;
 }
 
@@ -86,19 +116,20 @@ pub trait Delegate {
 /// result. In the case we return an initial provisional result depending
 /// on the kind of cycle.
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum CycleKind {
+pub enum PathKind {
     Coinductive,
     Inductive,
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
 pub enum UsageKind {
-    Single(CycleKind),
+    Single(PathKind),
     Mixed,
 }
 impl UsageKind {
     fn merge(self, other: Self) -> Self {
         match (self, other) {
+            (UsageKind::Mixed, _) | (_, UsageKind::Mixed) => UsageKind::Mixed,
             (UsageKind::Single(lhs), UsageKind::Single(rhs)) => {
                 if lhs == rhs {
                     UsageKind::Single(lhs)
@@ -106,11 +137,11 @@ impl UsageKind {
                     UsageKind::Mixed
                 }
             }
-            (UsageKind::Mixed, UsageKind::Mixed)
-            | (UsageKind::Mixed, UsageKind::Single(_))
-            | (UsageKind::Single(_), UsageKind::Mixed) => UsageKind::Mixed,
         }
     }
+    fn and_merge(&mut self, other: Self) {
+        *self = self.merge(other);
+    }
 }
 
 #[derive(Debug, Clone, Copy)]
@@ -132,7 +163,7 @@ impl AvailableDepth {
             }
 
             Some(if last.encountered_overflow {
-                AvailableDepth(last.available_depth.0 / 2)
+                AvailableDepth(last.available_depth.0 / D::DIVIDE_AVAILABLE_DEPTH_ON_OVERFLOW)
             } else {
                 AvailableDepth(last.available_depth.0 - 1)
             })
@@ -148,97 +179,181 @@ impl AvailableDepth {
     }
 }
 
+/// All cycle heads a given goal depends on, ordered by their stack depth.
+///
+/// We therefore pop the cycle heads from highest to lowest.
+#[derive(Clone, Debug, PartialEq, Eq, Default)]
+struct CycleHeads {
+    heads: BTreeSet<StackDepth>,
+}
+
+impl CycleHeads {
+    fn is_empty(&self) -> bool {
+        self.heads.is_empty()
+    }
+
+    fn highest_cycle_head(&self) -> StackDepth {
+        *self.heads.last().unwrap()
+    }
+
+    fn opt_highest_cycle_head(&self) -> Option<StackDepth> {
+        self.heads.last().copied()
+    }
+
+    fn opt_lowest_cycle_head(&self) -> Option<StackDepth> {
+        self.heads.first().copied()
+    }
+
+    fn remove_highest_cycle_head(&mut self) {
+        let last = self.heads.pop_last();
+        debug_assert_ne!(last, None);
+    }
+
+    fn insert(&mut self, head: StackDepth) {
+        self.heads.insert(head);
+    }
+
+    fn merge(&mut self, heads: &CycleHeads) {
+        for &head in heads.heads.iter() {
+            self.insert(head);
+        }
+    }
+
+    /// Update the cycle heads of a goal at depth `this` given the cycle heads
+    /// of a nested goal. This merges the heads after filtering the parent goal
+    /// itself.
+    fn extend_from_child(&mut self, this: StackDepth, child: &CycleHeads) {
+        for &head in child.heads.iter() {
+            match head.cmp(&this) {
+                Ordering::Less => {}
+                Ordering::Equal => continue,
+                Ordering::Greater => unreachable!(),
+            }
+
+            self.insert(head);
+        }
+    }
+}
+
+/// The nested goals of each stack entry and the path from the
+/// stack entry to that nested goal.
+///
+/// We only start tracking nested goals once we've either encountered
+/// overflow or a solver cycle. This is a performance optimization to
+/// avoid tracking nested goals on the happy path.
+///
+/// We use nested goals for two reasons:
+/// - when rebasing provisional cache entries
+/// - when checking whether we have to ignore a global cache entry as reevaluating
+///   it would encounter a cycle or use a provisional cache entry.
+///
+/// We need to disable the global cache if using it would hide a cycle, as
+/// cycles can impact behavior. The cycle ABA may have different final
+/// results from a the cycle BAB depending on the cycle root.
+#[derive_where(Debug, Default; X: Cx)]
+struct NestedGoals<X: Cx> {
+    nested_goals: HashMap<X::Input, UsageKind>,
+}
+impl<X: Cx> NestedGoals<X> {
+    fn is_empty(&self) -> bool {
+        self.nested_goals.is_empty()
+    }
+
+    fn insert(&mut self, input: X::Input, path_from_entry: UsageKind) {
+        self.nested_goals.entry(input).or_insert(path_from_entry).and_merge(path_from_entry);
+    }
+
+    fn merge(&mut self, nested_goals: &NestedGoals<X>) {
+        #[allow(rustc::potential_query_instability)]
+        for (input, path_from_entry) in nested_goals.iter() {
+            self.insert(input, path_from_entry);
+        }
+    }
+
+    /// Adds the nested goals of a nested goal, given that the path `step_kind` from this goal
+    /// to the parent goal.
+    ///
+    /// If the path from this goal to the nested goal is inductive, the paths from this goal
+    /// to all nested goals of that nested goal are also inductive. Otherwise the paths are
+    /// the same as for the child.
+    fn extend_from_child(&mut self, step_kind: PathKind, nested_goals: &NestedGoals<X>) {
+        #[allow(rustc::potential_query_instability)]
+        for (input, path_from_entry) in nested_goals.iter() {
+            let path_from_entry = match step_kind {
+                PathKind::Coinductive => path_from_entry,
+                PathKind::Inductive => UsageKind::Single(PathKind::Inductive),
+            };
+            self.insert(input, path_from_entry);
+        }
+    }
+
+    #[rustc_lint_query_instability]
+    #[allow(rustc::potential_query_instability)]
+    fn iter(&self) -> impl Iterator<Item = (X::Input, UsageKind)> + '_ {
+        self.nested_goals.iter().map(|(i, p)| (*i, *p))
+    }
+
+    fn get(&self, input: X::Input) -> Option<UsageKind> {
+        self.nested_goals.get(&input).copied()
+    }
+
+    fn contains(&self, input: X::Input) -> bool {
+        self.nested_goals.contains_key(&input)
+    }
+}
+
 rustc_index::newtype_index! {
     #[orderable]
     #[gate_rustc_only]
     pub struct StackDepth {}
 }
 
+/// Stack entries of the evaluation stack. Its fields tend to be lazily
+/// when popping a child goal or completely immutable.
 #[derive_where(Debug; X: Cx)]
 struct StackEntry<X: Cx> {
     input: X::Input,
 
+    /// The available depth of a given goal, immutable.
     available_depth: AvailableDepth,
 
     /// The maximum depth reached by this stack entry, only up-to date
     /// for the top of the stack and lazily updated for the rest.
     reached_depth: StackDepth,
 
-    /// Whether this entry is a non-root cycle participant.
-    ///
-    /// We must not move the result of non-root cycle participants to the
-    /// global cache. We store the highest stack depth of a head of a cycle
-    /// this goal is involved in. This necessary to soundly cache its
-    /// provisional result.
-    non_root_cycle_participant: Option<StackDepth>,
+    /// All cycle heads this goal depends on. Lazily updated and only
+    /// up-to date for the top of the stack.
+    heads: CycleHeads,
 
+    /// Whether evaluating this goal encountered overflow. Lazily updated.
     encountered_overflow: bool,
 
+    /// Whether this goal has been used as the root of a cycle. This gets
+    /// eagerly updated when encountering a cycle.
     has_been_used: Option<UsageKind>,
 
-    /// We put only the root goal of a coinductive cycle into the global cache.
-    ///
-    /// If we were to use that result when later trying to prove another cycle
-    /// participant, we can end up with unstable query results.
-    ///
-    /// See tests/ui/next-solver/coinduction/incompleteness-unstable-result.rs for
-    /// an example of where this is needed.
-    ///
-    /// There can  be multiple roots on the same stack, so we need to track
-    /// cycle participants per root:
-    /// ```plain
-    /// A :- B
-    /// B :- A, C
-    /// C :- D
-    /// D :- C
-    /// ```
-    nested_goals: HashSet<X::Input>,
+    /// The nested goals of this goal, see the doc comment of the type.
+    nested_goals: NestedGoals<X>,
+
     /// Starts out as `None` and gets set when rerunning this
     /// goal in case we encounter a cycle.
     provisional_result: Option<X::Result>,
 }
 
-/// The provisional result for a goal which is not on the stack.
-#[derive(Debug)]
-struct DetachedEntry<X: Cx> {
-    /// The head of the smallest non-trivial cycle involving this entry.
-    ///
-    /// Given the following rules, when proving `A` the head for
-    /// the provisional entry of `C` would be `B`.
-    /// ```plain
-    /// A :- B
-    /// B :- C
-    /// C :- A + B + C
-    /// ```
-    head: StackDepth,
-    result: X::Result,
-}
-
-/// Stores the stack depth of a currently evaluated goal *and* already
-/// computed results for goals which depend on other goals still on the stack.
-///
-/// The provisional result may depend on whether the stack above it is inductive
-/// or coinductive. Because of this, we store separate provisional results for
-/// each case. If an provisional entry is not applicable, it may be the case
-/// that we already have provisional result while computing a goal. In this case
-/// we prefer the provisional result to potentially avoid fixpoint iterations.
-/// See tests/ui/traits/next-solver/cycles/mixed-cycles-2.rs for an example.
-///
-/// The provisional cache can theoretically result in changes to the observable behavior,
-/// see tests/ui/traits/next-solver/cycles/provisional-cache-impacts-behavior.rs.
-#[derive_where(Default; X: Cx)]
+/// A provisional result of an already computed goals which depends on other
+/// goals still on the stack.
+#[derive_where(Debug; X: Cx)]
 struct ProvisionalCacheEntry<X: Cx> {
-    stack_depth: Option<StackDepth>,
-    with_inductive_stack: Option<DetachedEntry<X>>,
-    with_coinductive_stack: Option<DetachedEntry<X>>,
-}
-
-impl<X: Cx> ProvisionalCacheEntry<X> {
-    fn is_empty(&self) -> bool {
-        self.stack_depth.is_none()
-            && self.with_inductive_stack.is_none()
-            && self.with_coinductive_stack.is_none()
-    }
+    /// Whether evaluating the goal encountered overflow. This is used to
+    /// disable the cache entry except if the last goal on the stack is
+    /// already involved in this cycle.
+    encountered_overflow: bool,
+    /// All cycle heads this cache entry depends on.
+    heads: CycleHeads,
+    /// The path from the highest cycle head to this goal.
+    path_from_head: PathKind,
+    nested_goals: NestedGoals<X>,
+    result: X::Result,
 }
 
 pub struct SearchGraph<D: Delegate<Cx = X>, X: Cx = <D as Delegate>::Cx> {
@@ -247,7 +362,11 @@ pub struct SearchGraph<D: Delegate<Cx = X>, X: Cx = <D as Delegate>::Cx> {
     ///
     /// An element is *deeper* in the stack if its index is *lower*.
     stack: IndexVec<StackDepth, StackEntry<X>>,
-    provisional_cache: HashMap<X::Input, ProvisionalCacheEntry<X>>,
+    /// The provisional cache contains entries for already computed goals which
+    /// still depend on goals higher-up in the stack. We don't move them to the
+    /// global cache and track them locally instead. A provisional cache entry
+    /// is only valid until the result of one of its cycle heads changes.
+    provisional_cache: HashMap<X::Input, Vec<ProvisionalCacheEntry<X>>>,
 
     _marker: PhantomData<D>,
 }
@@ -266,77 +385,66 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
         self.mode
     }
 
-    fn update_parent_goal(&mut self, reached_depth: StackDepth, encountered_overflow: bool) {
-        if let Some(parent) = self.stack.raw.last_mut() {
+    /// Lazily update the stack entry for the parent goal.
+    /// This behavior is shared between actually evaluating goals
+    /// and using existing global cache entries to make sure they
+    /// have the same impact on the remaining evaluation.
+    fn update_parent_goal(
+        cx: X,
+        stack: &mut IndexVec<StackDepth, StackEntry<X>>,
+        reached_depth: StackDepth,
+        heads: &CycleHeads,
+        encountered_overflow: bool,
+        nested_goals: &NestedGoals<X>,
+    ) {
+        if let Some(parent_index) = stack.last_index() {
+            let parent = &mut stack[parent_index];
             parent.reached_depth = parent.reached_depth.max(reached_depth);
             parent.encountered_overflow |= encountered_overflow;
+
+            parent.heads.extend_from_child(parent_index, heads);
+            let step_kind = Self::step_kind(cx, parent.input);
+            parent.nested_goals.extend_from_child(step_kind, nested_goals);
+            // Once we've got goals which encountered overflow or a cycle,
+            // we track all goals whose behavior may depend depend on these
+            // goals as this change may cause them to now depend on additional
+            // goals, resulting in new cycles. See the dev-guide for examples.
+            if !nested_goals.is_empty() {
+                parent.nested_goals.insert(parent.input, UsageKind::Single(PathKind::Coinductive))
+            }
         }
     }
 
     pub fn is_empty(&self) -> bool {
-        self.stack.is_empty()
+        if self.stack.is_empty() {
+            debug_assert!(self.provisional_cache.is_empty());
+            true
+        } else {
+            false
+        }
     }
 
-    fn stack_coinductive_from(
-        cx: X,
-        stack: &IndexVec<StackDepth, StackEntry<X>>,
-        head: StackDepth,
-    ) -> bool {
-        stack.raw[head.index()..].iter().all(|entry| D::step_is_coinductive(cx, entry.input))
-    }
-
-    // When encountering a solver cycle, the result of the current goal
-    // depends on goals lower on the stack.
-    //
-    // We have to therefore be careful when caching goals. Only the final result
-    // of the cycle root, i.e. the lowest goal on the stack involved in this cycle,
-    // is moved to the global cache while all others are stored in a provisional cache.
-    //
-    // We update both the head of this cycle to rerun its evaluation until
-    // we reach a fixpoint and all other cycle participants to make sure that
-    // their result does not get moved to the global cache.
-    fn tag_cycle_participants(
-        stack: &mut IndexVec<StackDepth, StackEntry<X>>,
-        usage_kind: Option<UsageKind>,
-        head: StackDepth,
-    ) {
-        if let Some(usage_kind) = usage_kind {
-            stack[head].has_been_used =
-                Some(stack[head].has_been_used.map_or(usage_kind, |prev| prev.merge(usage_kind)));
-        }
-        debug_assert!(stack[head].has_been_used.is_some());
-
-        // The current root of these cycles. Note that this may not be the final
-        // root in case a later goal depends on a goal higher up the stack.
-        let mut current_root = head;
-        while let Some(parent) = stack[current_root].non_root_cycle_participant {
-            current_root = parent;
-            debug_assert!(stack[current_root].has_been_used.is_some());
-        }
+    /// The number of goals currently in the search graph. This should only be
+    /// used for debugging purposes.
+    pub fn debug_current_depth(&self) -> usize {
+        self.stack.len()
+    }
 
-        let (stack, cycle_participants) = stack.raw.split_at_mut(head.index() + 1);
-        let current_cycle_root = &mut stack[current_root.as_usize()];
-        for entry in cycle_participants {
-            entry.non_root_cycle_participant = entry.non_root_cycle_participant.max(Some(head));
-            current_cycle_root.nested_goals.insert(entry.input);
-            current_cycle_root.nested_goals.extend(mem::take(&mut entry.nested_goals));
-        }
+    fn step_kind(cx: X, input: X::Input) -> PathKind {
+        if D::step_is_coinductive(cx, input) { PathKind::Coinductive } else { PathKind::Inductive }
     }
 
-    fn clear_dependent_provisional_results(
-        provisional_cache: &mut HashMap<X::Input, ProvisionalCacheEntry<X>>,
+    /// Whether the path from `head` to the current stack entry is inductive or coinductive.
+    fn stack_path_kind(
+        cx: X,
+        stack: &IndexVec<StackDepth, StackEntry<X>>,
         head: StackDepth,
-    ) {
-        #[allow(rustc::potential_query_instability)]
-        provisional_cache.retain(|_, entry| {
-            if entry.with_coinductive_stack.as_ref().is_some_and(|p| p.head == head) {
-                entry.with_coinductive_stack.take();
-            }
-            if entry.with_inductive_stack.as_ref().is_some_and(|p| p.head == head) {
-                entry.with_inductive_stack.take();
-            }
-            !entry.is_empty()
-        });
+    ) -> PathKind {
+        if stack.raw[head.index()..].iter().all(|entry| D::step_is_coinductive(cx, entry.input)) {
+            PathKind::Coinductive
+        } else {
+            PathKind::Inductive
+        }
     }
 
     /// Probably the most involved method of the whole solver.
@@ -348,89 +456,65 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
         cx: X,
         input: X::Input,
         inspect: &mut D::ProofTreeBuilder,
-        mut prove_goal: impl FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result,
+        mut evaluate_goal: impl FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result,
     ) -> X::Result {
-        self.check_invariants();
-        // Check for overflow.
         let Some(available_depth) = AvailableDepth::allowed_depth_for_nested::<D>(cx, &self.stack)
         else {
-            if let Some(last) = self.stack.raw.last_mut() {
-                last.encountered_overflow = true;
-            }
-
-            debug!("encountered stack overflow");
-            return D::on_stack_overflow(cx, inspect, input);
+            return self.handle_overflow(cx, input, inspect);
         };
 
-        if let Some(result) = self.lookup_global_cache(cx, input, available_depth, inspect) {
+        // We check the provisional cache before checking the global cache. This simplifies
+        // the implementation as we can avoid worrying about cases where both the global and
+        // provisional cache may apply, e.g. consider the following example
+        //
+        // - xxBA overflow
+        // - A
+        //     - BA cycle
+        //     - CB :x:
+        if let Some(result) = self.lookup_provisional_cache(cx, input) {
             return result;
         }
 
-        // Check whether the goal is in the provisional cache.
-        // The provisional result may rely on the path to its cycle roots,
-        // so we have to check the path of the current goal matches that of
-        // the cache entry.
-        let cache_entry = self.provisional_cache.entry(input).or_default();
-        if let Some(entry) = cache_entry
-            .with_coinductive_stack
-            .as_ref()
-            .filter(|p| Self::stack_coinductive_from(cx, &self.stack, p.head))
-            .or_else(|| {
-                cache_entry
-                    .with_inductive_stack
-                    .as_ref()
-                    .filter(|p| !Self::stack_coinductive_from(cx, &self.stack, p.head))
-            })
-        {
-            debug!("provisional cache hit");
-            // We have a nested goal which is already in the provisional cache, use
-            // its result. We do not provide any usage kind as that should have been
-            // already set correctly while computing the cache entry.
-            inspect.on_provisional_cache_hit();
-            Self::tag_cycle_participants(&mut self.stack, None, entry.head);
-            return entry.result;
-        } else if let Some(stack_depth) = cache_entry.stack_depth {
-            debug!("encountered cycle with depth {stack_depth:?}");
-            // We have a nested goal which directly relies on a goal deeper in the stack.
-            //
-            // We start by tagging all cycle participants, as that's necessary for caching.
-            //
-            // Finally we can return either the provisional response or the initial response
-            // in case we're in the first fixpoint iteration for this goal.
-            inspect.on_cycle_in_stack();
-
-            let is_coinductive_cycle = Self::stack_coinductive_from(cx, &self.stack, stack_depth);
-            let cycle_kind =
-                if is_coinductive_cycle { CycleKind::Coinductive } else { CycleKind::Inductive };
-            Self::tag_cycle_participants(
-                &mut self.stack,
-                Some(UsageKind::Single(cycle_kind)),
-                stack_depth,
-            );
-
-            // Return the provisional result or, if we're in the first iteration,
-            // start with no constraints.
-            return if let Some(result) = self.stack[stack_depth].provisional_result {
-                result
-            } else {
-                D::initial_provisional_result(cx, cycle_kind, input)
-            };
+        // Lookup the global cache unless we're building proof trees or are currently
+        // fuzzing.
+        let validate_cache = if !D::inspect_is_noop(inspect) {
+            None
+        } else if let Some(scope) = D::enter_validation_scope(cx, input) {
+            // When validating the global cache we need to track the goals for which the
+            // global cache has been disabled as it may otherwise change the result for
+            // cyclic goals. We don't care about goals which are not on the current stack
+            // so it's fine to drop their scope eagerly.
+            self.lookup_global_cache_untracked(cx, input, available_depth)
+                .inspect(|expected| debug!(?expected, "validate cache entry"))
+                .map(|r| (scope, r))
+        } else if let Some(result) = self.lookup_global_cache(cx, input, available_depth) {
+            return result;
         } else {
-            // No entry, we push this goal on the stack and try to prove it.
-            let depth = self.stack.next_index();
-            let entry = StackEntry {
-                input,
-                available_depth,
-                reached_depth: depth,
-                non_root_cycle_participant: None,
-                encountered_overflow: false,
-                has_been_used: None,
-                nested_goals: Default::default(),
-                provisional_result: None,
-            };
-            assert_eq!(self.stack.push(entry), depth);
-            cache_entry.stack_depth = Some(depth);
+            None
+        };
+
+        // Detect cycles on the stack. We do this after the global cache lookup to
+        // avoid iterating over the stack in case a goal has already been computed.
+        // This may not have an actual performance impact and we could reorder them
+        // as it may reduce the number of `nested_goals` we need to track.
+        if let Some(result) = self.check_cycle_on_stack(cx, input) {
+            debug_assert!(validate_cache.is_none(), "global cache and cycle on stack");
+            return result;
+        }
+
+        // Unfortunate, it looks like we actually have to compute this goalrar.
+        let depth = self.stack.next_index();
+        let entry = StackEntry {
+            input,
+            available_depth,
+            reached_depth: depth,
+            heads: Default::default(),
+            encountered_overflow: false,
+            has_been_used: None,
+            nested_goals: Default::default(),
+            provisional_result: None,
         };
+        assert_eq!(self.stack.push(entry), depth);
 
         // This is for global caching, so we properly track query dependencies.
         // Everything that affects the `result` should be performed within this
@@ -439,65 +523,320 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
         // must not be added to the global cache. Notably, this is the case for
         // trait solver cycles participants.
         let ((final_entry, result), dep_node) = cx.with_cached_task(|| {
-            for _ in 0..D::FIXPOINT_STEP_LIMIT {
-                match self.fixpoint_step_in_task(cx, input, inspect, &mut prove_goal) {
-                    StepResult::Done(final_entry, result) => return (final_entry, result),
-                    StepResult::HasChanged => debug!("fixpoint changed provisional results"),
-                }
+            self.evaluate_goal_in_task(cx, input, inspect, &mut evaluate_goal)
+        });
+
+        // We've finished computing the goal and have popped it from the stack,
+        // lazily update its parent goal.
+        Self::update_parent_goal(
+            cx,
+            &mut self.stack,
+            final_entry.reached_depth,
+            &final_entry.heads,
+            final_entry.encountered_overflow,
+            &final_entry.nested_goals,
+        );
+
+        // We're now done with this goal. We only add the root of cycles to the global cache.
+        // In case this goal is involved in a larger cycle add it to the provisional cache.
+        if final_entry.heads.is_empty() {
+            if let Some((_scope, expected)) = validate_cache {
+                // Do not try to move a goal into the cache again if we're testing
+                // the global cache.
+                assert_eq!(result, expected, "input={input:?}");
+            } else if D::inspect_is_noop(inspect) {
+                self.insert_global_cache(cx, input, final_entry, result, dep_node)
             }
+        } else if D::ENABLE_PROVISIONAL_CACHE {
+            debug_assert!(validate_cache.is_none());
+            let entry = self.provisional_cache.entry(input).or_default();
+            let StackEntry { heads, nested_goals, encountered_overflow, .. } = final_entry;
+            let path_from_head = Self::stack_path_kind(cx, &self.stack, heads.highest_cycle_head());
+            entry.push(ProvisionalCacheEntry {
+                encountered_overflow,
+                heads,
+                path_from_head,
+                nested_goals,
+                result,
+            });
+        } else {
+            debug_assert!(validate_cache.is_none());
+        }
+
+        result
+    }
+
+    fn handle_overflow(
+        &mut self,
+        cx: X,
+        input: X::Input,
+        inspect: &mut D::ProofTreeBuilder,
+    ) -> X::Result {
+        if let Some(last) = self.stack.raw.last_mut() {
+            last.encountered_overflow = true;
+            // If computing a goal `B` depends on another goal `A` and
+            // `A` has a nested goal which overflows, then computing `B`
+            // at the same depth, but with `A` already on the stack,
+            // would encounter a solver cycle instead, potentially
+            // changing the result.
+            //
+            // We must therefore not use the global cache entry for `B` in that case.
+            // See tests/ui/traits/next-solver/cycles/hidden-by-overflow.rs
+            last.nested_goals.insert(last.input, UsageKind::Single(PathKind::Coinductive));
+        }
 
-            debug!("canonical cycle overflow");
-            let current_entry = self.stack.pop().unwrap();
-            debug_assert!(current_entry.has_been_used.is_none());
-            let result = D::on_fixpoint_overflow(cx, input);
-            (current_entry, result)
+        debug!("encountered stack overflow");
+        D::on_stack_overflow(cx, inspect, input)
+    }
+
+    /// When reevaluating a goal with a changed provisional result, all provisional cache entry
+    /// which depend on this goal get invalidated.
+    fn clear_dependent_provisional_results(&mut self) {
+        let head = self.stack.next_index();
+        #[allow(rustc::potential_query_instability)]
+        self.provisional_cache.retain(|_, entries| {
+            entries.retain(|entry| entry.heads.highest_cycle_head() != head);
+            !entries.is_empty()
         });
+    }
 
-        let proof_tree = inspect.finalize_canonical_goal_evaluation(cx);
+    /// A necessary optimization to handle complex solver cycles. A provisional cache entry
+    /// relies on a set of cycle heads and the path towards these heads. When popping a cycle
+    /// head from the stack after we've finished computing it, we can't be sure that the
+    /// provisional cache entry is still applicable. We need to keep the cache entries to
+    /// prevent hangs.
+    ///
+    /// What we therefore do is check whether the cycle kind of all cycles the goal of a
+    /// provisional cache entry is involved in would stay the same when computing the
+    /// goal without its cycle head on the stack. For more details, see the relevant
+    /// [rustc-dev-guide chapter](https://rustc-dev-guide.rust-lang.org/solve/caching.html).
+    ///
+    /// This can be thought of rotating the sub-tree of this provisional result and changing
+    /// its entry point while making sure that all paths through this sub-tree stay the same.
+    ///
+    ///
+    /// In case the popped cycle head failed to reach a fixpoint anything which depends on
+    /// its provisional result is invalid. Actually discarding provisional cache entries in
+    /// this case would cause hangs, so we instead change the result of dependant provisional
+    /// cache entries to also be ambiguous. This causes some undesirable ambiguity for nested
+    /// goals whose result doesn't actually depend on this cycle head, but that's acceptable
+    /// to me.
+    fn rebase_provisional_cache_entries(
+        &mut self,
+        cx: X,
+        stack_entry: &StackEntry<X>,
+        mut mutate_result: impl FnMut(X::Input, X::Result) -> X::Result,
+    ) {
+        let head = self.stack.next_index();
+        #[allow(rustc::potential_query_instability)]
+        self.provisional_cache.retain(|&input, entries| {
+            entries.retain_mut(|entry| {
+                let ProvisionalCacheEntry {
+                    encountered_overflow: _,
+                    heads,
+                    path_from_head,
+                    nested_goals,
+                    result,
+                } = entry;
+                if heads.highest_cycle_head() != head {
+                    return true;
+                }
 
-        self.update_parent_goal(final_entry.reached_depth, final_entry.encountered_overflow);
+                // We don't try rebasing if the path from the current head
+                // to the cache entry is not coinductive or if the path from
+                // the cache entry to the current head is not coinductive.
+                //
+                // Both of these constraints could be weakened, but by only
+                // accepting coinductive paths we don't have to worry about
+                // changing the cycle kind of the remaining cycles. We can
+                // extend this in the future once there's a known issue
+                // caused by it.
+                if *path_from_head != PathKind::Coinductive
+                    || nested_goals.get(stack_entry.input).unwrap()
+                        != UsageKind::Single(PathKind::Coinductive)
+                {
+                    return false;
+                }
 
-        // We're now done with this goal. In case this goal is involved in a larger cycle
-        // do not remove it from the provisional cache and update its provisional result.
-        // We only add the root of cycles to the global cache.
-        if let Some(head) = final_entry.non_root_cycle_participant {
-            let coinductive_stack = Self::stack_coinductive_from(cx, &self.stack, head);
+                // Merge the cycle heads of the provisional cache entry and the
+                // popped head. If the popped cycle head was a root, discard all
+                // provisional cache entries which depend on it.
+                heads.remove_highest_cycle_head();
+                heads.merge(&stack_entry.heads);
+                let Some(head) = heads.opt_highest_cycle_head() else {
+                    return false;
+                };
 
-            let entry = self.provisional_cache.get_mut(&input).unwrap();
-            entry.stack_depth = None;
-            if coinductive_stack {
-                entry.with_coinductive_stack = Some(DetachedEntry { head, result });
-            } else {
-                entry.with_inductive_stack = Some(DetachedEntry { head, result });
+                // As we've made sure that the path from the new highest cycle
+                // head to the uses of the popped cycle head are fully coinductive,
+                // we can be sure that the paths to all nested goals of the popped
+                // cycle head remain the same. We can simply merge them.
+                nested_goals.merge(&stack_entry.nested_goals);
+                // We now care about the path from the next highest cycle head to the
+                // provisional cache entry.
+                *path_from_head = Self::stack_path_kind(cx, &self.stack, head);
+                // Mutate the result of the provisional cache entry in case we did
+                // not reach a fixpoint.
+                *result = mutate_result(input, *result);
+                true
+            });
+            !entries.is_empty()
+        });
+    }
+
+    fn lookup_provisional_cache(&mut self, cx: X, input: X::Input) -> Option<X::Result> {
+        if !D::ENABLE_PROVISIONAL_CACHE {
+            return None;
+        }
+
+        let entries = self.provisional_cache.get(&input)?;
+        for &ProvisionalCacheEntry {
+            encountered_overflow,
+            ref heads,
+            path_from_head,
+            ref nested_goals,
+            result,
+        } in entries
+        {
+            let head = heads.highest_cycle_head();
+            if encountered_overflow {
+                // This check is overly strict and very subtle. We need to make sure that if
+                // a global cache entry depends on some goal without adding it to its
+                // `nested_goals`, that goal must never have an applicable provisional
+                // cache entry to avoid incorrectly applying the cache entry.
+                //
+                // As we'd have to otherwise track literally all nested goals, we only
+                // apply provisional cache entries which encountered overflow once the
+                // current goal is already part of the same cycle. This check could be
+                // improved but seems to be good enough for now.
+                let last = self.stack.raw.last().unwrap();
+                if !last.heads.opt_lowest_cycle_head().is_some_and(|lowest| lowest <= head) {
+                    continue;
+                }
             }
-        } else {
-            // When encountering a cycle, both inductive and coinductive, we only
-            // move the root into the global cache. We also store all other cycle
-            // participants involved.
-            //
-            // We must not use the global cache entry of a root goal if a cycle
-            // participant is on the stack. This is necessary to prevent unstable
-            // results. See the comment of `StackEntry::nested_goals` for
-            // more details.
-            self.provisional_cache.remove(&input);
-            let additional_depth = final_entry.reached_depth.as_usize() - self.stack.len();
-            cx.with_global_cache(self.mode, |cache| {
-                cache.insert(
+
+            // A provisional cache entry is only valid if the current path from its
+            // highest cycle head to the goal is the same.
+            if path_from_head == Self::stack_path_kind(cx, &self.stack, head) {
+                // While we don't have to track the full depth of the provisional cache entry,
+                // we do have to increment the required depth by one as we'd have already failed
+                // with overflow otherwise
+                let next_index = self.stack.next_index();
+                let last = &mut self.stack.raw.last_mut().unwrap();
+                let path_from_entry = Self::step_kind(cx, last.input);
+                last.nested_goals.insert(input, UsageKind::Single(path_from_entry));
+
+                Self::update_parent_goal(
                     cx,
-                    input,
-                    result,
-                    proof_tree,
-                    dep_node,
-                    additional_depth,
-                    final_entry.encountered_overflow,
-                    &final_entry.nested_goals,
-                )
-            })
+                    &mut self.stack,
+                    next_index,
+                    heads,
+                    false,
+                    nested_goals,
+                );
+                debug_assert!(self.stack[head].has_been_used.is_some());
+                debug!(?head, ?path_from_head, "provisional cache hit");
+                return Some(result);
+            }
         }
 
-        self.check_invariants();
+        None
+    }
 
-        result
+    /// Even if there is a global cache entry for a given goal, we need to make sure
+    /// evaluating this entry would not have ended up depending on either a goal
+    /// already on the stack or a provisional cache entry.
+    fn candidate_is_applicable(
+        cx: X,
+        stack: &IndexVec<StackDepth, StackEntry<X>>,
+        provisional_cache: &HashMap<X::Input, Vec<ProvisionalCacheEntry<X>>>,
+        nested_goals: &NestedGoals<X>,
+    ) -> bool {
+        // If the global cache entry didn't depend on any nested goals, it always
+        // applies.
+        if nested_goals.is_empty() {
+            return true;
+        }
+
+        // If a nested goal of the global cache entry is on the stack, we would
+        // definitely encounter a cycle.
+        if stack.iter().any(|e| nested_goals.contains(e.input)) {
+            debug!("cache entry not applicable due to stack");
+            return false;
+        }
+
+        // The global cache entry is also invalid if there's a provisional cache entry
+        // would apply for any of its nested goals.
+        #[allow(rustc::potential_query_instability)]
+        for (input, path_from_global_entry) in nested_goals.iter() {
+            let Some(entries) = provisional_cache.get(&input) else {
+                continue;
+            };
+
+            debug!(?input, ?path_from_global_entry, ?entries, "candidate_is_applicable");
+            // A provisional cache entry is applicable if the path to
+            // its highest cycle head is equal to the expected path.
+            for &ProvisionalCacheEntry {
+                encountered_overflow,
+                ref heads,
+                path_from_head,
+                nested_goals: _,
+                result: _,
+            } in entries.iter()
+            {
+                // We don't have to worry about provisional cache entries which encountered
+                // overflow, see the relevant comment in `lookup_provisional_cache`.
+                if encountered_overflow {
+                    continue;
+                }
+
+                // A provisional cache entry only applies if the path from its highest head
+                // matches the path when encountering the goal.
+                let head = heads.highest_cycle_head();
+                let full_path = match Self::stack_path_kind(cx, stack, head) {
+                    PathKind::Coinductive => path_from_global_entry,
+                    PathKind::Inductive => UsageKind::Single(PathKind::Inductive),
+                };
+
+                match (full_path, path_from_head) {
+                    (UsageKind::Mixed, _)
+                    | (UsageKind::Single(PathKind::Coinductive), PathKind::Coinductive)
+                    | (UsageKind::Single(PathKind::Inductive), PathKind::Inductive) => {
+                        debug!(
+                            ?full_path,
+                            ?path_from_head,
+                            "cache entry not applicable due to matching paths"
+                        );
+                        return false;
+                    }
+                    _ => debug!(?full_path, ?path_from_head, "paths don't match"),
+                }
+            }
+        }
+
+        true
+    }
+
+    /// Used when fuzzing the global cache. Accesses the global cache without
+    /// updating the state of the search graph.
+    fn lookup_global_cache_untracked(
+        &self,
+        cx: X,
+        input: X::Input,
+        available_depth: AvailableDepth,
+    ) -> Option<X::Result> {
+        cx.with_global_cache(self.mode, |cache| {
+            cache
+                .get(cx, input, available_depth, |nested_goals| {
+                    Self::candidate_is_applicable(
+                        cx,
+                        &self.stack,
+                        &self.provisional_cache,
+                        nested_goals,
+                    )
+                })
+                .map(|c| c.result)
+        })
     }
 
     /// Try to fetch a previously computed result from the global cache,
@@ -508,97 +847,206 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
         cx: X,
         input: X::Input,
         available_depth: AvailableDepth,
-        inspect: &mut D::ProofTreeBuilder,
     ) -> Option<X::Result> {
         cx.with_global_cache(self.mode, |cache| {
-            let CacheData {
-                result,
-                proof_tree,
-                additional_depth,
-                encountered_overflow,
-                nested_goals: _, // FIXME: consider nested goals here.
-            } = cache.get(cx, input, &self.stack, available_depth)?;
-
-            // If we're building a proof tree and the current cache entry does not
-            // contain a proof tree, we do not use the entry but instead recompute
-            // the goal. We simply overwrite the existing entry once we're done,
-            // caching the proof tree.
-            if !inspect.try_apply_proof_tree(proof_tree) {
-                return None;
-            }
+            let CacheData { result, additional_depth, encountered_overflow, nested_goals } = cache
+                .get(cx, input, available_depth, |nested_goals| {
+                    Self::candidate_is_applicable(
+                        cx,
+                        &self.stack,
+                        &self.provisional_cache,
+                        nested_goals,
+                    )
+                })?;
 
             // Update the reached depth of the current goal to make sure
             // its state is the same regardless of whether we've used the
             // global cache or not.
             let reached_depth = self.stack.next_index().plus(additional_depth);
-            self.update_parent_goal(reached_depth, encountered_overflow);
+            // We don't move cycle participants to the global cache, so the
+            // cycle heads are always empty.
+            let heads = Default::default();
+            Self::update_parent_goal(
+                cx,
+                &mut self.stack,
+                reached_depth,
+                &heads,
+                encountered_overflow,
+                nested_goals,
+            );
 
-            debug!("global cache hit");
+            debug!(?additional_depth, "global cache hit");
             Some(result)
         })
     }
-}
 
-enum StepResult<X: Cx> {
-    Done(StackEntry<X>, X::Result),
-    HasChanged,
-}
+    fn check_cycle_on_stack(&mut self, cx: X, input: X::Input) -> Option<X::Result> {
+        let (head, _stack_entry) = self.stack.iter_enumerated().find(|(_, e)| e.input == input)?;
+        debug!("encountered cycle with depth {head:?}");
+        // We have a nested goal which directly relies on a goal deeper in the stack.
+        //
+        // We start by tagging all cycle participants, as that's necessary for caching.
+        //
+        // Finally we can return either the provisional response or the initial response
+        // in case we're in the first fixpoint iteration for this goal.
+        let path_kind = Self::stack_path_kind(cx, &self.stack, head);
+        let usage_kind = UsageKind::Single(path_kind);
+        self.stack[head].has_been_used =
+            Some(self.stack[head].has_been_used.map_or(usage_kind, |prev| prev.merge(usage_kind)));
+
+        // Subtle: when encountering a cyclic goal, we still first checked for overflow,
+        // so we have to update the reached depth.
+        let next_index = self.stack.next_index();
+        let last_index = self.stack.last_index().unwrap();
+        let last = &mut self.stack[last_index];
+        last.reached_depth = last.reached_depth.max(next_index);
+
+        let path_from_entry = Self::step_kind(cx, last.input);
+        last.nested_goals.insert(input, UsageKind::Single(path_from_entry));
+        last.nested_goals.insert(last.input, UsageKind::Single(PathKind::Coinductive));
+        if last_index != head {
+            last.heads.insert(head);
+        }
+
+        // Return the provisional result or, if we're in the first iteration,
+        // start with no constraints.
+        if let Some(result) = self.stack[head].provisional_result {
+            Some(result)
+        } else {
+            Some(D::initial_provisional_result(cx, path_kind, input))
+        }
+    }
+
+    /// Whether we've reached a fixpoint when evaluating a cycle head.
+    fn reached_fixpoint(
+        &mut self,
+        cx: X,
+        stack_entry: &StackEntry<X>,
+        usage_kind: UsageKind,
+        result: X::Result,
+    ) -> bool {
+        if let Some(prev) = stack_entry.provisional_result {
+            prev == result
+        } else if let UsageKind::Single(kind) = usage_kind {
+            D::is_initial_provisional_result(cx, kind, stack_entry.input, result)
+        } else {
+            false
+        }
+    }
 
-impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
     /// When we encounter a coinductive cycle, we have to fetch the
     /// result of that cycle while we are still computing it. Because
     /// of this we continuously recompute the cycle until the result
     /// of the previous iteration is equal to the final result, at which
     /// point we are done.
-    fn fixpoint_step_in_task<F>(
+    fn evaluate_goal_in_task(
         &mut self,
         cx: X,
         input: X::Input,
         inspect: &mut D::ProofTreeBuilder,
-        prove_goal: &mut F,
-    ) -> StepResult<X>
-    where
-        F: FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result,
-    {
-        let result = prove_goal(self, inspect);
-        let stack_entry = self.stack.pop().unwrap();
-        debug_assert_eq!(stack_entry.input, input);
-
-        // If the current goal is not the root of a cycle, we are done.
-        let Some(usage_kind) = stack_entry.has_been_used else {
-            return StepResult::Done(stack_entry, result);
-        };
+        mut evaluate_goal: impl FnMut(&mut Self, &mut D::ProofTreeBuilder) -> X::Result,
+    ) -> (StackEntry<X>, X::Result) {
+        let mut i = 0;
+        loop {
+            let result = evaluate_goal(self, inspect);
+            let stack_entry = self.stack.pop().unwrap();
+            debug_assert_eq!(stack_entry.input, input);
 
-        // If it is a cycle head, we have to keep trying to prove it until
-        // we reach a fixpoint. We need to do so for all cycle heads,
-        // not only for the root.
-        //
-        // See tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs
-        // for an example.
-
-        // Start by clearing all provisional cache entries which depend on this
-        // the current goal.
-        Self::clear_dependent_provisional_results(
-            &mut self.provisional_cache,
-            self.stack.next_index(),
-        );
+            // If the current goal is not the root of a cycle, we are done.
+            //
+            // There are no provisional cache entries which depend on this goal.
+            let Some(usage_kind) = stack_entry.has_been_used else {
+                return (stack_entry, result);
+            };
 
-        // Check whether we reached a fixpoint, either because the final result
-        // is equal to the provisional result of the previous iteration, or because
-        // this was only the root of either coinductive or inductive cycles, and the
-        // final result is equal to the initial response for that case.
-        //
-        // If we did not reach a fixpoint, update the provisional result and reevaluate.
-        if D::reached_fixpoint(cx, usage_kind, input, stack_entry.provisional_result, result) {
-            StepResult::Done(stack_entry, result)
-        } else {
-            let depth = self.stack.push(StackEntry {
+            // If it is a cycle head, we have to keep trying to prove it until
+            // we reach a fixpoint. We need to do so for all cycle heads,
+            // not only for the root.
+            //
+            // See tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs
+            // for an example.
+            //
+            // Check whether we reached a fixpoint, either because the final result
+            // is equal to the provisional result of the previous iteration, or because
+            // this was only the root of either coinductive or inductive cycles, and the
+            // final result is equal to the initial response for that case.
+            if self.reached_fixpoint(cx, &stack_entry, usage_kind, result) {
+                self.rebase_provisional_cache_entries(cx, &stack_entry, |_, result| result);
+                return (stack_entry, result);
+            }
+
+            // If computing this goal results in ambiguity with no constraints,
+            // we do not rerun it. It's incredibly difficult to get a different
+            // response in the next iteration in this case. These changes would
+            // likely either be caused by incompleteness or can change the maybe
+            // cause from ambiguity to overflow. Returning ambiguity always
+            // preserves soundness and completeness even if the goal is be known
+            // to succeed or fail.
+            //
+            // This prevents exponential blowup affecting multiple major crates.
+            // As we only get to this branch if we haven't yet reached a fixpoint,
+            // we also taint all provisional cache entries which depend on the
+            // current goal.
+            if D::is_ambiguous_result(result) {
+                self.rebase_provisional_cache_entries(cx, &stack_entry, |input, _| {
+                    D::propagate_ambiguity(cx, input, result)
+                });
+                return (stack_entry, result);
+            };
+
+            // If we've reached the fixpoint step limit, we bail with overflow and taint all
+            // provisional cache entries which depend on the current goal.
+            i += 1;
+            if i >= D::FIXPOINT_STEP_LIMIT {
+                debug!("canonical cycle overflow");
+                let result = D::on_fixpoint_overflow(cx, input);
+                self.rebase_provisional_cache_entries(cx, &stack_entry, |input, _| {
+                    D::on_fixpoint_overflow(cx, input)
+                });
+                return (stack_entry, result);
+            }
+
+            // Clear all provisional cache entries which depend on a previous provisional
+            // result of this goal and rerun.
+            self.clear_dependent_provisional_results();
+
+            debug!(?result, "fixpoint changed provisional results");
+            self.stack.push(StackEntry {
                 has_been_used: None,
                 provisional_result: Some(result),
                 ..stack_entry
             });
-            debug_assert_eq!(self.provisional_cache[&input].stack_depth, Some(depth));
-            StepResult::HasChanged
         }
     }
+
+    /// When encountering a cycle, both inductive and coinductive, we only
+    /// move the root into the global cache. We also store all other cycle
+    /// participants involved.
+    ///
+    /// We must not use the global cache entry of a root goal if a cycle
+    /// participant is on the stack. This is necessary to prevent unstable
+    /// results. See the comment of `StackEntry::nested_goals` for
+    /// more details.
+    fn insert_global_cache(
+        &mut self,
+        cx: X,
+        input: X::Input,
+        final_entry: StackEntry<X>,
+        result: X::Result,
+        dep_node: X::DepNodeIndex,
+    ) {
+        let additional_depth = final_entry.reached_depth.as_usize() - self.stack.len();
+        debug!(?final_entry, ?result, "insert global cache");
+        cx.with_global_cache(self.mode, |cache| {
+            cache.insert(
+                cx,
+                input,
+                result,
+                dep_node,
+                additional_depth,
+                final_entry.encountered_overflow,
+                final_entry.nested_goals,
+            )
+        })
+    }
 }
diff --git a/compiler/rustc_type_ir/src/search_graph/validate.rs b/compiler/rustc_type_ir/src/search_graph/validate.rs
deleted file mode 100644
index 1ae806834ba..00000000000
--- a/compiler/rustc_type_ir/src/search_graph/validate.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-use super::*;
-
-impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
-    #[allow(rustc::potential_query_instability)]
-    pub(super) fn check_invariants(&self) {
-        if !cfg!(debug_assertions) {
-            return;
-        }
-
-        let SearchGraph { mode: _, stack, provisional_cache, _marker } = self;
-        if stack.is_empty() {
-            assert!(provisional_cache.is_empty());
-        }
-
-        for (depth, entry) in stack.iter_enumerated() {
-            let StackEntry {
-                input,
-                available_depth: _,
-                reached_depth: _,
-                non_root_cycle_participant,
-                encountered_overflow: _,
-                has_been_used,
-                ref nested_goals,
-                provisional_result,
-            } = *entry;
-            let cache_entry = provisional_cache.get(&entry.input).unwrap();
-            assert_eq!(cache_entry.stack_depth, Some(depth));
-            if let Some(head) = non_root_cycle_participant {
-                assert!(head < depth);
-                assert!(nested_goals.is_empty());
-                assert_ne!(stack[head].has_been_used, None);
-
-                let mut current_root = head;
-                while let Some(parent) = stack[current_root].non_root_cycle_participant {
-                    current_root = parent;
-                }
-                assert!(stack[current_root].nested_goals.contains(&input));
-            }
-
-            if !nested_goals.is_empty() {
-                assert!(provisional_result.is_some() || has_been_used.is_some());
-                for entry in stack.iter().take(depth.as_usize()) {
-                    assert_eq!(nested_goals.get(&entry.input), None);
-                }
-            }
-        }
-
-        for (&input, entry) in &self.provisional_cache {
-            let ProvisionalCacheEntry { stack_depth, with_coinductive_stack, with_inductive_stack } =
-                entry;
-            assert!(
-                stack_depth.is_some()
-                    || with_coinductive_stack.is_some()
-                    || with_inductive_stack.is_some()
-            );
-
-            if let &Some(stack_depth) = stack_depth {
-                assert_eq!(stack[stack_depth].input, input);
-            }
-
-            let check_detached = |detached_entry: &DetachedEntry<X>| {
-                let DetachedEntry { head, result: _ } = *detached_entry;
-                assert_ne!(stack[head].has_been_used, None);
-            };
-
-            if let Some(with_coinductive_stack) = with_coinductive_stack {
-                check_detached(with_coinductive_stack);
-            }
-
-            if let Some(with_inductive_stack) = with_inductive_stack {
-                check_detached(with_inductive_stack);
-            }
-        }
-    }
-}
diff --git a/compiler/rustc_type_ir/src/solve/inspect.rs b/compiler/rustc_type_ir/src/solve/inspect.rs
index 47d5e0dace7..099c66f6bdc 100644
--- a/compiler/rustc_type_ir/src/solve/inspect.rs
+++ b/compiler/rustc_type_ir/src/solve/inspect.rs
@@ -69,9 +69,7 @@ pub struct CanonicalGoalEvaluation<I: Interner> {
 #[derive_where(PartialEq, Eq, Hash, Debug; I: Interner)]
 pub enum CanonicalGoalEvaluationKind<I: Interner> {
     Overflow,
-    CycleInStack,
-    ProvisionalCacheHit,
-    Evaluation { final_revision: I::CanonicalGoalEvaluationStepRef },
+    Evaluation { final_revision: CanonicalGoalEvaluationStep<I> },
 }
 
 #[derive_where(PartialEq, Eq, Hash, Debug; I: Interner)]
diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs
index 444fd01f012..00fc6ba1c5c 100644
--- a/compiler/rustc_type_ir/src/solve/mod.rs
+++ b/compiler/rustc_type_ir/src/solve/mod.rs
@@ -340,11 +340,3 @@ impl MaybeCause {
         }
     }
 }
-
-#[derive_where(PartialEq, Eq, Debug; I: Interner)]
-pub struct CacheData<I: Interner> {
-    pub result: QueryResult<I>,
-    pub proof_tree: Option<I::CanonicalGoalEvaluationStepRef>,
-    pub additional_depth: usize,
-    pub encountered_overflow: bool,
-}
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index cb9cab748e7..e3640627c56 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -193,12 +193,12 @@
 //
 // Language features:
 // tidy-alphabetical-start
+#![cfg_attr(bootstrap, feature(asm_const))]
 #![cfg_attr(bootstrap, feature(min_exhaustive_patterns))]
 #![feature(abi_unadjusted)]
 #![feature(adt_const_params)]
 #![feature(allow_internal_unsafe)]
 #![feature(allow_internal_unstable)]
-#![feature(asm_const)]
 #![feature(auto_traits)]
 #![feature(cfg_sanitize)]
 #![feature(cfg_target_has_atomic)]
diff --git a/library/core/src/ops/control_flow.rs b/library/core/src/ops/control_flow.rs
index a2709c66b06..ab73dc19fcc 100644
--- a/library/core/src/ops/control_flow.rs
+++ b/library/core/src/ops/control_flow.rs
@@ -116,7 +116,9 @@ impl<B, C> ops::Try for ControlFlow<B, C> {
 }
 
 #[unstable(feature = "try_trait_v2", issue = "84277")]
-impl<B, C> ops::FromResidual for ControlFlow<B, C> {
+// Note: manually specifying the residual type instead of using the default to work around
+// https://github.com/rust-lang/rust/issues/99940
+impl<B, C> ops::FromResidual<ControlFlow<B, convert::Infallible>> for ControlFlow<B, C> {
     #[inline]
     fn from_residual(residual: ControlFlow<B, convert::Infallible>) -> Self {
         match residual {
diff --git a/library/core/src/option.rs b/library/core/src/option.rs
index 6c89c810180..9cec79c17ca 100644
--- a/library/core/src/option.rs
+++ b/library/core/src/option.rs
@@ -2495,7 +2495,9 @@ impl<T> ops::Try for Option<T> {
 }
 
 #[unstable(feature = "try_trait_v2", issue = "84277")]
-impl<T> ops::FromResidual for Option<T> {
+// Note: manually specifying the residual type instead of using the default to work around
+// https://github.com/rust-lang/rust/issues/99940
+impl<T> ops::FromResidual<Option<convert::Infallible>> for Option<T> {
     #[inline]
     fn from_residual(residual: Option<convert::Infallible>) -> Self {
         match residual {
diff --git a/library/core/tests/ops.rs b/library/core/tests/ops.rs
index 2ee0abd399b..501e0f33fe4 100644
--- a/library/core/tests/ops.rs
+++ b/library/core/tests/ops.rs
@@ -1,4 +1,5 @@
 mod control_flow;
+mod from_residual;
 
 use core::ops::{
     Bound, Deref, DerefMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
diff --git a/library/core/tests/ops/from_residual.rs b/library/core/tests/ops/from_residual.rs
new file mode 100644
index 00000000000..d5c86ccbcd3
--- /dev/null
+++ b/library/core/tests/ops/from_residual.rs
@@ -0,0 +1,26 @@
+//! Regression test that Option and ControlFlow can have downstream FromResidual impls.
+//! cc https://github.com/rust-lang/rust/issues/99940,
+//! This does NOT test that issue in general; Option and ControlFlow's FromResidual
+//! impls in core were changed to not be affected by that issue.
+
+use core::ops::{ControlFlow, FromResidual};
+
+struct Local;
+
+impl<T> FromResidual<Local> for Option<T> {
+    fn from_residual(_: Local) -> Option<T> {
+        unimplemented!()
+    }
+}
+
+impl<B, C> FromResidual<Local> for ControlFlow<B, C> {
+    fn from_residual(_: Local) -> ControlFlow<B, C> {
+        unimplemented!()
+    }
+}
+
+impl<T, E> FromResidual<Local> for Result<T, E> {
+    fn from_residual(_: Local) -> Result<T, E> {
+        unimplemented!()
+    }
+}
diff --git a/src/doc/unstable-book/src/language-features/asm-const.md b/src/doc/unstable-book/src/language-features/asm-const.md
deleted file mode 100644
index 670c4df414f..00000000000
--- a/src/doc/unstable-book/src/language-features/asm-const.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# `asm_const`
-
-The tracking issue for this feature is: [#93332]
-
-[#93332]: https://github.com/rust-lang/rust/issues/93332
-
-------------------------
-
-This feature adds a `const <expr>` operand type to `asm!` and `global_asm!`.
-- `<expr>` must be an integer constant expression.
-- The value of the expression is formatted as a string and substituted directly into the asm template string.
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index 2d25de46f6e..25fa8c5e1af 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -20,6 +20,5 @@ run-make/reproducible-build/Makefile
 run-make/rlib-format-packed-bundled-libs/Makefile
 run-make/split-debuginfo/Makefile
 run-make/symbol-mangling-hashed/Makefile
-run-make/sysroot-crates-are-unstable/Makefile
 run-make/translation/Makefile
 run-make/x86_64-fortanix-unknown-sgx-lvi/Makefile
diff --git a/tests/assembly/asm/global_asm.rs b/tests/assembly/asm/global_asm.rs
index 22cf4bdb15b..8a4bf98c745 100644
--- a/tests/assembly/asm/global_asm.rs
+++ b/tests/assembly/asm/global_asm.rs
@@ -4,7 +4,6 @@
 //@ compile-flags: -C llvm-args=--x86-asm-syntax=intel
 //@ compile-flags: -C symbol-mangling-version=v0
 
-#![feature(asm_const)]
 #![crate_type = "rlib"]
 
 use std::arch::global_asm;
diff --git a/tests/assembly/asm/msp430-types.rs b/tests/assembly/asm/msp430-types.rs
index 4f51d4020a6..ae09b8b070d 100644
--- a/tests/assembly/asm/msp430-types.rs
+++ b/tests/assembly/asm/msp430-types.rs
@@ -2,7 +2,7 @@
 //@ compile-flags: --target msp430-none-elf
 //@ needs-llvm-components: msp430
 
-#![feature(no_core, lang_items, rustc_attrs, asm_experimental_arch, asm_const)]
+#![feature(no_core, lang_items, rustc_attrs, asm_experimental_arch)]
 #![crate_type = "rlib"]
 #![no_core]
 #![allow(non_camel_case_types)]
diff --git a/tests/mir-opt/build_correct_coerce.main.built.after.mir b/tests/mir-opt/build_correct_coerce.main.built.after.mir
new file mode 100644
index 00000000000..061174d69bb
--- /dev/null
+++ b/tests/mir-opt/build_correct_coerce.main.built.after.mir
@@ -0,0 +1,18 @@
+// MIR for `main` after built
+
+fn main() -> () {
+    let mut _0: ();
+    let _1: for<'a> fn(&'a (), &'a ());
+    scope 1 {
+        debug x => _1;
+    }
+
+    bb0: {
+        StorageLive(_1);
+        _1 = foo as for<'a> fn(&'a (), &'a ()) (PointerCoercion(ReifyFnPointer));
+        FakeRead(ForLet(None), _1);
+        _0 = const ();
+        StorageDead(_1);
+        return;
+    }
+}
diff --git a/tests/mir-opt/build_correct_coerce.rs b/tests/mir-opt/build_correct_coerce.rs
new file mode 100644
index 00000000000..b6c861636dc
--- /dev/null
+++ b/tests/mir-opt/build_correct_coerce.rs
@@ -0,0 +1,12 @@
+// skip-filecheck
+
+// Validate that we record the target for the `as` coercion as `for<'a> fn(&'a (), &'a ())`,
+// and not `for<'a, 'b>(&'a (), &'b ())`. We previously did the latter due to a bug in
+// the code that records adjustments in HIR typeck.
+
+fn foo<'a, 'b>(_: &'a (), _: &'b ()) {}
+
+// EMIT_MIR build_correct_coerce.main.built.after.mir
+fn main() {
+    let x = foo as for<'a> fn(&'a (), &'a ());
+}
diff --git a/tests/run-make/sysroot-crates-are-unstable/Makefile b/tests/run-make/sysroot-crates-are-unstable/Makefile
deleted file mode 100644
index 1e267fb9576..00000000000
--- a/tests/run-make/sysroot-crates-are-unstable/Makefile
+++ /dev/null
@@ -1,2 +0,0 @@
-all:
-	'$(PYTHON)' test.py
diff --git a/tests/run-make/sysroot-crates-are-unstable/rmake.rs b/tests/run-make/sysroot-crates-are-unstable/rmake.rs
new file mode 100644
index 00000000000..24da387eb80
--- /dev/null
+++ b/tests/run-make/sysroot-crates-are-unstable/rmake.rs
@@ -0,0 +1,5 @@
+use run_make_support::python_command;
+
+fn main() {
+    python_command().arg("test.py").run();
+}
diff --git a/tests/ui/asm/aarch64/bad-reg.rs b/tests/ui/asm/aarch64/bad-reg.rs
index 1e54b6505db..b99e5fe4b9e 100644
--- a/tests/ui/asm/aarch64/bad-reg.rs
+++ b/tests/ui/asm/aarch64/bad-reg.rs
@@ -1,8 +1,6 @@
 //@ only-aarch64
 //@ compile-flags: -C target-feature=+neon
 
-#![feature(asm_const)]
-
 use std::arch::asm;
 
 fn main() {
diff --git a/tests/ui/asm/aarch64/bad-reg.stderr b/tests/ui/asm/aarch64/bad-reg.stderr
index 717a788caf6..370752ad0f1 100644
--- a/tests/ui/asm/aarch64/bad-reg.stderr
+++ b/tests/ui/asm/aarch64/bad-reg.stderr
@@ -1,17 +1,17 @@
 error: invalid register class `foo`: unknown register class
-  --> $DIR/bad-reg.rs:14:20
+  --> $DIR/bad-reg.rs:12:20
    |
 LL |         asm!("{}", in(foo) foo);
    |                    ^^^^^^^^^^^
 
 error: invalid register `foo`: unknown register
-  --> $DIR/bad-reg.rs:16:18
+  --> $DIR/bad-reg.rs:14:18
    |
 LL |         asm!("", in("foo") foo);
    |                  ^^^^^^^^^^^^^
 
 error: invalid asm template modifier for this register class
-  --> $DIR/bad-reg.rs:18:15
+  --> $DIR/bad-reg.rs:16:15
    |
 LL |         asm!("{:z}", in(reg) foo);
    |               ^^^^   ----------- argument
@@ -21,7 +21,7 @@ LL |         asm!("{:z}", in(reg) foo);
    = note: the `reg` register class supports the following template modifiers: `w`, `x`
 
 error: invalid asm template modifier for this register class
-  --> $DIR/bad-reg.rs:20:15
+  --> $DIR/bad-reg.rs:18:15
    |
 LL |         asm!("{:r}", in(vreg) foo);
    |               ^^^^   ------------ argument
@@ -31,7 +31,7 @@ LL |         asm!("{:r}", in(vreg) foo);
    = note: the `vreg` register class supports the following template modifiers: `b`, `h`, `s`, `d`, `q`, `v`
 
 error: invalid asm template modifier for this register class
-  --> $DIR/bad-reg.rs:22:15
+  --> $DIR/bad-reg.rs:20:15
    |
 LL |         asm!("{:r}", in(vreg_low16) foo);
    |               ^^^^   ------------------ argument
@@ -41,7 +41,7 @@ LL |         asm!("{:r}", in(vreg_low16) foo);
    = note: the `vreg_low16` register class supports the following template modifiers: `b`, `h`, `s`, `d`, `q`, `v`
 
 error: asm template modifiers are not allowed for `const` arguments
-  --> $DIR/bad-reg.rs:24:15
+  --> $DIR/bad-reg.rs:22:15
    |
 LL |         asm!("{:a}", const 0);
    |               ^^^^   ------- argument
@@ -49,7 +49,7 @@ LL |         asm!("{:a}", const 0);
    |               template modifier
 
 error: asm template modifiers are not allowed for `sym` arguments
-  --> $DIR/bad-reg.rs:26:15
+  --> $DIR/bad-reg.rs:24:15
    |
 LL |         asm!("{:a}", sym main);
    |               ^^^^   -------- argument
@@ -57,49 +57,49 @@ LL |         asm!("{:a}", sym main);
    |               template modifier
 
 error: invalid register `x29`: the frame pointer cannot be used as an operand for inline asm
-  --> $DIR/bad-reg.rs:28:18
+  --> $DIR/bad-reg.rs:26:18
    |
 LL |         asm!("", in("x29") foo);
    |                  ^^^^^^^^^^^^^
 
 error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm
-  --> $DIR/bad-reg.rs:30:18
+  --> $DIR/bad-reg.rs:28:18
    |
 LL |         asm!("", in("sp") foo);
    |                  ^^^^^^^^^^^^
 
 error: invalid register `xzr`: the zero register cannot be used as an operand for inline asm
-  --> $DIR/bad-reg.rs:32:18
+  --> $DIR/bad-reg.rs:30:18
    |
 LL |         asm!("", in("xzr") foo);
    |                  ^^^^^^^^^^^^^
 
 error: invalid register `x19`: x19 is used internally by LLVM and cannot be used as an operand for inline asm
-  --> $DIR/bad-reg.rs:34:18
+  --> $DIR/bad-reg.rs:32:18
    |
 LL |         asm!("", in("x19") foo);
    |                  ^^^^^^^^^^^^^
 
 error: register class `preg` can only be used as a clobber, not as an input or output
-  --> $DIR/bad-reg.rs:37:18
+  --> $DIR/bad-reg.rs:35:18
    |
 LL |         asm!("", in("p0") foo);
    |                  ^^^^^^^^^^^^
 
 error: register class `preg` can only be used as a clobber, not as an input or output
-  --> $DIR/bad-reg.rs:41:20
+  --> $DIR/bad-reg.rs:39:20
    |
 LL |         asm!("{}", in(preg) foo);
    |                    ^^^^^^^^^^^^
 
 error: register class `preg` can only be used as a clobber, not as an input or output
-  --> $DIR/bad-reg.rs:44:20
+  --> $DIR/bad-reg.rs:42:20
    |
 LL |         asm!("{}", out(preg) _);
    |                    ^^^^^^^^^^^
 
 error: register `w0` conflicts with register `x0`
-  --> $DIR/bad-reg.rs:50:32
+  --> $DIR/bad-reg.rs:48:32
    |
 LL |         asm!("", in("x0") foo, in("w0") bar);
    |                  ------------  ^^^^^^^^^^^^ register `w0`
@@ -107,7 +107,7 @@ LL |         asm!("", in("x0") foo, in("w0") bar);
    |                  register `x0`
 
 error: register `x0` conflicts with register `x0`
-  --> $DIR/bad-reg.rs:52:32
+  --> $DIR/bad-reg.rs:50:32
    |
 LL |         asm!("", in("x0") foo, out("x0") bar);
    |                  ------------  ^^^^^^^^^^^^^ register `x0`
@@ -115,13 +115,13 @@ LL |         asm!("", in("x0") foo, out("x0") bar);
    |                  register `x0`
    |
 help: use `lateout` instead of `out` to avoid conflict
-  --> $DIR/bad-reg.rs:52:18
+  --> $DIR/bad-reg.rs:50:18
    |
 LL |         asm!("", in("x0") foo, out("x0") bar);
    |                  ^^^^^^^^^^^^
 
 error: register `q0` conflicts with register `v0`
-  --> $DIR/bad-reg.rs:55:32
+  --> $DIR/bad-reg.rs:53:32
    |
 LL |         asm!("", in("v0") foo, in("q0") bar);
    |                  ------------  ^^^^^^^^^^^^ register `q0`
@@ -129,7 +129,7 @@ LL |         asm!("", in("v0") foo, in("q0") bar);
    |                  register `v0`
 
 error: register `q0` conflicts with register `v0`
-  --> $DIR/bad-reg.rs:57:32
+  --> $DIR/bad-reg.rs:55:32
    |
 LL |         asm!("", in("v0") foo, out("q0") bar);
    |                  ------------  ^^^^^^^^^^^^^ register `q0`
@@ -137,13 +137,13 @@ LL |         asm!("", in("v0") foo, out("q0") bar);
    |                  register `v0`
    |
 help: use `lateout` instead of `out` to avoid conflict
-  --> $DIR/bad-reg.rs:57:18
+  --> $DIR/bad-reg.rs:55:18
    |
 LL |         asm!("", in("v0") foo, out("q0") bar);
    |                  ^^^^^^^^^^^^
 
 error: type `i32` cannot be used with this register class
-  --> $DIR/bad-reg.rs:37:27
+  --> $DIR/bad-reg.rs:35:27
    |
 LL |         asm!("", in("p0") foo);
    |                           ^^^
@@ -151,7 +151,7 @@ LL |         asm!("", in("p0") foo);
    = note: register class `preg` supports these types: 
 
 error: type `i32` cannot be used with this register class
-  --> $DIR/bad-reg.rs:41:29
+  --> $DIR/bad-reg.rs:39:29
    |
 LL |         asm!("{}", in(preg) foo);
    |                             ^^^
diff --git a/tests/ui/asm/aarch64/const.rs b/tests/ui/asm/aarch64/const.rs
index a1fadb2115b..3eab5138d7d 100644
--- a/tests/ui/asm/aarch64/const.rs
+++ b/tests/ui/asm/aarch64/const.rs
@@ -2,8 +2,6 @@
 //@ run-pass
 //@ needs-asm-support
 
-#![feature(asm_const)]
-
 use std::arch::{asm, global_asm};
 
 fn const_generic<const X: usize>() -> usize {
diff --git a/tests/ui/asm/aarch64/parse-error.rs b/tests/ui/asm/aarch64/parse-error.rs
index ac73bbf99c9..aa731c35dda 100644
--- a/tests/ui/asm/aarch64/parse-error.rs
+++ b/tests/ui/asm/aarch64/parse-error.rs
@@ -1,7 +1,5 @@
 //@ only-aarch64
 
-#![feature(asm_const)]
-
 use std::arch::{asm, global_asm};
 
 fn main() {
diff --git a/tests/ui/asm/aarch64/parse-error.stderr b/tests/ui/asm/aarch64/parse-error.stderr
index e2c798c798e..7b273282ee6 100644
--- a/tests/ui/asm/aarch64/parse-error.stderr
+++ b/tests/ui/asm/aarch64/parse-error.stderr
@@ -1,107 +1,107 @@
 error: requires at least a template string argument
-  --> $DIR/parse-error.rs:11:9
+  --> $DIR/parse-error.rs:9:9
    |
 LL |         asm!();
    |         ^^^^^^
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:13:14
+  --> $DIR/parse-error.rs:11:14
    |
 LL |         asm!(foo);
    |              ^^^
 
 error: expected token: `,`
-  --> $DIR/parse-error.rs:15:19
+  --> $DIR/parse-error.rs:13:19
    |
 LL |         asm!("{}" foo);
    |                   ^^^ expected `,`
 
 error: expected operand, clobber_abi, options, or additional template string
-  --> $DIR/parse-error.rs:17:20
+  --> $DIR/parse-error.rs:15:20
    |
 LL |         asm!("{}", foo);
    |                    ^^^ expected operand, clobber_abi, options, or additional template string
 
 error: expected `(`, found `foo`
-  --> $DIR/parse-error.rs:19:23
+  --> $DIR/parse-error.rs:17:23
    |
 LL |         asm!("{}", in foo);
    |                       ^^^ expected `(`
 
 error: expected `)`, found `foo`
-  --> $DIR/parse-error.rs:21:27
+  --> $DIR/parse-error.rs:19:27
    |
 LL |         asm!("{}", in(reg foo));
    |                           ^^^ expected `)`
 
 error: expected expression, found end of macro arguments
-  --> $DIR/parse-error.rs:23:27
+  --> $DIR/parse-error.rs:21:27
    |
 LL |         asm!("{}", in(reg));
    |                           ^ expected expression
 
 error: expected register class or explicit register
-  --> $DIR/parse-error.rs:25:26
+  --> $DIR/parse-error.rs:23:26
    |
 LL |         asm!("{}", inout(=) foo => bar);
    |                          ^
 
 error: expected expression, found end of macro arguments
-  --> $DIR/parse-error.rs:27:37
+  --> $DIR/parse-error.rs:25:37
    |
 LL |         asm!("{}", inout(reg) foo =>);
    |                                     ^ expected expression
 
 error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, or an operator, found `=>`
-  --> $DIR/parse-error.rs:29:32
+  --> $DIR/parse-error.rs:27:32
    |
 LL |         asm!("{}", in(reg) foo => bar);
    |                                ^^ expected one of 7 possible tokens
 
 error: expected a path for argument to `sym`
-  --> $DIR/parse-error.rs:31:24
+  --> $DIR/parse-error.rs:29:24
    |
 LL |         asm!("{}", sym foo + bar);
    |                        ^^^^^^^^^
 
 error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo`
-  --> $DIR/parse-error.rs:33:26
+  --> $DIR/parse-error.rs:31:26
    |
 LL |         asm!("", options(foo));
    |                          ^^^ expected one of 10 possible tokens
 
 error: expected one of `)` or `,`, found `foo`
-  --> $DIR/parse-error.rs:35:32
+  --> $DIR/parse-error.rs:33:32
    |
 LL |         asm!("", options(nomem foo));
    |                                ^^^ expected one of `)` or `,`
 
 error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo`
-  --> $DIR/parse-error.rs:37:33
+  --> $DIR/parse-error.rs:35:33
    |
 LL |         asm!("", options(nomem, foo));
    |                                 ^^^ expected one of 10 possible tokens
 
 error: expected string literal
-  --> $DIR/parse-error.rs:41:30
+  --> $DIR/parse-error.rs:39:30
    |
 LL |         asm!("", clobber_abi(foo));
    |                              ^^^ not a string literal
 
 error: expected one of `)` or `,`, found `foo`
-  --> $DIR/parse-error.rs:43:34
+  --> $DIR/parse-error.rs:41:34
    |
 LL |         asm!("", clobber_abi("C" foo));
    |                                  ^^^ expected one of `)` or `,`
 
 error: expected string literal
-  --> $DIR/parse-error.rs:45:35
+  --> $DIR/parse-error.rs:43:35
    |
 LL |         asm!("", clobber_abi("C", foo));
    |                                   ^^^ not a string literal
 
 error: duplicate argument named `a`
-  --> $DIR/parse-error.rs:52:36
+  --> $DIR/parse-error.rs:50:36
    |
 LL |         asm!("{a}", a = const foo, a = const bar);
    |                     -------------  ^^^^^^^^^^^^^ duplicate argument
@@ -109,7 +109,7 @@ LL |         asm!("{a}", a = const foo, a = const bar);
    |                     previously here
 
 error: argument never used
-  --> $DIR/parse-error.rs:52:36
+  --> $DIR/parse-error.rs:50:36
    |
 LL |         asm!("{a}", a = const foo, a = const bar);
    |                                    ^^^^^^^^^^^^^ argument never used
@@ -117,13 +117,13 @@ LL |         asm!("{a}", a = const foo, a = const bar);
    = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`
 
 error: explicit register arguments cannot have names
-  --> $DIR/parse-error.rs:57:18
+  --> $DIR/parse-error.rs:55:18
    |
 LL |         asm!("", a = in("x0") foo);
    |                  ^^^^^^^^^^^^^^^^
 
 error: positional arguments cannot follow named arguments or explicit register arguments
-  --> $DIR/parse-error.rs:63:35
+  --> $DIR/parse-error.rs:61:35
    |
 LL |         asm!("{1}", in("x0") foo, const bar);
    |                     ------------  ^^^^^^^^^ positional argument
@@ -131,19 +131,19 @@ LL |         asm!("{1}", in("x0") foo, const bar);
    |                     explicit register argument
 
 error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""`
-  --> $DIR/parse-error.rs:66:29
+  --> $DIR/parse-error.rs:64:29
    |
 LL |         asm!("", options(), "");
    |                             ^^ expected one of 10 possible tokens
 
 error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `"{}"`
-  --> $DIR/parse-error.rs:68:33
+  --> $DIR/parse-error.rs:66:33
    |
 LL |         asm!("{}", in(reg) foo, "{}", out(reg) foo);
    |                                 ^^^^ expected one of 10 possible tokens
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:70:14
+  --> $DIR/parse-error.rs:68:14
    |
 LL |         asm!(format!("{{{}}}", 0), in(reg) foo);
    |              ^^^^^^^^^^^^^^^^^^^^
@@ -151,7 +151,7 @@ LL |         asm!(format!("{{{}}}", 0), in(reg) foo);
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:72:21
+  --> $DIR/parse-error.rs:70:21
    |
 LL |         asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar);
    |                     ^^^^^^^^^^^^^^^^^^^^
@@ -159,127 +159,127 @@ LL |         asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar);
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: _ cannot be used for input operands
-  --> $DIR/parse-error.rs:74:28
+  --> $DIR/parse-error.rs:72:28
    |
 LL |         asm!("{}", in(reg) _);
    |                            ^
 
 error: _ cannot be used for input operands
-  --> $DIR/parse-error.rs:76:31
+  --> $DIR/parse-error.rs:74:31
    |
 LL |         asm!("{}", inout(reg) _);
    |                               ^
 
 error: _ cannot be used for input operands
-  --> $DIR/parse-error.rs:78:35
+  --> $DIR/parse-error.rs:76:35
    |
 LL |         asm!("{}", inlateout(reg) _);
    |                                   ^
 
 error: requires at least a template string argument
-  --> $DIR/parse-error.rs:85:1
+  --> $DIR/parse-error.rs:83:1
    |
 LL | global_asm!();
    | ^^^^^^^^^^^^^
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:87:13
+  --> $DIR/parse-error.rs:85:13
    |
 LL | global_asm!(FOO);
    |             ^^^
 
 error: expected token: `,`
-  --> $DIR/parse-error.rs:89:18
+  --> $DIR/parse-error.rs:87:18
    |
 LL | global_asm!("{}" FOO);
    |                  ^^^ expected `,`
 
 error: expected operand, options, or additional template string
-  --> $DIR/parse-error.rs:91:19
+  --> $DIR/parse-error.rs:89:19
    |
 LL | global_asm!("{}", FOO);
    |                   ^^^ expected operand, options, or additional template string
 
 error: expected expression, found end of macro arguments
-  --> $DIR/parse-error.rs:93:24
+  --> $DIR/parse-error.rs:91:24
    |
 LL | global_asm!("{}", const);
    |                        ^ expected expression
 
 error: expected one of `,`, `.`, `?`, or an operator, found `FOO`
-  --> $DIR/parse-error.rs:95:30
+  --> $DIR/parse-error.rs:93:30
    |
 LL | global_asm!("{}", const(reg) FOO);
    |                              ^^^ expected one of `,`, `.`, `?`, or an operator
 
 error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
-  --> $DIR/parse-error.rs:97:25
+  --> $DIR/parse-error.rs:95:25
    |
 LL | global_asm!("", options(FOO));
    |                         ^^^ expected one of `)`, `att_syntax`, or `raw`
 
 error: the `nomem` option cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:99:25
+  --> $DIR/parse-error.rs:97:25
    |
 LL | global_asm!("", options(nomem FOO));
    |                         ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly
 
 error: expected one of `)` or `,`, found `FOO`
-  --> $DIR/parse-error.rs:99:31
+  --> $DIR/parse-error.rs:97:31
    |
 LL | global_asm!("", options(nomem FOO));
    |                               ^^^ expected one of `)` or `,`
 
 error: the `nomem` option cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:102:25
+  --> $DIR/parse-error.rs:100:25
    |
 LL | global_asm!("", options(nomem, FOO));
    |                         ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly
 
 error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
-  --> $DIR/parse-error.rs:102:32
+  --> $DIR/parse-error.rs:100:32
    |
 LL | global_asm!("", options(nomem, FOO));
    |                                ^^^ expected one of `)`, `att_syntax`, or `raw`
 
 error: expected string literal
-  --> $DIR/parse-error.rs:106:29
+  --> $DIR/parse-error.rs:104:29
    |
 LL | global_asm!("", clobber_abi(FOO));
    |                             ^^^ not a string literal
 
 error: expected one of `)` or `,`, found `FOO`
-  --> $DIR/parse-error.rs:108:33
+  --> $DIR/parse-error.rs:106:33
    |
 LL | global_asm!("", clobber_abi("C" FOO));
    |                                 ^^^ expected one of `)` or `,`
 
 error: expected string literal
-  --> $DIR/parse-error.rs:110:34
+  --> $DIR/parse-error.rs:108:34
    |
 LL | global_asm!("", clobber_abi("C", FOO));
    |                                  ^^^ not a string literal
 
 error: `clobber_abi` cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:112:19
+  --> $DIR/parse-error.rs:110:19
    |
 LL | global_asm!("{}", clobber_abi("C"), const FOO);
    |                   ^^^^^^^^^^^^^^^^
 
 error: `clobber_abi` cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:114:28
+  --> $DIR/parse-error.rs:112:28
    |
 LL | global_asm!("", options(), clobber_abi("C"));
    |                            ^^^^^^^^^^^^^^^^
 
 error: `clobber_abi` cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:116:30
+  --> $DIR/parse-error.rs:114:30
    |
 LL | global_asm!("{}", options(), clobber_abi("C"), const FOO);
    |                              ^^^^^^^^^^^^^^^^
 
 error: duplicate argument named `a`
-  --> $DIR/parse-error.rs:118:35
+  --> $DIR/parse-error.rs:116:35
    |
 LL | global_asm!("{a}", a = const FOO, a = const BAR);
    |                    -------------  ^^^^^^^^^^^^^ duplicate argument
@@ -287,7 +287,7 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR);
    |                    previously here
 
 error: argument never used
-  --> $DIR/parse-error.rs:118:35
+  --> $DIR/parse-error.rs:116:35
    |
 LL | global_asm!("{a}", a = const FOO, a = const BAR);
    |                                   ^^^^^^^^^^^^^ argument never used
@@ -295,19 +295,19 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR);
    = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`
 
 error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""`
-  --> $DIR/parse-error.rs:121:28
+  --> $DIR/parse-error.rs:119:28
    |
 LL | global_asm!("", options(), "");
    |                            ^^ expected one of `clobber_abi`, `const`, `options`, or `sym`
 
 error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"`
-  --> $DIR/parse-error.rs:123:30
+  --> $DIR/parse-error.rs:121:30
    |
 LL | global_asm!("{}", const FOO, "{}", const FOO);
    |                              ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym`
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:125:13
+  --> $DIR/parse-error.rs:123:13
    |
 LL | global_asm!(format!("{{{}}}", 0), const FOO);
    |             ^^^^^^^^^^^^^^^^^^^^
@@ -315,7 +315,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO);
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:127:20
+  --> $DIR/parse-error.rs:125:20
    |
 LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
    |                    ^^^^^^^^^^^^^^^^^^^^
@@ -323,7 +323,7 @@ LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:39:37
+  --> $DIR/parse-error.rs:37:37
    |
 LL |         asm!("{}", options(), const foo);
    |                                     ^^^ non-constant value
@@ -334,7 +334,7 @@ LL |     const foo: /* Type */ = 0;
    |     ~~~~~    ++++++++++++
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:47:44
+  --> $DIR/parse-error.rs:45:44
    |
 LL |         asm!("{}", clobber_abi("C"), const foo);
    |                                            ^^^ non-constant value
@@ -345,7 +345,7 @@ LL |     const foo: /* Type */ = 0;
    |     ~~~~~    ++++++++++++
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:50:55
+  --> $DIR/parse-error.rs:48:55
    |
 LL |         asm!("{}", options(), clobber_abi("C"), const foo);
    |                                                       ^^^ non-constant value
@@ -356,7 +356,7 @@ LL |     const foo: /* Type */ = 0;
    |     ~~~~~    ++++++++++++
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:52:31
+  --> $DIR/parse-error.rs:50:31
    |
 LL |         asm!("{a}", a = const foo, a = const bar);
    |                               ^^^ non-constant value
@@ -367,7 +367,7 @@ LL |     const foo: /* Type */ = 0;
    |     ~~~~~    ++++++++++++
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:52:46
+  --> $DIR/parse-error.rs:50:46
    |
 LL |         asm!("{a}", a = const foo, a = const bar);
    |                                              ^^^ non-constant value
@@ -378,7 +378,7 @@ LL |     const bar: /* Type */ = 0;
    |     ~~~~~    ++++++++++++
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:59:45
+  --> $DIR/parse-error.rs:57:45
    |
 LL |         asm!("{a}", in("x0") foo, a = const bar);
    |                                             ^^^ non-constant value
@@ -389,7 +389,7 @@ LL |     const bar: /* Type */ = 0;
    |     ~~~~~    ++++++++++++
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:61:45
+  --> $DIR/parse-error.rs:59:45
    |
 LL |         asm!("{a}", in("x0") foo, a = const bar);
    |                                             ^^^ non-constant value
@@ -400,7 +400,7 @@ LL |     const bar: /* Type */ = 0;
    |     ~~~~~    ++++++++++++
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:63:41
+  --> $DIR/parse-error.rs:61:41
    |
 LL |         asm!("{1}", in("x0") foo, const bar);
    |                                         ^^^ non-constant value
diff --git a/tests/ui/asm/aarch64/type-check-3.rs b/tests/ui/asm/aarch64/type-check-3.rs
index 3fc8e506069..b64473f98c0 100644
--- a/tests/ui/asm/aarch64/type-check-3.rs
+++ b/tests/ui/asm/aarch64/type-check-3.rs
@@ -1,7 +1,7 @@
 //@ only-aarch64
 //@ compile-flags: -C target-feature=+neon
 
-#![feature(repr_simd, asm_const)]
+#![feature(repr_simd)]
 
 use std::arch::aarch64::float64x2_t;
 use std::arch::{asm, global_asm};
diff --git a/tests/ui/asm/aarch64/type-check-4.rs b/tests/ui/asm/aarch64/type-check-4.rs
index f00b4d4c46f..41eb9de5669 100644
--- a/tests/ui/asm/aarch64/type-check-4.rs
+++ b/tests/ui/asm/aarch64/type-check-4.rs
@@ -1,7 +1,7 @@
 //@ only-aarch64
 //@ compile-flags: -C target-feature=+neon
 
-#![feature(repr_simd, asm_const)]
+#![feature(repr_simd)]
 
 use std::arch::aarch64::float64x2_t;
 use std::arch::{asm, global_asm};
@@ -10,8 +10,7 @@ use std::arch::{asm, global_asm};
 #[derive(Copy, Clone)]
 struct Simd256bit(f64, f64, f64, f64);
 
-fn main() {
-}
+fn main() {}
 
 // Constants must be... constant
 
diff --git a/tests/ui/asm/aarch64/type-check-4.stderr b/tests/ui/asm/aarch64/type-check-4.stderr
index 3e675f69e84..89eb8467cde 100644
--- a/tests/ui/asm/aarch64/type-check-4.stderr
+++ b/tests/ui/asm/aarch64/type-check-4.stderr
@@ -1,5 +1,5 @@
 error[E0658]: referencing statics in constants is unstable
-  --> $DIR/type-check-4.rs:25:25
+  --> $DIR/type-check-4.rs:24:25
    |
 LL | global_asm!("{}", const S);
    |                         ^
@@ -11,7 +11,7 @@ LL | global_asm!("{}", const S);
    = help: to fix this, the value can be extracted to a `const` and then used.
 
 error[E0658]: referencing statics in constants is unstable
-  --> $DIR/type-check-4.rs:28:35
+  --> $DIR/type-check-4.rs:27:35
    |
 LL | global_asm!("{}", const const_foo(S));
    |                                   ^
@@ -23,7 +23,7 @@ LL | global_asm!("{}", const const_foo(S));
    = help: to fix this, the value can be extracted to a `const` and then used.
 
 error[E0658]: referencing statics in constants is unstable
-  --> $DIR/type-check-4.rs:31:35
+  --> $DIR/type-check-4.rs:30:35
    |
 LL | global_asm!("{}", const const_bar(S));
    |                                   ^
diff --git a/tests/ui/asm/bad-template.rs b/tests/ui/asm/bad-template.rs
index 41a906e32a4..6b00905a393 100644
--- a/tests/ui/asm/bad-template.rs
+++ b/tests/ui/asm/bad-template.rs
@@ -6,7 +6,7 @@
 //@ [x86_64] needs-llvm-components: x86
 //@ [aarch64] needs-llvm-components: aarch64
 
-#![feature(no_core, lang_items, rustc_attrs, asm_const)]
+#![feature(no_core, lang_items, rustc_attrs)]
 #![no_core]
 
 #[rustc_builtin_macro]
diff --git a/tests/ui/asm/const-error.rs b/tests/ui/asm/const-error.rs
index f2cead399b6..40d0590c33e 100644
--- a/tests/ui/asm/const-error.rs
+++ b/tests/ui/asm/const-error.rs
@@ -1,15 +1,15 @@
 //@ only-x86_64
 //@ needs-asm-support
 
-#![feature(asm_const)]
-
 // Test to make sure that we emit const errors eagerly for inline asm
 
 use std::arch::asm;
 
 fn test<T>() {
-    unsafe { asm!("/* {} */", const 1 / 0); }
-    //~^ ERROR evaluation of
+    unsafe {
+        asm!("/* {} */", const 1 / 0);
+        //~^ ERROR evaluation of
+    }
 }
 
 fn main() {}
diff --git a/tests/ui/asm/const-error.stderr b/tests/ui/asm/const-error.stderr
index fe311832177..02e54457e89 100644
--- a/tests/ui/asm/const-error.stderr
+++ b/tests/ui/asm/const-error.stderr
@@ -1,8 +1,8 @@
 error[E0080]: evaluation of `test::<T>::{constant#0}` failed
-  --> $DIR/const-error.rs:11:37
+  --> $DIR/const-error.rs:10:32
    |
-LL |     unsafe { asm!("/* {} */", const 1 / 0); }
-   |                                     ^^^^^ attempt to divide `1_i32` by zero
+LL |         asm!("/* {} */", const 1 / 0);
+   |                                ^^^^^ attempt to divide `1_i32` by zero
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/asm/fail-const-eval-issue-121099.rs b/tests/ui/asm/fail-const-eval-issue-121099.rs
index bed6fc9b39f..36d00b1e5d2 100644
--- a/tests/ui/asm/fail-const-eval-issue-121099.rs
+++ b/tests/ui/asm/fail-const-eval-issue-121099.rs
@@ -1,6 +1,5 @@
 //@ build-fail
 //@ needs-asm-support
-#![feature(asm_const)]
 
 use std::arch::global_asm;
 
diff --git a/tests/ui/asm/fail-const-eval-issue-121099.stderr b/tests/ui/asm/fail-const-eval-issue-121099.stderr
index 51d283218d2..5d86c3a5f7b 100644
--- a/tests/ui/asm/fail-const-eval-issue-121099.stderr
+++ b/tests/ui/asm/fail-const-eval-issue-121099.stderr
@@ -1,11 +1,11 @@
 error[E0080]: evaluation of constant value failed
-  --> $DIR/fail-const-eval-issue-121099.rs:9:31
+  --> $DIR/fail-const-eval-issue-121099.rs:8:31
    |
 LL | global_asm!("/* {} */", const 1 << 500);
    |                               ^^^^^^^^ attempt to shift left by `500_i32`, which would overflow
 
 error[E0080]: evaluation of constant value failed
-  --> $DIR/fail-const-eval-issue-121099.rs:11:31
+  --> $DIR/fail-const-eval-issue-121099.rs:10:31
    |
 LL | global_asm!("/* {} */", const 1 / 0);
    |                               ^^^^^ attempt to divide `1_i32` by zero
diff --git a/tests/ui/asm/generic-const.rs b/tests/ui/asm/generic-const.rs
index 133d093d200..3b69a4e86e3 100644
--- a/tests/ui/asm/generic-const.rs
+++ b/tests/ui/asm/generic-const.rs
@@ -1,8 +1,6 @@
 //@ needs-asm-support
 //@ build-pass
 
-#![feature(asm_const)]
-
 use std::arch::asm;
 
 fn foofoo<const N: usize>() {}
diff --git a/tests/ui/asm/invalid-const-operand.rs b/tests/ui/asm/invalid-const-operand.rs
index eff335ff6aa..a688f5042db 100644
--- a/tests/ui/asm/invalid-const-operand.rs
+++ b/tests/ui/asm/invalid-const-operand.rs
@@ -2,8 +2,6 @@
 //@ ignore-nvptx64
 //@ ignore-spirv
 
-#![feature(asm_const)]
-
 use std::arch::{asm, global_asm};
 
 // Const operands must be integers and must be constants.
diff --git a/tests/ui/asm/invalid-const-operand.stderr b/tests/ui/asm/invalid-const-operand.stderr
index a6d742b53c2..bda4b0355b7 100644
--- a/tests/ui/asm/invalid-const-operand.stderr
+++ b/tests/ui/asm/invalid-const-operand.stderr
@@ -1,5 +1,5 @@
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/invalid-const-operand.rs:42:26
+  --> $DIR/invalid-const-operand.rs:40:26
    |
 LL |         asm!("{}", const x);
    |                          ^ non-constant value
@@ -10,7 +10,7 @@ LL |         const x: /* Type */ = 0;
    |         ~~~~~  ++++++++++++
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/invalid-const-operand.rs:45:36
+  --> $DIR/invalid-const-operand.rs:43:36
    |
 LL |         asm!("{}", const const_foo(x));
    |                                    ^ non-constant value
@@ -21,7 +21,7 @@ LL |         const x: /* Type */ = 0;
    |         ~~~~~  ++++++++++++
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/invalid-const-operand.rs:48:36
+  --> $DIR/invalid-const-operand.rs:46:36
    |
 LL |         asm!("{}", const const_bar(x));
    |                                    ^ non-constant value
@@ -32,7 +32,7 @@ LL |         const x: /* Type */ = 0;
    |         ~~~~~  ++++++++++++
 
 error: invalid type for `const` operand
-  --> $DIR/invalid-const-operand.rs:14:19
+  --> $DIR/invalid-const-operand.rs:12:19
    |
 LL | global_asm!("{}", const 0f32);
    |                   ^^^^^^----
@@ -42,7 +42,7 @@ LL | global_asm!("{}", const 0f32);
    = help: `const` operands must be of an integer type
 
 error: invalid type for `const` operand
-  --> $DIR/invalid-const-operand.rs:16:19
+  --> $DIR/invalid-const-operand.rs:14:19
    |
 LL | global_asm!("{}", const 0 as *mut u8);
    |                   ^^^^^^------------
@@ -52,7 +52,7 @@ LL | global_asm!("{}", const 0 as *mut u8);
    = help: `const` operands must be of an integer type
 
 error: invalid type for `const` operand
-  --> $DIR/invalid-const-operand.rs:26:20
+  --> $DIR/invalid-const-operand.rs:24:20
    |
 LL |         asm!("{}", const 0f32);
    |                    ^^^^^^----
@@ -62,7 +62,7 @@ LL |         asm!("{}", const 0f32);
    = help: `const` operands must be of an integer type
 
 error: invalid type for `const` operand
-  --> $DIR/invalid-const-operand.rs:28:20
+  --> $DIR/invalid-const-operand.rs:26:20
    |
 LL |         asm!("{}", const 0 as *mut u8);
    |                    ^^^^^^------------
@@ -72,7 +72,7 @@ LL |         asm!("{}", const 0 as *mut u8);
    = help: `const` operands must be of an integer type
 
 error: invalid type for `const` operand
-  --> $DIR/invalid-const-operand.rs:30:20
+  --> $DIR/invalid-const-operand.rs:28:20
    |
 LL |         asm!("{}", const &0);
    |                    ^^^^^^--
diff --git a/tests/ui/asm/naked-functions.rs b/tests/ui/asm/naked-functions.rs
index cb1e5c325c2..116a84506c5 100644
--- a/tests/ui/asm/naked-functions.rs
+++ b/tests/ui/asm/naked-functions.rs
@@ -3,7 +3,7 @@
 //@ ignore-spirv
 
 #![feature(naked_functions)]
-#![feature(asm_const, asm_unwind, linkage)]
+#![feature(asm_unwind, linkage)]
 #![crate_type = "lib"]
 
 use std::arch::asm;
diff --git a/tests/ui/asm/named-asm-labels.rs b/tests/ui/asm/named-asm-labels.rs
index d2ca6fe8808..043aab9029d 100644
--- a/tests/ui/asm/named-asm-labels.rs
+++ b/tests/ui/asm/named-asm-labels.rs
@@ -10,7 +10,7 @@
 // which causes less readable LLVM errors and in the worst cases causes ICEs
 // or segfaults based on system dependent behavior and codegen flags.
 
-#![feature(naked_functions, asm_const)]
+#![feature(naked_functions)]
 
 use std::arch::{asm, global_asm};
 
@@ -128,6 +128,7 @@ fn main() {
 
         // Tests usage of colons in non-label positions
         asm!(":lo12:FOO"); // this is apparently valid aarch64
+
         // is there an example that is valid x86 for this test?
         asm!(":bbb nop");
 
@@ -176,7 +177,8 @@ fn main() {
 // label or LTO can cause labels to break
 #[naked]
 pub extern "C" fn foo() -> i32 {
-    unsafe { asm!(".Lfoo: mov rax, {}; ret;", "nop", const 1, options(noreturn)) } //~ ERROR avoid using named labels
+    unsafe { asm!(".Lfoo: mov rax, {}; ret;", "nop", const 1, options(noreturn)) }
+    //~^ ERROR avoid using named labels
 }
 
 // Make sure that non-naked attributes *do* still let the lint happen
diff --git a/tests/ui/asm/named-asm-labels.stderr b/tests/ui/asm/named-asm-labels.stderr
index 20b7d64f9e7..e5e177fb8b8 100644
--- a/tests/ui/asm/named-asm-labels.stderr
+++ b/tests/ui/asm/named-asm-labels.stderr
@@ -328,7 +328,7 @@ LL |             ab: nop // ab: does foo
    = note: see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
 
 error: avoid using named labels in inline assembly
-  --> $DIR/named-asm-labels.rs:143:19
+  --> $DIR/named-asm-labels.rs:144:19
    |
 LL |             asm!("test_{}: nop", in(reg) 10);
    |                   ^^^^^^^
@@ -338,7 +338,7 @@ LL |             asm!("test_{}: nop", in(reg) 10);
    = note: see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
 
 error: avoid using named labels in inline assembly
-  --> $DIR/named-asm-labels.rs:145:15
+  --> $DIR/named-asm-labels.rs:146:15
    |
 LL |         asm!("test_{}: nop", const 10);
    |               ^^^^^^^
@@ -348,7 +348,7 @@ LL |         asm!("test_{}: nop", const 10);
    = note: see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
 
 error: avoid using named labels in inline assembly
-  --> $DIR/named-asm-labels.rs:146:15
+  --> $DIR/named-asm-labels.rs:147:15
    |
 LL |         asm!("test_{}: nop", sym main);
    |               ^^^^^^^
@@ -358,7 +358,7 @@ LL |         asm!("test_{}: nop", sym main);
    = note: see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
 
 error: avoid using named labels in inline assembly
-  --> $DIR/named-asm-labels.rs:147:15
+  --> $DIR/named-asm-labels.rs:148:15
    |
 LL |         asm!("{}_test: nop", const 10);
    |               ^^^^^^^
@@ -368,7 +368,7 @@ LL |         asm!("{}_test: nop", const 10);
    = note: see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
 
 error: avoid using named labels in inline assembly
-  --> $DIR/named-asm-labels.rs:148:15
+  --> $DIR/named-asm-labels.rs:149:15
    |
 LL |         asm!("test_{}_test: nop", const 10);
    |               ^^^^^^^^^^^^
@@ -378,7 +378,7 @@ LL |         asm!("test_{}_test: nop", const 10);
    = note: see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
 
 error: avoid using named labels in inline assembly
-  --> $DIR/named-asm-labels.rs:149:15
+  --> $DIR/named-asm-labels.rs:150:15
    |
 LL |         asm!("{}: nop", const 10);
    |               ^^
@@ -388,7 +388,7 @@ LL |         asm!("{}: nop", const 10);
    = note: see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
 
 error: avoid using named labels in inline assembly
-  --> $DIR/named-asm-labels.rs:151:15
+  --> $DIR/named-asm-labels.rs:152:15
    |
 LL |         asm!("{uwu}: nop", uwu = const 10);
    |               ^^^^^
@@ -398,7 +398,7 @@ LL |         asm!("{uwu}: nop", uwu = const 10);
    = note: see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
 
 error: avoid using named labels in inline assembly
-  --> $DIR/named-asm-labels.rs:152:15
+  --> $DIR/named-asm-labels.rs:153:15
    |
 LL |         asm!("{0}: nop", const 10);
    |               ^^^
@@ -408,7 +408,7 @@ LL |         asm!("{0}: nop", const 10);
    = note: see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
 
 error: avoid using named labels in inline assembly
-  --> $DIR/named-asm-labels.rs:153:15
+  --> $DIR/named-asm-labels.rs:154:15
    |
 LL |         asm!("{1}: nop", "/* {0} */", const 10, const 20);
    |               ^^^
@@ -418,7 +418,7 @@ LL |         asm!("{1}: nop", "/* {0} */", const 10, const 20);
    = note: see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
 
 error: avoid using named labels in inline assembly
-  --> $DIR/named-asm-labels.rs:156:14
+  --> $DIR/named-asm-labels.rs:157:14
    |
 LL |         asm!(include_str!("named-asm-labels.s"));
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -428,7 +428,7 @@ LL |         asm!(include_str!("named-asm-labels.s"));
    = note: the label may be declared in the expansion of a macro
 
 error: avoid using named labels in inline assembly
-  --> $DIR/named-asm-labels.rs:156:14
+  --> $DIR/named-asm-labels.rs:157:14
    |
 LL |         asm!(include_str!("named-asm-labels.s"));
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -439,7 +439,7 @@ LL |         asm!(include_str!("named-asm-labels.s"));
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: avoid using named labels in inline assembly
-  --> $DIR/named-asm-labels.rs:156:14
+  --> $DIR/named-asm-labels.rs:157:14
    |
 LL |         asm!(include_str!("named-asm-labels.s"));
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -450,7 +450,7 @@ LL |         asm!(include_str!("named-asm-labels.s"));
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: avoid using named labels in inline assembly
-  --> $DIR/named-asm-labels.rs:156:14
+  --> $DIR/named-asm-labels.rs:157:14
    |
 LL |         asm!(include_str!("named-asm-labels.s"));
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -461,7 +461,7 @@ LL |         asm!(include_str!("named-asm-labels.s"));
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 warning: avoid using named labels in inline assembly
-  --> $DIR/named-asm-labels.rs:170:19
+  --> $DIR/named-asm-labels.rs:171:19
    |
 LL |             asm!("warned: nop");
    |                   ^^^^^^
@@ -469,13 +469,13 @@ LL |             asm!("warned: nop");
    = help: only local labels of the form `<number>:` should be used in inline asm
    = note: see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
 note: the lint level is defined here
-  --> $DIR/named-asm-labels.rs:168:16
+  --> $DIR/named-asm-labels.rs:169:16
    |
 LL |         #[warn(named_asm_labels)]
    |                ^^^^^^^^^^^^^^^^
 
 error: avoid using named labels in inline assembly
-  --> $DIR/named-asm-labels.rs:179:20
+  --> $DIR/named-asm-labels.rs:180:20
    |
 LL |     unsafe { asm!(".Lfoo: mov rax, {}; ret;", "nop", const 1, options(noreturn)) }
    |                    ^^^^^
@@ -484,7 +484,7 @@ LL |     unsafe { asm!(".Lfoo: mov rax, {}; ret;", "nop", const 1, options(noret
    = note: see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
 
 error: avoid using named labels in inline assembly
-  --> $DIR/named-asm-labels.rs:185:20
+  --> $DIR/named-asm-labels.rs:187:20
    |
 LL |     unsafe { asm!(".Lbar: mov rax, {}; ret;", "nop", const 1, options(noreturn)) }
    |                    ^^^^^
@@ -493,7 +493,7 @@ LL |     unsafe { asm!(".Lbar: mov rax, {}; ret;", "nop", const 1, options(noret
    = note: see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
 
 error: avoid using named labels in inline assembly
-  --> $DIR/named-asm-labels.rs:193:20
+  --> $DIR/named-asm-labels.rs:195:20
    |
 LL |     unsafe { asm!(".Laaa: nop; ret;", options(noreturn)) }
    |                    ^^^^^
@@ -502,7 +502,7 @@ LL |     unsafe { asm!(".Laaa: nop; ret;", options(noreturn)) }
    = note: see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
 
 error: avoid using named labels in inline assembly
-  --> $DIR/named-asm-labels.rs:203:24
+  --> $DIR/named-asm-labels.rs:205:24
    |
 LL |         unsafe { asm!(".Lbbb: nop; ret;", options(noreturn)) }
    |                        ^^^^^
@@ -511,7 +511,7 @@ LL |         unsafe { asm!(".Lbbb: nop; ret;", options(noreturn)) }
    = note: see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
 
 error: avoid using named labels in inline assembly
-  --> $DIR/named-asm-labels.rs:212:15
+  --> $DIR/named-asm-labels.rs:214:15
    |
 LL |         asm!("closure1: nop");
    |               ^^^^^^^^
@@ -520,7 +520,7 @@ LL |         asm!("closure1: nop");
    = note: see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
 
 error: avoid using named labels in inline assembly
-  --> $DIR/named-asm-labels.rs:216:15
+  --> $DIR/named-asm-labels.rs:218:15
    |
 LL |         asm!("closure2: nop");
    |               ^^^^^^^^
@@ -529,7 +529,7 @@ LL |         asm!("closure2: nop");
    = note: see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
 
 error: avoid using named labels in inline assembly
-  --> $DIR/named-asm-labels.rs:226:19
+  --> $DIR/named-asm-labels.rs:228:19
    |
 LL |             asm!("closure3: nop");
    |                   ^^^^^^^^
diff --git a/tests/ui/asm/parse-error.rs b/tests/ui/asm/parse-error.rs
index 16ae0282864..4d7b522f5fc 100644
--- a/tests/ui/asm/parse-error.rs
+++ b/tests/ui/asm/parse-error.rs
@@ -1,7 +1,5 @@
 //@ needs-asm-support
 
-#![feature(asm_const)]
-
 use std::arch::{asm, global_asm};
 
 fn main() {
diff --git a/tests/ui/asm/parse-error.stderr b/tests/ui/asm/parse-error.stderr
index f5f8d537d86..6d0e629b937 100644
--- a/tests/ui/asm/parse-error.stderr
+++ b/tests/ui/asm/parse-error.stderr
@@ -1,167 +1,167 @@
 error: requires at least a template string argument
-  --> $DIR/parse-error.rs:11:9
+  --> $DIR/parse-error.rs:9:9
    |
 LL |         asm!();
    |         ^^^^^^
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:13:14
+  --> $DIR/parse-error.rs:11:14
    |
 LL |         asm!(foo);
    |              ^^^
 
 error: expected token: `,`
-  --> $DIR/parse-error.rs:15:19
+  --> $DIR/parse-error.rs:13:19
    |
 LL |         asm!("{}" foo);
    |                   ^^^ expected `,`
 
 error: expected operand, clobber_abi, options, or additional template string
-  --> $DIR/parse-error.rs:17:20
+  --> $DIR/parse-error.rs:15:20
    |
 LL |         asm!("{}", foo);
    |                    ^^^ expected operand, clobber_abi, options, or additional template string
 
 error: expected `(`, found `foo`
-  --> $DIR/parse-error.rs:19:23
+  --> $DIR/parse-error.rs:17:23
    |
 LL |         asm!("{}", in foo);
    |                       ^^^ expected `(`
 
 error: expected `)`, found `foo`
-  --> $DIR/parse-error.rs:21:27
+  --> $DIR/parse-error.rs:19:27
    |
 LL |         asm!("{}", in(reg foo));
    |                           ^^^ expected `)`
 
 error: expected expression, found end of macro arguments
-  --> $DIR/parse-error.rs:23:27
+  --> $DIR/parse-error.rs:21:27
    |
 LL |         asm!("{}", in(reg));
    |                           ^ expected expression
 
 error: expected register class or explicit register
-  --> $DIR/parse-error.rs:25:26
+  --> $DIR/parse-error.rs:23:26
    |
 LL |         asm!("{}", inout(=) foo => bar);
    |                          ^
 
 error: expected expression, found end of macro arguments
-  --> $DIR/parse-error.rs:27:37
+  --> $DIR/parse-error.rs:25:37
    |
 LL |         asm!("{}", inout(reg) foo =>);
    |                                     ^ expected expression
 
 error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, or an operator, found `=>`
-  --> $DIR/parse-error.rs:29:32
+  --> $DIR/parse-error.rs:27:32
    |
 LL |         asm!("{}", in(reg) foo => bar);
    |                                ^^ expected one of 7 possible tokens
 
 error: expected a path for argument to `sym`
-  --> $DIR/parse-error.rs:31:24
+  --> $DIR/parse-error.rs:29:24
    |
 LL |         asm!("{}", sym foo + bar);
    |                        ^^^^^^^^^
 
 error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo`
-  --> $DIR/parse-error.rs:33:26
+  --> $DIR/parse-error.rs:31:26
    |
 LL |         asm!("", options(foo));
    |                          ^^^ expected one of 10 possible tokens
 
 error: expected one of `)` or `,`, found `foo`
-  --> $DIR/parse-error.rs:35:32
+  --> $DIR/parse-error.rs:33:32
    |
 LL |         asm!("", options(nomem foo));
    |                                ^^^ expected one of `)` or `,`
 
 error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo`
-  --> $DIR/parse-error.rs:37:33
+  --> $DIR/parse-error.rs:35:33
    |
 LL |         asm!("", options(nomem, foo));
    |                                 ^^^ expected one of 10 possible tokens
 
 error: at least one abi must be provided as an argument to `clobber_abi`
-  --> $DIR/parse-error.rs:44:30
+  --> $DIR/parse-error.rs:42:30
    |
 LL |         asm!("", clobber_abi());
    |                              ^
 
 error: expected string literal
-  --> $DIR/parse-error.rs:46:30
+  --> $DIR/parse-error.rs:44:30
    |
 LL |         asm!("", clobber_abi(foo));
    |                              ^^^ not a string literal
 
 error: expected one of `)` or `,`, found `foo`
-  --> $DIR/parse-error.rs:48:34
+  --> $DIR/parse-error.rs:46:34
    |
 LL |         asm!("", clobber_abi("C" foo));
    |                                  ^^^ expected one of `)` or `,`
 
 error: expected string literal
-  --> $DIR/parse-error.rs:50:35
+  --> $DIR/parse-error.rs:48:35
    |
 LL |         asm!("", clobber_abi("C", foo));
    |                                   ^^^ not a string literal
 
 error: expected string literal
-  --> $DIR/parse-error.rs:52:30
+  --> $DIR/parse-error.rs:50:30
    |
 LL |         asm!("", clobber_abi(1));
    |                              ^ not a string literal
 
 error: expected string literal
-  --> $DIR/parse-error.rs:54:30
+  --> $DIR/parse-error.rs:52:30
    |
 LL |         asm!("", clobber_abi(()));
    |                              ^ not a string literal
 
 error: expected string literal
-  --> $DIR/parse-error.rs:56:30
+  --> $DIR/parse-error.rs:54:30
    |
 LL |         asm!("", clobber_abi(uwu));
    |                              ^^^ not a string literal
 
 error: expected string literal
-  --> $DIR/parse-error.rs:58:30
+  --> $DIR/parse-error.rs:56:30
    |
 LL |         asm!("", clobber_abi({}));
    |                              ^ not a string literal
 
 error: expected string literal
-  --> $DIR/parse-error.rs:60:30
+  --> $DIR/parse-error.rs:58:30
    |
 LL |         asm!("", clobber_abi(loop {}));
    |                              ^^^^ not a string literal
 
 error: expected string literal
-  --> $DIR/parse-error.rs:62:30
+  --> $DIR/parse-error.rs:60:30
    |
 LL |         asm!("", clobber_abi(if));
    |                              ^^ not a string literal
 
 error: expected string literal
-  --> $DIR/parse-error.rs:64:30
+  --> $DIR/parse-error.rs:62:30
    |
 LL |         asm!("", clobber_abi(do));
    |                              ^^ not a string literal
 
 error: expected string literal
-  --> $DIR/parse-error.rs:66:30
+  --> $DIR/parse-error.rs:64:30
    |
 LL |         asm!("", clobber_abi(<));
    |                              ^ not a string literal
 
 error: expected string literal
-  --> $DIR/parse-error.rs:68:30
+  --> $DIR/parse-error.rs:66:30
    |
 LL |         asm!("", clobber_abi(.));
    |                              ^ not a string literal
 
 error: duplicate argument named `a`
-  --> $DIR/parse-error.rs:76:36
+  --> $DIR/parse-error.rs:74:36
    |
 LL |         asm!("{a}", a = const foo, a = const bar);
    |                     -------------  ^^^^^^^^^^^^^ duplicate argument
@@ -169,7 +169,7 @@ LL |         asm!("{a}", a = const foo, a = const bar);
    |                     previously here
 
 error: argument never used
-  --> $DIR/parse-error.rs:76:36
+  --> $DIR/parse-error.rs:74:36
    |
 LL |         asm!("{a}", a = const foo, a = const bar);
    |                                    ^^^^^^^^^^^^^ argument never used
@@ -177,19 +177,19 @@ LL |         asm!("{a}", a = const foo, a = const bar);
    = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`
 
 error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `""`
-  --> $DIR/parse-error.rs:82:29
+  --> $DIR/parse-error.rs:80:29
    |
 LL |         asm!("", options(), "");
    |                             ^^ expected one of 10 possible tokens
 
 error: expected one of `clobber_abi`, `const`, `in`, `inlateout`, `inout`, `label`, `lateout`, `options`, `out`, or `sym`, found `"{}"`
-  --> $DIR/parse-error.rs:84:33
+  --> $DIR/parse-error.rs:82:33
    |
 LL |         asm!("{}", in(reg) foo, "{}", out(reg) foo);
    |                                 ^^^^ expected one of 10 possible tokens
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:86:14
+  --> $DIR/parse-error.rs:84:14
    |
 LL |         asm!(format!("{{{}}}", 0), in(reg) foo);
    |              ^^^^^^^^^^^^^^^^^^^^
@@ -197,7 +197,7 @@ LL |         asm!(format!("{{{}}}", 0), in(reg) foo);
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:88:21
+  --> $DIR/parse-error.rs:86:21
    |
 LL |         asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar);
    |                     ^^^^^^^^^^^^^^^^^^^^
@@ -205,139 +205,139 @@ LL |         asm!("{1}", format!("{{{}}}", 0), in(reg) foo, out(reg) bar);
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: _ cannot be used for input operands
-  --> $DIR/parse-error.rs:90:28
+  --> $DIR/parse-error.rs:88:28
    |
 LL |         asm!("{}", in(reg) _);
    |                            ^
 
 error: _ cannot be used for input operands
-  --> $DIR/parse-error.rs:92:31
+  --> $DIR/parse-error.rs:90:31
    |
 LL |         asm!("{}", inout(reg) _);
    |                               ^
 
 error: _ cannot be used for input operands
-  --> $DIR/parse-error.rs:94:35
+  --> $DIR/parse-error.rs:92:35
    |
 LL |         asm!("{}", inlateout(reg) _);
    |                                   ^
 
 error: requires at least a template string argument
-  --> $DIR/parse-error.rs:101:1
+  --> $DIR/parse-error.rs:99:1
    |
 LL | global_asm!();
    | ^^^^^^^^^^^^^
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:103:13
+  --> $DIR/parse-error.rs:101:13
    |
 LL | global_asm!(FOO);
    |             ^^^
 
 error: expected token: `,`
-  --> $DIR/parse-error.rs:105:18
+  --> $DIR/parse-error.rs:103:18
    |
 LL | global_asm!("{}" FOO);
    |                  ^^^ expected `,`
 
 error: expected operand, options, or additional template string
-  --> $DIR/parse-error.rs:107:19
+  --> $DIR/parse-error.rs:105:19
    |
 LL | global_asm!("{}", FOO);
    |                   ^^^ expected operand, options, or additional template string
 
 error: expected expression, found end of macro arguments
-  --> $DIR/parse-error.rs:109:24
+  --> $DIR/parse-error.rs:107:24
    |
 LL | global_asm!("{}", const);
    |                        ^ expected expression
 
 error: expected one of `,`, `.`, `?`, or an operator, found `FOO`
-  --> $DIR/parse-error.rs:111:30
+  --> $DIR/parse-error.rs:109:30
    |
 LL | global_asm!("{}", const(reg) FOO);
    |                              ^^^ expected one of `,`, `.`, `?`, or an operator
 
 error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
-  --> $DIR/parse-error.rs:113:25
+  --> $DIR/parse-error.rs:111:25
    |
 LL | global_asm!("", options(FOO));
    |                         ^^^ expected one of `)`, `att_syntax`, or `raw`
 
 error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
-  --> $DIR/parse-error.rs:115:25
+  --> $DIR/parse-error.rs:113:25
    |
 LL | global_asm!("", options(FOO,));
    |                         ^^^ expected one of `)`, `att_syntax`, or `raw`
 
 error: the `nomem` option cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:117:25
+  --> $DIR/parse-error.rs:115:25
    |
 LL | global_asm!("", options(nomem FOO));
    |                         ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly
 
 error: expected one of `)` or `,`, found `FOO`
-  --> $DIR/parse-error.rs:117:31
+  --> $DIR/parse-error.rs:115:31
    |
 LL | global_asm!("", options(nomem FOO));
    |                               ^^^ expected one of `)` or `,`
 
 error: the `nomem` option cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:120:25
+  --> $DIR/parse-error.rs:118:25
    |
 LL | global_asm!("", options(nomem, FOO));
    |                         ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly
 
 error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
-  --> $DIR/parse-error.rs:120:32
+  --> $DIR/parse-error.rs:118:32
    |
 LL | global_asm!("", options(nomem, FOO));
    |                                ^^^ expected one of `)`, `att_syntax`, or `raw`
 
 error: expected string literal
-  --> $DIR/parse-error.rs:124:29
+  --> $DIR/parse-error.rs:122:29
    |
 LL | global_asm!("", clobber_abi(FOO));
    |                             ^^^ not a string literal
 
 error: expected one of `)` or `,`, found `FOO`
-  --> $DIR/parse-error.rs:126:33
+  --> $DIR/parse-error.rs:124:33
    |
 LL | global_asm!("", clobber_abi("C" FOO));
    |                                 ^^^ expected one of `)` or `,`
 
 error: expected string literal
-  --> $DIR/parse-error.rs:128:34
+  --> $DIR/parse-error.rs:126:34
    |
 LL | global_asm!("", clobber_abi("C", FOO));
    |                                  ^^^ not a string literal
 
 error: `clobber_abi` cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:130:19
+  --> $DIR/parse-error.rs:128:19
    |
 LL | global_asm!("{}", clobber_abi("C"), const FOO);
    |                   ^^^^^^^^^^^^^^^^
 
 error: `clobber_abi` cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:132:28
+  --> $DIR/parse-error.rs:130:28
    |
 LL | global_asm!("", options(), clobber_abi("C"));
    |                            ^^^^^^^^^^^^^^^^
 
 error: `clobber_abi` cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:134:30
+  --> $DIR/parse-error.rs:132:30
    |
 LL | global_asm!("{}", options(), clobber_abi("C"), const FOO);
    |                              ^^^^^^^^^^^^^^^^
 
 error: `clobber_abi` cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:136:17
+  --> $DIR/parse-error.rs:134:17
    |
 LL | global_asm!("", clobber_abi("C"), clobber_abi("C"));
    |                 ^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^
 
 error: duplicate argument named `a`
-  --> $DIR/parse-error.rs:138:35
+  --> $DIR/parse-error.rs:136:35
    |
 LL | global_asm!("{a}", a = const FOO, a = const BAR);
    |                    -------------  ^^^^^^^^^^^^^ duplicate argument
@@ -345,7 +345,7 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR);
    |                    previously here
 
 error: argument never used
-  --> $DIR/parse-error.rs:138:35
+  --> $DIR/parse-error.rs:136:35
    |
 LL | global_asm!("{a}", a = const FOO, a = const BAR);
    |                                   ^^^^^^^^^^^^^ argument never used
@@ -353,19 +353,19 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR);
    = help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`
 
 error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""`
-  --> $DIR/parse-error.rs:141:28
+  --> $DIR/parse-error.rs:139:28
    |
 LL | global_asm!("", options(), "");
    |                            ^^ expected one of `clobber_abi`, `const`, `options`, or `sym`
 
 error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"`
-  --> $DIR/parse-error.rs:143:30
+  --> $DIR/parse-error.rs:141:30
    |
 LL | global_asm!("{}", const FOO, "{}", const FOO);
    |                              ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym`
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:145:13
+  --> $DIR/parse-error.rs:143:13
    |
 LL | global_asm!(format!("{{{}}}", 0), const FOO);
    |             ^^^^^^^^^^^^^^^^^^^^
@@ -373,7 +373,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO);
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: asm template must be a string literal
-  --> $DIR/parse-error.rs:147:20
+  --> $DIR/parse-error.rs:145:20
    |
 LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
    |                    ^^^^^^^^^^^^^^^^^^^^
@@ -381,43 +381,43 @@ LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
    = note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: the `in` operand cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:150:19
+  --> $DIR/parse-error.rs:148:19
    |
 LL | global_asm!("{}", in(reg));
    |                   ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it
 
 error: the `out` operand cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:152:19
+  --> $DIR/parse-error.rs:150:19
    |
 LL | global_asm!("{}", out(reg));
    |                   ^^^ the `out` operand is not meaningful for global-scoped inline assembly, remove it
 
 error: the `lateout` operand cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:154:19
+  --> $DIR/parse-error.rs:152:19
    |
 LL | global_asm!("{}", lateout(reg));
    |                   ^^^^^^^ the `lateout` operand is not meaningful for global-scoped inline assembly, remove it
 
 error: the `inout` operand cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:156:19
+  --> $DIR/parse-error.rs:154:19
    |
 LL | global_asm!("{}", inout(reg));
    |                   ^^^^^ the `inout` operand is not meaningful for global-scoped inline assembly, remove it
 
 error: the `inlateout` operand cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:158:19
+  --> $DIR/parse-error.rs:156:19
    |
 LL | global_asm!("{}", inlateout(reg));
    |                   ^^^^^^^^^ the `inlateout` operand is not meaningful for global-scoped inline assembly, remove it
 
 error: the `label` operand cannot be used with `global_asm!`
-  --> $DIR/parse-error.rs:160:19
+  --> $DIR/parse-error.rs:158:19
    |
 LL | global_asm!("{}", label(reg));
    |                   ^^^^^ the `label` operand is not meaningful for global-scoped inline assembly, remove it
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:39:37
+  --> $DIR/parse-error.rs:37:37
    |
 LL |         asm!("{}", options(), const foo);
    |                                     ^^^ non-constant value
@@ -428,7 +428,7 @@ LL |     const foo: /* Type */ = 0;
    |     ~~~~~    ++++++++++++
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:71:44
+  --> $DIR/parse-error.rs:69:44
    |
 LL |         asm!("{}", clobber_abi("C"), const foo);
    |                                            ^^^ non-constant value
@@ -439,7 +439,7 @@ LL |     const foo: /* Type */ = 0;
    |     ~~~~~    ++++++++++++
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:74:55
+  --> $DIR/parse-error.rs:72:55
    |
 LL |         asm!("{}", options(), clobber_abi("C"), const foo);
    |                                                       ^^^ non-constant value
@@ -450,7 +450,7 @@ LL |     const foo: /* Type */ = 0;
    |     ~~~~~    ++++++++++++
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:76:31
+  --> $DIR/parse-error.rs:74:31
    |
 LL |         asm!("{a}", a = const foo, a = const bar);
    |                               ^^^ non-constant value
@@ -461,7 +461,7 @@ LL |     const foo: /* Type */ = 0;
    |     ~~~~~    ++++++++++++
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/parse-error.rs:76:46
+  --> $DIR/parse-error.rs:74:46
    |
 LL |         asm!("{a}", a = const foo, a = const bar);
    |                                              ^^^ non-constant value
diff --git a/tests/ui/asm/type-check-1.rs b/tests/ui/asm/type-check-1.rs
index 22669dce280..4dc30fb5838 100644
--- a/tests/ui/asm/type-check-1.rs
+++ b/tests/ui/asm/type-check-1.rs
@@ -2,8 +2,6 @@
 //@ ignore-nvptx64
 //@ ignore-spirv
 
-#![feature(asm_const)]
-
 use std::arch::{asm, global_asm};
 
 fn main() {
diff --git a/tests/ui/asm/type-check-1.stderr b/tests/ui/asm/type-check-1.stderr
index d47e6ae1d2a..aa9eed2fce6 100644
--- a/tests/ui/asm/type-check-1.stderr
+++ b/tests/ui/asm/type-check-1.stderr
@@ -1,17 +1,17 @@
 error: invalid asm output
-  --> $DIR/type-check-1.rs:14:29
+  --> $DIR/type-check-1.rs:12:29
    |
 LL |         asm!("{}", out(reg) 1 + 2);
    |                             ^^^^^ cannot assign to this expression
 
 error: invalid asm output
-  --> $DIR/type-check-1.rs:16:31
+  --> $DIR/type-check-1.rs:14:31
    |
 LL |         asm!("{}", inout(reg) 1 + 2);
    |                               ^^^^^ cannot assign to this expression
 
 error[E0277]: the size for values of type `[u64]` cannot be known at compilation time
-  --> $DIR/type-check-1.rs:22:28
+  --> $DIR/type-check-1.rs:20:28
    |
 LL |         asm!("{}", in(reg) v[..]);
    |                            ^^^^^ doesn't have a size known at compile-time
@@ -20,7 +20,7 @@ LL |         asm!("{}", in(reg) v[..]);
    = note: all inline asm arguments must have a statically known size
 
 error[E0277]: the size for values of type `[u64]` cannot be known at compilation time
-  --> $DIR/type-check-1.rs:25:29
+  --> $DIR/type-check-1.rs:23:29
    |
 LL |         asm!("{}", out(reg) v[..]);
    |                             ^^^^^ doesn't have a size known at compile-time
@@ -29,7 +29,7 @@ LL |         asm!("{}", out(reg) v[..]);
    = note: all inline asm arguments must have a statically known size
 
 error[E0277]: the size for values of type `[u64]` cannot be known at compilation time
-  --> $DIR/type-check-1.rs:28:31
+  --> $DIR/type-check-1.rs:26:31
    |
 LL |         asm!("{}", inout(reg) v[..]);
    |                               ^^^^^ doesn't have a size known at compile-time
@@ -38,7 +38,7 @@ LL |         asm!("{}", inout(reg) v[..]);
    = note: all inline asm arguments must have a statically known size
 
 error: cannot use value of type `[u64]` for inline assembly
-  --> $DIR/type-check-1.rs:22:28
+  --> $DIR/type-check-1.rs:20:28
    |
 LL |         asm!("{}", in(reg) v[..]);
    |                            ^^^^^
@@ -46,7 +46,7 @@ LL |         asm!("{}", in(reg) v[..]);
    = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
 
 error: cannot use value of type `[u64]` for inline assembly
-  --> $DIR/type-check-1.rs:25:29
+  --> $DIR/type-check-1.rs:23:29
    |
 LL |         asm!("{}", out(reg) v[..]);
    |                             ^^^^^
@@ -54,7 +54,7 @@ LL |         asm!("{}", out(reg) v[..]);
    = note: only integers, floats, SIMD vectors, pointers and function pointers can be used as arguments for inline assembly
 
 error: cannot use value of type `[u64]` for inline assembly
-  --> $DIR/type-check-1.rs:28:31
+  --> $DIR/type-check-1.rs:26:31
    |
 LL |         asm!("{}", inout(reg) v[..]);
    |                               ^^^^^
diff --git a/tests/ui/asm/x86_64/bad-reg.rs b/tests/ui/asm/x86_64/bad-reg.rs
index d41c46d57bb..2a189a91c5a 100644
--- a/tests/ui/asm/x86_64/bad-reg.rs
+++ b/tests/ui/asm/x86_64/bad-reg.rs
@@ -1,8 +1,6 @@
 //@ only-x86_64
 //@ compile-flags: -C target-feature=+avx2
 
-#![feature(asm_const)]
-
 use std::arch::asm;
 
 fn main() {
diff --git a/tests/ui/asm/x86_64/bad-reg.stderr b/tests/ui/asm/x86_64/bad-reg.stderr
index 8017008e97d..3df1f7b2208 100644
--- a/tests/ui/asm/x86_64/bad-reg.stderr
+++ b/tests/ui/asm/x86_64/bad-reg.stderr
@@ -1,17 +1,17 @@
 error: invalid register class `foo`: unknown register class
-  --> $DIR/bad-reg.rs:14:20
+  --> $DIR/bad-reg.rs:12:20
    |
 LL |         asm!("{}", in(foo) foo);
    |                    ^^^^^^^^^^^
 
 error: invalid register `foo`: unknown register
-  --> $DIR/bad-reg.rs:16:18
+  --> $DIR/bad-reg.rs:14:18
    |
 LL |         asm!("", in("foo") foo);
    |                  ^^^^^^^^^^^^^
 
 error: invalid asm template modifier for this register class
-  --> $DIR/bad-reg.rs:18:15
+  --> $DIR/bad-reg.rs:16:15
    |
 LL |         asm!("{:z}", in(reg) foo);
    |               ^^^^   ----------- argument
@@ -21,7 +21,7 @@ LL |         asm!("{:z}", in(reg) foo);
    = note: the `reg` register class supports the following template modifiers: `l`, `x`, `e`, `r`
 
 error: invalid asm template modifier for this register class
-  --> $DIR/bad-reg.rs:20:15
+  --> $DIR/bad-reg.rs:18:15
    |
 LL |         asm!("{:r}", in(xmm_reg) foo);
    |               ^^^^   --------------- argument
@@ -31,7 +31,7 @@ LL |         asm!("{:r}", in(xmm_reg) foo);
    = note: the `xmm_reg` register class supports the following template modifiers: `x`, `y`, `z`
 
 error: asm template modifiers are not allowed for `const` arguments
-  --> $DIR/bad-reg.rs:22:15
+  --> $DIR/bad-reg.rs:20:15
    |
 LL |         asm!("{:a}", const 0);
    |               ^^^^   ------- argument
@@ -39,7 +39,7 @@ LL |         asm!("{:a}", const 0);
    |               template modifier
 
 error: asm template modifiers are not allowed for `sym` arguments
-  --> $DIR/bad-reg.rs:24:15
+  --> $DIR/bad-reg.rs:22:15
    |
 LL |         asm!("{:a}", sym main);
    |               ^^^^   -------- argument
@@ -47,67 +47,67 @@ LL |         asm!("{:a}", sym main);
    |               template modifier
 
 error: invalid register `ebp`: the frame pointer cannot be used as an operand for inline asm
-  --> $DIR/bad-reg.rs:26:18
+  --> $DIR/bad-reg.rs:24:18
    |
 LL |         asm!("", in("ebp") foo);
    |                  ^^^^^^^^^^^^^
 
 error: invalid register `rsp`: the stack pointer cannot be used as an operand for inline asm
-  --> $DIR/bad-reg.rs:28:18
+  --> $DIR/bad-reg.rs:26:18
    |
 LL |         asm!("", in("rsp") foo);
    |                  ^^^^^^^^^^^^^
 
 error: invalid register `ip`: the instruction pointer cannot be used as an operand for inline asm
-  --> $DIR/bad-reg.rs:30:18
+  --> $DIR/bad-reg.rs:28:18
    |
 LL |         asm!("", in("ip") foo);
    |                  ^^^^^^^^^^^^
 
 error: register class `x87_reg` can only be used as a clobber, not as an input or output
-  --> $DIR/bad-reg.rs:33:18
+  --> $DIR/bad-reg.rs:31:18
    |
 LL |         asm!("", in("st(2)") foo);
    |                  ^^^^^^^^^^^^^^^
 
 error: register class `mmx_reg` can only be used as a clobber, not as an input or output
-  --> $DIR/bad-reg.rs:36:18
+  --> $DIR/bad-reg.rs:34:18
    |
 LL |         asm!("", in("mm0") foo);
    |                  ^^^^^^^^^^^^^
 
 error: register class `kreg0` can only be used as a clobber, not as an input or output
-  --> $DIR/bad-reg.rs:39:18
+  --> $DIR/bad-reg.rs:37:18
    |
 LL |         asm!("", in("k0") foo);
    |                  ^^^^^^^^^^^^
 
 error: register class `x87_reg` can only be used as a clobber, not as an input or output
-  --> $DIR/bad-reg.rs:44:20
+  --> $DIR/bad-reg.rs:42:20
    |
 LL |         asm!("{}", in(x87_reg) foo);
    |                    ^^^^^^^^^^^^^^^
 
 error: register class `mmx_reg` can only be used as a clobber, not as an input or output
-  --> $DIR/bad-reg.rs:47:20
+  --> $DIR/bad-reg.rs:45:20
    |
 LL |         asm!("{}", in(mmx_reg) foo);
    |                    ^^^^^^^^^^^^^^^
 
 error: register class `x87_reg` can only be used as a clobber, not as an input or output
-  --> $DIR/bad-reg.rs:50:20
+  --> $DIR/bad-reg.rs:48:20
    |
 LL |         asm!("{}", out(x87_reg) _);
    |                    ^^^^^^^^^^^^^^
 
 error: register class `mmx_reg` can only be used as a clobber, not as an input or output
-  --> $DIR/bad-reg.rs:52:20
+  --> $DIR/bad-reg.rs:50:20
    |
 LL |         asm!("{}", out(mmx_reg) _);
    |                    ^^^^^^^^^^^^^^
 
 error: register `al` conflicts with register `eax`
-  --> $DIR/bad-reg.rs:58:33
+  --> $DIR/bad-reg.rs:56:33
    |
 LL |         asm!("", in("eax") foo, in("al") bar);
    |                  -------------  ^^^^^^^^^^^^ register `al`
@@ -115,7 +115,7 @@ LL |         asm!("", in("eax") foo, in("al") bar);
    |                  register `eax`
 
 error: register `rax` conflicts with register `rax`
-  --> $DIR/bad-reg.rs:61:33
+  --> $DIR/bad-reg.rs:59:33
    |
 LL |         asm!("", in("rax") foo, out("rax") bar);
    |                  -------------  ^^^^^^^^^^^^^^ register `rax`
@@ -123,13 +123,13 @@ LL |         asm!("", in("rax") foo, out("rax") bar);
    |                  register `rax`
    |
 help: use `lateout` instead of `out` to avoid conflict
-  --> $DIR/bad-reg.rs:61:18
+  --> $DIR/bad-reg.rs:59:18
    |
 LL |         asm!("", in("rax") foo, out("rax") bar);
    |                  ^^^^^^^^^^^^^
 
 error: register `ymm0` conflicts with register `xmm0`
-  --> $DIR/bad-reg.rs:66:34
+  --> $DIR/bad-reg.rs:64:34
    |
 LL |         asm!("", in("xmm0") foo, in("ymm0") bar);
    |                  --------------  ^^^^^^^^^^^^^^ register `ymm0`
@@ -137,7 +137,7 @@ LL |         asm!("", in("xmm0") foo, in("ymm0") bar);
    |                  register `xmm0`
 
 error: register `ymm0` conflicts with register `xmm0`
-  --> $DIR/bad-reg.rs:68:34
+  --> $DIR/bad-reg.rs:66:34
    |
 LL |         asm!("", in("xmm0") foo, out("ymm0") bar);
    |                  --------------  ^^^^^^^^^^^^^^^ register `ymm0`
@@ -145,13 +145,13 @@ LL |         asm!("", in("xmm0") foo, out("ymm0") bar);
    |                  register `xmm0`
    |
 help: use `lateout` instead of `out` to avoid conflict
-  --> $DIR/bad-reg.rs:68:18
+  --> $DIR/bad-reg.rs:66:18
    |
 LL |         asm!("", in("xmm0") foo, out("ymm0") bar);
    |                  ^^^^^^^^^^^^^^
 
 error: type `i32` cannot be used with this register class
-  --> $DIR/bad-reg.rs:33:30
+  --> $DIR/bad-reg.rs:31:30
    |
 LL |         asm!("", in("st(2)") foo);
    |                              ^^^
@@ -159,7 +159,7 @@ LL |         asm!("", in("st(2)") foo);
    = note: register class `x87_reg` supports these types: 
 
 error: type `i32` cannot be used with this register class
-  --> $DIR/bad-reg.rs:36:28
+  --> $DIR/bad-reg.rs:34:28
    |
 LL |         asm!("", in("mm0") foo);
    |                            ^^^
@@ -167,7 +167,7 @@ LL |         asm!("", in("mm0") foo);
    = note: register class `mmx_reg` supports these types: 
 
 error: type `i32` cannot be used with this register class
-  --> $DIR/bad-reg.rs:39:27
+  --> $DIR/bad-reg.rs:37:27
    |
 LL |         asm!("", in("k0") foo);
    |                           ^^^
@@ -175,7 +175,7 @@ LL |         asm!("", in("k0") foo);
    = note: register class `kreg0` supports these types: 
 
 error: type `i32` cannot be used with this register class
-  --> $DIR/bad-reg.rs:44:32
+  --> $DIR/bad-reg.rs:42:32
    |
 LL |         asm!("{}", in(x87_reg) foo);
    |                                ^^^
@@ -183,7 +183,7 @@ LL |         asm!("{}", in(x87_reg) foo);
    = note: register class `x87_reg` supports these types: 
 
 error: type `i32` cannot be used with this register class
-  --> $DIR/bad-reg.rs:47:32
+  --> $DIR/bad-reg.rs:45:32
    |
 LL |         asm!("{}", in(mmx_reg) foo);
    |                                ^^^
@@ -191,7 +191,7 @@ LL |         asm!("{}", in(mmx_reg) foo);
    = note: register class `mmx_reg` supports these types: 
 
 error: type `i32` cannot be used with this register class
-  --> $DIR/bad-reg.rs:58:42
+  --> $DIR/bad-reg.rs:56:42
    |
 LL |         asm!("", in("eax") foo, in("al") bar);
    |                                          ^^^
@@ -199,7 +199,7 @@ LL |         asm!("", in("eax") foo, in("al") bar);
    = note: register class `reg_byte` supports these types: i8
 
 error: type `i32` cannot be used with this register class
-  --> $DIR/bad-reg.rs:63:27
+  --> $DIR/bad-reg.rs:61:27
    |
 LL |         asm!("", in("al") foo, lateout("al") bar);
    |                           ^^^
@@ -207,7 +207,7 @@ LL |         asm!("", in("al") foo, lateout("al") bar);
    = note: register class `reg_byte` supports these types: i8
 
 error: type `i32` cannot be used with this register class
-  --> $DIR/bad-reg.rs:63:46
+  --> $DIR/bad-reg.rs:61:46
    |
 LL |         asm!("", in("al") foo, lateout("al") bar);
    |                                              ^^^
diff --git a/tests/ui/asm/x86_64/const.rs b/tests/ui/asm/x86_64/const.rs
index 817a338a5b9..eaaaf92e823 100644
--- a/tests/ui/asm/x86_64/const.rs
+++ b/tests/ui/asm/x86_64/const.rs
@@ -2,8 +2,6 @@
 //@ run-pass
 //@ needs-asm-support
 
-#![feature(asm_const)]
-
 use std::arch::{asm, global_asm};
 
 fn const_generic<const X: usize>() -> usize {
diff --git a/tests/ui/asm/x86_64/type-check-3.rs b/tests/ui/asm/x86_64/type-check-3.rs
index bd242af3dbc..bfb795d2624 100644
--- a/tests/ui/asm/x86_64/type-check-3.rs
+++ b/tests/ui/asm/x86_64/type-check-3.rs
@@ -1,8 +1,6 @@
 //@ only-x86_64
 //@ compile-flags: -C target-feature=+avx512f
 
-#![feature(asm_const)]
-
 use std::arch::{asm, global_asm};
 
 use std::arch::x86_64::{_mm256_setzero_ps, _mm_setzero_ps};
diff --git a/tests/ui/asm/x86_64/type-check-3.stderr b/tests/ui/asm/x86_64/type-check-3.stderr
index 202b97ca5c0..5a7b349413e 100644
--- a/tests/ui/asm/x86_64/type-check-3.stderr
+++ b/tests/ui/asm/x86_64/type-check-3.stderr
@@ -1,5 +1,5 @@
 error: type `i128` cannot be used with this register class
-  --> $DIR/type-check-3.rs:14:28
+  --> $DIR/type-check-3.rs:12:28
    |
 LL |         asm!("{}", in(reg) 0i128);
    |                            ^^^^^
@@ -7,7 +7,7 @@ LL |         asm!("{}", in(reg) 0i128);
    = note: register class `reg` supports these types: i16, i32, i64, f16, f32, f64
 
 error: type `__m128` cannot be used with this register class
-  --> $DIR/type-check-3.rs:16:28
+  --> $DIR/type-check-3.rs:14:28
    |
 LL |         asm!("{}", in(reg) _mm_setzero_ps());
    |                            ^^^^^^^^^^^^^^^^
@@ -15,7 +15,7 @@ LL |         asm!("{}", in(reg) _mm_setzero_ps());
    = note: register class `reg` supports these types: i16, i32, i64, f16, f32, f64
 
 error: type `__m256` cannot be used with this register class
-  --> $DIR/type-check-3.rs:18:28
+  --> $DIR/type-check-3.rs:16:28
    |
 LL |         asm!("{}", in(reg) _mm256_setzero_ps());
    |                            ^^^^^^^^^^^^^^^^^^^
@@ -23,7 +23,7 @@ LL |         asm!("{}", in(reg) _mm256_setzero_ps());
    = note: register class `reg` supports these types: i16, i32, i64, f16, f32, f64
 
 error: type `u8` cannot be used with this register class
-  --> $DIR/type-check-3.rs:20:32
+  --> $DIR/type-check-3.rs:18:32
    |
 LL |         asm!("{}", in(xmm_reg) 0u8);
    |                                ^^^
@@ -31,7 +31,7 @@ LL |         asm!("{}", in(xmm_reg) 0u8);
    = note: register class `xmm_reg` supports these types: i32, i64, f16, f32, f64, f128, i8x16, i16x8, i32x4, i64x2, f16x8, f32x4, f64x2
 
 error: `avx512bw` target feature is not enabled
-  --> $DIR/type-check-3.rs:29:29
+  --> $DIR/type-check-3.rs:27:29
    |
 LL |         asm!("{}", in(kreg) 0u64);
    |                             ^^^^
@@ -39,7 +39,7 @@ LL |         asm!("{}", in(kreg) 0u64);
    = note: this is required to use type `u64` with register class `kreg`
 
 warning: formatting may not be suitable for sub-register argument
-  --> $DIR/type-check-3.rs:34:15
+  --> $DIR/type-check-3.rs:32:15
    |
 LL |         asm!("{0} {0}", in(reg) 0i16);
    |               ^^^ ^^^           ---- for this argument
@@ -49,7 +49,7 @@ LL |         asm!("{0} {0}", in(reg) 0i16);
    = note: `#[warn(asm_sub_register)]` on by default
 
 warning: formatting may not be suitable for sub-register argument
-  --> $DIR/type-check-3.rs:36:15
+  --> $DIR/type-check-3.rs:34:15
    |
 LL |         asm!("{0} {0:x}", in(reg) 0i16);
    |               ^^^                 ---- for this argument
@@ -58,7 +58,7 @@ LL |         asm!("{0} {0:x}", in(reg) 0i16);
    = help: or use `{0:r}` to keep the default formatting of `rax` (for 64-bit values)
 
 warning: formatting may not be suitable for sub-register argument
-  --> $DIR/type-check-3.rs:38:15
+  --> $DIR/type-check-3.rs:36:15
    |
 LL |         asm!("{}", in(reg) 0i32);
    |               ^^           ---- for this argument
@@ -67,7 +67,7 @@ LL |         asm!("{}", in(reg) 0i32);
    = help: or use `{0:r}` to keep the default formatting of `rax` (for 64-bit values)
 
 warning: formatting may not be suitable for sub-register argument
-  --> $DIR/type-check-3.rs:41:15
+  --> $DIR/type-check-3.rs:39:15
    |
 LL |         asm!("{}", in(ymm_reg) 0i64);
    |               ^^               ---- for this argument
@@ -76,7 +76,7 @@ LL |         asm!("{}", in(ymm_reg) 0i64);
    = help: or use `{0:y}` to keep the default formatting of `ymm0` (for 256-bit values)
 
 error: type `i8` cannot be used with this register class
-  --> $DIR/type-check-3.rs:52:28
+  --> $DIR/type-check-3.rs:50:28
    |
 LL |         asm!("{}", in(reg) 0i8);
    |                            ^^^
@@ -85,7 +85,7 @@ LL |         asm!("{}", in(reg) 0i8);
    = help: consider using the `reg_byte` register class instead
 
 error: incompatible types for asm inout argument
-  --> $DIR/type-check-3.rs:64:33
+  --> $DIR/type-check-3.rs:62:33
    |
 LL |         asm!("{:r}", inout(reg) 0u32 => val_f32);
    |                                 ^^^^    ^^^^^^^ type `f32`
@@ -95,7 +95,7 @@ LL |         asm!("{:r}", inout(reg) 0u32 => val_f32);
    = note: asm inout arguments must have the same type, unless they are both pointers or integers of the same size
 
 error: incompatible types for asm inout argument
-  --> $DIR/type-check-3.rs:66:33
+  --> $DIR/type-check-3.rs:64:33
    |
 LL |         asm!("{:r}", inout(reg) 0u32 => val_ptr);
    |                                 ^^^^    ^^^^^^^ type `*mut u8`
@@ -105,7 +105,7 @@ LL |         asm!("{:r}", inout(reg) 0u32 => val_ptr);
    = note: asm inout arguments must have the same type, unless they are both pointers or integers of the same size
 
 error: incompatible types for asm inout argument
-  --> $DIR/type-check-3.rs:68:33
+  --> $DIR/type-check-3.rs:66:33
    |
 LL |         asm!("{:r}", inout(reg) main => val_u32);
    |                                 ^^^^    ^^^^^^^ type `u32`
diff --git a/tests/ui/asm/x86_64/type-check-4.rs b/tests/ui/asm/x86_64/type-check-4.rs
index f7bf60d04df..9503cd6d8ab 100644
--- a/tests/ui/asm/x86_64/type-check-4.rs
+++ b/tests/ui/asm/x86_64/type-check-4.rs
@@ -1,8 +1,6 @@
 //@ only-x86_64
 //@ compile-flags: -C target-feature=+avx512f
 
-#![feature(asm_const)]
-
 use std::arch::{asm, global_asm};
 
 use std::arch::x86_64::{_mm256_setzero_ps, _mm_setzero_ps};
diff --git a/tests/ui/asm/x86_64/type-check-4.stderr b/tests/ui/asm/x86_64/type-check-4.stderr
index cbdc051b343..f1bbc9e7d33 100644
--- a/tests/ui/asm/x86_64/type-check-4.stderr
+++ b/tests/ui/asm/x86_64/type-check-4.stderr
@@ -1,5 +1,5 @@
 error[E0658]: referencing statics in constants is unstable
-  --> $DIR/type-check-4.rs:21:25
+  --> $DIR/type-check-4.rs:19:25
    |
 LL | global_asm!("{}", const S);
    |                         ^
@@ -11,7 +11,7 @@ LL | global_asm!("{}", const S);
    = help: to fix this, the value can be extracted to a `const` and then used.
 
 error[E0658]: referencing statics in constants is unstable
-  --> $DIR/type-check-4.rs:24:35
+  --> $DIR/type-check-4.rs:22:35
    |
 LL | global_asm!("{}", const const_foo(S));
    |                                   ^
@@ -23,7 +23,7 @@ LL | global_asm!("{}", const const_foo(S));
    = help: to fix this, the value can be extracted to a `const` and then used.
 
 error[E0658]: referencing statics in constants is unstable
-  --> $DIR/type-check-4.rs:27:35
+  --> $DIR/type-check-4.rs:25:35
    |
 LL | global_asm!("{}", const const_bar(S));
    |                                   ^
diff --git a/tests/ui/asm/x86_64/x86_64_parse_error.rs b/tests/ui/asm/x86_64/x86_64_parse_error.rs
index 850033d4ce0..3df0febf6b0 100644
--- a/tests/ui/asm/x86_64/x86_64_parse_error.rs
+++ b/tests/ui/asm/x86_64/x86_64_parse_error.rs
@@ -1,7 +1,5 @@
 //@ only-x86_64
 
-#![feature(asm_const)]
-
 use std::arch::asm;
 
 fn main() {
diff --git a/tests/ui/asm/x86_64/x86_64_parse_error.stderr b/tests/ui/asm/x86_64/x86_64_parse_error.stderr
index 9751f7b09d0..b64f6c1127e 100644
--- a/tests/ui/asm/x86_64/x86_64_parse_error.stderr
+++ b/tests/ui/asm/x86_64/x86_64_parse_error.stderr
@@ -1,11 +1,11 @@
 error: explicit register arguments cannot have names
-  --> $DIR/x86_64_parse_error.rs:11:18
+  --> $DIR/x86_64_parse_error.rs:9:18
    |
 LL |         asm!("", a = in("eax") foo);
    |                  ^^^^^^^^^^^^^^^^^
 
 error: positional arguments cannot follow named arguments or explicit register arguments
-  --> $DIR/x86_64_parse_error.rs:17:36
+  --> $DIR/x86_64_parse_error.rs:15:36
    |
 LL |         asm!("{1}", in("eax") foo, const bar);
    |                     -------------  ^^^^^^^^^ positional argument
@@ -13,7 +13,7 @@ LL |         asm!("{1}", in("eax") foo, const bar);
    |                     explicit register argument
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/x86_64_parse_error.rs:13:46
+  --> $DIR/x86_64_parse_error.rs:11:46
    |
 LL |         asm!("{a}", in("eax") foo, a = const bar);
    |                                              ^^^ non-constant value
@@ -24,7 +24,7 @@ LL |     const bar: /* Type */ = 0;
    |     ~~~~~    ++++++++++++
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/x86_64_parse_error.rs:15:46
+  --> $DIR/x86_64_parse_error.rs:13:46
    |
 LL |         asm!("{a}", in("eax") foo, a = const bar);
    |                                              ^^^ non-constant value
@@ -35,7 +35,7 @@ LL |     const bar: /* Type */ = 0;
    |     ~~~~~    ++++++++++++
 
 error[E0435]: attempt to use a non-constant value in a constant
-  --> $DIR/x86_64_parse_error.rs:17:42
+  --> $DIR/x86_64_parse_error.rs:15:42
    |
 LL |         asm!("{1}", in("eax") foo, const bar);
    |                                          ^^^ non-constant value
diff --git a/tests/ui/feature-gates/feature-gate-asm_const.rs b/tests/ui/feature-gates/feature-gate-asm_const.rs
deleted file mode 100644
index 42d5ba69222..00000000000
--- a/tests/ui/feature-gates/feature-gate-asm_const.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-//@ only-x86_64
-
-use std::arch::asm;
-
-unsafe fn foo<const N: usize>() {
-    asm!("mov eax, {}", const N + 1);
-    //~^ ERROR const operands for inline assembly are unstable
-}
-
-fn main() {
-    unsafe {
-        foo::<0>();
-        asm!("mov eax, {}", const 123);
-        //~^ ERROR const operands for inline assembly are unstable
-    }
-}
diff --git a/tests/ui/feature-gates/feature-gate-asm_const.stderr b/tests/ui/feature-gates/feature-gate-asm_const.stderr
deleted file mode 100644
index 4f83fee6759..00000000000
--- a/tests/ui/feature-gates/feature-gate-asm_const.stderr
+++ /dev/null
@@ -1,23 +0,0 @@
-error[E0658]: const operands for inline assembly are unstable
-  --> $DIR/feature-gate-asm_const.rs:6:25
-   |
-LL |     asm!("mov eax, {}", const N + 1);
-   |                         ^^^^^^^^^^^
-   |
-   = note: see issue #93332 <https://github.com/rust-lang/rust/issues/93332> for more information
-   = help: add `#![feature(asm_const)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error[E0658]: const operands for inline assembly are unstable
-  --> $DIR/feature-gate-asm_const.rs:13:29
-   |
-LL |         asm!("mov eax, {}", const 123);
-   |                             ^^^^^^^^^
-   |
-   = note: see issue #93332 <https://github.com/rust-lang/rust/issues/93332> for more information
-   = help: add `#![feature(asm_const)]` to the crate attributes to enable
-   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/higher-ranked/subtyping-fn-ptr-coercion.rs b/tests/ui/higher-ranked/subtyping-fn-ptr-coercion.rs
new file mode 100644
index 00000000000..0cecf6808f2
--- /dev/null
+++ b/tests/ui/higher-ranked/subtyping-fn-ptr-coercion.rs
@@ -0,0 +1,10 @@
+//@ check-pass
+
+// Check that we use subtyping when reifying a closure into a function pointer.
+
+fn foo(x: &str) {}
+
+fn main() {
+    let c = |_: &str| {};
+    let x = c as fn(&'static str);
+}
diff --git a/tests/ui/impl-trait/recursive-ice-101862.stderr b/tests/ui/impl-trait/recursive-ice-101862.stderr
index f4148720c33..970373422e8 100644
--- a/tests/ui/impl-trait/recursive-ice-101862.stderr
+++ b/tests/ui/impl-trait/recursive-ice-101862.stderr
@@ -11,13 +11,13 @@ LL |     vec![].append(&mut ice(x.as_ref()));
    = note: `#[warn(unconditional_recursion)]` on by default
 
 error[E0792]: expected generic type parameter, found `&str`
-  --> $DIR/recursive-ice-101862.rs:6:5
+  --> $DIR/recursive-ice-101862.rs:6:19
    |
 LL | pub fn ice(x: impl AsRef<str>) -> impl IntoIterator<Item = ()> {
    |               --------------- this generic parameter must be used with a generic type parameter
 LL |
 LL |     vec![].append(&mut ice(x.as_ref()));
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                   ^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 1 previous error; 1 warning emitted
 
diff --git a/tests/ui/traits/next-solver/alias-bound-unsound.rs b/tests/ui/traits/next-solver/alias-bound-unsound.rs
index a5bd3e7afa8..272e5db3b7a 100644
--- a/tests/ui/traits/next-solver/alias-bound-unsound.rs
+++ b/tests/ui/traits/next-solver/alias-bound-unsound.rs
@@ -27,5 +27,6 @@ fn main() {
     //~| ERROR overflow evaluating the requirement `&<() as Foo>::Item well-formed`
     //~| ERROR overflow evaluating the requirement `<() as Foo>::Item == _`
     //~| ERROR overflow evaluating the requirement `<() as Foo>::Item == _`
+    //~| ERROR overflow evaluating the requirement `<() as Foo>::Item == _`
     println!("{x}");
 }
diff --git a/tests/ui/traits/next-solver/alias-bound-unsound.stderr b/tests/ui/traits/next-solver/alias-bound-unsound.stderr
index a5c2f215134..e5cf5b6bc3d 100644
--- a/tests/ui/traits/next-solver/alias-bound-unsound.stderr
+++ b/tests/ui/traits/next-solver/alias-bound-unsound.stderr
@@ -44,6 +44,12 @@ LL |     drop(<() as Foo>::copy_me(&x));
    |
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
-error: aborting due to 6 previous errors
+error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _`
+  --> $DIR/alias-bound-unsound.rs:24:31
+   |
+LL |     drop(<() as Foo>::copy_me(&x));
+   |                               ^^
+
+error: aborting due to 7 previous errors
 
 For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/try-trait/bad-interconversion.stderr b/tests/ui/try-trait/bad-interconversion.stderr
index 642a93d64e2..9aab2cf6ab8 100644
--- a/tests/ui/try-trait/bad-interconversion.stderr
+++ b/tests/ui/try-trait/bad-interconversion.stderr
@@ -45,7 +45,7 @@ LL |     Some(Err("hello")?)
    |                      ^ use `.ok()?` if you want to discard the `Result<Infallible, &str>` error information
    |
    = help: the trait `FromResidual<Result<Infallible, &str>>` is not implemented for `Option<u16>`
-   = help: the trait `FromResidual` is implemented for `Option<T>`
+   = help: the trait `FromResidual<Option<Infallible>>` is implemented for `Option<T>`
 
 error[E0277]: the `?` operator can only be used on `Option`s in a function that returns `Option`
   --> $DIR/bad-interconversion.rs:27:33
@@ -56,7 +56,7 @@ LL |     Some(ControlFlow::Break(123)?)
    |                                 ^ this `?` produces `ControlFlow<{integer}, Infallible>`, which is incompatible with `Option<u64>`
    |
    = help: the trait `FromResidual<ControlFlow<{integer}, Infallible>>` is not implemented for `Option<u64>`
-   = help: the trait `FromResidual` is implemented for `Option<T>`
+   = help: the trait `FromResidual<Option<Infallible>>` is implemented for `Option<T>`
 
 error[E0277]: the `?` operator can only be used on `ControlFlow`s in a function that returns `ControlFlow`
   --> $DIR/bad-interconversion.rs:32:39
diff --git a/tests/ui/try-trait/option-to-result.stderr b/tests/ui/try-trait/option-to-result.stderr
index 8055b2a0b04..1a5a925f92f 100644
--- a/tests/ui/try-trait/option-to-result.stderr
+++ b/tests/ui/try-trait/option-to-result.stderr
@@ -20,7 +20,7 @@ LL |     a?;
    |      ^ use `.ok()?` if you want to discard the `Result<Infallible, i32>` error information
    |
    = help: the trait `FromResidual<Result<Infallible, i32>>` is not implemented for `Option<i32>`
-   = help: the trait `FromResidual` is implemented for `Option<T>`
+   = help: the trait `FromResidual<Option<Infallible>>` is implemented for `Option<T>`
 
 error: aborting due to 2 previous errors