about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--RELEASES.md6
-rw-r--r--compiler/rustc_ast/src/ast.rs4
-rw-r--r--compiler/rustc_ast_lowering/src/asm.rs6
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs8
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs4
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs34
-rw-r--r--compiler/rustc_ast_lowering/src/path.rs4
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs22
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs18
-rw-r--r--compiler/rustc_attr/src/builtin.rs10
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/assert.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs10
-rw-r--r--compiler/rustc_codegen_ssa/src/target_features.rs26
-rw-r--r--compiler/rustc_const_eval/src/check_consts/check.rs4
-rw-r--r--compiler/rustc_const_eval/src/check_consts/mod.rs2
-rw-r--r--compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs2
-rw-r--r--compiler/rustc_data_structures/src/graph/dominators/mod.rs23
-rw-r--r--compiler/rustc_expand/src/base.rs8
-rw-r--r--compiler/rustc_expand/src/config.rs15
-rw-r--r--compiler/rustc_expand/src/expand.rs4
-rw-r--r--compiler/rustc_expand/src/mbe/quoted.rs4
-rw-r--r--compiler/rustc_feature/src/accepted.rs2
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs48
-rw-r--r--compiler/rustc_feature/src/lib.rs14
-rw-r--r--compiler/rustc_feature/src/removed.rs2
-rw-r--r--compiler/rustc_feature/src/unstable.rs154
-rw-r--r--compiler/rustc_hir/src/hir.rs29
-rw-r--r--compiler/rustc_hir_analysis/src/bounds.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs157
-rw-r--r--compiler/rustc_hir_analysis/src/check/region.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/mod.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs37
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/mod.rs2
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs14
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs14
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs38
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/gather_locals.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/method/confirm.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs16
-rw-r--r--compiler/rustc_hir_typeck/src/upvar.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs2
-rw-r--r--compiler/rustc_incremental/src/assert_dep_graph.rs2
-rw-r--r--compiler/rustc_incremental/src/persist/dirty_clean.rs2
-rw-r--r--compiler/rustc_lint/src/async_fn_in_trait.rs2
-rw-r--r--compiler/rustc_lint/src/builtin.rs6
-rw-r--r--compiler/rustc_lint/src/if_let_rescope.rs2
-rw-r--r--compiler/rustc_lint/src/impl_trait_overcaptures.rs4
-rw-r--r--compiler/rustc_lint/src/tail_expr_drop_order.rs2
-rw-r--r--compiler/rustc_lint/src/traits.rs5
-rw-r--r--compiler/rustc_metadata/src/dependency_format.rs2
-rw-r--r--compiler/rustc_metadata/src/native_libs.rs6
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs6
-rw-r--r--compiler/rustc_middle/src/mir/interpret/queries.rs2
-rw-r--r--compiler/rustc_middle/src/traits/specialization_graph.rs2
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs1
-rw-r--r--compiler/rustc_middle/src/ty/context.rs26
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs2
-rw-r--r--compiler/rustc_middle/src/ty/error.rs3
-rw-r--r--compiler/rustc_middle/src/ty/generics.rs4
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs2
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs2
-rw-r--r--compiler/rustc_middle/src/ty/relate.rs22
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs7
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs2
-rw-r--r--compiler/rustc_middle/src/ty/util.rs8
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_operand.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/matches/test.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs2
-rw-r--r--compiler/rustc_mir_transform/src/coroutine.rs2
-rw-r--r--compiler/rustc_mir_transform/src/coverage/graph.rs25
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs2
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs2
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs2
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/inspect/build.rs16
-rw-r--r--compiler/rustc_passes/messages.ftl13
-rw-r--r--compiler/rustc_passes/src/abi_test.rs2
-rw-r--r--compiler/rustc_passes/src/check_attr.rs30
-rw-r--r--compiler/rustc_passes/src/check_const.rs2
-rw-r--r--compiler/rustc_passes/src/errors.rs21
-rw-r--r--compiler/rustc_passes/src/layout_test.rs2
-rw-r--r--compiler/rustc_passes/src/lib_features.rs2
-rw-r--r--compiler/rustc_passes/src/reachable.rs2
-rw-r--r--compiler/rustc_passes/src/stability.rs16
-rw-r--r--compiler/rustc_pattern_analysis/src/rustc.rs6
-rw-r--r--compiler/rustc_privacy/src/lib.rs8
-rw-r--r--compiler/rustc_query_system/src/ich/impls_syntax.rs6
-rw-r--r--compiler/rustc_resolve/src/ident.rs4
-rw-r--r--compiler/rustc_resolve/src/late.rs10
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs6
-rw-r--r--compiler/rustc_resolve/src/macros.rs4
-rw-r--r--compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs21
-rw-r--r--compiler/rustc_smir/src/rustc_internal/internal.rs20
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/ty.rs4
-rw-r--r--compiler/rustc_symbol_mangling/src/test.rs2
-rw-r--r--compiler/rustc_target/src/callconv/loongarch.rs28
-rw-r--r--compiler/rustc_target/src/callconv/mod.rs119
-rw-r--r--compiler/rustc_target/src/callconv/riscv.rs27
-rw-r--r--compiler/rustc_target/src/callconv/x86.rs37
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs104
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/analyse.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/normalize.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_normalize.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs2
-rw-r--r--compiler/rustc_ty_utils/src/abi.rs145
-rw-r--r--compiler/rustc_ty_utils/src/assoc.rs2
-rw-r--r--compiler/rustc_ty_utils/src/consts.rs2
-rw-r--r--compiler/rustc_type_ir/src/binder.rs42
-rw-r--r--compiler/rustc_type_ir/src/error.rs13
-rw-r--r--compiler/rustc_type_ir/src/inherent.rs4
-rw-r--r--compiler/rustc_type_ir/src/interner.rs8
-rw-r--r--compiler/rustc_type_ir/src/predicate.rs45
-rw-r--r--compiler/rustc_type_ir/src/relate.rs58
-rw-r--r--compiler/rustc_type_ir/src/solve/inspect.rs17
-rw-r--r--compiler/rustc_type_ir/src/ty_kind.rs4
-rw-r--r--compiler/rustc_type_ir/src/ty_kind/closure.rs4
-rw-r--r--compiler/rustc_type_ir_macros/src/lib.rs114
-rw-r--r--library/alloc/src/rc.rs11
-rw-r--r--library/alloc/src/str.rs12
-rw-r--r--library/alloc/src/sync.rs17
-rw-r--r--library/core/src/slice/cmp.rs26
-rw-r--r--library/core/tests/lib.rs35
-rw-r--r--library/core/tests/num/int_macros.rs606
-rw-r--r--library/core/tests/num/uint_macros.rs423
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs20
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs32
-rw-r--r--src/librustdoc/clean/mod.rs6
-rw-r--r--src/librustdoc/clean/types.rs23
-rw-r--r--src/librustdoc/core.rs39
-rw-r--r--src/librustdoc/html/format.rs14
-rw-r--r--src/librustdoc/html/render/write_shared.rs2
-rw-r--r--src/librustdoc/json/conversions.rs24
-rw-r--r--src/librustdoc/lib.rs2
-rw-r--r--src/librustdoc/markdown.rs12
-rw-r--r--src/librustdoc/passes/check_doc_test_visibility.rs2
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/empty_enum.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/trait_bounds.rs14
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs14
-rw-r--r--src/tools/compiletest/src/header.rs79
-rw-r--r--src/tools/compiletest/src/runtest/debugger.rs15
-rw-r--r--src/tools/compiletest/src/runtest/debuginfo.rs27
-rw-r--r--tests/assembly/rust-abi-arg-attr.rs108
-rw-r--r--tests/assembly/x86-return-float.rs6
-rw-r--r--tests/codegen/checked_ilog.rs2
-rw-r--r--tests/codegen/checked_math.rs12
-rw-r--r--tests/codegen/comparison-operators-newtype.rs8
-rw-r--r--tests/codegen/fewer-names.rs4
-rw-r--r--tests/codegen/float/f128.rs6
-rw-r--r--tests/codegen/float/f16.rs49
-rw-r--r--tests/codegen/function-arguments.rs8
-rw-r--r--tests/codegen/intrinsics/three_way_compare.rs6
-rw-r--r--tests/codegen/mir-aggregate-no-alloca.rs14
-rw-r--r--tests/codegen/placement-new.rs12
-rw-r--r--tests/codegen/range-attribute.rs4
-rw-r--r--tests/codegen/rust-abi-arch-specific-adjustment.rs111
-rw-r--r--tests/codegen/sanitizer/cfi/emit-type-checks-attr-no-sanitize.rs2
-rw-r--r--tests/codegen/sanitizer/cfi/emit-type-checks.rs2
-rw-r--r--tests/codegen/transmute-scalar.rs2
-rw-r--r--tests/codegen/union-abi.rs2
-rw-r--r--tests/codegen/var-names.rs2
-rw-r--r--tests/run-make/import-macro-verbatim/include/include.txt1
-rw-r--r--tests/run-make/import-macro-verbatim/rmake.rs8
-rw-r--r--tests/run-make/import-macro-verbatim/verbatim.rs12
-rw-r--r--tests/run-make/rust-lld-link-script-provide/main.rs7
-rw-r--r--tests/run-make/rust-lld-link-script-provide/rmake.rs18
-rw-r--r--tests/run-make/rust-lld-link-script-provide/script.t1
-rw-r--r--tests/rustdoc-ui/deprecated-attrs.rs19
-rw-r--r--tests/rustdoc-ui/deprecated-attrs.stderr35
-rw-r--r--tests/ui/abi/compatibility.rs18
-rw-r--r--tests/ui/async-await/field-in-sync.rs13
-rw-r--r--tests/ui/async-await/field-in-sync.stderr17
-rw-r--r--tests/ui/async-await/issue-61076.stderr4
-rw-r--r--tests/ui/async-await/try-in-sync.rs9
-rw-r--r--tests/ui/async-await/try-in-sync.stderr18
-rw-r--r--tests/ui/stats/hir-stats.stderr16
210 files changed, 2313 insertions, 1734 deletions
diff --git a/RELEASES.md b/RELEASES.md
index ac72a1d885c..40ddba6dbc5 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -68,15 +68,15 @@ Stabilized APIs
 - [`impl Default for std::collections::vec_deque::Iter`](https://doc.rust-lang.org/nightly/std/collections/vec_deque/struct.Iter.html#impl-Default-for-Iter%3C'_,+T%3E)
 - [`impl Default for std::collections::vec_deque::IterMut`](https://doc.rust-lang.org/nightly/std/collections/vec_deque/struct.IterMut.html#impl-Default-for-IterMut%3C'_,+T%3E)
 - [`Rc<T>::new_uninit`](https://doc.rust-lang.org/nightly/std/rc/struct.Rc.html#method.new_uninit)
-- [`Rc<T>::assume_init`](https://doc.rust-lang.org/nightly/std/rc/struct.Rc.html#method.assume_init)
+- [`Rc<MaybeUninit<T>>::assume_init`](https://doc.rust-lang.org/nightly/std/rc/struct.Rc.html#method.assume_init)
 - [`Rc<[T]>::new_uninit_slice`](https://doc.rust-lang.org/nightly/std/rc/struct.Rc.html#method.new_uninit_slice)
 - [`Rc<[MaybeUninit<T>]>::assume_init`](https://doc.rust-lang.org/nightly/std/rc/struct.Rc.html#method.assume_init-1)
 - [`Arc<T>::new_uninit`](https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.new_uninit)
-- [`Arc<T>::assume_init`](https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.assume_init)
+- [`Arc<MaybeUninit<T>>::assume_init`](https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.assume_init)
 - [`Arc<[T]>::new_uninit_slice`](https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.new_uninit_slice)
 - [`Arc<[MaybeUninit<T>]>::assume_init`](https://doc.rust-lang.org/nightly/std/sync/struct.Arc.html#method.assume_init-1)
 - [`Box<T>::new_uninit`](https://doc.rust-lang.org/nightly/std/boxed/struct.Box.html#method.new_uninit)
-- [`Box<T>::assume_init`](https://doc.rust-lang.org/nightly/std/boxed/struct.Box.html#method.assume_init)
+- [`Box<MaybeUninit<T>>::assume_init`](https://doc.rust-lang.org/nightly/std/boxed/struct.Box.html#method.assume_init)
 - [`Box<[T]>::new_uninit_slice`](https://doc.rust-lang.org/nightly/std/boxed/struct.Box.html#method.new_uninit_slice)
 - [`Box<[MaybeUninit<T>]>::assume_init`](https://doc.rust-lang.org/nightly/std/boxed/struct.Box.html#method.assume_init-1)
 - [`core::arch::x86_64::_bextri_u64`](https://doc.rust-lang.org/stable/core/arch/x86_64/fn._bextri_u64.html)
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 02cb6f188a7..8e4f4c8e71a 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2697,7 +2697,7 @@ impl fmt::Debug for ImplPolarity {
 }
 
 /// The polarity of a trait bound.
-#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, Hash)]
 #[derive(HashStable_Generic)]
 pub enum BoundPolarity {
     /// `Type: Trait`
@@ -2719,7 +2719,7 @@ impl BoundPolarity {
 }
 
 /// The constness of a trait bound.
-#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Debug, Hash)]
 #[derive(HashStable_Generic)]
 pub enum BoundConstness {
     /// `Type: Trait`
diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs
index 88cdb2ec363..6585a7de245 100644
--- a/compiler/rustc_ast_lowering/src/asm.rs
+++ b/compiler/rustc_ast_lowering/src/asm.rs
@@ -49,7 +49,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     | asm::InlineAsmArch::RiscV64
                     | asm::InlineAsmArch::LoongArch64
             );
-            if !is_stable && !self.tcx.features().asm_experimental_arch {
+            if !is_stable && !self.tcx.features().asm_experimental_arch() {
                 feature_err(
                     &self.tcx.sess,
                     sym::asm_experimental_arch,
@@ -65,7 +65,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         {
             self.dcx().emit_err(AttSyntaxOnlyX86 { span: sp });
         }
-        if asm.options.contains(InlineAsmOptions::MAY_UNWIND) && !self.tcx.features().asm_unwind {
+        if asm.options.contains(InlineAsmOptions::MAY_UNWIND) && !self.tcx.features().asm_unwind() {
             feature_err(
                 &self.tcx.sess,
                 sym::asm_unwind,
@@ -237,7 +237,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         }
                     }
                     InlineAsmOperand::Label { block } => {
-                        if !self.tcx.features().asm_goto {
+                        if !self.tcx.features().asm_goto() {
                             feature_err(
                                 sess,
                                 sym::asm_goto,
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index ae1e1b3f8a2..a1a16d0ca26 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -575,7 +575,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         } else {
             // Either `body.is_none()` or `is_never_pattern` here.
             if !is_never_pattern {
-                if self.tcx.features().never_patterns {
+                if self.tcx.features().never_patterns() {
                     // If the feature is off we already emitted the error after parsing.
                     let suggestion = span.shrink_to_hi();
                     self.dcx().emit_err(MatchArmWithNoBody { span, suggestion });
@@ -717,7 +717,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         outer_hir_id: HirId,
         inner_hir_id: HirId,
     ) {
-        if self.tcx.features().async_fn_track_caller
+        if self.tcx.features().async_fn_track_caller()
             && let Some(attrs) = self.attrs.get(&outer_hir_id.local_id)
             && attrs.into_iter().any(|attr| attr.has_name(sym::track_caller))
         {
@@ -1572,7 +1572,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 );
             }
             Some(hir::CoroutineKind::Coroutine(_)) => {
-                if !self.tcx.features().coroutines {
+                if !self.tcx.features().coroutines() {
                     rustc_session::parse::feature_err(
                         &self.tcx.sess,
                         sym::coroutines,
@@ -1584,7 +1584,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 false
             }
             None => {
-                if !self.tcx.features().coroutines {
+                if !self.tcx.features().coroutines() {
                     rustc_session::parse::feature_err(
                         &self.tcx.sess,
                         sym::coroutines,
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 7416a1e39eb..c43106c6e5f 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -1512,7 +1512,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     continue;
                 }
                 let is_param = *is_param.get_or_insert_with(compute_is_param);
-                if !is_param && !self.tcx.features().more_maybe_bounds {
+                if !is_param && !self.tcx.features().more_maybe_bounds() {
                     self.tcx
                         .sess
                         .create_feature_err(
@@ -1530,7 +1530,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let host_param_parts = if let Const::Yes(span) = constness
             // if this comes from implementing a `const` trait, we must force constness to be appended
             // to the impl item, no matter whether effects is enabled.
-            && (self.tcx.features().effects || force_append_constness)
+            && (self.tcx.features().effects() || force_append_constness)
         {
             let span = self.lower_span(span);
             let param_node_id = self.next_node_id();
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 4d8d22e09d9..7cd0cd1ebcd 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -193,7 +193,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             impl_trait_defs: Vec::new(),
             impl_trait_bounds: Vec::new(),
             allow_try_trait: [sym::try_trait_v2, sym::yeet_desugar_details].into(),
-            allow_gen_future: if tcx.features().async_fn_track_caller {
+            allow_gen_future: if tcx.features().async_fn_track_caller() {
                 [sym::gen_future, sym::closure_track_caller].into()
             } else {
                 [sym::gen_future].into()
@@ -1035,7 +1035,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                                 span: data.inputs_span,
                             })
                         };
-                        if !self.tcx.features().return_type_notation
+                        if !self.tcx.features().return_type_notation()
                             && self.tcx.sess.is_nightly_build()
                         {
                             add_feature_diagnostics(
@@ -1160,7 +1160,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime(lt)),
             ast::GenericArg::Type(ty) => {
                 match &ty.kind {
-                    TyKind::Infer if self.tcx.features().generic_arg_infer => {
+                    TyKind::Infer if self.tcx.features().generic_arg_infer() => {
                         return GenericArg::Infer(hir::InferArg {
                             hir_id: self.lower_node_id(ty.id),
                             span: self.lower_span(ty.span),
@@ -1500,7 +1500,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         // is enabled. We don't check the span of the edition, since this is done
         // on a per-opaque basis to account for nested opaques.
         let always_capture_in_scope = match origin {
-            _ if self.tcx.features().lifetime_capture_rules_2024 => true,
+            _ if self.tcx.features().lifetime_capture_rules_2024() => true,
             hir::OpaqueTyOrigin::TyAlias { .. } => true,
             hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl, .. } => in_trait_or_impl.is_some(),
             hir::OpaqueTyOrigin::AsyncFn { .. } => {
@@ -1519,7 +1519,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         // Feature gate for RPITIT + use<..>
         match origin {
             rustc_hir::OpaqueTyOrigin::FnReturn { in_trait_or_impl: Some(_), .. } => {
-                if !self.tcx.features().precise_capturing_in_traits
+                if !self.tcx.features().precise_capturing_in_traits()
                     && let Some(span) = bounds.iter().find_map(|bound| match *bound {
                         ast::GenericBound::Use(_, span) => Some(span),
                         _ => None,
@@ -1956,7 +1956,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
         hir::GenericBound::Trait(hir::PolyTraitRef {
             bound_generic_params: &[],
-            modifiers: hir::TraitBoundModifier::None,
+            modifiers: hir::TraitBoundModifiers::NONE,
             trait_ref: hir::TraitRef {
                 path: self.make_lang_item_path(trait_lang_item, opaque_ty_span, Some(bound_args)),
                 hir_ref_id: self.next_id(),
@@ -2270,7 +2270,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     fn lower_array_length(&mut self, c: &AnonConst) -> hir::ArrayLen<'hir> {
         match c.value.kind {
             ExprKind::Underscore => {
-                if self.tcx.features().generic_arg_infer {
+                if self.tcx.features().generic_arg_infer() {
                     hir::ArrayLen::Infer(hir::InferArg {
                         hir_id: self.lower_node_id(c.id),
                         span: self.lower_span(c.value.span),
@@ -2445,22 +2445,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     fn lower_trait_bound_modifiers(
         &mut self,
         modifiers: TraitBoundModifiers,
-    ) -> hir::TraitBoundModifier {
-        // Invalid modifier combinations will cause an error during AST validation.
-        // Arbitrarily pick a placeholder for them to make compilation proceed.
-        match (modifiers.constness, modifiers.polarity) {
-            (BoundConstness::Never, BoundPolarity::Positive) => hir::TraitBoundModifier::None,
-            (_, BoundPolarity::Maybe(_)) => hir::TraitBoundModifier::Maybe,
-            (BoundConstness::Never, BoundPolarity::Negative(_)) => {
-                if self.tcx.features().negative_bounds {
-                    hir::TraitBoundModifier::Negative
-                } else {
-                    hir::TraitBoundModifier::None
-                }
-            }
-            (BoundConstness::Always(_), _) => hir::TraitBoundModifier::Const,
-            (BoundConstness::Maybe(_), _) => hir::TraitBoundModifier::MaybeConst,
-        }
+    ) -> hir::TraitBoundModifiers {
+        hir::TraitBoundModifiers { constness: modifiers.constness, polarity: modifiers.polarity }
     }
 
     // Helper methods for building HIR.
@@ -2626,7 +2612,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     Res::Def(DefKind::Trait | DefKind::TraitAlias, _) => {
                         let principal = hir::PolyTraitRef {
                             bound_generic_params: &[],
-                            modifiers: hir::TraitBoundModifier::None,
+                            modifiers: hir::TraitBoundModifiers::NONE,
                             trait_ref: hir::TraitRef { path, hir_ref_id: hir_id },
                             span: self.lower_span(span),
                         };
diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs
index e60488fdc8c..258655de486 100644
--- a/compiler/rustc_ast_lowering/src/path.rs
+++ b/compiler/rustc_ast_lowering/src/path.rs
@@ -268,7 +268,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                                 span: data.inputs_span,
                             })
                         };
-                        if !self.tcx.features().return_type_notation
+                        if !self.tcx.features().return_type_notation()
                             && self.tcx.sess.is_nightly_build()
                         {
                             add_feature_diagnostics(
@@ -496,7 +496,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             // //      disallowed --^^^^^^^^^^        allowed --^^^^^^^^^^
             // ```
             FnRetTy::Ty(ty) if matches!(itctx, ImplTraitContext::OpaqueTy { .. }) => {
-                if self.tcx.features().impl_trait_in_fn_trait_return {
+                if self.tcx.features().impl_trait_in_fn_trait_return() {
                     self.lower_ty(ty, itctx)
                 } else {
                     self.lower_ty(
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 20a4f2120dc..bf6ebfb160b 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -295,7 +295,8 @@ impl<'a> AstValidator<'a> {
             return;
         };
 
-        let make_impl_const_sugg = if self.features.const_trait_impl
+        let const_trait_impl = self.features.const_trait_impl();
+        let make_impl_const_sugg = if const_trait_impl
             && let TraitOrTraitImpl::TraitImpl {
                 constness: Const::No,
                 polarity: ImplPolarity::Positive,
@@ -308,13 +309,12 @@ impl<'a> AstValidator<'a> {
             None
         };
 
-        let make_trait_const_sugg = if self.features.const_trait_impl
-            && let TraitOrTraitImpl::Trait { span, constness: None } = parent
-        {
-            Some(span.shrink_to_lo())
-        } else {
-            None
-        };
+        let make_trait_const_sugg =
+            if const_trait_impl && let TraitOrTraitImpl::Trait { span, constness: None } = parent {
+                Some(span.shrink_to_lo())
+            } else {
+                None
+            };
 
         let parent_constness = parent.constness();
         self.dcx().emit_err(errors::TraitFnConst {
@@ -1145,7 +1145,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 }
                 self.check_type_no_bounds(bounds, "this context");
 
-                if self.features.lazy_type_alias {
+                if self.features.lazy_type_alias() {
                     if let Err(err) = self.check_type_alias_where_clause_location(ty_alias) {
                         self.dcx().emit_err(err);
                     }
@@ -1286,7 +1286,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             GenericBound::Trait(trait_ref) => {
                 match (ctxt, trait_ref.modifiers.constness, trait_ref.modifiers.polarity) {
                     (BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_))
-                        if !self.features.more_maybe_bounds =>
+                        if !self.features.more_maybe_bounds() =>
                     {
                         self.sess
                             .create_feature_err(
@@ -1299,7 +1299,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                             .emit();
                     }
                     (BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_))
-                        if !self.features.more_maybe_bounds =>
+                        if !self.features.more_maybe_bounds() =>
                     {
                         self.sess
                             .create_feature_err(
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 94fcfabc32c..348a602fd59 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -15,13 +15,13 @@ use crate::errors;
 /// The common case.
 macro_rules! gate {
     ($visitor:expr, $feature:ident, $span:expr, $explain:expr) => {{
-        if !$visitor.features.$feature && !$span.allows_unstable(sym::$feature) {
+        if !$visitor.features.$feature() && !$span.allows_unstable(sym::$feature) {
             #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
             feature_err(&$visitor.sess, sym::$feature, $span, $explain).emit();
         }
     }};
     ($visitor:expr, $feature:ident, $span:expr, $explain:expr, $help:expr) => {{
-        if !$visitor.features.$feature && !$span.allows_unstable(sym::$feature) {
+        if !$visitor.features.$feature() && !$span.allows_unstable(sym::$feature) {
             // FIXME: make this translatable
             #[allow(rustc::diagnostic_outside_of_impl)]
             #[allow(rustc::untranslatable_diagnostic)]
@@ -43,7 +43,7 @@ macro_rules! gate_alt {
 /// The case involving a multispan.
 macro_rules! gate_multi {
     ($visitor:expr, $feature:ident, $spans:expr, $explain:expr) => {{
-        if !$visitor.features.$feature {
+        if !$visitor.features.$feature() {
             let spans: Vec<_> =
                 $spans.filter(|span| !span.allows_unstable(sym::$feature)).collect();
             if !spans.is_empty() {
@@ -56,7 +56,7 @@ macro_rules! gate_multi {
 /// The legacy case.
 macro_rules! gate_legacy {
     ($visitor:expr, $feature:ident, $span:expr, $explain:expr) => {{
-        if !$visitor.features.$feature && !$span.allows_unstable(sym::$feature) {
+        if !$visitor.features.$feature() && !$span.allows_unstable(sym::$feature) {
             feature_warn(&$visitor.sess, sym::$feature, $span, $explain);
         }
     }};
@@ -150,7 +150,7 @@ impl<'a> PostExpansionVisitor<'a> {
 
         // FIXME(non_lifetime_binders): Const bound params are pretty broken.
         // Let's keep users from using this feature accidentally.
-        if self.features.non_lifetime_binders {
+        if self.features.non_lifetime_binders() {
             let const_param_spans: Vec<_> = params
                 .iter()
                 .filter_map(|param| match param.kind {
@@ -210,7 +210,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
         }
 
         // Emit errors for non-staged-api crates.
-        if !self.features.staged_api {
+        if !self.features.staged_api() {
             if attr.has_name(sym::unstable)
                 || attr.has_name(sym::stable)
                 || attr.has_name(sym::rustc_const_unstable)
@@ -470,7 +470,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
             // Limit `min_specialization` to only specializing functions.
             gate_alt!(
                 &self,
-                self.features.specialization || (is_fn && self.features.min_specialization),
+                self.features.specialization() || (is_fn && self.features.min_specialization()),
                 sym::specialization,
                 i.span,
                 "specialization is unstable"
@@ -548,7 +548,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
     gate_all!(return_type_notation, "return type notation is experimental");
     gate_all!(pin_ergonomics, "pinned reference syntax is experimental");
 
-    if !visitor.features.never_patterns {
+    if !visitor.features.never_patterns() {
         if let Some(spans) = spans.get(&sym::never_patterns) {
             for &span in spans {
                 if span.allows_unstable(sym::never_patterns) {
@@ -572,7 +572,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
         }
     }
 
-    if !visitor.features.negative_bounds {
+    if !visitor.features.negative_bounds() {
         for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {
             sess.dcx().emit_err(errors::NegativeBoundUnsupported { span });
         }
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index bbb17497684..e412417a9e9 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -622,7 +622,7 @@ pub fn eval_condition(
                     &((
                         if *b { kw::True } else { kw::False },
                         sym::cfg_boolean_literals,
-                        |features: &Features| features.cfg_boolean_literals,
+                        |features: &Features| features.cfg_boolean_literals(),
                     )),
                     cfg.span(),
                     sess,
@@ -711,7 +711,7 @@ pub fn eval_condition(
                 }
                 sym::target => {
                     if let Some(features) = features
-                        && !features.cfg_target_compact
+                        && !features.cfg_target_compact()
                     {
                         feature_err(
                             sess,
@@ -831,7 +831,7 @@ pub fn find_deprecation(
     attrs: &[Attribute],
 ) -> Option<(Deprecation, Span)> {
     let mut depr: Option<(Deprecation, Span)> = None;
-    let is_rustc = features.staged_api;
+    let is_rustc = features.staged_api();
 
     'outer: for attr in attrs {
         if !attr.has_name(sym::deprecated) {
@@ -891,7 +891,7 @@ pub fn find_deprecation(
                                 }
                             }
                             sym::suggestion => {
-                                if !features.deprecated_suggestion {
+                                if !features.deprecated_suggestion() {
                                     sess.dcx().emit_err(
                                         session_diagnostics::DeprecatedItemSuggestion {
                                             span: mi.span,
@@ -909,7 +909,7 @@ pub fn find_deprecation(
                                 sess.dcx().emit_err(session_diagnostics::UnknownMetaItem {
                                     span: meta.span(),
                                     item: pprust::path_to_string(&mi.path),
-                                    expected: if features.deprecated_suggestion {
+                                    expected: if features.deprecated_suggestion() {
                                         &["since", "note", "suggestion"]
                                     } else {
                                         &["since", "note"]
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 7b60a632c30..a544d88e832 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -1035,7 +1035,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
 
     fn unsized_feature_enabled(&self) -> bool {
         let features = self.tcx().features();
-        features.unsized_locals || features.unsized_fn_params
+        features.unsized_locals() || features.unsized_fn_params()
     }
 
     /// Equate the inferred type and the annotated type for user type annotations
diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs
index 71449350985..599b180f879 100644
--- a/compiler/rustc_builtin_macros/src/assert.rs
+++ b/compiler/rustc_builtin_macros/src/assert.rs
@@ -69,7 +69,7 @@ pub(crate) fn expand_assert<'cx>(
     // If `generic_assert` is enabled, generates rich captured outputs
     //
     // FIXME(c410-f3r) See https://github.com/rust-lang/rust/issues/96949
-    else if cx.ecfg.features.generic_assert {
+    else if cx.ecfg.features.generic_assert() {
         context::Context::new(cx, call_site_span).build(cond_expr, panic_path())
     }
     // If `generic_assert` is not enabled, only outputs a literal "assertion failed: ..."
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index d536419ab3c..a5bd3adbcdd 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -137,7 +137,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                 let inner = attr.meta_item_list();
                 match inner.as_deref() {
                     Some([item]) if item.has_name(sym::linker) => {
-                        if !tcx.features().used_with_arg {
+                        if !tcx.features().used_with_arg() {
                             feature_err(
                                 &tcx.sess,
                                 sym::used_with_arg,
@@ -149,7 +149,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                         codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER;
                     }
                     Some([item]) if item.has_name(sym::compiler) => {
-                        if !tcx.features().used_with_arg {
+                        if !tcx.features().used_with_arg() {
                             feature_err(
                                 &tcx.sess,
                                 sym::used_with_arg,
@@ -213,7 +213,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                     .emit();
                 }
                 if is_closure
-                    && !tcx.features().closure_track_caller
+                    && !tcx.features().closure_track_caller()
                     && !attr.span.allows_unstable(sym::closure_track_caller)
                 {
                     feature_err(
@@ -268,7 +268,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                         //
                         // This exception needs to be kept in sync with allowing
                         // `#[target_feature]` on `main` and `start`.
-                    } else if !tcx.features().target_feature_11 {
+                    } else if !tcx.features().target_feature_11() {
                         feature_err(
                             &tcx.sess,
                             sym::target_feature_11,
@@ -584,7 +584,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
     // its parent function, which effectively inherits the features anyway. Boxing this closure
     // would result in this closure being compiled without the inherited target features, but this
     // is probably a poor usage of `#[inline(always)]` and easily avoided by not using the attribute.
-    if tcx.features().target_feature_11
+    if tcx.features().target_feature_11()
         && tcx.is_closure_like(did.to_def_id())
         && codegen_fn_attrs.inline != InlineAttr::Always
     {
diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs
index dfe8fd616e4..0845bcc5749 100644
--- a/compiler/rustc_codegen_ssa/src/target_features.rs
+++ b/compiler/rustc_codegen_ssa/src/target_features.rs
@@ -5,7 +5,6 @@ use rustc_data_structures::unord::{UnordMap, UnordSet};
 use rustc_errors::Applicability;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
-use rustc_middle::bug;
 use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
@@ -61,30 +60,9 @@ pub(crate) fn from_target_feature(
                 return None;
             };
 
-            // Only allow features whose feature gates have been enabled.
+            // Only allow target features whose feature gates have been enabled.
             let allowed = match feature_gate.as_ref().copied() {
-                Some(sym::arm_target_feature) => rust_features.arm_target_feature,
-                Some(sym::hexagon_target_feature) => rust_features.hexagon_target_feature,
-                Some(sym::powerpc_target_feature) => rust_features.powerpc_target_feature,
-                Some(sym::mips_target_feature) => rust_features.mips_target_feature,
-                Some(sym::riscv_target_feature) => rust_features.riscv_target_feature,
-                Some(sym::avx512_target_feature) => rust_features.avx512_target_feature,
-                Some(sym::sse4a_target_feature) => rust_features.sse4a_target_feature,
-                Some(sym::tbm_target_feature) => rust_features.tbm_target_feature,
-                Some(sym::wasm_target_feature) => rust_features.wasm_target_feature,
-                Some(sym::rtm_target_feature) => rust_features.rtm_target_feature,
-                Some(sym::ermsb_target_feature) => rust_features.ermsb_target_feature,
-                Some(sym::bpf_target_feature) => rust_features.bpf_target_feature,
-                Some(sym::aarch64_ver_target_feature) => rust_features.aarch64_ver_target_feature,
-                Some(sym::csky_target_feature) => rust_features.csky_target_feature,
-                Some(sym::loongarch_target_feature) => rust_features.loongarch_target_feature,
-                Some(sym::lahfsahf_target_feature) => rust_features.lahfsahf_target_feature,
-                Some(sym::prfchw_target_feature) => rust_features.prfchw_target_feature,
-                Some(sym::sha512_sm_x86) => rust_features.sha512_sm_x86,
-                Some(sym::x86_amx_intrinsics) => rust_features.x86_amx_intrinsics,
-                Some(sym::xop_target_feature) => rust_features.xop_target_feature,
-                Some(sym::s390x_target_feature) => rust_features.s390x_target_feature,
-                Some(name) => bug!("unknown target feature gate {}", name),
+                Some(name) => rust_features.enabled(name),
                 None => true,
             };
             if !allowed {
diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index 73344508a9d..288798bf0c2 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -611,7 +611,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                     // the trait into the concrete method, and uses that for const stability
                     // checks.
                     // FIXME(effects) we might consider moving const stability checks to typeck as well.
-                    if tcx.features().effects {
+                    if tcx.features().effects() {
                         is_trait = true;
 
                         if let Ok(Some(instance)) =
@@ -631,7 +631,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                             args: fn_args,
                             span: *fn_span,
                             call_source,
-                            feature: Some(if tcx.features().const_trait_impl {
+                            feature: Some(if tcx.features().const_trait_impl() {
                                 sym::effects
                             } else {
                                 sym::const_trait_impl
diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs
index 15ac4cedcc3..3720418d4f0 100644
--- a/compiler/rustc_const_eval/src/check_consts/mod.rs
+++ b/compiler/rustc_const_eval/src/check_consts/mod.rs
@@ -61,7 +61,7 @@ impl<'mir, 'tcx> ConstCx<'mir, 'tcx> {
 
     pub fn is_const_stable_const_fn(&self) -> bool {
         self.const_kind == Some(hir::ConstContext::ConstFn)
-            && self.tcx.features().staged_api
+            && self.tcx.features().staged_api()
             && is_const_stable_const_fn(self.tcx, self.def_id().to_def_id())
     }
 
diff --git a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs
index f98234ce7f3..d04d7b273f0 100644
--- a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs
+++ b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs
@@ -24,7 +24,7 @@ pub fn checking_enabled(ccx: &ConstCx<'_, '_>) -> bool {
         );
     }
 
-    ccx.tcx.features().const_precise_live_drops
+    ccx.tcx.features().const_precise_live_drops()
 }
 
 /// Look for live drops in a const context.
diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs
index 7cb013fdbd8..85b030c1a48 100644
--- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs
+++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs
@@ -9,8 +9,6 @@
 //! Thomas Lengauer and Robert Endre Tarjan.
 //! <https://www.cs.princeton.edu/courses/archive/spr03/cs423/download/dominators.pdf>
 
-use std::cmp::Ordering;
-
 use rustc_index::{Idx, IndexSlice, IndexVec};
 
 use super::ControlFlowGraph;
@@ -64,9 +62,6 @@ fn is_small_path_graph<G: ControlFlowGraph>(g: &G) -> bool {
 }
 
 fn dominators_impl<G: ControlFlowGraph>(graph: &G) -> Inner<G::Node> {
-    // compute the post order index (rank) for each node
-    let mut post_order_rank = IndexVec::from_elem_n(0, graph.num_nodes());
-
     // We allocate capacity for the full set of nodes, because most of the time
     // most of the nodes *are* reachable.
     let mut parent: IndexVec<PreorderIndex, PreorderIndex> =
@@ -83,12 +78,10 @@ fn dominators_impl<G: ControlFlowGraph>(graph: &G) -> Inner<G::Node> {
     pre_order_to_real.push(graph.start_node());
     parent.push(PreorderIndex::ZERO); // the parent of the root node is the root for now.
     real_to_pre_order[graph.start_node()] = Some(PreorderIndex::ZERO);
-    let mut post_order_idx = 0;
 
     // Traverse the graph, collecting a number of things:
     //
     // * Preorder mapping (to it, and back to the actual ordering)
-    // * Postorder mapping (used exclusively for `cmp_in_dominator_order` on the final product)
     // * Parents for each vertex in the preorder tree
     //
     // These are all done here rather than through one of the 'standard'
@@ -104,8 +97,6 @@ fn dominators_impl<G: ControlFlowGraph>(graph: &G) -> Inner<G::Node> {
                 continue 'recurse;
             }
         }
-        post_order_rank[pre_order_to_real[frame.pre_order_idx]] = post_order_idx;
-        post_order_idx += 1;
 
         stack.pop();
     }
@@ -282,7 +273,7 @@ fn dominators_impl<G: ControlFlowGraph>(graph: &G) -> Inner<G::Node> {
 
     let time = compute_access_time(start_node, &immediate_dominators);
 
-    Inner { post_order_rank, immediate_dominators, time }
+    Inner { immediate_dominators, time }
 }
 
 /// Evaluate the link-eval virtual forest, providing the currently minimum semi
@@ -348,7 +339,6 @@ fn compress(
 /// Tracks the list of dominators for each node.
 #[derive(Clone, Debug)]
 struct Inner<N: Idx> {
-    post_order_rank: IndexVec<N, usize>,
     // Even though we track only the immediate dominator of each node, it's
     // possible to get its full list of dominators by looking up the dominator
     // of each dominator.
@@ -379,17 +369,6 @@ impl<Node: Idx> Dominators<Node> {
         }
     }
 
-    /// Provide deterministic ordering of nodes such that, if any two nodes have a dominator
-    /// relationship, the dominator will always precede the dominated. (The relative ordering
-    /// of two unrelated nodes will also be consistent, but otherwise the order has no
-    /// meaning.) This method cannot be used to determine if either Node dominates the other.
-    pub fn cmp_in_dominator_order(&self, lhs: Node, rhs: Node) -> Ordering {
-        match &self.kind {
-            Kind::Path => lhs.index().cmp(&rhs.index()),
-            Kind::General(g) => g.post_order_rank[rhs].cmp(&g.post_order_rank[lhs]),
-        }
-    }
-
     /// Returns true if `a` dominates `b`.
     ///
     /// # Panics
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index f0cfe133a49..bed500c3032 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -1,5 +1,6 @@
 use std::default::Default;
 use std::iter;
+use std::path::Component::Prefix;
 use std::path::{Path, PathBuf};
 use std::rc::Rc;
 
@@ -1293,7 +1294,12 @@ pub fn resolve_path(sess: &Session, path: impl Into<PathBuf>, span: Span) -> PRe
         base_path.push(path);
         Ok(base_path)
     } else {
-        Ok(path)
+        // This ensures that Windows verbatim paths are fixed if mixed path separators are used,
+        // which can happen when `concat!` is used to join paths.
+        match path.components().next() {
+            Some(Prefix(prefix)) if prefix.kind().is_verbatim() => Ok(path.components().collect()),
+            _ => Ok(path),
+        }
     }
 }
 
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index a6b7291ee8f..5bd9e2fbcb9 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -11,7 +11,8 @@ use rustc_ast::{
 use rustc_attr as attr;
 use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
 use rustc_feature::{
-    ACCEPTED_FEATURES, AttributeSafety, Features, REMOVED_FEATURES, UNSTABLE_FEATURES,
+    ACCEPTED_LANG_FEATURES, AttributeSafety, Features, REMOVED_LANG_FEATURES,
+    UNSTABLE_LANG_FEATURES,
 };
 use rustc_lint_defs::BuiltinLintDiag;
 use rustc_parse::validate_attr;
@@ -77,7 +78,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
             };
 
             // If the enabled feature has been removed, issue an error.
-            if let Some(f) = REMOVED_FEATURES.iter().find(|f| name == f.feature.name) {
+            if let Some(f) = REMOVED_LANG_FEATURES.iter().find(|f| name == f.feature.name) {
                 sess.dcx().emit_err(FeatureRemoved {
                     span: mi.span(),
                     reason: f.reason.map(|reason| FeatureRemovedReason { reason }),
@@ -86,9 +87,9 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
             }
 
             // If the enabled feature is stable, record it.
-            if let Some(f) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) {
+            if let Some(f) = ACCEPTED_LANG_FEATURES.iter().find(|f| name == f.name) {
                 let since = Some(Symbol::intern(f.since));
-                features.set_enabled_lang_feature(name, mi.span(), since, None);
+                features.set_enabled_lang_feature(name, mi.span(), since);
                 continue;
             }
 
@@ -103,7 +104,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
             }
 
             // If the enabled feature is unstable, record it.
-            if let Some(f) = UNSTABLE_FEATURES.iter().find(|f| name == f.feature.name) {
+            if UNSTABLE_LANG_FEATURES.iter().find(|f| name == f.name).is_some() {
                 // When the ICE comes from core, alloc or std (approximation of the standard
                 // library), there's a chance that the person hitting the ICE may be using
                 // -Zbuild-std or similar with an untested target. The bug is probably in the
@@ -114,7 +115,7 @@ pub fn features(sess: &Session, krate_attrs: &[Attribute], crate_name: Symbol) -
                 {
                     sess.using_internal_features.store(true, std::sync::atomic::Ordering::Relaxed);
                 }
-                features.set_enabled_lang_feature(name, mi.span(), None, Some(f));
+                features.set_enabled_lang_feature(name, mi.span(), None);
                 continue;
             }
 
@@ -395,7 +396,7 @@ impl<'a> StripUnconfigured<'a> {
     /// If attributes are not allowed on expressions, emit an error for `attr`
     #[instrument(level = "trace", skip(self))]
     pub(crate) fn maybe_emit_expr_attr_err(&self, attr: &Attribute) {
-        if self.features.is_some_and(|features| !features.stmt_expr_attributes)
+        if self.features.is_some_and(|features| !features.stmt_expr_attributes())
             && !attr.span.allows_unstable(sym::stmt_expr_attributes)
         {
             let mut err = feature_err(
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index a872b12e744..5ffafcaa542 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -867,7 +867,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             | Annotatable::FieldDef(..)
             | Annotatable::Variant(..) => panic!("unexpected annotatable"),
         };
-        if self.cx.ecfg.features.proc_macro_hygiene {
+        if self.cx.ecfg.features.proc_macro_hygiene() {
             return;
         }
         feature_err(
@@ -905,7 +905,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             }
         }
 
-        if !self.cx.ecfg.features.proc_macro_hygiene {
+        if !self.cx.ecfg.features.proc_macro_hygiene() {
             annotatable.visit_with(&mut GateProcMacroInput { sess: &self.cx.sess });
         }
     }
diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs
index e518b27e419..1345f06d5ac 100644
--- a/compiler/rustc_expand/src/mbe/quoted.rs
+++ b/compiler/rustc_expand/src/mbe/quoted.rs
@@ -121,14 +121,14 @@ pub(super) fn parse(
 
 /// Asks for the `macro_metavar_expr` feature if it is not enabled
 fn maybe_emit_macro_metavar_expr_feature(features: &Features, sess: &Session, span: Span) {
-    if !features.macro_metavar_expr {
+    if !features.macro_metavar_expr() {
         let msg = "meta-variable expressions are unstable";
         feature_err(sess, sym::macro_metavar_expr, span, msg).emit();
     }
 }
 
 fn maybe_emit_macro_metavar_expr_concat_feature(features: &Features, sess: &Session, span: Span) {
-    if !features.macro_metavar_expr_concat {
+    if !features.macro_metavar_expr_concat() {
         let msg = "the `concat` meta-variable expression is unstable";
         feature_err(sess, sym::macro_metavar_expr_concat, span, msg).emit();
     }
diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs
index 53445804694..4f71bdaca1b 100644
--- a/compiler/rustc_feature/src/accepted.rs
+++ b/compiler/rustc_feature/src/accepted.rs
@@ -9,7 +9,7 @@ macro_rules! declare_features {
         $(#[doc = $doc:tt])* (accepted, $feature:ident, $ver:expr, $issue:expr),
     )+) => {
         /// Formerly unstable features that have now been accepted (stabilized).
-        pub const ACCEPTED_FEATURES: &[Feature] = &[
+        pub const ACCEPTED_LANG_FEATURES: &[Feature] = &[
             $(Feature {
                 name: sym::$feature,
                 since: $ver,
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 477760a4597..753195bf691 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -12,33 +12,31 @@ use crate::{Features, Stability};
 
 type GateFn = fn(&Features) -> bool;
 
-macro_rules! cfg_fn {
-    ($field: ident) => {
-        (|features| features.$field) as GateFn
-    };
-}
-
 pub type GatedCfg = (Symbol, Symbol, GateFn);
 
 /// `cfg(...)`'s that are feature gated.
 const GATED_CFGS: &[GatedCfg] = &[
     // (name in cfg, feature, function to check if the feature is enabled)
-    (sym::overflow_checks, sym::cfg_overflow_checks, cfg_fn!(cfg_overflow_checks)),
-    (sym::ub_checks, sym::cfg_ub_checks, cfg_fn!(cfg_ub_checks)),
-    (sym::target_thread_local, sym::cfg_target_thread_local, cfg_fn!(cfg_target_thread_local)),
+    (sym::overflow_checks, sym::cfg_overflow_checks, Features::cfg_overflow_checks),
+    (sym::ub_checks, sym::cfg_ub_checks, Features::cfg_ub_checks),
+    (sym::target_thread_local, sym::cfg_target_thread_local, Features::cfg_target_thread_local),
     (
         sym::target_has_atomic_equal_alignment,
         sym::cfg_target_has_atomic_equal_alignment,
-        cfg_fn!(cfg_target_has_atomic_equal_alignment),
-    ),
-    (sym::target_has_atomic_load_store, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
-    (sym::sanitize, sym::cfg_sanitize, cfg_fn!(cfg_sanitize)),
-    (sym::version, sym::cfg_version, cfg_fn!(cfg_version)),
-    (sym::relocation_model, sym::cfg_relocation_model, cfg_fn!(cfg_relocation_model)),
-    (sym::sanitizer_cfi_generalize_pointers, sym::cfg_sanitizer_cfi, cfg_fn!(cfg_sanitizer_cfi)),
-    (sym::sanitizer_cfi_normalize_integers, sym::cfg_sanitizer_cfi, cfg_fn!(cfg_sanitizer_cfi)),
+        Features::cfg_target_has_atomic_equal_alignment,
+    ),
+    (
+        sym::target_has_atomic_load_store,
+        sym::cfg_target_has_atomic,
+        Features::cfg_target_has_atomic,
+    ),
+    (sym::sanitize, sym::cfg_sanitize, Features::cfg_sanitize),
+    (sym::version, sym::cfg_version, Features::cfg_version),
+    (sym::relocation_model, sym::cfg_relocation_model, Features::cfg_relocation_model),
+    (sym::sanitizer_cfi_generalize_pointers, sym::cfg_sanitizer_cfi, Features::cfg_sanitizer_cfi),
+    (sym::sanitizer_cfi_normalize_integers, sym::cfg_sanitizer_cfi, Features::cfg_sanitizer_cfi),
     // this is consistent with naming of the compiler flag it's for
-    (sym::fmt_debug, sym::fmt_debug, cfg_fn!(fmt_debug)),
+    (sym::fmt_debug, sym::fmt_debug, Features::fmt_debug),
 ];
 
 /// Find a gated cfg determined by the `pred`icate which is given the cfg's name.
@@ -220,7 +218,7 @@ macro_rules! gated {
             safety: AttributeSafety::Unsafe,
             template: $tpl,
             duplicates: $duplicates,
-            gate: Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)),
+            gate: Gated(Stability::Unstable, sym::$gate, $msg, Features::$gate),
         }
     };
     (unsafe $attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => {
@@ -231,7 +229,7 @@ macro_rules! gated {
             safety: AttributeSafety::Unsafe,
             template: $tpl,
             duplicates: $duplicates,
-            gate: Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr)),
+            gate: Gated(Stability::Unstable, sym::$attr, $msg, Features::$attr),
         }
     };
     ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $gate:ident, $msg:expr $(,)?) => {
@@ -242,7 +240,7 @@ macro_rules! gated {
             safety: AttributeSafety::Normal,
             template: $tpl,
             duplicates: $duplicates,
-            gate: Gated(Stability::Unstable, sym::$gate, $msg, cfg_fn!($gate)),
+            gate: Gated(Stability::Unstable, sym::$gate, $msg, Features::$gate),
         }
     };
     ($attr:ident, $typ:expr, $tpl:expr, $duplicates:expr, $encode_cross_crate:expr, $msg:expr $(,)?) => {
@@ -253,7 +251,7 @@ macro_rules! gated {
             safety: AttributeSafety::Normal,
             template: $tpl,
             duplicates: $duplicates,
-            gate: Gated(Stability::Unstable, sym::$attr, $msg, cfg_fn!($attr)),
+            gate: Gated(Stability::Unstable, sym::$attr, $msg, Features::$attr),
         }
     };
 }
@@ -282,7 +280,7 @@ macro_rules! rustc_attr {
             safety: AttributeSafety::Normal,
             template: $tpl,
             duplicates: $duplicates,
-            gate: Gated(Stability::Unstable, sym::rustc_attrs, $msg, cfg_fn!(rustc_attrs)),
+            gate: Gated(Stability::Unstable, sym::rustc_attrs, $msg, Features::rustc_attrs),
         }
     };
 }
@@ -935,7 +933,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
             Stability::Unstable,
             sym::rustc_attrs,
             "diagnostic items compiler internal support for linting",
-            cfg_fn!(rustc_attrs),
+            Features::rustc_attrs,
         ),
     },
     gated!(
@@ -1193,7 +1191,7 @@ pub static BUILTIN_ATTRIBUTE_MAP: LazyLock<FxHashMap<Symbol, &BuiltinAttribute>>
 pub fn is_stable_diagnostic_attribute(sym: Symbol, features: &Features) -> bool {
     match sym {
         sym::on_unimplemented => true,
-        sym::do_not_recommend => features.do_not_recommend,
+        sym::do_not_recommend => features.do_not_recommend(),
         _ => false,
     }
 }
diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs
index 8f4c0b0ac95..216793485e5 100644
--- a/compiler/rustc_feature/src/lib.rs
+++ b/compiler/rustc_feature/src/lib.rs
@@ -94,13 +94,13 @@ impl UnstableFeatures {
 
 fn find_lang_feature_issue(feature: Symbol) -> Option<NonZero<u32>> {
     // Search in all the feature lists.
-    if let Some(f) = UNSTABLE_FEATURES.iter().find(|f| f.feature.name == feature) {
-        return f.feature.issue;
+    if let Some(f) = UNSTABLE_LANG_FEATURES.iter().find(|f| f.name == feature) {
+        return f.issue;
     }
-    if let Some(f) = ACCEPTED_FEATURES.iter().find(|f| f.name == feature) {
+    if let Some(f) = ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature) {
         return f.issue;
     }
-    if let Some(f) = REMOVED_FEATURES.iter().find(|f| f.feature.name == feature) {
+    if let Some(f) = REMOVED_LANG_FEATURES.iter().find(|f| f.feature.name == feature) {
         return f.feature.issue;
     }
     panic!("feature `{feature}` is not declared anywhere");
@@ -127,12 +127,12 @@ pub fn find_feature_issue(feature: Symbol, issue: GateIssue) -> Option<NonZero<u
     }
 }
 
-pub use accepted::ACCEPTED_FEATURES;
+pub use accepted::ACCEPTED_LANG_FEATURES;
 pub use builtin_attrs::{
     AttributeDuplicates, AttributeGate, AttributeSafety, AttributeTemplate, AttributeType,
     BUILTIN_ATTRIBUTE_MAP, BUILTIN_ATTRIBUTES, BuiltinAttribute, GatedCfg, deprecated_attributes,
     encode_cross_crate, find_gated_cfg, is_builtin_attr_name, is_stable_diagnostic_attribute,
     is_valid_for_get_attr,
 };
-pub use removed::REMOVED_FEATURES;
-pub use unstable::{Features, INCOMPATIBLE_FEATURES, UNSTABLE_FEATURES};
+pub use removed::REMOVED_LANG_FEATURES;
+pub use unstable::{Features, INCOMPATIBLE_FEATURES, UNSTABLE_LANG_FEATURES};
diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs
index d797fee000d..fe3a67fd667 100644
--- a/compiler/rustc_feature/src/removed.rs
+++ b/compiler/rustc_feature/src/removed.rs
@@ -14,7 +14,7 @@ macro_rules! declare_features {
         $(#[doc = $doc:tt])* (removed, $feature:ident, $ver:expr, $issue:expr, $reason:expr),
     )+) => {
         /// Formerly unstable features that have now been removed.
-        pub const REMOVED_FEATURES: &[RemovedFeature] = &[
+        pub const REMOVED_LANG_FEATURES: &[RemovedFeature] = &[
             $(RemovedFeature {
                 feature: Feature {
                     name: sym::$feature,
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 0606f2914c3..8f4c208f1fb 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -6,11 +6,6 @@ use rustc_span::symbol::{Symbol, sym};
 
 use super::{Feature, to_nonzero};
 
-pub struct UnstableFeature {
-    pub feature: Feature,
-    set_enabled: fn(&mut Features),
-}
-
 #[derive(PartialEq)]
 enum FeatureStatus {
     Default,
@@ -30,110 +25,73 @@ macro_rules! status_to_enum {
     };
 }
 
+/// A set of features to be used by later passes.
+///
+/// There are two ways to check if a language feature `foo` is enabled:
+/// - Directly with the `foo` method, e.g. `if tcx.features().foo() { ... }`.
+/// - With the `enabled` method, e.g. `if tcx.features.enabled(sym::foo) { ... }`.
+///
+/// The former is preferred. `enabled` should only be used when the feature symbol is not a
+/// constant, e.g. a parameter, or when the feature is a library feature.
+#[derive(Clone, Default, Debug)]
+pub struct Features {
+    /// `#![feature]` attrs for language features, for error reporting.
+    enabled_lang_features: Vec<(Symbol, Span, Option<Symbol>)>,
+    /// `#![feature]` attrs for non-language (library) features.
+    enabled_lib_features: Vec<(Symbol, Span)>,
+    /// `enabled_lang_features` + `enabled_lib_features`.
+    enabled_features: FxHashSet<Symbol>,
+}
+
+impl Features {
+    pub fn set_enabled_lang_feature(&mut self, name: Symbol, span: Span, since: Option<Symbol>) {
+        self.enabled_lang_features.push((name, span, since));
+        self.enabled_features.insert(name);
+    }
+
+    pub fn set_enabled_lib_feature(&mut self, name: Symbol, span: Span) {
+        self.enabled_lib_features.push((name, span));
+        self.enabled_features.insert(name);
+    }
+
+    pub fn enabled_lang_features(&self) -> &Vec<(Symbol, Span, Option<Symbol>)> {
+        &self.enabled_lang_features
+    }
+
+    pub fn enabled_lib_features(&self) -> &Vec<(Symbol, Span)> {
+        &self.enabled_lib_features
+    }
+
+    pub fn enabled_features(&self) -> &FxHashSet<Symbol> {
+        &self.enabled_features
+    }
+
+    /// Is the given feature enabled (via `#[feature(...)]`)?
+    pub fn enabled(&self, feature: Symbol) -> bool {
+        self.enabled_features.contains(&feature)
+    }
+}
+
 macro_rules! declare_features {
     ($(
         $(#[doc = $doc:tt])* ($status:ident, $feature:ident, $ver:expr, $issue:expr),
     )+) => {
         /// Unstable language features that are being implemented or being
         /// considered for acceptance (stabilization) or removal.
-        pub const UNSTABLE_FEATURES: &[UnstableFeature] = &[
-            $(UnstableFeature {
-                feature: Feature {
-                    name: sym::$feature,
-                    since: $ver,
-                    issue: to_nonzero($issue),
-                },
-                // Sets this feature's corresponding bool within `features`.
-                set_enabled: |features| features.$feature = true,
+        pub const UNSTABLE_LANG_FEATURES: &[Feature] = &[
+            $(Feature {
+                name: sym::$feature,
+                since: $ver,
+                issue: to_nonzero($issue),
             }),+
         ];
 
-        const NUM_FEATURES: usize = UNSTABLE_FEATURES.len();
-
-        /// A set of features to be used by later passes.
-        #[derive(Clone, Default, Debug)]
-        pub struct Features {
-            /// `#![feature]` attrs for language features, for error reporting.
-            enabled_lang_features: Vec<(Symbol, Span, Option<Symbol>)>,
-            /// `#![feature]` attrs for non-language (library) features.
-            enabled_lib_features: Vec<(Symbol, Span)>,
-            /// `enabled_lang_features` + `enabled_lib_features`.
-            enabled_features: FxHashSet<Symbol>,
-            /// State of individual features (unstable lang features only).
-            /// This is `true` if and only if the corresponding feature is listed in `enabled_lang_features`.
-            $(
-                $(#[doc = $doc])*
-                pub $feature: bool
-            ),+
-        }
-
         impl Features {
-            pub fn set_enabled_lang_feature(
-                &mut self,
-                name: Symbol,
-                span: Span,
-                since: Option<Symbol>,
-                feature: Option<&UnstableFeature>,
-            ) {
-                self.enabled_lang_features.push((name, span, since));
-                self.enabled_features.insert(name);
-                if let Some(feature) = feature {
-                    assert_eq!(feature.feature.name, name);
-                    (feature.set_enabled)(self);
-                } else {
-                    // Ensure we don't skip a `set_enabled` call.
-                    debug_assert!(UNSTABLE_FEATURES.iter().find(|f| name == f.feature.name).is_none());
-                }
-            }
-
-            pub fn set_enabled_lib_feature(&mut self, name: Symbol, span: Span) {
-                self.enabled_lib_features.push((name, span));
-                self.enabled_features.insert(name);
-                // Ensure we don't skip a `set_enabled` call.
-                debug_assert!(UNSTABLE_FEATURES.iter().find(|f| name == f.feature.name).is_none());
-            }
-
-            /// This is intended for hashing the set of enabled language features.
-            ///
-            /// The expectation is that this produces much smaller code than other alternatives.
-            ///
-            /// Note that the total feature count is pretty small, so this is not a huge array.
-            #[inline]
-            pub fn all_lang_features(&self) -> [u8; NUM_FEATURES] {
-                [$(self.$feature as u8),+]
-            }
-
-            pub fn enabled_lang_features(&self) -> &Vec<(Symbol, Span, Option<Symbol>)> {
-                &self.enabled_lang_features
-            }
-
-            pub fn enabled_lib_features(&self) -> &Vec<(Symbol, Span)> {
-                &self.enabled_lib_features
-            }
-
-            pub fn enabled_features(&self) -> &FxHashSet<Symbol> {
-                &self.enabled_features
-            }
-
-            /// Is the given feature enabled (via `#[feature(...)]`)?
-            pub fn enabled(&self, feature: Symbol) -> bool {
-                let e = self.enabled_features.contains(&feature);
-                if cfg!(debug_assertions) {
-                    // Ensure this matches `self.$feature`, if that exists.
-                    let e2 = match feature {
-                        $( sym::$feature => Some(self.$feature), )*
-                        _ => None,
-                    };
-                    if let Some(e2) = e2 {
-                        assert_eq!(
-                            e, e2,
-                            "mismatch in feature state for `{feature}`: \
-                            `enabled_features` says {e} but `self.{feature}` says {e2}"
-                        );
-                    }
+            $(
+                pub fn $feature(&self) -> bool {
+                    self.enabled_features.contains(&sym::$feature)
                 }
-                e
-            }
+            )*
 
             /// Some features are known to be incomplete and using them is likely to have
             /// unanticipated results, such as compiler crashes. We warn the user about these
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 009c6c4aea5..45be04c6db9 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -6,8 +6,8 @@ use rustc_ast::{
     LitKind, TraitObjectSyntax, UintTy,
 };
 pub use rustc_ast::{
-    BinOp, BinOpKind, BindingMode, BorrowKind, ByRef, CaptureBy, ImplPolarity, IsAuto, Movability,
-    Mutability, UnOp,
+    BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, ByRef, CaptureBy,
+    ImplPolarity, IsAuto, Movability, Mutability, UnOp,
 };
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::sorted_map::SortedMap;
@@ -502,19 +502,16 @@ pub enum GenericArgsParentheses {
     ParenSugar,
 }
 
-/// A modifier on a trait bound.
+/// The modifiers on a trait bound.
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
-pub enum TraitBoundModifier {
-    /// `Type: Trait`
-    None,
-    /// `Type: !Trait`
-    Negative,
-    /// `Type: ?Trait`
-    Maybe,
-    /// `Type: const Trait`
-    Const,
-    /// `Type: ~const Trait`
-    MaybeConst,
+pub struct TraitBoundModifiers {
+    pub constness: BoundConstness,
+    pub polarity: BoundPolarity,
+}
+
+impl TraitBoundModifiers {
+    pub const NONE: Self =
+        TraitBoundModifiers { constness: BoundConstness::Never, polarity: BoundPolarity::Positive };
 }
 
 #[derive(Clone, Copy, Debug, HashStable_Generic)]
@@ -3180,7 +3177,7 @@ pub struct PolyTraitRef<'hir> {
     /// The constness and polarity of the trait ref.
     ///
     /// The `async` modifier is lowered directly into a different trait for now.
-    pub modifiers: TraitBoundModifier,
+    pub modifiers: TraitBoundModifiers,
 
     /// The `Foo<&'a T>` in `for<'a> Foo<&'a T>`.
     pub trait_ref: TraitRef<'hir>,
@@ -4085,7 +4082,7 @@ mod size_asserts {
     static_assert_size!(ForeignItem<'_>, 88);
     static_assert_size!(ForeignItemKind<'_>, 56);
     static_assert_size!(GenericArg<'_>, 16);
-    static_assert_size!(GenericBound<'_>, 48);
+    static_assert_size!(GenericBound<'_>, 64);
     static_assert_size!(Generics<'_>, 56);
     static_assert_size!(Impl<'_>, 80);
     static_assert_size!(ImplItem<'_>, 88);
diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs
index 9a2c38e51e2..77df0cb7ef9 100644
--- a/compiler/rustc_hir_analysis/src/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/bounds.rs
@@ -72,7 +72,7 @@ impl<'tcx> Bounds<'tcx> {
         // FIXME(effects): Lift this out of `push_trait_bound`, and move it somewhere else.
         // Perhaps moving this into `lower_poly_trait_ref`, just like we lower associated
         // type bounds.
-        if !tcx.features().effects {
+        if !tcx.features().effects() {
             return;
         }
         match predicate_filter {
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 2aeeb9450ce..97f3f1c8ef2 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -1166,7 +1166,7 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
         return;
     }
 
-    if adt.is_union() && !tcx.features().transparent_unions {
+    if adt.is_union() && !tcx.features().transparent_unions() {
         feature_err(
             &tcx.sess,
             sym::transparent_unions,
@@ -1301,7 +1301,7 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) {
 
     let repr_type_ty = def.repr().discr_type().to_ty(tcx);
     if repr_type_ty == tcx.types.i128 || repr_type_ty == tcx.types.u128 {
-        if !tcx.features().repr128 {
+        if !tcx.features().repr128() {
             feature_err(
                 &tcx.sess,
                 sym::repr128,
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 75956165e87..fc5ec31cda8 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -43,14 +43,13 @@ mod refine;
 /// - `impl_m`: type of the method we are checking
 /// - `trait_m`: the method in the trait
 /// - `impl_trait_ref`: the TraitRef corresponding to the trait implementation
+#[instrument(level = "debug", skip(tcx))]
 pub(super) fn compare_impl_method<'tcx>(
     tcx: TyCtxt<'tcx>,
     impl_m: ty::AssocItem,
     trait_m: ty::AssocItem,
     impl_trait_ref: ty::TraitRef<'tcx>,
 ) {
-    debug!("compare_impl_method(impl_trait_ref={:?})", impl_trait_ref);
-
     let _: Result<_, ErrorGuaranteed> = try {
         check_method_is_structurally_compatible(tcx, impl_m, trait_m, impl_trait_ref, false)?;
         compare_method_predicate_entailment(tcx, impl_m, trait_m, impl_trait_ref)?;
@@ -167,8 +166,6 @@ fn compare_method_predicate_entailment<'tcx>(
     trait_m: ty::AssocItem,
     impl_trait_ref: ty::TraitRef<'tcx>,
 ) -> Result<(), ErrorGuaranteed> {
-    let trait_to_impl_args = impl_trait_ref.args;
-
     // This node-id should be used for the `body_id` field on each
     // `ObligationCause` (and the `FnCtxt`).
     //
@@ -183,27 +180,17 @@ fn compare_method_predicate_entailment<'tcx>(
             kind: impl_m.kind,
         });
 
-    // Create mapping from impl to placeholder.
-    let impl_to_placeholder_args = GenericArgs::identity_for_item(tcx, impl_m.def_id);
-
-    // Create mapping from trait to placeholder.
-    let trait_to_placeholder_args =
-        impl_to_placeholder_args.rebase_onto(tcx, impl_m.container_id(tcx), trait_to_impl_args);
-    debug!("compare_impl_method: trait_to_placeholder_args={:?}", trait_to_placeholder_args);
+    // Create mapping from trait method to impl method.
+    let trait_to_impl_args = GenericArgs::identity_for_item(tcx, impl_m.def_id).rebase_onto(
+        tcx,
+        impl_m.container_id(tcx),
+        impl_trait_ref.args,
+    );
+    debug!(?trait_to_impl_args);
 
     let impl_m_predicates = tcx.predicates_of(impl_m.def_id);
     let trait_m_predicates = tcx.predicates_of(trait_m.def_id);
 
-    // Create obligations for each predicate declared by the impl
-    // definition in the context of the trait's parameter
-    // environment. We can't just use `impl_env.caller_bounds`,
-    // however, because we want to replace all late-bound regions with
-    // region variables.
-    let impl_predicates = tcx.predicates_of(impl_m_predicates.parent.unwrap());
-    let mut hybrid_preds = impl_predicates.instantiate_identity(tcx);
-
-    debug!("compare_impl_method: impl_bounds={:?}", hybrid_preds);
-
     // This is the only tricky bit of the new way we check implementation methods
     // We need to build a set of predicates where only the method-level bounds
     // are from the trait and we assume all other bounds from the implementation
@@ -211,25 +198,25 @@ fn compare_method_predicate_entailment<'tcx>(
     //
     // We then register the obligations from the impl_m and check to see
     // if all constraints hold.
-    hybrid_preds.predicates.extend(
-        trait_m_predicates
-            .instantiate_own(tcx, trait_to_placeholder_args)
-            .map(|(predicate, _)| predicate),
+    let impl_predicates = tcx.predicates_of(impl_m_predicates.parent.unwrap());
+    let mut hybrid_preds = impl_predicates.instantiate_identity(tcx).predicates;
+    hybrid_preds.extend(
+        trait_m_predicates.instantiate_own(tcx, trait_to_impl_args).map(|(predicate, _)| predicate),
     );
 
-    // Construct trait parameter environment and then shift it into the placeholder viewpoint.
-    // The key step here is to update the caller_bounds's predicates to be
-    // the new hybrid bounds we computed.
     let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_def_id);
-    let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing);
+    let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds), Reveal::UserFacing);
     let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
+    debug!(caller_bounds=?param_env.caller_bounds());
 
     let infcx = &tcx.infer_ctxt().build();
     let ocx = ObligationCtxt::new_with_diagnostics(infcx);
 
-    debug!("compare_impl_method: caller_bounds={:?}", param_env.caller_bounds());
-
-    let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_args);
+    // Create obligations for each predicate declared by the impl
+    // definition in the context of the hybrid param-env. This makes
+    // sure that the impl's method's where clauses are not more
+    // restrictive than the trait's method (and the impl itself).
+    let impl_m_own_bounds = impl_m_predicates.instantiate_own_identity();
     for (predicate, span) in impl_m_own_bounds {
         let normalize_cause = traits::ObligationCause::misc(span, impl_m_def_id);
         let predicate = ocx.normalize(&normalize_cause, param_env, predicate);
@@ -256,7 +243,6 @@ fn compare_method_predicate_entailment<'tcx>(
     // any associated types appearing in the fn arguments or return
     // type.
 
-    // Compute placeholder form of impl and trait method tys.
     let mut wf_tys = FxIndexSet::default();
 
     let unnormalized_impl_sig = infcx.instantiate_binder_with_fresh_vars(
@@ -267,9 +253,9 @@ fn compare_method_predicate_entailment<'tcx>(
 
     let norm_cause = ObligationCause::misc(impl_m_span, impl_m_def_id);
     let impl_sig = ocx.normalize(&norm_cause, param_env, unnormalized_impl_sig);
-    debug!("compare_impl_method: impl_fty={:?}", impl_sig);
+    debug!(?impl_sig);
 
-    let trait_sig = tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_to_placeholder_args);
+    let trait_sig = tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_to_impl_args);
     let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig);
 
     // Next, add all inputs and output as well-formed tys. Importantly,
@@ -280,9 +266,7 @@ fn compare_method_predicate_entailment<'tcx>(
     // We also have to add the normalized trait signature
     // as we don't normalize during implied bounds computation.
     wf_tys.extend(trait_sig.inputs_and_output.iter());
-    let trait_fty = Ty::new_fn_ptr(tcx, ty::Binder::dummy(trait_sig));
-
-    debug!("compare_impl_method: trait_fty={:?}", trait_fty);
+    debug!(?trait_sig);
 
     // FIXME: We'd want to keep more accurate spans than "the method signature" when
     // processing the comparison between the trait and impl fn, but we sadly lose them
@@ -455,8 +439,6 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
     // just so we don't ICE during instantiation later.
     check_method_is_structurally_compatible(tcx, impl_m, trait_m, impl_trait_ref, true)?;
 
-    let trait_to_impl_args = impl_trait_ref.args;
-
     let impl_m_hir_id = tcx.local_def_id_to_hir_id(impl_m_def_id);
     let return_span = tcx.hir().fn_decl_by_hir_id(impl_m_hir_id).unwrap().output.span();
     let cause =
@@ -466,18 +448,18 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
             kind: impl_m.kind,
         });
 
-    // Create mapping from impl to placeholder.
-    let impl_to_placeholder_args = GenericArgs::identity_for_item(tcx, impl_m.def_id);
-
-    // Create mapping from trait to placeholder.
-    let trait_to_placeholder_args =
-        impl_to_placeholder_args.rebase_onto(tcx, impl_m.container_id(tcx), trait_to_impl_args);
+    // Create mapping from trait to impl (i.e. impl trait header + impl method identity args).
+    let trait_to_impl_args = GenericArgs::identity_for_item(tcx, impl_m.def_id).rebase_onto(
+        tcx,
+        impl_m.container_id(tcx),
+        impl_trait_ref.args,
+    );
 
     let hybrid_preds = tcx
         .predicates_of(impl_m.container_id(tcx))
         .instantiate_identity(tcx)
         .into_iter()
-        .chain(tcx.predicates_of(trait_m.def_id).instantiate_own(tcx, trait_to_placeholder_args))
+        .chain(tcx.predicates_of(trait_m.def_id).instantiate_own(tcx, trait_to_impl_args))
         .map(|(clause, _)| clause);
     let param_env = ty::ParamEnv::new(tcx.mk_clauses_from_iter(hybrid_preds), Reveal::UserFacing);
     let param_env = traits::normalize_param_env_or_error(
@@ -511,7 +493,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
         .instantiate_binder_with_fresh_vars(
             return_span,
             infer::HigherRankedType,
-            tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_to_placeholder_args),
+            tcx.fn_sig(trait_m.def_id).instantiate(tcx, trait_to_impl_args),
         )
         .fold_with(&mut collector);
 
@@ -705,7 +687,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
                 // Also, we only need to account for a difference in trait and impl args,
                 // since we previously enforce that the trait method and impl method have the
                 // same generics.
-                let num_trait_args = trait_to_impl_args.len();
+                let num_trait_args = impl_trait_ref.args.len();
                 let num_impl_args = tcx.generics_of(impl_m.container_id(tcx)).own_params.len();
                 let ty = match ty.try_fold_with(&mut RemapHiddenTyRegions {
                     tcx,
@@ -1041,12 +1023,7 @@ fn check_region_bounds_on_impl_item<'tcx>(
     let trait_generics = tcx.generics_of(trait_m.def_id);
     let trait_params = trait_generics.own_counts().lifetimes;
 
-    debug!(
-        "check_region_bounds_on_impl_item: \
-            trait_generics={:?} \
-            impl_generics={:?}",
-        trait_generics, impl_generics
-    );
+    debug!(?trait_generics, ?impl_generics);
 
     // Must have same number of early-bound lifetime parameters.
     // Unfortunately, if the user screws up the bounds, then this
@@ -1710,8 +1687,7 @@ pub(super) fn compare_impl_const_raw(
     let trait_const_item = tcx.associated_item(trait_const_item_def);
     let impl_trait_ref =
         tcx.impl_trait_ref(impl_const_item.container_id(tcx)).unwrap().instantiate_identity();
-
-    debug!("compare_impl_const(impl_trait_ref={:?})", impl_trait_ref);
+    debug!(?impl_trait_ref);
 
     compare_number_of_generics(tcx, impl_const_item, trait_const_item, false)?;
     compare_generic_param_kinds(tcx, impl_const_item, trait_const_item, false)?;
@@ -1722,6 +1698,7 @@ pub(super) fn compare_impl_const_raw(
 /// The equivalent of [compare_method_predicate_entailment], but for associated constants
 /// instead of associated functions.
 // FIXME(generic_const_items): If possible extract the common parts of `compare_{type,const}_predicate_entailment`.
+#[instrument(level = "debug", skip(tcx))]
 fn compare_const_predicate_entailment<'tcx>(
     tcx: TyCtxt<'tcx>,
     impl_ct: ty::AssocItem,
@@ -1736,13 +1713,14 @@ fn compare_const_predicate_entailment<'tcx>(
     // because we shouldn't really have to deal with lifetimes or
     // predicates. In fact some of this should probably be put into
     // shared functions because of DRY violations...
-    let impl_args = GenericArgs::identity_for_item(tcx, impl_ct.def_id);
-    let trait_to_impl_args =
-        impl_args.rebase_onto(tcx, impl_ct.container_id(tcx), impl_trait_ref.args);
+    let trait_to_impl_args = GenericArgs::identity_for_item(tcx, impl_ct.def_id).rebase_onto(
+        tcx,
+        impl_ct.container_id(tcx),
+        impl_trait_ref.args,
+    );
 
     // Create a parameter environment that represents the implementation's
-    // method.
-    // Compute placeholder form of impl and trait const tys.
+    // associated const.
     let impl_ty = tcx.type_of(impl_ct_def_id).instantiate_identity();
 
     let trait_ty = tcx.type_of(trait_ct.def_id).instantiate(tcx, trait_to_impl_args);
@@ -1759,14 +1737,14 @@ fn compare_const_predicate_entailment<'tcx>(
     // The predicates declared by the impl definition, the trait and the
     // associated const in the trait are assumed.
     let impl_predicates = tcx.predicates_of(impl_ct_predicates.parent.unwrap());
-    let mut hybrid_preds = impl_predicates.instantiate_identity(tcx);
-    hybrid_preds.predicates.extend(
+    let mut hybrid_preds = impl_predicates.instantiate_identity(tcx).predicates;
+    hybrid_preds.extend(
         trait_ct_predicates
             .instantiate_own(tcx, trait_to_impl_args)
             .map(|(predicate, _)| predicate),
     );
 
-    let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing);
+    let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds), Reveal::UserFacing);
     let param_env = traits::normalize_param_env_or_error(
         tcx,
         param_env,
@@ -1776,7 +1754,7 @@ fn compare_const_predicate_entailment<'tcx>(
     let infcx = tcx.infer_ctxt().build();
     let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
 
-    let impl_ct_own_bounds = impl_ct_predicates.instantiate_own(tcx, impl_args);
+    let impl_ct_own_bounds = impl_ct_predicates.instantiate_own_identity();
     for (predicate, span) in impl_ct_own_bounds {
         let cause = ObligationCause::misc(span, impl_ct_def_id);
         let predicate = ocx.normalize(&cause, param_env, predicate);
@@ -1787,20 +1765,15 @@ fn compare_const_predicate_entailment<'tcx>(
 
     // There is no "body" here, so just pass dummy id.
     let impl_ty = ocx.normalize(&cause, param_env, impl_ty);
-
-    debug!("compare_const_impl: impl_ty={:?}", impl_ty);
+    debug!(?impl_ty);
 
     let trait_ty = ocx.normalize(&cause, param_env, trait_ty);
-
-    debug!("compare_const_impl: trait_ty={:?}", trait_ty);
+    debug!(?trait_ty);
 
     let err = ocx.sup(&cause, param_env, trait_ty, impl_ty);
 
     if let Err(terr) = err {
-        debug!(
-            "checking associated const for compatibility: impl ty {:?}, trait ty {:?}",
-            impl_ty, trait_ty
-        );
+        debug!(?impl_ty, ?trait_ty);
 
         // Locate the Span containing just the type of the offending impl
         let (ty, _) = tcx.hir().expect_impl_item(impl_ct_def_id).expect_const();
@@ -1845,14 +1818,13 @@ fn compare_const_predicate_entailment<'tcx>(
     ocx.resolve_regions_and_report_errors(impl_ct_def_id, &outlives_env)
 }
 
+#[instrument(level = "debug", skip(tcx))]
 pub(super) fn compare_impl_ty<'tcx>(
     tcx: TyCtxt<'tcx>,
     impl_ty: ty::AssocItem,
     trait_ty: ty::AssocItem,
     impl_trait_ref: ty::TraitRef<'tcx>,
 ) {
-    debug!("compare_impl_type(impl_trait_ref={:?})", impl_trait_ref);
-
     let _: Result<(), ErrorGuaranteed> = try {
         compare_number_of_generics(tcx, impl_ty, trait_ty, false)?;
         compare_generic_param_kinds(tcx, impl_ty, trait_ty, false)?;
@@ -1864,20 +1836,23 @@ pub(super) fn compare_impl_ty<'tcx>(
 
 /// The equivalent of [compare_method_predicate_entailment], but for associated types
 /// instead of associated functions.
+#[instrument(level = "debug", skip(tcx))]
 fn compare_type_predicate_entailment<'tcx>(
     tcx: TyCtxt<'tcx>,
     impl_ty: ty::AssocItem,
     trait_ty: ty::AssocItem,
     impl_trait_ref: ty::TraitRef<'tcx>,
 ) -> Result<(), ErrorGuaranteed> {
-    let impl_args = GenericArgs::identity_for_item(tcx, impl_ty.def_id);
-    let trait_to_impl_args =
-        impl_args.rebase_onto(tcx, impl_ty.container_id(tcx), impl_trait_ref.args);
+    let trait_to_impl_args = GenericArgs::identity_for_item(tcx, impl_ty.def_id).rebase_onto(
+        tcx,
+        impl_ty.container_id(tcx),
+        impl_trait_ref.args,
+    );
 
     let impl_ty_predicates = tcx.predicates_of(impl_ty.def_id);
     let trait_ty_predicates = tcx.predicates_of(trait_ty.def_id);
 
-    let impl_ty_own_bounds = impl_ty_predicates.instantiate_own(tcx, impl_args);
+    let impl_ty_own_bounds = impl_ty_predicates.instantiate_own_identity();
     if impl_ty_own_bounds.len() == 0 {
         // Nothing to check.
         return Ok(());
@@ -1887,29 +1862,29 @@ fn compare_type_predicate_entailment<'tcx>(
     // `ObligationCause` (and the `FnCtxt`). This is what
     // `regionck_item` expects.
     let impl_ty_def_id = impl_ty.def_id.expect_local();
-    debug!("compare_type_predicate_entailment: trait_to_impl_args={:?}", trait_to_impl_args);
+    debug!(?trait_to_impl_args);
 
     // The predicates declared by the impl definition, the trait and the
     // associated type in the trait are assumed.
     let impl_predicates = tcx.predicates_of(impl_ty_predicates.parent.unwrap());
-    let mut hybrid_preds = impl_predicates.instantiate_identity(tcx);
-    hybrid_preds.predicates.extend(
+    let mut hybrid_preds = impl_predicates.instantiate_identity(tcx).predicates;
+    hybrid_preds.extend(
         trait_ty_predicates
             .instantiate_own(tcx, trait_to_impl_args)
             .map(|(predicate, _)| predicate),
     );
-
-    debug!("compare_type_predicate_entailment: bounds={:?}", hybrid_preds);
+    debug!(?hybrid_preds);
 
     let impl_ty_span = tcx.def_span(impl_ty_def_id);
     let normalize_cause = ObligationCause::misc(impl_ty_span, impl_ty_def_id);
-    let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing);
+
+    let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds), Reveal::UserFacing);
     let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
+    debug!(caller_bounds=?param_env.caller_bounds());
+
     let infcx = tcx.infer_ctxt().build();
     let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
 
-    debug!("compare_type_predicate_entailment: caller_bounds={:?}", param_env.caller_bounds());
-
     for (predicate, span) in impl_ty_own_bounds {
         let cause = ObligationCause::misc(span, impl_ty_def_id);
         let predicate = ocx.normalize(&cause, param_env, predicate);
@@ -2009,11 +1984,11 @@ pub(super) fn check_type_bounds<'tcx>(
         .explicit_item_bounds(trait_ty.def_id)
         .iter_instantiated_copied(tcx, rebased_args)
         .map(|(concrete_ty_bound, span)| {
-            debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound);
+            debug!(?concrete_ty_bound);
             traits::Obligation::new(tcx, mk_cause(span), param_env, concrete_ty_bound)
         })
         .collect();
-    debug!("check_type_bounds: item_bounds={:?}", obligations);
+    debug!(item_bounds=?obligations);
 
     // Normalize predicates with the assumption that the GAT may always normalize
     // to its definition type. This should be the param-env we use to *prove* the
@@ -2032,7 +2007,7 @@ pub(super) fn check_type_bounds<'tcx>(
         } else {
             ocx.normalize(&normalize_cause, normalize_param_env, obligation.predicate)
         };
-        debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate);
+        debug!(?normalized_predicate);
         obligation.predicate = normalized_predicate;
 
         ocx.register_obligation(obligation);
diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs
index 1a5f4659812..312fb16c93a 100644
--- a/compiler/rustc_hir_analysis/src/check/region.rs
+++ b/compiler/rustc_hir_analysis/src/check/region.rs
@@ -167,7 +167,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
             }
         }
         if let Some(tail_expr) = blk.expr {
-            if visitor.tcx.features().shorter_tail_lifetimes
+            if visitor.tcx.features().shorter_tail_lifetimes()
                 && blk.span.edition().at_least_rust_2024()
             {
                 visitor.terminating_scopes.insert(tail_expr.hir_id.local_id);
@@ -466,7 +466,8 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
 
         hir::ExprKind::If(cond, then, Some(otherwise)) => {
             let expr_cx = visitor.cx;
-            let data = if expr.span.at_least_rust_2024() && visitor.tcx.features().if_let_rescope {
+            let data = if expr.span.at_least_rust_2024() && visitor.tcx.features().if_let_rescope()
+            {
                 ScopeData::IfThenRescope
             } else {
                 ScopeData::IfThen
@@ -481,7 +482,8 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
 
         hir::ExprKind::If(cond, then, None) => {
             let expr_cx = visitor.cx;
-            let data = if expr.span.at_least_rust_2024() && visitor.tcx.features().if_let_rescope {
+            let data = if expr.span.at_least_rust_2024() && visitor.tcx.features().if_let_rescope()
+            {
                 ScopeData::IfThenRescope
             } else {
                 ScopeData::IfThen
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index f788456d4e9..3170c0ac990 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -110,7 +110,7 @@ where
 
     let mut wfcx = WfCheckingCtxt { ocx, span, body_def_id, param_env };
 
-    if !tcx.features().trivial_bounds {
+    if !tcx.features().trivial_bounds() {
         wfcx.check_false_global_bounds()
     }
     f(&mut wfcx)?;
@@ -921,7 +921,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
         } => {
             let ty = tcx.type_of(param.def_id).instantiate_identity();
 
-            if tcx.features().unsized_const_params {
+            if tcx.features().unsized_const_params() {
                 enter_wf_checking_ctxt(tcx, hir_ty.span, param.def_id, |wfcx| {
                     wfcx.register_bound(
                         ObligationCause::new(
@@ -935,7 +935,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
                     );
                     Ok(())
                 })
-            } else if tcx.features().adt_const_params {
+            } else if tcx.features().adt_const_params() {
                 enter_wf_checking_ctxt(tcx, hir_ty.span, param.def_id, |wfcx| {
                     wfcx.register_bound(
                         ObligationCause::new(
@@ -1698,9 +1698,9 @@ fn check_method_receiver<'tcx>(
         return Ok(());
     }
 
-    let arbitrary_self_types_level = if tcx.features().arbitrary_self_types_pointers {
+    let arbitrary_self_types_level = if tcx.features().arbitrary_self_types_pointers() {
         Some(ArbitrarySelfTypesLevel::WithPointers)
-    } else if tcx.features().arbitrary_self_types {
+    } else if tcx.features().arbitrary_self_types() {
         Some(ArbitrarySelfTypesLevel::Basic)
     } else {
         None
diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
index dfb3c088afb..2afc2aec1ba 100644
--- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
@@ -77,7 +77,7 @@ impl<'tcx> InherentCollect<'tcx> {
             return Ok(());
         }
 
-        if self.tcx.features().rustc_attrs {
+        if self.tcx.features().rustc_attrs() {
             let items = self.tcx.associated_item_def_ids(impl_def_id);
 
             if !self.tcx.has_attr(ty_def_id, sym::rustc_has_incoherent_inherent_impls) {
@@ -115,7 +115,7 @@ impl<'tcx> InherentCollect<'tcx> {
     ) -> Result<(), ErrorGuaranteed> {
         let items = self.tcx.associated_item_def_ids(impl_def_id);
         if !self.tcx.hir().rustc_coherence_is_core() {
-            if self.tcx.features().rustc_attrs {
+            if self.tcx.features().rustc_attrs() {
                 for &impl_item in items {
                     if !self.tcx.has_attr(impl_item, sym::rustc_allow_incoherent_impl) {
                         let span = self.tcx.def_span(impl_def_id);
diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs
index eea5a16ac6f..3aad4bafeb5 100644
--- a/compiler/rustc_hir_analysis/src/coherence/mod.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs
@@ -53,7 +53,7 @@ fn enforce_trait_manually_implementable(
 ) -> Result<(), ErrorGuaranteed> {
     let impl_header_span = tcx.def_span(impl_def_id);
 
-    if tcx.is_lang_item(trait_def_id, LangItem::Freeze) && !tcx.features().freeze_impls {
+    if tcx.is_lang_item(trait_def_id, LangItem::Freeze) && !tcx.features().freeze_impls() {
         feature_err(
             &tcx.sess,
             sym::freeze_impls,
@@ -86,8 +86,8 @@ fn enforce_trait_manually_implementable(
 
     if let ty::trait_def::TraitSpecializationKind::AlwaysApplicable = trait_def.specialization_kind
     {
-        if !tcx.features().specialization
-            && !tcx.features().min_specialization
+        if !tcx.features().specialization()
+            && !tcx.features().min_specialization()
             && !impl_header_span.allows_unstable(sym::specialization)
             && !impl_header_span.allows_unstable(sym::min_specialization)
         {
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index f63e2d40e39..acc21d0994b 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -1129,7 +1129,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
     };
 
     let paren_sugar = tcx.has_attr(def_id, sym::rustc_paren_sugar);
-    if paren_sugar && !tcx.features().unboxed_closures {
+    if paren_sugar && !tcx.features().unboxed_closures() {
         tcx.dcx().emit_err(errors::ParenSugarAttribute { span: item.span });
     }
 
@@ -1696,7 +1696,7 @@ fn compute_sig_of_foreign_fn_decl<'tcx>(
 
     // Feature gate SIMD types in FFI, since I am not sure that the
     // ABIs are handled at all correctly. -huonw
-    if abi != abi::Abi::RustIntrinsic && !tcx.features().simd_ffi {
+    if abi != abi::Abi::RustIntrinsic && !tcx.features().simd_ffi() {
         let check = |hir_ty: &hir::Ty<'_>, ty: Ty<'_>| {
             if ty.is_simd() {
                 let snip = tcx
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index 14b6b17ed18..348b2260d26 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -109,7 +109,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
                 // We do not allow generic parameters in anon consts if we are inside
                 // of a const parameter type, e.g. `struct Foo<const N: usize, const M: [u8; N]>` is not allowed.
                 None
-            } else if tcx.features().generic_const_exprs {
+            } else if tcx.features().generic_const_exprs() {
                 let parent_node = tcx.parent_hir_node(hir_id);
                 debug!(?parent_node);
                 if let Node::Variant(Variant { disr_expr: Some(constant), .. }) = parent_node
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index 9bd8c70dcfe..097a1fbc393 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -308,7 +308,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
         }
     }
 
-    if tcx.features().generic_const_exprs {
+    if tcx.features().generic_const_exprs() {
         predicates.extend(const_evaluatable_predicates_of(tcx, def_id));
     }
 
@@ -524,7 +524,7 @@ pub(super) fn explicit_predicates_of<'tcx>(
         }
     } else {
         if matches!(def_kind, DefKind::AnonConst)
-            && tcx.features().generic_const_exprs
+            && tcx.features().generic_const_exprs()
             && let Some(defaulted_param_def_id) =
                 tcx.hir().opt_const_param_default_param_def_id(tcx.local_def_id_to_hir_id(def_id))
         {
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index cb7f0901c7e..95e07244a6b 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -1161,7 +1161,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                         && let Some(param) = generics.params.iter().find(|p| p.def_id == param_id)
                         && param.is_elided_lifetime()
                         && !self.tcx.asyncness(lifetime_ref.hir_id.owner.def_id).is_async()
-                        && !self.tcx.features().anonymous_lifetime_in_impl_trait
+                        && !self.tcx.features().anonymous_lifetime_in_impl_trait()
                     {
                         let mut diag: rustc_errors::Diag<'_> = rustc_session::parse::feature_err(
                             &self.tcx.sess,
@@ -2239,7 +2239,7 @@ fn deny_non_region_late_bound(
             format!("late-bound {what} parameter not allowed on {where_}"),
         );
 
-        let guar = diag.emit_unless(!tcx.features().non_lifetime_binders || !first);
+        let guar = diag.emit_unless(!tcx.features().non_lifetime_binders() || !first);
 
         first = false;
         *arg = ResolvedArg::Error(guar);
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index 470bcaeded1..84161ec7648 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -699,7 +699,7 @@ fn infer_placeholder_type<'tcx>(
 }
 
 fn check_feature_inherent_assoc_ty(tcx: TyCtxt<'_>, span: Span) {
-    if !tcx.features().inherent_associated_types {
+    if !tcx.features().inherent_associated_types() {
         use rustc_session::parse::feature_err;
         use rustc_span::symbol::sym;
         feature_err(
@@ -714,7 +714,7 @@ fn check_feature_inherent_assoc_ty(tcx: TyCtxt<'_>, span: Span) {
 
 pub(crate) fn type_alias_is_lazy<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool {
     use hir::intravisit::Visitor;
-    if tcx.features().lazy_type_alias {
+    if tcx.features().lazy_type_alias() {
         return true;
     }
     struct HasTait;
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
index 4721a3a0cf5..2b0e1350108 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -45,23 +45,22 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 let hir::GenericBound::Trait(ptr) = hir_bound else {
                     continue;
                 };
-                match ptr.modifiers {
-                    hir::TraitBoundModifier::Maybe => unbounds.push(ptr),
-                    hir::TraitBoundModifier::Negative => {
+                match ptr.modifiers.polarity {
+                    hir::BoundPolarity::Maybe(_) => unbounds.push(ptr),
+                    hir::BoundPolarity::Negative(_) => {
                         if let Some(sized_def_id) = sized_def_id
                             && ptr.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id)
                         {
                             seen_negative_sized_bound = true;
                         }
                     }
-                    hir::TraitBoundModifier::None => {
+                    hir::BoundPolarity::Positive => {
                         if let Some(sized_def_id) = sized_def_id
                             && ptr.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id)
                         {
                             seen_positive_sized_bound = true;
                         }
                     }
-                    _ => {}
                 }
             }
         };
@@ -89,7 +88,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             };
             if seen_repeat {
                 self.dcx().emit_err(err);
-            } else if !tcx.features().more_maybe_bounds {
+            } else if !tcx.features().more_maybe_bounds() {
                 self.tcx().sess.create_feature_err(err, sym::more_maybe_bounds).emit();
             };
         }
@@ -169,20 +168,20 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
             match hir_bound {
                 hir::GenericBound::Trait(poly_trait_ref) => {
-                    let (constness, polarity) = match poly_trait_ref.modifiers {
-                        hir::TraitBoundModifier::Const => {
-                            (Some(ty::BoundConstness::Const), ty::PredicatePolarity::Positive)
-                        }
-                        hir::TraitBoundModifier::MaybeConst => (
-                            Some(ty::BoundConstness::ConstIfConst),
-                            ty::PredicatePolarity::Positive,
-                        ),
-                        hir::TraitBoundModifier::None => (None, ty::PredicatePolarity::Positive),
-                        hir::TraitBoundModifier::Negative => {
-                            (None, ty::PredicatePolarity::Negative)
-                        }
-                        hir::TraitBoundModifier::Maybe => continue,
+                    let hir::TraitBoundModifiers { constness, polarity } = poly_trait_ref.modifiers;
+                    // FIXME: We could pass these directly into `lower_poly_trait_ref`
+                    // so that we could use these spans in diagnostics within that function...
+                    let constness = match constness {
+                        hir::BoundConstness::Never => None,
+                        hir::BoundConstness::Always(_) => Some(ty::BoundConstness::Const),
+                        hir::BoundConstness::Maybe(_) => Some(ty::BoundConstness::ConstIfConst),
                     };
+                    let polarity = match polarity {
+                        rustc_ast::BoundPolarity::Positive => ty::PredicatePolarity::Positive,
+                        rustc_ast::BoundPolarity::Negative(_) => ty::PredicatePolarity::Negative,
+                        rustc_ast::BoundPolarity::Maybe(_) => continue,
+                    };
+
                     let _ = self.lower_poly_trait_ref(
                         &poly_trait_ref.trait_ref,
                         poly_trait_ref.span,
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs
index a1ee120e855..2cee7c77aa5 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs
@@ -40,8 +40,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let mut potential_assoc_types = Vec::new();
         let dummy_self = self.tcx().types.trait_object_dummy_self;
         for trait_bound in hir_trait_bounds.iter().rev() {
-            // FIXME: This doesn't handle `? const`.
-            if trait_bound.modifiers == hir::TraitBoundModifier::Maybe {
+            if let hir::BoundPolarity::Maybe(_) = trait_bound.modifiers.polarity {
                 continue;
             }
             if let GenericArgCountResult {
@@ -259,7 +258,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         }
                     })
                     .collect();
-                let args = tcx.mk_args(&args);
 
                 let span = i.bottom().1;
                 let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| {
@@ -292,7 +290,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     .emit();
                 }
 
-                ty::ExistentialTraitRef { def_id: trait_ref.def_id, args }
+                ty::ExistentialTraitRef::new(tcx, trait_ref.def_id, args)
             })
         });
 
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
index 01768c89cca..dd0f250a8e2 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -63,7 +63,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         trait_segment: &'_ hir::PathSegment<'_>,
         is_impl: bool,
     ) {
-        if self.tcx().features().unboxed_closures {
+        if self.tcx().features().unboxed_closures() {
             return;
         }
 
@@ -343,7 +343,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             && let Some(hir_ty) = constraint.ty()
             && let ty = self.lower_ty(hir_ty)
             && (ty.is_enum() || ty.references_error())
-            && tcx.features().associated_const_equality
+            && tcx.features().associated_const_equality()
         {
             Some(errors::AssocKindMismatchWrapInBracesSugg {
                 lo: hir_ty.span.shrink_to_lo(),
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index fe0cd572609..a73a2f925cd 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -1241,7 +1241,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         // selection during HIR ty lowering instead of in the trait solver), IATs can lead to cycle
         // errors (#108491) which mask the feature-gate error, needlessly confusing users
         // who use IATs by accident (#113265).
-        if !tcx.features().inherent_associated_types {
+        if !tcx.features().inherent_associated_types() {
             return Ok(None);
         }
 
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs
index 7f183324f04..d9c70c3cee6 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs
@@ -57,7 +57,7 @@ pub(crate) fn check_impl_wf(
     tcx: TyCtxt<'_>,
     impl_def_id: LocalDefId,
 ) -> Result<(), ErrorGuaranteed> {
-    let min_specialization = tcx.features().min_specialization;
+    let min_specialization = tcx.features().min_specialization();
     let mut res = Ok(());
     debug_assert_matches!(tcx.def_kind(impl_def_id), DefKind::Impl { .. });
     res = res.and(enforce_impl_params_are_constrained(tcx, impl_def_id));
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index 71ee77f8f61..3ad35163191 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -116,7 +116,7 @@ fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi
         return;
     }
 
-    let extended_abi_support = tcx.features().extended_varargs_abi_support;
+    let extended_abi_support = tcx.features().extended_varargs_abi_support();
     let conventions = match (extended_abi_support, abi.supports_varargs()) {
         // User enabled additional ABI support for varargs and function ABI matches those ones.
         (true, true) => return,
@@ -155,7 +155,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
 
     // FIXME(effects): remove once effects is implemented in old trait solver
     // or if the next solver is stabilized.
-    if tcx.features().effects && !tcx.next_trait_solver_globally() {
+    if tcx.features().effects() && !tcx.next_trait_solver_globally() {
         tcx.dcx().emit_err(errors::EffectsWithoutNextSolver);
     }
 
@@ -172,7 +172,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
         let _ = tcx.ensure().crate_inherent_impls_overlap_check(());
     });
 
-    if tcx.features().rustc_attrs {
+    if tcx.features().rustc_attrs() {
         tcx.sess.time("outlives_dumping", || outlives::dump::inferred_outlives(tcx));
         tcx.sess.time("variance_dumping", || variance::dump::variances(tcx));
         collect::dump::opaque_hidden_types(tcx);
diff --git a/compiler/rustc_hir_analysis/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs
index e3cdb1bf5f7..c43917649de 100644
--- a/compiler/rustc_hir_analysis/src/outlives/mod.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs
@@ -23,7 +23,7 @@ fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[(ty::Clau
             let crate_map = tcx.inferred_outlives_crate(());
             crate_map.predicates.get(&item_def_id.to_def_id()).copied().unwrap_or(&[])
         }
-        DefKind::AnonConst if tcx.features().generic_const_exprs => {
+        DefKind::AnonConst if tcx.features().generic_const_exprs() => {
             let id = tcx.local_def_id_to_hir_id(item_def_id);
             if tcx.hir().opt_const_param_default_param_def_id(id).is_some() {
                 // In `generics_of` we set the generics' parent to be our parent's parent which means that
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 9ebfd4f15ab..3ba9c76bcee 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -16,7 +16,6 @@ use rustc_ast_pretty::pprust::{Comments, PrintState};
 use rustc_hir::{
     BindingMode, ByRef, ConstArgKind, GenericArg, GenericBound, GenericParam, GenericParamKind,
     HirId, LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term,
-    TraitBoundModifier,
 };
 use rustc_span::FileName;
 use rustc_span::source_map::SourceMap;
@@ -676,9 +675,16 @@ impl<'a> State<'a> {
     }
 
     fn print_poly_trait_ref(&mut self, t: &hir::PolyTraitRef<'_>) {
-        // FIXME: This isn't correct!
-        if t.modifiers == TraitBoundModifier::Maybe {
-            self.word("?");
+        let hir::TraitBoundModifiers { constness, polarity } = t.modifiers;
+        match constness {
+            hir::BoundConstness::Never => {}
+            hir::BoundConstness::Always(_) => self.word("const"),
+            hir::BoundConstness::Maybe(_) => self.word("~const"),
+        }
+        match polarity {
+            hir::BoundPolarity::Positive => {}
+            hir::BoundPolarity::Negative(_) => self.word("!"),
+            hir::BoundPolarity::Maybe(_) => self.word("?"),
         }
         self.print_formal_generic_params(t.bound_generic_params);
         self.print_trait_ref(&t.trait_ref);
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index 8fa6ab8503d..f7306063c16 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -721,7 +721,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
         use rustc_middle::ty::cast::IntTy::*;
 
         if self.cast_ty.is_dyn_star() {
-            if fcx.tcx.features().dyn_star {
+            if fcx.tcx.features().dyn_star() {
                 span_bug!(self.span, "should be handled by `coerce`");
             } else {
                 // Report "casting is invalid" rather than "non-primitive cast"
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 406d668e7a5..de80ccf790c 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -223,11 +223,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             ty::Ref(r_b, _, mutbl_b) => {
                 return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b);
             }
-            ty::Dynamic(predicates, region, ty::DynStar) if self.tcx.features().dyn_star => {
+            ty::Dynamic(predicates, region, ty::DynStar) if self.tcx.features().dyn_star() => {
                 return self.coerce_dyn_star(a, b, predicates, region);
             }
             ty::Adt(pin, _)
-                if self.tcx.features().pin_ergonomics
+                if self.tcx.features().pin_ergonomics()
                     && self.tcx.is_lang_item(pin.did(), hir::LangItem::Pin) =>
             {
                 return self.coerce_pin(a, b);
@@ -698,7 +698,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         }
 
         if let Some((sub, sup)) = has_trait_upcasting_coercion
-            && !self.tcx().features().trait_upcasting
+            && !self.tcx().features().trait_upcasting()
         {
             // Renders better when we erase regions, since they're not really the point here.
             let (sub, sup) = self.tcx.erase_regions((sub, sup));
@@ -712,7 +712,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             err.emit();
         }
 
-        if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion {
+        if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion() {
             feature_err(
                 &self.tcx.sess,
                 sym::unsized_tuple_coercion,
@@ -732,7 +732,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
         predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
         b_region: ty::Region<'tcx>,
     ) -> CoerceResult<'tcx> {
-        if !self.tcx.features().dyn_star {
+        if !self.tcx.features().dyn_star() {
             return Err(TypeError::Mismatch);
         }
 
@@ -1695,7 +1695,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
                             blk_id,
                             expression,
                         );
-                        if !fcx.tcx.features().unsized_locals {
+                        if !fcx.tcx.features().unsized_locals() {
                             unsized_return = self.is_return_ty_definitely_unsized(fcx);
                         }
                     }
@@ -1709,7 +1709,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
                             return_expr_id,
                             expression,
                         );
-                        if !fcx.tcx.features().unsized_locals {
+                        if !fcx.tcx.features().unsized_locals() {
                             unsized_return = self.is_return_ty_definitely_unsized(fcx);
                         }
                     }
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index f4d7b59e9c8..da231acbb0f 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -10,7 +10,8 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_data_structures::unord::UnordMap;
 use rustc_errors::codes::*;
 use rustc_errors::{
-    Applicability, Diag, ErrorGuaranteed, StashKey, Subdiagnostic, pluralize, struct_span_code_err,
+    Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, Subdiagnostic, pluralize,
+    struct_span_code_err,
 };
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::DefId;
@@ -734,7 +735,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // be known if explicitly specified via turbofish).
                 self.deferred_transmute_checks.borrow_mut().push((*from, to, expr.hir_id));
             }
-            if !tcx.features().unsized_fn_params {
+            if !tcx.features().unsized_fn_params() {
                 // We want to remove some Sized bounds from std functions,
                 // but don't want to expose the removal to stable Rust.
                 // i.e., we don't want to allow
@@ -1995,7 +1996,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if let Some(base_expr) = base_expr {
             // FIXME: We are currently creating two branches here in order to maintain
             // consistency. But they should be merged as much as possible.
-            let fru_tys = if self.tcx.features().type_changing_struct_update {
+            let fru_tys = if self.tcx.features().type_changing_struct_update() {
                 if adt.is_struct() {
                     // Make some fresh generic parameters for our ADT type.
                     let fresh_args = self.fresh_args_for_item(base_expr.span, adt.did());
@@ -2763,12 +2764,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             field_ident.span,
             "field not available in `impl Future`, but it is available in its `Output`",
         );
-        err.span_suggestion_verbose(
-            base.span.shrink_to_hi(),
-            "consider `await`ing on the `Future` and access the field of its `Output`",
-            ".await",
-            Applicability::MaybeIncorrect,
-        );
+        match self.tcx.coroutine_kind(self.body_id) {
+            Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) => {
+                err.span_suggestion_verbose(
+                    base.span.shrink_to_hi(),
+                    "consider `await`ing on the `Future` to access the field",
+                    ".await",
+                    Applicability::MaybeIncorrect,
+                );
+            }
+            _ => {
+                let mut span: MultiSpan = base.span.into();
+                span.push_span_label(self.tcx.def_span(self.body_id), "this is not `async`");
+                err.span_note(
+                    span,
+                    "this implements `Future` and its output type has the field, \
+                    but the future cannot be awaited in a synchronous function",
+                );
+            }
+        }
     }
 
     fn ban_nonexisting_field(
@@ -3538,7 +3552,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     let (ident, _def_scope) =
                         self.tcx.adjust_ident_and_get_scope(field, container_def.did(), block);
 
-                    if !self.tcx.features().offset_of_enum {
+                    if !self.tcx.features().offset_of_enum() {
                         rustc_session::parse::feature_err(
                             &self.tcx.sess,
                             sym::offset_of_enum,
@@ -3628,7 +3642,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     {
                         let field_ty = self.field_ty(expr.span, field, args);
 
-                        if self.tcx.features().offset_of_slice {
+                        if self.tcx.features().offset_of_slice() {
                             self.require_type_has_static_alignment(
                                 field_ty,
                                 expr.span,
@@ -3661,7 +3675,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         && field.name == sym::integer(index)
                     {
                         if let Some(&field_ty) = tys.get(index) {
-                            if self.tcx.features().offset_of_slice {
+                            if self.tcx.features().offset_of_slice() {
                                 self.require_type_has_static_alignment(
                                     field_ty,
                                     expr.span,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 2bc84d1ac67..d2d311ef565 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -1486,7 +1486,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     return ty::Const::new_error(self.tcx, guar);
                 }
             }
-        } else if self.tcx.features().generic_const_exprs {
+        } else if self.tcx.features().generic_const_exprs() {
             ct.normalize_internal(self.tcx, self.param_env)
         } else {
             ct
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index e60bd1a312a..4123feebb40 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -404,7 +404,7 @@ fn default_fallback(tcx: TyCtxt<'_>) -> DivergingFallbackBehavior {
     }
 
     // `feature(never_type_fallback)`: fallback to `!` or `()` trying to not break stuff
-    if tcx.features().never_type_fallback {
+    if tcx.features().never_type_fallback() {
         return DivergingFallbackBehavior::ContextDependent;
     }
 
diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs
index 5ae8d2230f8..f427b0b805e 100644
--- a/compiler/rustc_hir_typeck/src/gather_locals.rs
+++ b/compiler/rustc_hir_typeck/src/gather_locals.rs
@@ -141,7 +141,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
             let var_ty = self.assign(p.span, p.hir_id, None);
 
             if let Some((ty_span, hir_id)) = self.outermost_fn_param_pat {
-                if !self.fcx.tcx.features().unsized_fn_params {
+                if !self.fcx.tcx.features().unsized_fn_params() {
                     self.fcx.require_type_is_sized(
                         var_ty,
                         ty_span,
@@ -158,7 +158,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
                         ),
                     );
                 }
-            } else if !self.fcx.tcx.features().unsized_locals {
+            } else if !self.fcx.tcx.features().unsized_locals() {
                 self.fcx.require_type_is_sized(
                     var_ty,
                     p.span,
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index 85ee0a5cf7d..85e11ff6745 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -161,7 +161,7 @@ fn typeck_with_fallback<'tcx>(
         let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig);
         let fn_sig = fcx.normalize(body.value.span, fn_sig);
 
-        check_fn(&mut fcx, fn_sig, None, decl, def_id, body, tcx.features().unsized_fn_params);
+        check_fn(&mut fcx, fn_sig, None, decl, def_id, body, tcx.features().unsized_fn_params());
     } else {
         let expected_type = infer_type_if_missing(&fcx, node);
         let expected_type = expected_type.unwrap_or_else(fallback);
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
index 1d7b3433fe5..f4685725dd3 100644
--- a/compiler/rustc_hir_typeck/src/method/confirm.rs
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -536,7 +536,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
                 // FIXME(arbitrary_self_types): We probably should limit the
                 // situations where this can occur by adding additional restrictions
                 // to the feature, like the self type can't reference method args.
-                if self.tcx.features().arbitrary_self_types {
+                if self.tcx.features().arbitrary_self_types() {
                     self.err_ctxt()
                         .report_mismatched_types(&cause, method_self_ty, self_ty, terr)
                         .emit();
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 1be711887d9..b60fa8bbfa1 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -404,7 +404,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     mode,
                 }));
             } else if bad_ty.reached_raw_pointer
-                && !self.tcx.features().arbitrary_self_types_pointers
+                && !self.tcx.features().arbitrary_self_types_pointers()
                 && !self.tcx.sess.at_least_rust_2018()
             {
                 // this case used to be allowed by the compiler,
@@ -429,8 +429,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let ty = self.resolve_vars_if_possible(ty.value);
                 let guar = match *ty.kind() {
                     ty::Infer(ty::TyVar(_)) => {
-                        let raw_ptr_call =
-                            bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types;
+                        let raw_ptr_call = bad_ty.reached_raw_pointer
+                            && !self.tcx.features().arbitrary_self_types();
                         let mut err = self.err_ctxt().emit_inference_failure_err(
                             self.body_id,
                             span,
@@ -1146,7 +1146,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                     }
 
                     ty::Adt(def, args)
-                        if self.tcx.features().pin_ergonomics
+                        if self.tcx.features().pin_ergonomics()
                             && self.tcx.is_lang_item(def.did(), hir::LangItem::Pin) =>
                     {
                         // make sure this is a pinned reference (and not a `Pin<Box>` or something)
@@ -1195,7 +1195,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         self_ty: Ty<'tcx>,
         unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>,
     ) -> Option<PickResult<'tcx>> {
-        if !self.tcx.features().pin_ergonomics {
+        if !self.tcx.features().pin_ergonomics() {
             return None;
         }
 
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index e961752b24c..cba6586f01d 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -442,7 +442,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         let features = self.tcx.features();
-        if features.ref_pat_eat_one_layer_2024 || features.ref_pat_eat_one_layer_2024_structural {
+        if features.ref_pat_eat_one_layer_2024() || features.ref_pat_eat_one_layer_2024_structural()
+        {
             def_br = def_br.cap_ref_mutability(max_ref_mutbl.as_mutbl());
             if def_br == ByRef::Yes(Mutability::Not) {
                 max_ref_mutbl = MutblCap::Not;
@@ -490,7 +491,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         }
 
-        if self.tcx.features().string_deref_patterns
+        if self.tcx.features().string_deref_patterns()
             && let hir::ExprKind::Lit(Spanned { node: ast::LitKind::Str(..), .. }) = lt.kind
         {
             let tcx = self.tcx;
@@ -675,10 +676,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let bm = match user_bind_annot {
             BindingMode(ByRef::No, Mutability::Mut) if matches!(def_br, ByRef::Yes(_)) => {
                 if pat.span.at_least_rust_2024()
-                    && (self.tcx.features().ref_pat_eat_one_layer_2024
-                        || self.tcx.features().ref_pat_eat_one_layer_2024_structural)
+                    && (self.tcx.features().ref_pat_eat_one_layer_2024()
+                        || self.tcx.features().ref_pat_eat_one_layer_2024_structural())
                 {
-                    if !self.tcx.features().mut_ref {
+                    if !self.tcx.features().mut_ref() {
                         feature_err(
                             &self.tcx.sess,
                             sym::mut_ref,
@@ -2152,8 +2153,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) -> Ty<'tcx> {
         let tcx = self.tcx;
         let features = tcx.features();
-        let ref_pat_eat_one_layer_2024 = features.ref_pat_eat_one_layer_2024;
-        let ref_pat_eat_one_layer_2024_structural = features.ref_pat_eat_one_layer_2024_structural;
+        let ref_pat_eat_one_layer_2024 = features.ref_pat_eat_one_layer_2024();
+        let ref_pat_eat_one_layer_2024_structural =
+            features.ref_pat_eat_one_layer_2024_structural();
 
         let no_ref_mut_behind_and =
             ref_pat_eat_one_layer_2024 || ref_pat_eat_one_layer_2024_structural;
diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs
index 63cf483aa22..9e8a314bc4a 100644
--- a/compiler/rustc_hir_typeck/src/upvar.rs
+++ b/compiler/rustc_hir_typeck/src/upvar.rs
@@ -488,7 +488,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let final_upvar_tys = self.final_upvar_tys(closure_def_id);
         debug!(?closure_hir_id, ?args, ?final_upvar_tys);
 
-        if self.tcx.features().unsized_locals || self.tcx.features().unsized_fn_params {
+        if self.tcx.features().unsized_locals() || self.tcx.features().unsized_fn_params() {
             for capture in
                 self.typeck_results.borrow().closure_min_captures_flattened(closure_def_id)
             {
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index d3d092be222..391e640f8bc 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -830,7 +830,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
         value = tcx.fold_regions(value, |_, _| tcx.lifetimes.re_erased);
 
         // Normalize consts in writeback, because GCE doesn't normalize eagerly.
-        if tcx.features().generic_const_exprs {
+        if tcx.features().generic_const_exprs() {
             value =
                 value.fold_with(&mut EagerlyNormalizeConsts { tcx, param_env: self.fcx.param_env });
         }
diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs
index a006786aa75..1f46155abc8 100644
--- a/compiler/rustc_incremental/src/assert_dep_graph.rs
+++ b/compiler/rustc_incremental/src/assert_dep_graph.rs
@@ -68,7 +68,7 @@ pub(crate) fn assert_dep_graph(tcx: TyCtxt<'_>) {
         // if the `rustc_attrs` feature is not enabled, then the
         // attributes we are interested in cannot be present anyway, so
         // skip the walk.
-        if !tcx.features().rustc_attrs {
+        if !tcx.features().rustc_attrs() {
             return;
         }
 
diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs
index d25fe4219b5..2075d4214c1 100644
--- a/compiler/rustc_incremental/src/persist/dirty_clean.rs
+++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs
@@ -140,7 +140,7 @@ pub(crate) fn check_dirty_clean_annotations(tcx: TyCtxt<'_>) {
     }
 
     // can't add `#[rustc_clean]` etc without opting into this feature
-    if !tcx.features().rustc_attrs {
+    if !tcx.features().rustc_attrs() {
         return;
     }
 
diff --git a/compiler/rustc_lint/src/async_fn_in_trait.rs b/compiler/rustc_lint/src/async_fn_in_trait.rs
index 63a8a949e96..9923f05df3c 100644
--- a/compiler/rustc_lint/src/async_fn_in_trait.rs
+++ b/compiler/rustc_lint/src/async_fn_in_trait.rs
@@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for AsyncFnInTrait {
             && let hir::IsAsync::Async(async_span) = sig.header.asyncness
         {
             // RTN can be used to bound `async fn` in traits in a better way than "always"
-            if cx.tcx.features().return_type_notation {
+            if cx.tcx.features().return_type_notation() {
                 return;
             }
 
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 125fe9b3f16..71c667b2cd6 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -1235,7 +1235,7 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller {
         def_id: LocalDefId,
     ) {
         if fn_kind.asyncness().is_async()
-            && !cx.tcx.features().async_fn_track_caller
+            && !cx.tcx.features().async_fn_track_caller()
             // Now, check if the function has the `#[track_caller]` attribute
             && let Some(attr) = cx.tcx.get_attr(def_id, sym::track_caller)
         {
@@ -1424,7 +1424,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
         // See also `tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs`.
         let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
         if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION)
-            && cx.tcx.features().generic_const_exprs
+            && cx.tcx.features().generic_const_exprs()
         {
             return;
         }
@@ -1538,7 +1538,7 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
         use rustc_middle::ty::ClauseKind;
 
-        if cx.tcx.features().trivial_bounds {
+        if cx.tcx.features().trivial_bounds() {
             let predicates = cx.tcx.predicates_of(item.owner_id);
             for &(predicate, span) in predicates.predicates {
                 let predicate_kind_name = match predicate.kind().skip_binder() {
diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs
index 58fd11fcc29..bdfcc2c0a10 100644
--- a/compiler/rustc_lint/src/if_let_rescope.rs
+++ b/compiler/rustc_lint/src/if_let_rescope.rs
@@ -243,7 +243,7 @@ impl_lint_pass!(
 
 impl<'tcx> LateLintPass<'tcx> for IfLetRescope {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
-        if expr.span.edition().at_least_rust_2024() || !cx.tcx.features().if_let_rescope {
+        if expr.span.edition().at_least_rust_2024() || !cx.tcx.features().if_let_rescope() {
             return;
         }
         if let (Level::Allow, _) = cx.tcx.lint_level_at_node(IF_LET_RESCOPE, expr.hir_id) {
diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
index d029ad93407..cc40b67ab27 100644
--- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs
+++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
@@ -263,8 +263,8 @@ where
             && parent == self.parent_def_id
         {
             let opaque_span = self.tcx.def_span(opaque_def_id);
-            let new_capture_rules =
-                opaque_span.at_least_rust_2024() || self.tcx.features().lifetime_capture_rules_2024;
+            let new_capture_rules = opaque_span.at_least_rust_2024()
+                || self.tcx.features().lifetime_capture_rules_2024();
             if !new_capture_rules
                 && !opaque.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Use(..)))
             {
diff --git a/compiler/rustc_lint/src/tail_expr_drop_order.rs b/compiler/rustc_lint/src/tail_expr_drop_order.rs
index 44a36142ed4..04f769bf551 100644
--- a/compiler/rustc_lint/src/tail_expr_drop_order.rs
+++ b/compiler/rustc_lint/src/tail_expr_drop_order.rs
@@ -137,7 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for TailExprDropOrder {
         _: Span,
         def_id: rustc_span::def_id::LocalDefId,
     ) {
-        if cx.tcx.sess.at_least_rust_2024() && cx.tcx.features().shorter_tail_lifetimes {
+        if cx.tcx.sess.at_least_rust_2024() && cx.tcx.features().shorter_tail_lifetimes() {
             Self::check_fn_or_closure(cx, fn_kind, body, def_id);
         }
     }
diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs
index 5a3666dcbd4..b793ec6a493 100644
--- a/compiler/rustc_lint/src/traits.rs
+++ b/compiler/rustc_lint/src/traits.rs
@@ -114,10 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
         let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else { return };
         for bound in &bounds[..] {
             let def_id = bound.trait_ref.trait_def_id();
-            if def_id.is_some_and(|def_id| cx.tcx.is_lang_item(def_id, LangItem::Drop))
-                // FIXME: ?Drop is not a thing.
-                && bound.modifiers != hir::TraitBoundModifier::Maybe
-            {
+            if def_id.is_some_and(|def_id| cx.tcx.is_lang_item(def_id, LangItem::Drop)) {
                 let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { return };
                 cx.emit_span_lint(DYN_DROP, bound.span, DropGlue { tcx: cx.tcx, def_id });
             }
diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs
index 39fa23766b5..641d1d8e798 100644
--- a/compiler/rustc_metadata/src/dependency_format.rs
+++ b/compiler/rustc_metadata/src/dependency_format.rs
@@ -170,7 +170,7 @@ fn calculate_type(tcx: TyCtxt<'_>, ty: CrateType) -> DependencyList {
 
     let mut upstream_in_dylibs = FxHashSet::default();
 
-    if tcx.features().rustc_private {
+    if tcx.features().rustc_private() {
         // We need this to prevent users of `rustc_driver` from linking dynamically to `std`
         // which does not work as `std` is also statically linked into `rustc_driver`.
 
diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs
index c7953d50406..1e6bc413118 100644
--- a/compiler/rustc_metadata/src/native_libs.rs
+++ b/compiler/rustc_metadata/src/native_libs.rs
@@ -265,7 +265,7 @@ impl<'tcx> Collector<'tcx> {
                                 NativeLibKind::RawDylib
                             }
                             "link-arg" => {
-                                if !features.link_arg_attribute {
+                                if !features.link_arg_attribute() {
                                     feature_err(
                                         sess,
                                         sym::link_arg_attribute,
@@ -314,7 +314,7 @@ impl<'tcx> Collector<'tcx> {
                                 .emit_err(errors::LinkCfgSinglePredicate { span: item.span() });
                             continue;
                         };
-                        if !features.link_cfg {
+                        if !features.link_cfg() {
                             feature_err(
                                 sess,
                                 sym::link_cfg,
@@ -384,7 +384,7 @@ impl<'tcx> Collector<'tcx> {
                     };
 
                     macro report_unstable_modifier($feature: ident) {
-                        if !features.$feature {
+                        if !features.$feature() {
                             // FIXME: make this translatable
                             #[expect(rustc::untranslatable_diagnostic)]
                             feature_err(
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index afe03531861..ec678c7515b 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1765,7 +1765,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
     fn encode_stability(&mut self, def_id: DefId) {
         // The query lookup can take a measurable amount of time in crates with many items. Check if
         // the stability attributes are even enabled before using their queries.
-        if self.feat.staged_api || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked {
+        if self.feat.staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked {
             if let Some(stab) = self.tcx.lookup_stability(def_id) {
                 record!(self.tables.lookup_stability[def_id] <- stab)
             }
@@ -1776,7 +1776,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
     fn encode_const_stability(&mut self, def_id: DefId) {
         // The query lookup can take a measurable amount of time in crates with many items. Check if
         // the stability attributes are even enabled before using their queries.
-        if self.feat.staged_api || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked {
+        if self.feat.staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked {
             if let Some(stab) = self.tcx.lookup_const_stability(def_id) {
                 record!(self.tables.lookup_const_stability[def_id] <- stab)
             }
@@ -1787,7 +1787,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
     fn encode_default_body_stability(&mut self, def_id: DefId) {
         // The query lookup can take a measurable amount of time in crates with many items. Check if
         // the stability attributes are even enabled before using their queries.
-        if self.feat.staged_api || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked {
+        if self.feat.staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked {
             if let Some(stab) = self.tcx.lookup_default_body_stability(def_id) {
                 record!(self.tables.lookup_default_body_stability[def_id] <- stab)
             }
diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs
index 59bc55269a3..2ecf1d0bcf8 100644
--- a/compiler/rustc_middle/src/mir/interpret/queries.rs
+++ b/compiler/rustc_middle/src/mir/interpret/queries.rs
@@ -116,7 +116,7 @@ impl<'tcx> TyCtxt<'tcx> {
                     // @lcnr believes that successfully evaluating even though there are
                     // used generic parameters is a bug of evaluation, so checking for it
                     // here does feel somewhat sensible.
-                    if !self.features().generic_const_exprs && ct.args.has_non_region_param() {
+                    if !self.features().generic_const_exprs() && ct.args.has_non_region_param() {
                         let def_kind = self.def_kind(instance.def_id());
                         assert!(
                             matches!(
diff --git a/compiler/rustc_middle/src/traits/specialization_graph.rs b/compiler/rustc_middle/src/traits/specialization_graph.rs
index 26dcae001e0..515aabbe2fc 100644
--- a/compiler/rustc_middle/src/traits/specialization_graph.rs
+++ b/compiler/rustc_middle/src/traits/specialization_graph.rs
@@ -61,7 +61,7 @@ pub enum OverlapMode {
 
 impl OverlapMode {
     pub fn get(tcx: TyCtxt<'_>, trait_id: DefId) -> OverlapMode {
-        let with_negative_coherence = tcx.features().with_negative_coherence;
+        let with_negative_coherence = tcx.features().with_negative_coherence();
         let strict_coherence = tcx.has_attr(trait_id, sym::rustc_strict_coherence);
 
         if with_negative_coherence {
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index eb715be22b1..5ab85a69ce6 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -109,6 +109,7 @@ impl<'tcx> Const<'tcx> {
 
     #[inline]
     pub fn new_unevaluated(tcx: TyCtxt<'tcx>, uv: ty::UnevaluatedConst<'tcx>) -> Const<'tcx> {
+        tcx.debug_assert_args_compatible(uv.def, uv.args);
         Const::new(tcx, ty::ConstKind::Unevaluated(uv))
     }
 
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 210b115ae37..b682524ae39 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -279,6 +279,26 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.debug_assert_args_compatible(def_id, args);
     }
 
+    /// Assert that the args from an `ExistentialTraitRef` or `ExistentialProjection`
+    /// are compatible with the `DefId`. Since we're missing a `Self` type, stick on
+    /// a dummy self type and forward to `debug_assert_args_compatible`.
+    fn debug_assert_existential_args_compatible(
+        self,
+        def_id: Self::DefId,
+        args: Self::GenericArgs,
+    ) {
+        // FIXME: We could perhaps add a `skip: usize` to `debug_assert_args_compatible`
+        // to avoid needing to reintern the set of args...
+        if cfg!(debug_assertions) {
+            self.debug_assert_args_compatible(
+                def_id,
+                self.mk_args_from_iter(
+                    [self.types.trait_object_dummy_self.into()].into_iter().chain(args.iter()),
+                ),
+            );
+        }
+    }
+
     fn mk_type_list_from_iter<I, T>(self, args: I) -> T::Output
     where
         I: Iterator<Item = T>,
@@ -690,15 +710,15 @@ impl<'tcx> rustc_type_ir::inherent::Safety<TyCtxt<'tcx>> for hir::Safety {
 
 impl<'tcx> rustc_type_ir::inherent::Features<TyCtxt<'tcx>> for &'tcx rustc_feature::Features {
     fn generic_const_exprs(self) -> bool {
-        self.generic_const_exprs
+        self.generic_const_exprs()
     }
 
     fn coroutine_clone(self) -> bool {
-        self.coroutine_clone
+        self.coroutine_clone()
     }
 
     fn associated_const_equality(self) -> bool {
-        self.associated_const_equality
+        self.associated_const_equality()
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index 4f408ee1574..8bd2ae9128f 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -193,7 +193,7 @@ fn suggest_changing_unsized_bound(
             .enumerate()
             .filter(|(_, bound)| {
                 if let hir::GenericBound::Trait(poly) = bound
-                    && poly.modifiers == hir::TraitBoundModifier::Maybe
+                    && let hir::BoundPolarity::Maybe(_) = poly.modifiers.polarity
                     && poly.trait_ref.trait_def_id() == def_id
                 {
                     true
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index b02eff3bfd6..c49824bb418 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -35,9 +35,6 @@ impl<'tcx> TypeError<'tcx> {
             TypeError::CyclicTy(_) => "cyclic type of infinite size".into(),
             TypeError::CyclicConst(_) => "encountered a self-referencing constant".into(),
             TypeError::Mismatch => "types differ".into(),
-            TypeError::ConstnessMismatch(values) => {
-                format!("expected {} bound, found {} bound", values.expected, values.found).into()
-            }
             TypeError::PolarityMismatch(values) => {
                 format!("expected {} polarity, found {} polarity", values.expected, values.found)
                     .into()
diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs
index 660686f4aa2..63534a3d017 100644
--- a/compiler/rustc_middle/src/ty/generics.rs
+++ b/compiler/rustc_middle/src/ty/generics.rs
@@ -395,7 +395,9 @@ impl<'tcx> GenericPredicates<'tcx> {
         EarlyBinder::bind(self.predicates).iter_instantiated_copied(tcx, args)
     }
 
-    pub fn instantiate_own_identity(self) -> impl Iterator<Item = (Clause<'tcx>, Span)> {
+    pub fn instantiate_own_identity(
+        self,
+    ) -> impl Iterator<Item = (Clause<'tcx>, Span)> + DoubleEndedIterator + ExactSizeIterator {
         EarlyBinder::bind(self.predicates).iter_identity_copied()
     }
 
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index dbf88e643c5..7e65df6b27c 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -398,7 +398,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
                     ),
                 }
             }
-            ty::Array(inner, len) if tcx.features().transmute_generic_consts => {
+            ty::Array(inner, len) if tcx.features().transmute_generic_consts() => {
                 let len_eval = len.try_to_target_usize(tcx);
                 if len_eval == Some(0) {
                     return Ok(SizeSkeleton::Known(Size::from_bytes(0), None));
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index b5495fa282b..10c3522eb0e 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -1208,7 +1208,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
             }
         }
 
-        if self.tcx().features().return_type_notation
+        if self.tcx().features().return_type_notation()
             && let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, .. }) =
                 self.tcx().opt_rpitit_info(def_id)
             && let ty::Alias(_, alias_ty) =
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index 4c7bcb1bf2e..504a3c8a6d8 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -1,7 +1,5 @@
 use std::iter;
 
-use rustc_hir as hir;
-use rustc_target::spec::abi;
 pub use rustc_type_ir::relate::*;
 
 use crate::ty::error::{ExpectedFound, TypeError};
@@ -121,26 +119,6 @@ impl<'tcx> Relate<TyCtxt<'tcx>> for &'tcx ty::List<ty::PolyExistentialPredicate<
     }
 }
 
-impl<'tcx> Relate<TyCtxt<'tcx>> for hir::Safety {
-    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
-        _relation: &mut R,
-        a: hir::Safety,
-        b: hir::Safety,
-    ) -> RelateResult<'tcx, hir::Safety> {
-        if a != b { Err(TypeError::SafetyMismatch(ExpectedFound::new(true, a, b))) } else { Ok(a) }
-    }
-}
-
-impl<'tcx> Relate<TyCtxt<'tcx>> for abi::Abi {
-    fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
-        _relation: &mut R,
-        a: abi::Abi,
-        b: abi::Abi,
-    ) -> RelateResult<'tcx, abi::Abi> {
-        if a == b { Ok(a) } else { Err(TypeError::AbiMismatch(ExpectedFound::new(true, a, b))) }
-    }
-}
-
 impl<'tcx> Relate<TyCtxt<'tcx>> for ty::GenericArgsRef<'tcx> {
     fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
         relation: &mut R,
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index cd9ff9b60d8..4872d8c89eb 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -264,8 +264,6 @@ TrivialTypeTraversalImpls! {
 // interners).
 TrivialTypeTraversalAndLiftImpls! {
     ::rustc_hir::def_id::DefId,
-    ::rustc_hir::Safety,
-    ::rustc_target::spec::abi::Abi,
     crate::ty::ClosureKind,
     crate::ty::ParamConst,
     crate::ty::ParamTy,
@@ -276,6 +274,11 @@ TrivialTypeTraversalAndLiftImpls! {
     rustc_target::abi::Size,
 }
 
+TrivialLiftImpls! {
+    ::rustc_hir::Safety,
+    ::rustc_target::spec::abi::Abi,
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // Lift implementations
 
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 74de378c4d7..d8362ccc0a9 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -750,7 +750,7 @@ impl<'tcx> Ty<'tcx> {
 
     #[inline]
     pub fn new_diverging_default(tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
-        if tcx.features().never_type_fallback { tcx.types.never } else { tcx.types.unit }
+        if tcx.features().never_type_fallback() { tcx.types.never } else { tcx.types.unit }
     }
 
     // lang and diagnostic tys
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 9a3049f3be6..06cbb6c9f1d 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -877,8 +877,8 @@ impl<'tcx> TyCtxt<'tcx> {
         // FIXME(effects): This is suspicious and should probably not be done,
         // especially now that we enforce host effects and then properly handle
         // effect vars during fallback.
-        let mut host_always_on =
-            !self.features().effects || self.sess.opts.unstable_opts.unleash_the_miri_inside_of_you;
+        let mut host_always_on = !self.features().effects()
+            || self.sess.opts.unstable_opts.unleash_the_miri_inside_of_you;
 
         // Compute the constness required by the context.
         let const_context = self.hir().body_const_context(def_id);
@@ -1826,8 +1826,8 @@ pub fn is_doc_notable_trait(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
 /// cause an ICE that we otherwise may want to prevent.
 pub fn intrinsic_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::IntrinsicDef> {
     if (matches!(tcx.fn_sig(def_id).skip_binder().abi(), Abi::RustIntrinsic)
-        && tcx.features().intrinsics)
-        || (tcx.has_attr(def_id, sym::rustc_intrinsic) && tcx.features().rustc_attrs)
+        && tcx.features().intrinsics())
+        || (tcx.has_attr(def_id, sym::rustc_intrinsic) && tcx.features().rustc_attrs())
     {
         Some(ty::IntrinsicDef {
             name: tcx.item_name(def_id.into()),
diff --git a/compiler/rustc_mir_build/src/build/expr/as_operand.rs b/compiler/rustc_mir_build/src/build/expr/as_operand.rs
index 1e67e759aa2..112eac32264 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_operand.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_operand.rs
@@ -163,7 +163,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
         let tcx = this.tcx;
 
-        if tcx.features().unsized_fn_params {
+        if tcx.features().unsized_fn_params() {
             let ty = expr.ty;
             let param_env = this.param_env;
 
diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs
index 7170aebee7d..37cedd8cf5c 100644
--- a/compiler/rustc_mir_build/src/build/matches/test.rs
+++ b/compiler/rustc_mir_build/src/build/matches/test.rs
@@ -149,7 +149,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 if let ty::Adt(def, _) = ty.kind()
                     && tcx.is_lang_item(def.did(), LangItem::String)
                 {
-                    if !tcx.features().string_deref_patterns {
+                    if !tcx.features().string_deref_patterns() {
                         span_bug!(
                             test.span,
                             "matching on `String` went through without enabling string_deref_patterns"
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index 5995d60e7e0..5faefe2f25f 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -777,7 +777,7 @@ impl<'tcx> Cx<'tcx> {
                 if_then_scope: region::Scope {
                     id: then.hir_id.local_id,
                     data: {
-                        if expr.span.at_least_rust_2024() && tcx.features().if_let_rescope {
+                        if expr.span.at_least_rust_2024() && tcx.features().if_let_rescope() {
                             region::ScopeData::IfThenRescope
                         } else {
                             region::ScopeData::IfThen
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index ae77bce6bb1..8498df59ce6 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -1126,7 +1126,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
             .map(|witness| cx.print_witness_pat(witness))
             .collect::<Vec<String>>()
             .join(" | ");
-        if witnesses.iter().all(|p| p.is_never_pattern()) && cx.tcx.features().never_patterns {
+        if witnesses.iter().all(|p| p.is_never_pattern()) && cx.tcx.features().never_patterns() {
             // Arms with a never pattern don't take a body.
             pattern
         } else {
diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index 0a413d68020..cd291058977 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -1497,7 +1497,7 @@ fn check_field_tys_sized<'tcx>(
 ) {
     // No need to check if unsized_locals/unsized_fn_params is disabled,
     // since we will error during typeck.
-    if !tcx.features().unsized_locals && !tcx.features().unsized_fn_params {
+    if !tcx.features().unsized_locals() && !tcx.features().unsized_fn_params() {
         return;
     }
 
diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs
index d839f46cfbd..930fa129ef2 100644
--- a/compiler/rustc_mir_transform/src/coverage/graph.rs
+++ b/compiler/rustc_mir_transform/src/coverage/graph.rs
@@ -21,6 +21,10 @@ pub(crate) struct CoverageGraph {
     pub(crate) successors: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
     pub(crate) predecessors: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
     dominators: Option<Dominators<BasicCoverageBlock>>,
+    /// Allows nodes to be compared in some total order such that _if_
+    /// `a` dominates `b`, then `a < b`. If neither node dominates the other,
+    /// their relative order is consistent but arbitrary.
+    dominator_order_rank: IndexVec<BasicCoverageBlock, u32>,
 }
 
 impl CoverageGraph {
@@ -54,10 +58,27 @@ impl CoverageGraph {
             }
         }
 
-        let mut this = Self { bcbs, bb_to_bcb, successors, predecessors, dominators: None };
+        let num_nodes = bcbs.len();
+        let mut this = Self {
+            bcbs,
+            bb_to_bcb,
+            successors,
+            predecessors,
+            dominators: None,
+            dominator_order_rank: IndexVec::from_elem_n(0, num_nodes),
+        };
+        assert_eq!(num_nodes, this.num_nodes());
 
         this.dominators = Some(dominators::dominators(&this));
 
+        // The dominator rank of each node is just its index in a reverse-postorder traversal.
+        let reverse_post_order = graph::iterate::reverse_post_order(&this, this.start_node());
+        // The coverage graph is created by traversal, so all nodes are reachable.
+        assert_eq!(reverse_post_order.len(), this.num_nodes());
+        for (rank, bcb) in (0u32..).zip(reverse_post_order) {
+            this.dominator_order_rank[bcb] = rank;
+        }
+
         // The coverage graph's entry-point node (bcb0) always starts with bb0,
         // which never has predecessors. Any other blocks merged into bcb0 can't
         // have multiple (coverage-relevant) predecessors, so bcb0 always has
@@ -162,7 +183,7 @@ impl CoverageGraph {
         a: BasicCoverageBlock,
         b: BasicCoverageBlock,
     ) -> Ordering {
-        self.dominators.as_ref().unwrap().cmp_in_dominator_order(a, b)
+        self.dominator_order_rank[a].cmp(&self.dominator_order_rank[b])
     }
 
     /// Returns the source of this node's sole in-edge, if it has exactly one.
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index daf868559bc..79c62372df0 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -288,7 +288,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
             values: FxIndexSet::with_capacity_and_hasher(num_values, Default::default()),
             evaluated: IndexVec::with_capacity(num_values),
             next_opaque: Some(1),
-            feature_unsized_locals: tcx.features().unsized_locals,
+            feature_unsized_locals: tcx.features().unsized_locals(),
             ssa,
             dominators,
             reused_locals: BitSet::new_empty(local_decls.len()),
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
index 71ce0cce772..e2fd0dd2a25 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
@@ -170,7 +170,7 @@ where
         //
         // We don't do so for `NormalizesTo` goals as we erased the expected term and
         // bailing with overflow here would prevent us from detecting a type-mismatch,
-        // causing a coherence error in diesel, see #131969. We still bail with verflow
+        // causing a coherence error in diesel, see #131969. We still bail with overflow
         // when later returning from the parent AliasRelate goal.
         if !self.is_normalizes_to_goal {
             let num_non_region_vars =
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
index cbefc826fb7..250174e033e 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
@@ -291,7 +291,7 @@ where
             search_graph,
             nested_goals: NestedGoals::new(),
             tainted: Ok(()),
-            inspect: canonical_goal_evaluation.new_goal_evaluation_step(var_values, input),
+            inspect: canonical_goal_evaluation.new_goal_evaluation_step(var_values),
         };
 
         for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
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 85474bf37b4..1607fbb1b6a 100644
--- a/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs
@@ -13,7 +13,7 @@ use rustc_type_ir::{self as ty, Interner};
 use crate::delegate::SolverDelegate;
 use crate::solve::eval_ctxt::canonical;
 use crate::solve::{
-    CanonicalInput, Certainty, GenerateProofTree, Goal, GoalEvaluationKind, GoalSource, QueryInput,
+    CanonicalInput, Certainty, GenerateProofTree, Goal, GoalEvaluationKind, GoalSource,
     QueryResult, inspect,
 };
 
@@ -119,6 +119,9 @@ impl<I: Interner> WipCanonicalGoalEvaluation<I> {
     }
 }
 
+/// This only exists during proof tree building and does not have
+/// a corresponding struct in `inspect`. We need this to track a
+/// bunch of metadata about the current evaluation.
 #[derive_where(PartialEq, Eq, Debug; I: Interner)]
 struct WipCanonicalGoalEvaluationStep<I: Interner> {
     /// Unlike `EvalCtxt::var_values`, we append a new
@@ -128,7 +131,6 @@ struct WipCanonicalGoalEvaluationStep<I: Interner> {
     /// This is necessary as we otherwise don't unify these
     /// vars when instantiating multiple `CanonicalState`.
     var_values: Vec<I::GenericArg>,
-    instantiated_goal: QueryInput<I, I::Predicate>,
     probe_depth: usize,
     evaluation: WipProbe<I>,
 }
@@ -145,16 +147,12 @@ impl<I: Interner> WipCanonicalGoalEvaluationStep<I> {
         current
     }
 
-    fn finalize(self) -> inspect::CanonicalGoalEvaluationStep<I> {
+    fn finalize(self) -> inspect::Probe<I> {
         let evaluation = self.evaluation.finalize();
         match evaluation.kind {
-            inspect::ProbeKind::Root { .. } => (),
+            inspect::ProbeKind::Root { .. } => evaluation,
             _ => unreachable!("unexpected root evaluation: {evaluation:?}"),
         }
-        inspect::CanonicalGoalEvaluationStep {
-            instantiated_goal: self.instantiated_goal,
-            evaluation,
-        }
     }
 }
 
@@ -328,11 +326,9 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> {
     pub(crate) fn new_goal_evaluation_step(
         &mut self,
         var_values: ty::CanonicalVarValues<I>,
-        instantiated_goal: QueryInput<I, I::Predicate>,
     ) -> ProofTreeBuilder<D> {
         self.nested(|| WipCanonicalGoalEvaluationStep {
             var_values: var_values.var_values.to_vec(),
-            instantiated_goal,
             evaluation: WipProbe {
                 initial_num_var_values: var_values.len(),
                 steps: vec![],
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index 899a7b84100..3f98236595b 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -245,6 +245,19 @@ passes_doc_test_unknown_include =
     unknown `doc` attribute `{$path}`
     .suggestion = use `doc = include_str!` instead
 
+passes_doc_test_unknown_passes =
+    unknown `doc` attribute `{$path}`
+    .note = `doc` attribute `{$path}` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136>
+    .label = no longer functions
+    .help = you may want to use `doc(document_private_items)`
+    .no_op_note = `doc({$path})` is now a no-op
+
+passes_doc_test_unknown_plugins =
+    unknown `doc` attribute `{$path}`
+    .note = `doc` attribute `{$path}` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136> and CVE-2018-1000622 <https://nvd.nist.gov/vuln/detail/CVE-2018-1000622>
+    .label = no longer functions
+    .no_op_note = `doc({$path})` is now a no-op
+
 passes_doc_test_unknown_spotlight =
     unknown `doc` attribute `{$path}`
     .note = `doc(spotlight)` was renamed to `doc(notable_trait)`
diff --git a/compiler/rustc_passes/src/abi_test.rs b/compiler/rustc_passes/src/abi_test.rs
index d0cc123c41a..b1267562f7b 100644
--- a/compiler/rustc_passes/src/abi_test.rs
+++ b/compiler/rustc_passes/src/abi_test.rs
@@ -12,7 +12,7 @@ use super::layout_test::ensure_wf;
 use crate::errors::{AbiInvalidAttribute, AbiNe, AbiOf, UnrecognizedField};
 
 pub fn test_abi(tcx: TyCtxt<'_>) {
-    if !tcx.features().rustc_attrs {
+    if !tcx.features().rustc_attrs() {
         // if the `rustc_attrs` feature is not enabled, don't bother testing ABI
         return;
     }
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 8b30546d5cc..86442844823 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -1183,19 +1183,11 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
 
                         sym::masked => self.check_doc_masked(attr, meta, hir_id, target),
 
-                        // no_default_passes: deprecated
-                        // passes: deprecated
-                        // plugins: removed, but rustdoc warns about it itself
-                        sym::cfg
-                        | sym::hidden
-                        | sym::no_default_passes
-                        | sym::notable_trait
-                        | sym::passes
-                        | sym::plugins => {}
+                        sym::cfg | sym::hidden | sym::notable_trait => {}
 
                         sym::rust_logo => {
                             if self.check_attr_crate_level(attr, meta, hir_id)
-                                && !self.tcx.features().rustdoc_internals
+                                && !self.tcx.features().rustdoc_internals()
                             {
                                 feature_err(
                                     &self.tcx.sess,
@@ -1240,6 +1232,22 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                                         sugg: (attr.meta().unwrap().span, applicability),
                                     },
                                 );
+                            } else if i_meta.has_name(sym::passes)
+                                || i_meta.has_name(sym::no_default_passes)
+                            {
+                                self.tcx.emit_node_span_lint(
+                                    INVALID_DOC_ATTRIBUTES,
+                                    hir_id,
+                                    i_meta.span,
+                                    errors::DocTestUnknownPasses { path, span: i_meta.span },
+                                );
+                            } else if i_meta.has_name(sym::plugins) {
+                                self.tcx.emit_node_span_lint(
+                                    INVALID_DOC_ATTRIBUTES,
+                                    hir_id,
+                                    i_meta.span,
+                                    errors::DocTestUnknownPlugins { path, span: i_meta.span },
+                                );
                             } else {
                                 self.tcx.emit_node_span_lint(
                                     INVALID_DOC_ATTRIBUTES,
@@ -1766,7 +1774,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 }
                 sym::align => {
                     if let (Target::Fn | Target::Method(MethodKind::Inherent), false) =
-                        (target, self.tcx.features().fn_align)
+                        (target, self.tcx.features().fn_align())
                     {
                         feature_err(
                             &self.tcx.sess,
diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs
index ca13ca11673..f5ece513956 100644
--- a/compiler/rustc_passes/src/check_const.rs
+++ b/compiler/rustc_passes/src/check_const.rs
@@ -105,7 +105,7 @@ impl<'tcx> CheckConstVisitor<'tcx> {
 
             // If this crate is not using stability attributes, or this function is not claiming to be a
             // stable `const fn`, that is all that is required.
-            if !tcx.features().staged_api || tcx.has_attr(def_id, sym::rustc_const_unstable) {
+            if !tcx.features().staged_api() || tcx.has_attr(def_id, sym::rustc_const_unstable) {
                 return true;
             }
 
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index f01ddbd47ef..042e50d890e 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -324,6 +324,27 @@ pub(crate) struct DocTestUnknownSpotlight {
 }
 
 #[derive(LintDiagnostic)]
+#[diag(passes_doc_test_unknown_passes)]
+#[note]
+#[help]
+#[note(passes_no_op_note)]
+pub(crate) struct DocTestUnknownPasses {
+    pub path: String,
+    #[label]
+    pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(passes_doc_test_unknown_plugins)]
+#[note]
+#[note(passes_no_op_note)]
+pub(crate) struct DocTestUnknownPlugins {
+    pub path: String,
+    #[label]
+    pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
 #[diag(passes_doc_test_unknown_include)]
 pub(crate) struct DocTestUnknownInclude {
     pub path: String,
diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs
index 3aef88e771f..93729a7f6df 100644
--- a/compiler/rustc_passes/src/layout_test.rs
+++ b/compiler/rustc_passes/src/layout_test.rs
@@ -18,7 +18,7 @@ use crate::errors::{
 };
 
 pub fn test_layout(tcx: TyCtxt<'_>) {
-    if !tcx.features().rustc_attrs {
+    if !tcx.features().rustc_attrs() {
         // if the `rustc_attrs` feature is not enabled, don't bother testing layout
         return;
     }
diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs
index 91ba2fa7548..8a360c017ad 100644
--- a/compiler/rustc_passes/src/lib_features.rs
+++ b/compiler/rustc_passes/src/lib_features.rs
@@ -144,7 +144,7 @@ impl<'tcx> Visitor<'tcx> for LibFeatureCollector<'tcx> {
 fn lib_features(tcx: TyCtxt<'_>, LocalCrate: LocalCrate) -> LibFeatures {
     // If `staged_api` is not enabled then we aren't allowed to define lib
     // features; there is no point collecting them.
-    if !tcx.features().staged_api {
+    if !tcx.features().staged_api() {
         return LibFeatures::default();
     }
 
diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs
index 056318fbcb7..0ec151ceb45 100644
--- a/compiler/rustc_passes/src/reachable.rs
+++ b/compiler/rustc_passes/src/reachable.rs
@@ -322,7 +322,7 @@ impl<'tcx> ReachableContext<'tcx> {
                     self.visit(ty);
                     // Manually visit to actually see the trait's `DefId`. Type visitors won't see it
                     if let Some(trait_ref) = dyn_ty.principal() {
-                        let ExistentialTraitRef { def_id, args } = trait_ref.skip_binder();
+                        let ExistentialTraitRef { def_id, args, .. } = trait_ref.skip_binder();
                         self.visit_def_id(def_id, "", &"");
                         self.visit(args);
                     }
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 80ac2741c38..a176b2bb1ad 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -10,7 +10,7 @@ use rustc_attr::{
 };
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet};
-use rustc_feature::ACCEPTED_FEATURES;
+use rustc_feature::ACCEPTED_LANG_FEATURES;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalModDefId};
@@ -144,7 +144,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
             }
         }
 
-        if !self.tcx.features().staged_api {
+        if !self.tcx.features().staged_api() {
             // Propagate unstability. This can happen even for non-staged-api crates in case
             // -Zforce-unstable-if-unmarked is set.
             if let Some(stab) = self.parent_stab {
@@ -248,7 +248,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
             }
 
             if let Stability { level: Unstable { .. }, feature } = stab {
-                if ACCEPTED_FEATURES.iter().find(|f| f.name == feature).is_some() {
+                if ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature).is_some() {
                     self.tcx
                         .dcx()
                         .emit_err(errors::UnstableAttrForAlreadyStableFeature { span, item_sp });
@@ -261,7 +261,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
             }
 
             if let Some(ConstStability { level: Unstable { .. }, feature, .. }) = const_stab {
-                if ACCEPTED_FEATURES.iter().find(|f| f.name == feature).is_some() {
+                if ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature).is_some() {
                     self.tcx.dcx().emit_err(errors::UnstableAttrForAlreadyStableFeature {
                         span: const_span.unwrap(), // If const_stab contains Some(..), same is true for const_span
                         item_sp,
@@ -541,7 +541,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> {
     }
 
     fn check_missing_const_stability(&self, def_id: LocalDefId, span: Span) {
-        if !self.tcx.features().staged_api {
+        if !self.tcx.features().staged_api() {
             return;
         }
 
@@ -734,7 +734,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
             // items.
             hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref t), self_ty, items, .. }) => {
                 let features = self.tcx.features();
-                if features.staged_api {
+                if features.staged_api() {
                     let attrs = self.tcx.hir().attrs(item.hir_id());
                     let stab = attr::find_stability(self.tcx.sess, attrs, item.span);
                     let const_stab = attr::find_const_stability(self.tcx.sess, attrs, item.span);
@@ -762,7 +762,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
 
                     // `#![feature(const_trait_impl)]` is unstable, so any impl declared stable
                     // needs to have an error emitted.
-                    if features.const_trait_impl
+                    if features.const_trait_impl()
                         && self.tcx.is_const_trait_impl_raw(item.owner_id.to_def_id())
                         && const_stab.is_some_and(|(stab, _)| stab.is_const_stable())
                     {
@@ -926,7 +926,7 @@ impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> {
 /// libraries, identify activated features that don't exist and error about them.
 pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
     let is_staged_api =
-        tcx.sess.opts.unstable_opts.force_unstable_if_unmarked || tcx.features().staged_api;
+        tcx.sess.opts.unstable_opts.force_unstable_if_unmarked || tcx.features().staged_api();
     if is_staged_api {
         let effective_visibilities = &tcx.effective_visibilities(());
         let mut missing = MissingStabilityAnnotations { tcx, effective_visibilities };
diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs
index 70a9319d666..0e132b27fb4 100644
--- a/compiler/rustc_pattern_analysis/src/rustc.rs
+++ b/compiler/rustc_pattern_analysis/src/rustc.rs
@@ -891,7 +891,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
                 print::write_slice_like(&mut s, &prefix, has_dot_dot, &suffix).unwrap();
                 s
             }
-            Never if self.tcx.features().never_patterns => "!".to_string(),
+            Never if self.tcx.features().never_patterns() => "!".to_string(),
             Never | Wildcard | NonExhaustive | Hidden | PrivateUninhabited => "_".to_string(),
             Missing { .. } => bug!(
                 "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
@@ -915,7 +915,7 @@ fn would_print_as_wildcard(tcx: TyCtxt<'_>, p: &WitnessPat<'_, '_>) -> bool {
         | Constructor::NonExhaustive
         | Constructor::Hidden
         | Constructor::PrivateUninhabited => true,
-        Constructor::Never if !tcx.features().never_patterns => true,
+        Constructor::Never if !tcx.features().never_patterns() => true,
         _ => false,
     }
 }
@@ -929,7 +929,7 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
     type PatData = &'p Pat<'tcx>;
 
     fn is_exhaustive_patterns_feature_on(&self) -> bool {
-        self.tcx.features().exhaustive_patterns
+        self.tcx.features().exhaustive_patterns()
     }
 
     fn ctor_arity(&self, ctor: &crate::constructor::Constructor<Self>, ty: &Self::Ty) -> usize {
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index f67c4cb922c..7827975d3ca 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -32,8 +32,8 @@ use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility,
 use rustc_middle::query::Providers;
 use rustc_middle::ty::print::PrintTraitRefExt as _;
 use rustc_middle::ty::{
-    self, Const, GenericArgs, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable,
-    TypeVisitable, TypeVisitor,
+    self, Const, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
+    TypeVisitor,
 };
 use rustc_middle::{bug, span_bug};
 use rustc_session::lint;
@@ -246,10 +246,10 @@ where
                         ty::ExistentialPredicate::Trait(trait_ref) => trait_ref,
                         ty::ExistentialPredicate::Projection(proj) => proj.trait_ref(tcx),
                         ty::ExistentialPredicate::AutoTrait(def_id) => {
-                            ty::ExistentialTraitRef { def_id, args: GenericArgs::empty() }
+                            ty::ExistentialTraitRef::new(tcx, def_id, ty::GenericArgs::empty())
                         }
                     };
-                    let ty::ExistentialTraitRef { def_id, args: _ } = trait_ref;
+                    let ty::ExistentialTraitRef { def_id, .. } = trait_ref;
                     try_visit!(self.def_id_visitor.visit_def_id(def_id, "trait", &trait_ref));
                 }
             }
diff --git a/compiler/rustc_query_system/src/ich/impls_syntax.rs b/compiler/rustc_query_system/src/ich/impls_syntax.rs
index 78c37688d34..29a28b10a06 100644
--- a/compiler/rustc_query_system/src/ich/impls_syntax.rs
+++ b/compiler/rustc_query_system/src/ich/impls_syntax.rs
@@ -115,9 +115,9 @@ impl<'tcx> HashStable<StableHashingContext<'tcx>> for rustc_feature::Features {
         self.enabled_lang_features().hash_stable(hcx, hasher);
         self.enabled_lib_features().hash_stable(hcx, hasher);
 
-        self.all_lang_features()[..].hash_stable(hcx, hasher);
-        for feature in rustc_feature::UNSTABLE_FEATURES.iter() {
-            feature.feature.name.hash_stable(hcx, hasher);
+        // FIXME: why do we hash something that is a compile-time constant?
+        for feature in rustc_feature::UNSTABLE_LANG_FEATURES.iter() {
+            feature.name.hash_stable(hcx, hasher);
         }
     }
 }
diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs
index 8ae77902bce..b80e0e196ca 100644
--- a/compiler/rustc_resolve/src/ident.rs
+++ b/compiler/rustc_resolve/src/ident.rs
@@ -606,7 +606,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                     Scope::BuiltinTypes => match this.builtin_types_bindings.get(&ident.name) {
                         Some(binding) => {
                             if matches!(ident.name, sym::f16)
-                                && !this.tcx.features().f16
+                                && !this.tcx.features().f16()
                                 && !ident.span.allows_unstable(sym::f16)
                                 && finalize.is_some()
                                 && innermost_result.is_none()
@@ -620,7 +620,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                                 .emit();
                             }
                             if matches!(ident.name, sym::f128)
-                                && !this.tcx.features().f128
+                                && !this.tcx.features().f128()
                                 && !ident.span.allows_unstable(sym::f128)
                                 && finalize.is_some()
                                 && innermost_result.is_none()
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 98db36b12be..033cd7d5870 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -2683,7 +2683,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 self.with_generic_param_rib(
                     &generics.params,
                     RibKind::Item(
-                        if self.r.tcx.features().generic_const_items {
+                        if self.r.tcx.features().generic_const_items() {
                             HasGenericParams::Yes(generics.span)
                         } else {
                             HasGenericParams::No
@@ -2888,7 +2888,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                     RibKind::Normal => {
                         // FIXME(non_lifetime_binders): Stop special-casing
                         // const params to error out here.
-                        if self.r.tcx.features().non_lifetime_binders
+                        if self.r.tcx.features().non_lifetime_binders()
                             && matches!(param.kind, GenericParamKind::Type { .. })
                         {
                             Res::Def(def_kind, def_id.to_def_id())
@@ -4411,10 +4411,10 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                 let tcx = self.r.tcx();
 
                 let gate_err_sym_msg = match prim {
-                    PrimTy::Float(FloatTy::F16) if !tcx.features().f16 => {
+                    PrimTy::Float(FloatTy::F16) if !tcx.features().f16() => {
                         Some((sym::f16, "the type `f16` is unstable"))
                     }
-                    PrimTy::Float(FloatTy::F128) if !tcx.features().f128 => {
+                    PrimTy::Float(FloatTy::F128) if !tcx.features().f128() => {
                         Some((sym::f128, "the type `f128` is unstable"))
                     }
                     _ => None,
@@ -4565,7 +4565,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
             }
             AnonConstKind::InlineConst => ConstantHasGenerics::Yes,
             AnonConstKind::ConstArg(_) => {
-                if self.r.tcx.features().generic_const_exprs || is_trivial_const_arg {
+                if self.r.tcx.features().generic_const_exprs() || is_trivial_const_arg {
                     ConstantHasGenerics::Yes
                 } else {
                     ConstantHasGenerics::No(NoConstantGenericsReason::NonTrivialConstArg)
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 50fbdcaf9dc..f0632a21091 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -1198,7 +1198,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
             // const generics. Of course, `Struct` and `Enum` may contain ty params, too, but the
             // benefits of including them here outweighs the small number of false positives.
             Some(Res::Def(DefKind::Struct | DefKind::Enum, _))
-                if self.r.tcx.features().adt_const_params =>
+                if self.r.tcx.features().adt_const_params() =>
             {
                 Applicability::MaybeIncorrect
             }
@@ -2773,7 +2773,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                     // Avoid suggesting placing lifetime parameters on constant items unless the relevant
                     // feature is enabled. Suggest the parent item as a possible location if applicable.
                     if let LifetimeBinderKind::ConstItem = kind
-                        && !self.r.tcx().features().generic_const_items
+                        && !self.r.tcx().features().generic_const_items()
                     {
                         continue;
                     }
@@ -2934,7 +2934,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                     .emit();
             }
             NoConstantGenericsReason::NonTrivialConstArg => {
-                assert!(!self.r.tcx.features().generic_const_exprs);
+                assert!(!self.r.tcx.features().generic_const_exprs());
                 self.r
                     .dcx()
                     .create_err(errors::ParamInNonTrivialAnonConst {
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 5e38fb5b6c5..a9ebffea8a1 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -661,7 +661,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         }
 
         // We are trying to avoid reporting this error if other related errors were reported.
-        if res != Res::Err && inner_attr && !self.tcx.features().custom_inner_attributes {
+        if res != Res::Err && inner_attr && !self.tcx.features().custom_inner_attributes() {
             let is_macro = match res {
                 Res::Def(..) => true,
                 Res::NonMacroAttr(..) => false,
@@ -690,7 +690,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             && namespace.ident.name == sym::diagnostic
             && !(attribute.ident.name == sym::on_unimplemented
                 || (attribute.ident.name == sym::do_not_recommend
-                    && self.tcx.features().do_not_recommend))
+                    && self.tcx.features().do_not_recommend()))
         {
             let distance =
                 edit_distance(attribute.ident.name.as_str(), sym::on_unimplemented.as_str(), 5);
diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
index 83dcceeaa84..cba79a02f8b 100644
--- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
+++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
@@ -245,11 +245,15 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc
                             alias_ty.to_ty(tcx),
                         );
                         debug!("Resolved {:?} -> {resolved}", alias_ty.to_ty(tcx));
-                        ty::ExistentialPredicate::Projection(ty::ExistentialProjection {
-                            def_id: assoc_ty.def_id,
-                            args: ty::ExistentialTraitRef::erase_self_ty(tcx, super_trait_ref).args,
-                            term: resolved.into(),
-                        })
+                        ty::ExistentialPredicate::Projection(
+                            ty::ExistentialProjection::erase_self_ty(
+                                tcx,
+                                ty::ProjectionPredicate {
+                                    projection_term: alias_ty.into(),
+                                    term: resolved.into(),
+                                },
+                            ),
+                        )
                     })
                 })
         })
@@ -318,10 +322,11 @@ pub(crate) fn transform_instance<'tcx>(
             .lang_items()
             .drop_trait()
             .unwrap_or_else(|| bug!("typeid_for_instance: couldn't get drop_trait lang item"));
-        let predicate = ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef {
+        let predicate = ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::new_from_args(
+            tcx,
             def_id,
-            args: List::empty(),
-        });
+            ty::List::empty(),
+        ));
         let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]);
         let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased, ty::Dyn);
         instance.args = tcx.mk_args_trait(self_ty, List::empty());
diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs
index e9c3b3ffc1d..7be7db1fd3d 100644
--- a/compiler/rustc_smir/src/rustc_internal/internal.rs
+++ b/compiler/rustc_smir/src/rustc_internal/internal.rs
@@ -380,11 +380,12 @@ impl RustcInternal for ExistentialProjection {
     type T<'tcx> = rustc_ty::ExistentialProjection<'tcx>;
 
     fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
-        rustc_ty::ExistentialProjection {
-            def_id: self.def_id.0.internal(tables, tcx),
-            args: self.generic_args.internal(tables, tcx),
-            term: self.term.internal(tables, tcx),
-        }
+        rustc_ty::ExistentialProjection::new_from_args(
+            tcx,
+            self.def_id.0.internal(tables, tcx),
+            self.generic_args.internal(tables, tcx),
+            self.term.internal(tables, tcx),
+        )
     }
 }
 
@@ -403,10 +404,11 @@ impl RustcInternal for ExistentialTraitRef {
     type T<'tcx> = rustc_ty::ExistentialTraitRef<'tcx>;
 
     fn internal<'tcx>(&self, tables: &mut Tables<'_>, tcx: TyCtxt<'tcx>) -> Self::T<'tcx> {
-        rustc_ty::ExistentialTraitRef {
-            def_id: self.def_id.0.internal(tables, tcx),
-            args: self.generic_args.internal(tables, tcx),
-        }
+        rustc_ty::ExistentialTraitRef::new_from_args(
+            tcx,
+            self.def_id.0.internal(tables, tcx),
+            self.generic_args.internal(tables, tcx),
+        )
     }
 }
 
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
index b9372283feb..ef2ee9a166a 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
@@ -68,7 +68,7 @@ impl<'tcx> Stable<'tcx> for ty::ExistentialTraitRef<'tcx> {
     type T = stable_mir::ty::ExistentialTraitRef;
 
     fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        let ty::ExistentialTraitRef { def_id, args } = self;
+        let ty::ExistentialTraitRef { def_id, args, .. } = self;
         stable_mir::ty::ExistentialTraitRef {
             def_id: tables.trait_def(*def_id),
             generic_args: args.stable(tables),
@@ -95,7 +95,7 @@ impl<'tcx> Stable<'tcx> for ty::ExistentialProjection<'tcx> {
     type T = stable_mir::ty::ExistentialProjection;
 
     fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
-        let ty::ExistentialProjection { def_id, args, term } = self;
+        let ty::ExistentialProjection { def_id, args, term, .. } = self;
         stable_mir::ty::ExistentialProjection {
             def_id: tables.trait_def(*def_id),
             generic_args: args.stable(tables),
diff --git a/compiler/rustc_symbol_mangling/src/test.rs b/compiler/rustc_symbol_mangling/src/test.rs
index 8cfb65c1c50..73bd8d7555b 100644
--- a/compiler/rustc_symbol_mangling/src/test.rs
+++ b/compiler/rustc_symbol_mangling/src/test.rs
@@ -18,7 +18,7 @@ pub fn report_symbol_names(tcx: TyCtxt<'_>) {
     // if the `rustc_attrs` feature is not enabled, then the
     // attributes we are interested in cannot be present anyway, so
     // skip the walk.
-    if !tcx.features().rustc_attrs {
+    if !tcx.features().rustc_attrs() {
         return;
     }
 
diff --git a/compiler/rustc_target/src/callconv/loongarch.rs b/compiler/rustc_target/src/callconv/loongarch.rs
index 4a21935623b..ffec76370d0 100644
--- a/compiler/rustc_target/src/callconv/loongarch.rs
+++ b/compiler/rustc_target/src/callconv/loongarch.rs
@@ -1,6 +1,7 @@
 use crate::abi::call::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform};
 use crate::abi::{self, Abi, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout};
 use crate::spec::HasTargetSpec;
+use crate::spec::abi::Abi as SpecAbi;
 
 #[derive(Copy, Clone)]
 enum RegPassKind {
@@ -359,3 +360,30 @@ where
         );
     }
 }
+
+pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, abi: SpecAbi)
+where
+    Ty: TyAbiInterface<'a, C> + Copy,
+    C: HasDataLayout + HasTargetSpec,
+{
+    if abi == SpecAbi::RustIntrinsic {
+        return;
+    }
+
+    let grlen = cx.data_layout().pointer_size.bits();
+
+    for arg in fn_abi.args.iter_mut() {
+        if arg.is_ignore() {
+            continue;
+        }
+
+        // LLVM integers types do not differentiate between signed or unsigned integers.
+        // Some LoongArch instructions do not have a `.w` suffix version, they use all the
+        // GRLEN bits. By explicitly setting the `signext` or `zeroext` attribute
+        // according to signedness to avoid unnecessary integer extending instructions.
+        //
+        // This is similar to the RISC-V case, see
+        // https://github.com/rust-lang/rust/issues/114508 for details.
+        extend_integer_width(arg, grlen);
+    }
+}
diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs
index 5d120a68059..25b001b57e8 100644
--- a/compiler/rustc_target/src/callconv/mod.rs
+++ b/compiler/rustc_target/src/callconv/mod.rs
@@ -1,11 +1,14 @@
-use std::fmt;
 use std::str::FromStr;
+use std::{fmt, iter};
 
 pub use rustc_abi::{Reg, RegKind};
 use rustc_macros::HashStable_Generic;
 use rustc_span::Symbol;
 
-use crate::abi::{self, Abi, Align, HasDataLayout, Size, TyAbiInterface, TyAndLayout};
+use crate::abi::{
+    self, Abi, AddressSpace, Align, HasDataLayout, Pointer, Size, TyAbiInterface, TyAndLayout,
+};
+use crate::spec::abi::Abi as SpecAbi;
 use crate::spec::{self, HasTargetSpec, HasWasmCAbiOpt, HasX86AbiOpt, WasmCAbi};
 
 mod aarch64;
@@ -720,6 +723,118 @@ impl<'a, Ty> FnAbi<'a, Ty> {
 
         Ok(())
     }
+
+    pub fn adjust_for_rust_abi<C>(&mut self, cx: &C, abi: SpecAbi)
+    where
+        Ty: TyAbiInterface<'a, C> + Copy,
+        C: HasDataLayout + HasTargetSpec,
+    {
+        let spec = cx.target_spec();
+        match &spec.arch[..] {
+            "x86" => x86::compute_rust_abi_info(cx, self, abi),
+            "riscv32" | "riscv64" => riscv::compute_rust_abi_info(cx, self, abi),
+            "loongarch64" => loongarch::compute_rust_abi_info(cx, self, abi),
+            _ => {}
+        };
+
+        for (arg_idx, arg) in self
+            .args
+            .iter_mut()
+            .enumerate()
+            .map(|(idx, arg)| (Some(idx), arg))
+            .chain(iter::once((None, &mut self.ret)))
+        {
+            if arg.is_ignore() {
+                continue;
+            }
+
+            if arg_idx.is_none() && arg.layout.size > Pointer(AddressSpace::DATA).size(cx) * 2 {
+                // Return values larger than 2 registers using a return area
+                // pointer. LLVM and Cranelift disagree about how to return
+                // values that don't fit in the registers designated for return
+                // values. LLVM will force the entire return value to be passed
+                // by return area pointer, while Cranelift will look at each IR level
+                // return value independently and decide to pass it in a
+                // register or not, which would result in the return value
+                // being passed partially in registers and partially through a
+                // return area pointer.
+                //
+                // While Cranelift may need to be fixed as the LLVM behavior is
+                // generally more correct with respect to the surface language,
+                // forcing this behavior in rustc itself makes it easier for
+                // other backends to conform to the Rust ABI and for the C ABI
+                // rustc already handles this behavior anyway.
+                //
+                // In addition LLVM's decision to pass the return value in
+                // registers or using a return area pointer depends on how
+                // exactly the return type is lowered to an LLVM IR type. For
+                // example `Option<u128>` can be lowered as `{ i128, i128 }`
+                // in which case the x86_64 backend would use a return area
+                // pointer, or it could be passed as `{ i32, i128 }` in which
+                // case the x86_64 backend would pass it in registers by taking
+                // advantage of an LLVM ABI extension that allows using 3
+                // registers for the x86_64 sysv call conv rather than the
+                // officially specified 2 registers.
+                //
+                // FIXME: Technically we should look at the amount of available
+                // return registers rather than guessing that there are 2
+                // registers for return values. In practice only a couple of
+                // architectures have less than 2 return registers. None of
+                // which supported by Cranelift.
+                //
+                // NOTE: This adjustment is only necessary for the Rust ABI as
+                // for other ABI's the calling convention implementations in
+                // rustc_target already ensure any return value which doesn't
+                // fit in the available amount of return registers is passed in
+                // the right way for the current target.
+                arg.make_indirect();
+                continue;
+            }
+
+            match arg.layout.abi {
+                Abi::Aggregate { .. } => {}
+
+                // This is a fun case! The gist of what this is doing is
+                // that we want callers and callees to always agree on the
+                // ABI of how they pass SIMD arguments. If we were to *not*
+                // make these arguments indirect then they'd be immediates
+                // in LLVM, which means that they'd used whatever the
+                // appropriate ABI is for the callee and the caller. That
+                // means, for example, if the caller doesn't have AVX
+                // enabled but the callee does, then passing an AVX argument
+                // across this boundary would cause corrupt data to show up.
+                //
+                // This problem is fixed by unconditionally passing SIMD
+                // arguments through memory between callers and callees
+                // which should get them all to agree on ABI regardless of
+                // target feature sets. Some more information about this
+                // issue can be found in #44367.
+                //
+                // Note that the intrinsic ABI is exempt here as
+                // that's how we connect up to LLVM and it's unstable
+                // anyway, we control all calls to it in libstd.
+                Abi::Vector { .. } if abi != SpecAbi::RustIntrinsic && spec.simd_types_indirect => {
+                    arg.make_indirect();
+                    continue;
+                }
+
+                _ => continue,
+            }
+            // Compute `Aggregate` ABI.
+
+            let is_indirect_not_on_stack =
+                matches!(arg.mode, PassMode::Indirect { on_stack: false, .. });
+            assert!(is_indirect_not_on_stack);
+
+            let size = arg.layout.size;
+            if !arg.layout.is_unsized() && size <= Pointer(AddressSpace::DATA).size(cx) {
+                // We want to pass small aggregates as immediates, but using
+                // an LLVM aggregate type for this leads to bad optimizations,
+                // so we pick an appropriately sized integer type instead.
+                arg.cast_to(Reg { kind: RegKind::Integer, size });
+            }
+        }
+    }
 }
 
 impl FromStr for Conv {
diff --git a/compiler/rustc_target/src/callconv/riscv.rs b/compiler/rustc_target/src/callconv/riscv.rs
index be6bc701b49..f96169e6a61 100644
--- a/compiler/rustc_target/src/callconv/riscv.rs
+++ b/compiler/rustc_target/src/callconv/riscv.rs
@@ -7,6 +7,7 @@
 use crate::abi::call::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Reg, RegKind, Uniform};
 use crate::abi::{self, Abi, FieldsShape, HasDataLayout, Size, TyAbiInterface, TyAndLayout};
 use crate::spec::HasTargetSpec;
+use crate::spec::abi::Abi as SpecAbi;
 
 #[derive(Copy, Clone)]
 enum RegPassKind {
@@ -365,3 +366,29 @@ where
         );
     }
 }
+
+pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, abi: SpecAbi)
+where
+    Ty: TyAbiInterface<'a, C> + Copy,
+    C: HasDataLayout + HasTargetSpec,
+{
+    if abi == SpecAbi::RustIntrinsic {
+        return;
+    }
+
+    let xlen = cx.data_layout().pointer_size.bits();
+
+    for arg in fn_abi.args.iter_mut() {
+        if arg.is_ignore() {
+            continue;
+        }
+
+        // LLVM integers types do not differentiate between signed or unsigned integers.
+        // Some RISC-V instructions do not have a `.w` suffix version, they use all the
+        // XLEN bits. By explicitly setting the `signext` or `zeroext` attribute
+        // according to signedness to avoid unnecessary integer extending instructions.
+        //
+        // See https://github.com/rust-lang/rust/issues/114508 for details.
+        extend_integer_width(arg, xlen);
+    }
+}
diff --git a/compiler/rustc_target/src/callconv/x86.rs b/compiler/rustc_target/src/callconv/x86.rs
index 40c3e7a891a..e907beecb38 100644
--- a/compiler/rustc_target/src/callconv/x86.rs
+++ b/compiler/rustc_target/src/callconv/x86.rs
@@ -1,6 +1,9 @@
 use crate::abi::call::{ArgAttribute, FnAbi, PassMode, Reg, RegKind};
-use crate::abi::{Abi, Align, HasDataLayout, TyAbiInterface, TyAndLayout};
+use crate::abi::{
+    Abi, AddressSpace, Align, Float, HasDataLayout, Pointer, TyAbiInterface, TyAndLayout,
+};
 use crate::spec::HasTargetSpec;
+use crate::spec::abi::Abi as SpecAbi;
 
 #[derive(PartialEq)]
 pub(crate) enum Flavor {
@@ -207,3 +210,35 @@ pub(crate) fn fill_inregs<'a, Ty, C>(
         }
     }
 }
+
+pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, abi: SpecAbi)
+where
+    Ty: TyAbiInterface<'a, C> + Copy,
+    C: HasDataLayout + HasTargetSpec,
+{
+    // Avoid returning floats in x87 registers on x86 as loading and storing from x87
+    // registers will quiet signalling NaNs. Also avoid using SSE registers since they
+    // are not always available (depending on target features).
+    if !fn_abi.ret.is_ignore()
+        // Intrinsics themselves are not actual "real" functions, so theres no need to change their ABIs.
+        && abi != SpecAbi::RustIntrinsic
+    {
+        let has_float = match fn_abi.ret.layout.abi {
+            Abi::Scalar(s) => matches!(s.primitive(), Float(_)),
+            Abi::ScalarPair(s1, s2) => {
+                matches!(s1.primitive(), Float(_)) || matches!(s2.primitive(), Float(_))
+            }
+            _ => false, // anyway not passed via registers on x86
+        };
+        if has_float {
+            if fn_abi.ret.layout.size <= Pointer(AddressSpace::DATA).size(cx) {
+                // Same size or smaller than pointer, return in a register.
+                fn_abi.ret.cast_to(Reg { kind: RegKind::Integer, size: fn_abi.ret.layout.size });
+            } else {
+                // Larger than a pointer, return indirectly.
+                fn_abi.ret.make_indirect();
+            }
+            return;
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs
index 62204f63dd0..0cf7c43beb5 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs
@@ -894,7 +894,7 @@ fn foo(&self) -> Self::T { String::new() }
         // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting.
 
         let trait_bounds = bounds.iter().filter_map(|bound| match bound {
-            hir::GenericBound::Trait(ptr) if ptr.modifiers == hir::TraitBoundModifier::None => {
+            hir::GenericBound::Trait(ptr) if ptr.modifiers == hir::TraitBoundModifiers::NONE => {
                 Some(ptr)
             }
             _ => None,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index 549beafd196..9ee4963a360 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -3026,7 +3026,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         obligation: &PredicateObligation<'tcx>,
         span: Span,
     ) -> Result<Diag<'a>, ErrorGuaranteed> {
-        if !self.tcx.features().generic_const_exprs {
+        if !self.tcx.features().generic_const_exprs() {
             let guar = self
                 .dcx()
                 .struct_span_err(span, "constant expression depends on a generic parameter")
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index 677905a3085..1fe93cb017a 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -3044,7 +3044,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 if local {
                     err.note("all local variables must have a statically known size");
                 }
-                if !tcx.features().unsized_locals {
+                if !tcx.features().unsized_locals() {
                     err.help("unsized locals are gated as an unstable feature");
                 }
             }
@@ -3125,7 +3125,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     err.note("all function arguments must have a statically known size");
                 }
                 if tcx.sess.opts.unstable_features.is_nightly_build()
-                    && !tcx.features().unsized_fn_params
+                    && !tcx.features().unsized_fn_params()
                 {
                     err.help("unsized fn params are gated as an unstable feature");
                 }
@@ -3594,52 +3594,64 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         trait_pred: ty::PolyTraitPredicate<'tcx>,
         span: Span,
     ) {
-        if let Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) =
-            self.tcx.coroutine_kind(obligation.cause.body_id)
-        {
-            let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
+        let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
 
-            let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
-            let impls_future = self.type_implements_trait(
-                future_trait,
-                [self.tcx.instantiate_bound_regions_with_erased(self_ty)],
-                obligation.param_env,
-            );
-            if !impls_future.must_apply_modulo_regions() {
-                return;
-            }
+        let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
+        let impls_future = self.type_implements_trait(
+            future_trait,
+            [self.tcx.instantiate_bound_regions_with_erased(self_ty)],
+            obligation.param_env,
+        );
+        if !impls_future.must_apply_modulo_regions() {
+            return;
+        }
 
-            let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
-            // `<T as Future>::Output`
-            let projection_ty = trait_pred.map_bound(|trait_pred| {
-                Ty::new_projection(
-                    self.tcx,
-                    item_def_id,
-                    // Future::Output has no args
-                    [trait_pred.self_ty()],
-                )
-            });
-            let InferOk { value: projection_ty, .. } =
-                self.at(&obligation.cause, obligation.param_env).normalize(projection_ty);
+        let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
+        // `<T as Future>::Output`
+        let projection_ty = trait_pred.map_bound(|trait_pred| {
+            Ty::new_projection(
+                self.tcx,
+                item_def_id,
+                // Future::Output has no args
+                [trait_pred.self_ty()],
+            )
+        });
+        let InferOk { value: projection_ty, .. } =
+            self.at(&obligation.cause, obligation.param_env).normalize(projection_ty);
 
-            debug!(
-                normalized_projection_type = ?self.resolve_vars_if_possible(projection_ty)
-            );
-            let try_obligation = self.mk_trait_obligation_with_new_self_ty(
-                obligation.param_env,
-                trait_pred.map_bound(|trait_pred| (trait_pred, projection_ty.skip_binder())),
-            );
-            debug!(try_trait_obligation = ?try_obligation);
-            if self.predicate_may_hold(&try_obligation)
-                && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
-                && snippet.ends_with('?')
-            {
-                err.span_suggestion_verbose(
-                    span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(),
-                    "consider `await`ing on the `Future`",
-                    ".await",
-                    Applicability::MaybeIncorrect,
-                );
+        debug!(
+            normalized_projection_type = ?self.resolve_vars_if_possible(projection_ty)
+        );
+        let try_obligation = self.mk_trait_obligation_with_new_self_ty(
+            obligation.param_env,
+            trait_pred.map_bound(|trait_pred| (trait_pred, projection_ty.skip_binder())),
+        );
+        debug!(try_trait_obligation = ?try_obligation);
+        if self.predicate_may_hold(&try_obligation)
+            && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
+            && snippet.ends_with('?')
+        {
+            match self.tcx.coroutine_kind(obligation.cause.body_id) {
+                Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) => {
+                    err.span_suggestion_verbose(
+                        span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(),
+                        "consider `await`ing on the `Future`",
+                        ".await",
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+                _ => {
+                    let mut span: MultiSpan = span.with_lo(span.hi() - BytePos(1)).into();
+                    span.push_span_label(
+                        self.tcx.def_span(obligation.cause.body_id),
+                        "this is not `async`",
+                    );
+                    err.span_note(
+                        span,
+                        "this implements `Future` and its output type supports \
+                        `?`, but the future cannot be awaited in a synchronous function",
+                    );
+                }
             }
         }
     }
@@ -4498,7 +4510,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         trait_ref: ty::PolyTraitRef<'tcx>,
     ) {
         // Don't suggest if RTN is active -- we should prefer a where-clause bound instead.
-        if self.tcx.features().return_type_notation {
+        if self.tcx.features().return_type_notation() {
             return;
         }
 
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
index 4975a9ce0c7..e735020a63e 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
@@ -344,7 +344,7 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
         };
 
         let mut nested_goals = vec![];
-        self.candidates_recur(&mut candidates, &mut nested_goals, &last_eval_step.evaluation);
+        self.candidates_recur(&mut candidates, &mut nested_goals, &last_eval_step);
 
         candidates
     }
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index c258832bf2b..1be3c964454 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -40,7 +40,7 @@ pub fn is_const_evaluatable<'tcx>(
         ty::ConstKind::Infer(_) => return Err(NotConstEvaluatable::MentionsInfer),
     };
 
-    if tcx.features().generic_const_exprs {
+    if tcx.features().generic_const_exprs() {
         let ct = tcx.expand_abstract_consts(unexpanded_ct);
 
         let is_anon_ct = if let ty::ConstKind::Unevaluated(uv) = ct.kind() {
diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
index 364a13b3a75..cc0450e0b05 100644
--- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
+++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
@@ -254,7 +254,7 @@ fn super_predicates_have_non_lifetime_binders(
     trait_def_id: DefId,
 ) -> SmallVec<[Span; 1]> {
     // If non_lifetime_binders is disabled, then exit early
-    if !tcx.features().non_lifetime_binders {
+    if !tcx.features().non_lifetime_binders() {
         return SmallVec::new();
     }
     tcx.explicit_super_predicates_of(trait_def_id)
@@ -327,7 +327,7 @@ pub fn dyn_compatibility_violations_for_assoc_item(
             .collect(),
         // Associated types can only be dyn-compatible if they have `Self: Sized` bounds.
         ty::AssocKind::Type => {
-            if !tcx.features().generic_associated_types_extended
+            if !tcx.features().generic_associated_types_extended()
                 && !tcx.generics_of(item.def_id).is_own_empty()
                 && !item.is_impl_trait_in_trait()
             {
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index e56f1866970..2bdeb00bdac 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -598,7 +598,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
                 ty::PredicateKind::ConstEquate(c1, c2) => {
                     let tcx = self.selcx.tcx();
                     assert!(
-                        tcx.features().generic_const_exprs,
+                        tcx.features().generic_const_exprs(),
                         "`ConstEquate` without a feature gate: {c1:?} {c2:?}",
                     );
                     // FIXME: we probably should only try to unify abstract constants
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index cffb9b59841..cdf24887e76 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -346,7 +346,7 @@ pub fn normalize_param_env_or_error<'tcx>(
     let mut predicates: Vec<_> = util::elaborate(
         tcx,
         unnormalized_env.caller_bounds().into_iter().map(|predicate| {
-            if tcx.features().generic_const_exprs {
+            if tcx.features().generic_const_exprs() {
                 return predicate;
             }
 
diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs
index d246d37f748..12e00ec79ac 100644
--- a/compiler/rustc_trait_selection/src/traits/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/normalize.rs
@@ -402,7 +402,7 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
     #[instrument(skip(self), level = "debug")]
     fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> {
         let tcx = self.selcx.tcx();
-        if tcx.features().generic_const_exprs
+        if tcx.features().generic_const_exprs()
             || !needs_normalization(&constant, self.param_env.reveal())
         {
             constant
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index cc634b65a0b..38722c0ff7c 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -189,7 +189,7 @@ pub(super) fn poly_project_and_unify_term<'cx, 'tcx>(
             ProjectAndUnifyResult::MismatchedProjectionTypes(e) => Err(e),
             ProjectAndUnifyResult::Holds(obligations)
                 if old_universe != new_universe
-                    && selcx.tcx().features().generic_associated_types_extended =>
+                    && selcx.tcx().features().generic_associated_types_extended() =>
             {
                 // If the `generic_associated_types_extended` feature is active, then we ignore any
                 // obligations references lifetimes from any universe greater than or equal to the
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index ac38c9e24a2..eea3867190d 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -876,7 +876,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                         }
 
                         if let Some(principal) = data.principal() {
-                            if !self.infcx.tcx.features().dyn_compatible_for_dispatch {
+                            if !self.infcx.tcx.features().dyn_compatible_for_dispatch() {
                                 principal.with_self_ty(self.tcx(), self_ty)
                             } else if self.tcx().is_dyn_compatible(principal.def_id()) {
                                 principal.with_self_ty(self.tcx(), self_ty)
@@ -936,7 +936,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
 
         let tcx = self.tcx();
-        if tcx.features().trait_upcasting {
+        if tcx.features().trait_upcasting() {
             return None;
         }
 
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 9a49fd7e77e..e7d3004aa20 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -402,7 +402,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
 
         let mut assume = predicate.trait_ref.args.const_at(2);
         // FIXME(min_generic_const_exprs): We should shallowly normalize this.
-        if self.tcx().features().generic_const_exprs {
+        if self.tcx().features().generic_const_exprs() {
             assume = assume.normalize_internal(self.tcx(), obligation.param_env);
         }
         let Some(assume) =
@@ -626,7 +626,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         for assoc_type in assoc_types {
             let defs: &ty::Generics = tcx.generics_of(assoc_type);
 
-            if !defs.own_params.is_empty() && !tcx.features().generic_associated_types_extended {
+            if !defs.own_params.is_empty() && !tcx.features().generic_associated_types_extended() {
                 tcx.dcx().span_delayed_bug(
                     obligation.cause.span,
                     "GATs in trait object shouldn't have been considered",
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 4757430a21c..d5b1c5a97da 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -865,7 +865,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 ty::PredicateKind::ConstEquate(c1, c2) => {
                     let tcx = self.tcx();
                     assert!(
-                        tcx.features().generic_const_exprs,
+                        tcx.features().generic_const_exprs(),
                         "`ConstEquate` without a feature gate: {c1:?} {c2:?}",
                     );
 
@@ -2195,7 +2195,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 match self.tcx().coroutine_movability(coroutine_def_id) {
                     hir::Movability::Static => None,
                     hir::Movability::Movable => {
-                        if self.tcx().features().coroutine_clone {
+                        if self.tcx().features().coroutine_clone() {
                             let resolved_upvars =
                                 self.infcx.shallow_resolve(args.as_coroutine().tupled_upvars_ty());
                             let resolved_witness =
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index b82a3433645..0e45f7a195f 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -136,7 +136,7 @@ pub fn translate_args_with_cause<'tcx>(
 }
 
 pub(super) fn specialization_enabled_in(tcx: TyCtxt<'_>, _: LocalCrate) -> bool {
-    tcx.features().specialization || tcx.features().min_specialization
+    tcx.features().specialization() || tcx.features().min_specialization()
 }
 
 /// Is `impl1` a specialization of `impl2`?
diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
index f8d98eb856e..23b5f62b5ca 100644
--- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
@@ -82,7 +82,7 @@ impl<'tcx> At<'_, 'tcx> {
             }
 
             Ok(self.infcx.resolve_vars_if_possible(new_infer_ct))
-        } else if self.infcx.tcx.features().generic_const_exprs {
+        } else if self.infcx.tcx.features().generic_const_exprs() {
             Ok(ct.normalize_internal(self.infcx.tcx, self.param_env))
         } else {
             Ok(self.normalize(ct).into_value_registering_obligations(self.infcx, fulfill_cx))
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index 07e68e5a3e8..8904a9a6858 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -836,7 +836,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
                 // obligations that don't refer to Self and
                 // checking those
 
-                let defer_to_coercion = tcx.features().dyn_compatible_for_dispatch;
+                let defer_to_coercion = tcx.features().dyn_compatible_for_dispatch();
 
                 if !defer_to_coercion {
                     if let Some(principal) = data.principal_def_id() {
diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs
index 13691204c96..48149a08de8 100644
--- a/compiler/rustc_ty_utils/src/abi.rs
+++ b/compiler/rustc_ty_utils/src/abi.rs
@@ -1,8 +1,7 @@
 use std::iter;
 
-use rustc_abi::Float::*;
-use rustc_abi::Primitive::{Float, Pointer};
-use rustc_abi::{Abi, AddressSpace, PointerKind, Scalar, Size};
+use rustc_abi::Primitive::Pointer;
+use rustc_abi::{Abi, PointerKind, Scalar, Size};
 use rustc_hir as hir;
 use rustc_hir::lang_items::LangItem;
 use rustc_middle::bug;
@@ -14,8 +13,7 @@ use rustc_middle::ty::{self, InstanceKind, Ty, TyCtxt};
 use rustc_session::config::OptLevel;
 use rustc_span::def_id::DefId;
 use rustc_target::abi::call::{
-    ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, Conv, FnAbi, PassMode, Reg, RegKind,
-    RiscvInterruptKind,
+    ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, Conv, FnAbi, PassMode, RiscvInterruptKind,
 };
 use rustc_target::spec::abi::Abi as SpecAbi;
 use tracing::debug;
@@ -679,6 +677,8 @@ fn fn_abi_adjust_for_abi<'tcx>(
     let tcx = cx.tcx();
 
     if abi == SpecAbi::Rust || abi == SpecAbi::RustCall || abi == SpecAbi::RustIntrinsic {
+        fn_abi.adjust_for_rust_abi(cx, abi);
+
         // Look up the deduced parameter attributes for this function, if we have its def ID and
         // we're optimizing in non-incremental mode. We'll tag its parameters with those attributes
         // as appropriate.
@@ -689,131 +689,9 @@ fn fn_abi_adjust_for_abi<'tcx>(
                 &[]
             };
 
-        let fixup = |arg: &mut ArgAbi<'tcx, Ty<'tcx>>, arg_idx: Option<usize>| {
+        for (arg_idx, arg) in fn_abi.args.iter_mut().enumerate() {
             if arg.is_ignore() {
-                return;
-            }
-
-            // Avoid returning floats in x87 registers on x86 as loading and storing from x87
-            // registers will quiet signalling NaNs.
-            if tcx.sess.target.arch == "x86"
-                && arg_idx.is_none()
-                // Intrinsics themselves are not actual "real" functions, so theres no need to
-                // change their ABIs.
-                && abi != SpecAbi::RustIntrinsic
-            {
-                match arg.layout.abi {
-                    // Handle similar to the way arguments with an `Abi::Aggregate` abi are handled
-                    // below, by returning arguments up to the size of a pointer (32 bits on x86)
-                    // cast to an appropriately sized integer.
-                    Abi::Scalar(s) if s.primitive() == Float(F32) => {
-                        // Same size as a pointer, return in a register.
-                        arg.cast_to(Reg::i32());
-                        return;
-                    }
-                    Abi::Scalar(s) if s.primitive() == Float(F64) => {
-                        // Larger than a pointer, return indirectly.
-                        arg.make_indirect();
-                        return;
-                    }
-                    Abi::ScalarPair(s1, s2)
-                        if matches!(s1.primitive(), Float(F32 | F64))
-                            || matches!(s2.primitive(), Float(F32 | F64)) =>
-                    {
-                        // Larger than a pointer, return indirectly.
-                        arg.make_indirect();
-                        return;
-                    }
-                    _ => {}
-                };
-            }
-
-            if arg_idx.is_none() && arg.layout.size > Pointer(AddressSpace::DATA).size(cx) * 2 {
-                // Return values larger than 2 registers using a return area
-                // pointer. LLVM and Cranelift disagree about how to return
-                // values that don't fit in the registers designated for return
-                // values. LLVM will force the entire return value to be passed
-                // by return area pointer, while Cranelift will look at each IR level
-                // return value independently and decide to pass it in a
-                // register or not, which would result in the return value
-                // being passed partially in registers and partially through a
-                // return area pointer.
-                //
-                // While Cranelift may need to be fixed as the LLVM behavior is
-                // generally more correct with respect to the surface language,
-                // forcing this behavior in rustc itself makes it easier for
-                // other backends to conform to the Rust ABI and for the C ABI
-                // rustc already handles this behavior anyway.
-                //
-                // In addition LLVM's decision to pass the return value in
-                // registers or using a return area pointer depends on how
-                // exactly the return type is lowered to an LLVM IR type. For
-                // example `Option<u128>` can be lowered as `{ i128, i128 }`
-                // in which case the x86_64 backend would use a return area
-                // pointer, or it could be passed as `{ i32, i128 }` in which
-                // case the x86_64 backend would pass it in registers by taking
-                // advantage of an LLVM ABI extension that allows using 3
-                // registers for the x86_64 sysv call conv rather than the
-                // officially specified 2 registers.
-                //
-                // FIXME: Technically we should look at the amount of available
-                // return registers rather than guessing that there are 2
-                // registers for return values. In practice only a couple of
-                // architectures have less than 2 return registers. None of
-                // which supported by Cranelift.
-                //
-                // NOTE: This adjustment is only necessary for the Rust ABI as
-                // for other ABI's the calling convention implementations in
-                // rustc_target already ensure any return value which doesn't
-                // fit in the available amount of return registers is passed in
-                // the right way for the current target.
-                arg.make_indirect();
-                return;
-            }
-
-            match arg.layout.abi {
-                Abi::Aggregate { .. } => {}
-
-                // This is a fun case! The gist of what this is doing is
-                // that we want callers and callees to always agree on the
-                // ABI of how they pass SIMD arguments. If we were to *not*
-                // make these arguments indirect then they'd be immediates
-                // in LLVM, which means that they'd used whatever the
-                // appropriate ABI is for the callee and the caller. That
-                // means, for example, if the caller doesn't have AVX
-                // enabled but the callee does, then passing an AVX argument
-                // across this boundary would cause corrupt data to show up.
-                //
-                // This problem is fixed by unconditionally passing SIMD
-                // arguments through memory between callers and callees
-                // which should get them all to agree on ABI regardless of
-                // target feature sets. Some more information about this
-                // issue can be found in #44367.
-                //
-                // Note that the intrinsic ABI is exempt here as
-                // that's how we connect up to LLVM and it's unstable
-                // anyway, we control all calls to it in libstd.
-                Abi::Vector { .. }
-                    if abi != SpecAbi::RustIntrinsic && tcx.sess.target.simd_types_indirect =>
-                {
-                    arg.make_indirect();
-                    return;
-                }
-
-                _ => return,
-            }
-            // Compute `Aggregate` ABI.
-
-            let is_indirect_not_on_stack =
-                matches!(arg.mode, PassMode::Indirect { on_stack: false, .. });
-            assert!(is_indirect_not_on_stack, "{:?}", arg);
-
-            let size = arg.layout.size;
-            if !arg.layout.is_unsized() && size <= Pointer(AddressSpace::DATA).size(cx) {
-                // We want to pass small aggregates as immediates, but using
-                // an LLVM aggregate type for this leads to bad optimizations,
-                // so we pick an appropriately sized integer type instead.
-                arg.cast_to(Reg { kind: RegKind::Integer, size });
+                continue;
             }
 
             // If we deduced that this parameter was read-only, add that to the attribute list now.
@@ -821,9 +699,7 @@ fn fn_abi_adjust_for_abi<'tcx>(
             // The `readonly` parameter only applies to pointers, so we can only do this if the
             // argument was passed indirectly. (If the argument is passed directly, it's an SSA
             // value, so it's implicitly immutable.)
-            if let (Some(arg_idx), &mut PassMode::Indirect { ref mut attrs, .. }) =
-                (arg_idx, &mut arg.mode)
-            {
+            if let &mut PassMode::Indirect { ref mut attrs, .. } = &mut arg.mode {
                 // The `deduced_param_attrs` list could be empty if this is a type of function
                 // we can't deduce any parameters for, so make sure the argument index is in
                 // bounds.
@@ -834,11 +710,6 @@ fn fn_abi_adjust_for_abi<'tcx>(
                     }
                 }
             }
-        };
-
-        fixup(&mut fn_abi.ret, None);
-        for (arg_idx, arg) in fn_abi.args.iter_mut().enumerate() {
-            fixup(arg, Some(arg_idx));
         }
     } else {
         fn_abi
diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs
index a057caa9329..a3210dd80d7 100644
--- a/compiler/rustc_ty_utils/src/assoc.rs
+++ b/compiler/rustc_ty_utils/src/assoc.rs
@@ -181,7 +181,7 @@ fn associated_item_from_impl_item_ref(impl_item_ref: &hir::ImplItemRef) -> ty::A
 fn associated_type_for_effects(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<DefId> {
     // don't synthesize the associated type even if the user has written `const_trait`
     // if the effects feature is disabled.
-    if !tcx.features().effects {
+    if !tcx.features().effects() {
         return None;
     }
     let (feed, parent_did) = match tcx.def_kind(def_id) {
diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs
index 391985ce88a..4b770d9938c 100644
--- a/compiler/rustc_ty_utils/src/consts.rs
+++ b/compiler/rustc_ty_utils/src/consts.rs
@@ -406,7 +406,7 @@ fn thir_abstract_const<'tcx>(
     tcx: TyCtxt<'tcx>,
     def: LocalDefId,
 ) -> Result<Option<ty::EarlyBinder<'tcx, ty::Const<'tcx>>>, ErrorGuaranteed> {
-    if !tcx.features().generic_const_exprs {
+    if !tcx.features().generic_const_exprs() {
         return Ok(None);
     }
 
diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs
index f20beb79750..c06a578d8ec 100644
--- a/compiler/rustc_type_ir/src/binder.rs
+++ b/compiler/rustc_type_ir/src/binder.rs
@@ -496,8 +496,8 @@ where
 
     /// Similar to [`instantiate_identity`](EarlyBinder::instantiate_identity),
     /// but on an iterator of values that deref to a `TypeFoldable`.
-    pub fn iter_identity_copied(self) -> impl Iterator<Item = <Iter::Item as Deref>::Target> {
-        self.value.into_iter().map(|v| *v)
+    pub fn iter_identity_copied(self) -> IterIdentityCopied<Iter> {
+        IterIdentityCopied { it: self.value.into_iter() }
     }
 }
 
@@ -546,6 +546,44 @@ where
 {
 }
 
+pub struct IterIdentityCopied<Iter: IntoIterator> {
+    it: Iter::IntoIter,
+}
+
+impl<Iter: IntoIterator> Iterator for IterIdentityCopied<Iter>
+where
+    Iter::Item: Deref,
+    <Iter::Item as Deref>::Target: Copy,
+{
+    type Item = <Iter::Item as Deref>::Target;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        self.it.next().map(|i| *i)
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.it.size_hint()
+    }
+}
+
+impl<Iter: IntoIterator> DoubleEndedIterator for IterIdentityCopied<Iter>
+where
+    Iter::IntoIter: DoubleEndedIterator,
+    Iter::Item: Deref,
+    <Iter::Item as Deref>::Target: Copy,
+{
+    fn next_back(&mut self) -> Option<Self::Item> {
+        self.it.next_back().map(|i| *i)
+    }
+}
+
+impl<Iter: IntoIterator> ExactSizeIterator for IterIdentityCopied<Iter>
+where
+    Iter::IntoIter: ExactSizeIterator,
+    Iter::Item: Deref,
+    <Iter::Item as Deref>::Target: Copy,
+{
+}
 pub struct EarlyBinderIter<I, T> {
     t: T,
     _tcx: PhantomData<I>,
diff --git a/compiler/rustc_type_ir/src/error.rs b/compiler/rustc_type_ir/src/error.rs
index 8a6d37b7d23..cdff77f742d 100644
--- a/compiler/rustc_type_ir/src/error.rs
+++ b/compiler/rustc_type_ir/src/error.rs
@@ -27,10 +27,9 @@ impl<T> ExpectedFound<T> {
 #[cfg_attr(feature = "nightly", rustc_pass_by_value)]
 pub enum TypeError<I: Interner> {
     Mismatch,
-    ConstnessMismatch(ExpectedFound<ty::BoundConstness>),
-    PolarityMismatch(ExpectedFound<ty::PredicatePolarity>),
-    SafetyMismatch(ExpectedFound<I::Safety>),
-    AbiMismatch(ExpectedFound<I::Abi>),
+    PolarityMismatch(#[type_visitable(ignore)] ExpectedFound<ty::PredicatePolarity>),
+    SafetyMismatch(#[type_visitable(ignore)] ExpectedFound<I::Safety>),
+    AbiMismatch(#[type_visitable(ignore)] ExpectedFound<I::Abi>),
     Mutability,
     ArgumentMutability(usize),
     TupleSize(ExpectedFound<usize>),
@@ -73,9 +72,9 @@ impl<I: Interner> TypeError<I> {
     pub fn must_include_note(self) -> bool {
         use self::TypeError::*;
         match self {
-            CyclicTy(_) | CyclicConst(_) | SafetyMismatch(_) | ConstnessMismatch(_)
-            | PolarityMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_)
-            | ArgumentSorts(..) | Sorts(_) | VariadicMismatch(_) | TargetFeatureCast(_) => false,
+            CyclicTy(_) | CyclicConst(_) | SafetyMismatch(_) | PolarityMismatch(_) | Mismatch
+            | AbiMismatch(_) | FixedArraySize(_) | ArgumentSorts(..) | Sorts(_)
+            | VariadicMismatch(_) | TargetFeatureCast(_) => false,
 
             Mutability
             | ArgumentMutability(_)
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index f7875bb5152..02ec29a7f3d 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -208,14 +208,14 @@ pub trait Tys<I: Interner<Tys = Self>>:
     fn output(self) -> I::Ty;
 }
 
-pub trait Abi<I: Interner<Abi = Self>>: Copy + Debug + Hash + Eq + Relate<I> {
+pub trait Abi<I: Interner<Abi = Self>>: Copy + Debug + Hash + Eq {
     fn rust() -> Self;
 
     /// Whether this ABI is `extern "Rust"`.
     fn is_rust(self) -> bool;
 }
 
-pub trait Safety<I: Interner<Safety = Self>>: Copy + Debug + Hash + Eq + Relate<I> {
+pub trait Safety<I: Interner<Safety = Self>>: Copy + Debug + Hash + Eq {
     fn safe() -> Self;
 
     fn is_safe(self) -> bool;
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index f06017d7e5c..4184e9e313f 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -15,9 +15,7 @@ use crate::solve::{
     CanonicalInput, ExternalConstraintsData, PredefinedOpaquesData, QueryResult, SolverMode,
 };
 use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
-use crate::{
-    search_graph, {self as ty},
-};
+use crate::{self as ty, search_graph};
 
 pub trait Interner:
     Sized
@@ -173,6 +171,10 @@ pub trait Interner:
 
     fn debug_assert_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs);
 
+    /// Assert that the args from an `ExistentialTraitRef` or `ExistentialProjection`
+    /// are compatible with the `DefId`.
+    fn debug_assert_existential_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs);
+
     fn mk_type_list_from_iter<I, T>(self, args: I) -> T::Output
     where
         I: Iterator<Item = T>,
diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs
index b613505f826..e8ce39be3e5 100644
--- a/compiler/rustc_type_ir/src/predicate.rs
+++ b/compiler/rustc_type_ir/src/predicate.rs
@@ -289,9 +289,26 @@ impl<I: Interner> ty::Binder<I, ExistentialPredicate<I>> {
 pub struct ExistentialTraitRef<I: Interner> {
     pub def_id: I::DefId,
     pub args: I::GenericArgs,
+    /// This field exists to prevent the creation of `ExistentialTraitRef` without
+    /// calling [`ExistentialTraitRef::new_from_args`].
+    _use_existential_trait_ref_new_instead: (),
 }
 
 impl<I: Interner> ExistentialTraitRef<I> {
+    pub fn new_from_args(interner: I, trait_def_id: I::DefId, args: I::GenericArgs) -> Self {
+        interner.debug_assert_existential_args_compatible(trait_def_id, args);
+        Self { def_id: trait_def_id, args, _use_existential_trait_ref_new_instead: () }
+    }
+
+    pub fn new(
+        interner: I,
+        trait_def_id: I::DefId,
+        args: impl IntoIterator<Item: Into<I::GenericArg>>,
+    ) -> Self {
+        let args = interner.mk_args_from_iter(args.into_iter().map(Into::into));
+        Self::new_from_args(interner, trait_def_id, args)
+    }
+
     pub fn erase_self_ty(interner: I, trait_ref: TraitRef<I>) -> ExistentialTraitRef<I> {
         // Assert there is a Self.
         trait_ref.args.type_at(0);
@@ -299,6 +316,7 @@ impl<I: Interner> ExistentialTraitRef<I> {
         ExistentialTraitRef {
             def_id: trait_ref.def_id,
             args: interner.mk_args(&trait_ref.args.as_slice()[1..]),
+            _use_existential_trait_ref_new_instead: (),
         }
     }
 
@@ -336,9 +354,33 @@ pub struct ExistentialProjection<I: Interner> {
     pub def_id: I::DefId,
     pub args: I::GenericArgs,
     pub term: I::Term,
+
+    /// This field exists to prevent the creation of `ExistentialProjection`
+    /// without using [`ExistentialProjection::new_from_args`].
+    use_existential_projection_new_instead: (),
 }
 
 impl<I: Interner> ExistentialProjection<I> {
+    pub fn new_from_args(
+        interner: I,
+        def_id: I::DefId,
+        args: I::GenericArgs,
+        term: I::Term,
+    ) -> ExistentialProjection<I> {
+        interner.debug_assert_existential_args_compatible(def_id, args);
+        Self { def_id, args, term, use_existential_projection_new_instead: () }
+    }
+
+    pub fn new(
+        interner: I,
+        def_id: I::DefId,
+        args: impl IntoIterator<Item: Into<I::GenericArg>>,
+        term: I::Term,
+    ) -> ExistentialProjection<I> {
+        let args = interner.mk_args_from_iter(args.into_iter().map(Into::into));
+        Self::new_from_args(interner, def_id, args, term)
+    }
+
     /// Extracts the underlying existential trait reference from this projection.
     /// For example, if this is a projection of `exists T. <T as Iterator>::Item == X`,
     /// then this function would return an `exists T. T: Iterator` existential trait
@@ -347,7 +389,7 @@ impl<I: Interner> ExistentialProjection<I> {
         let def_id = interner.parent(self.def_id);
         let args_count = interner.generics_of(def_id).count() - 1;
         let args = interner.mk_args(&self.args.as_slice()[..args_count]);
-        ExistentialTraitRef { def_id, args }
+        ExistentialTraitRef { def_id, args, _use_existential_trait_ref_new_instead: () }
     }
 
     pub fn with_self_ty(&self, interner: I, self_ty: I::Ty) -> ProjectionPredicate<I> {
@@ -372,6 +414,7 @@ impl<I: Interner> ExistentialProjection<I> {
             def_id: projection_predicate.projection_term.def_id,
             args: interner.mk_args(&projection_predicate.projection_term.args.as_slice()[1..]),
             term: projection_predicate.term,
+            use_existential_projection_new_instead: (),
         }
     }
 }
diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs
index e1f3e493e36..ad17911830b 100644
--- a/compiler/rustc_type_ir/src/relate.rs
+++ b/compiler/rustc_type_ir/src/relate.rs
@@ -174,12 +174,17 @@ impl<I: Interner> Relate<I> for ty::FnSig<I> {
                 ExpectedFound::new(true, a, b)
             }));
         }
-        let safety = relation.relate(a.safety, b.safety)?;
-        let abi = relation.relate(a.abi, b.abi)?;
+
+        if a.safety != b.safety {
+            return Err(TypeError::SafetyMismatch(ExpectedFound::new(true, a.safety, b.safety)));
+        }
+
+        if a.abi != b.abi {
+            return Err(TypeError::AbiMismatch(ExpectedFound::new(true, a.abi, b.abi)));
+        };
 
         let a_inputs = a.inputs();
         let b_inputs = b.inputs();
-
         if a_inputs.len() != b_inputs.len() {
             return Err(TypeError::ArgCount);
         }
@@ -212,26 +217,12 @@ impl<I: Interner> Relate<I> for ty::FnSig<I> {
         Ok(ty::FnSig {
             inputs_and_output: cx.mk_type_list_from_iter(inputs_and_output)?,
             c_variadic: a.c_variadic,
-            safety,
-            abi,
+            safety: a.safety,
+            abi: a.abi,
         })
     }
 }
 
-impl<I: Interner> Relate<I> for ty::BoundConstness {
-    fn relate<R: TypeRelation<I>>(
-        _relation: &mut R,
-        a: ty::BoundConstness,
-        b: ty::BoundConstness,
-    ) -> RelateResult<I, ty::BoundConstness> {
-        if a != b {
-            Err(TypeError::ConstnessMismatch(ExpectedFound::new(true, a, b)))
-        } else {
-            Ok(a)
-        }
-    }
-}
-
 impl<I: Interner> Relate<I> for ty::AliasTy<I> {
     fn relate<R: TypeRelation<I>>(
         relation: &mut R,
@@ -333,7 +324,7 @@ impl<I: Interner> Relate<I> for ty::ExistentialProjection<I> {
                 a.args,
                 b.args,
             )?;
-            Ok(ty::ExistentialProjection { def_id: a.def_id, args, term })
+            Ok(ty::ExistentialProjection::new_from_args(relation.cx(), a.def_id, args, term))
         }
     }
 }
@@ -373,7 +364,7 @@ impl<I: Interner> Relate<I> for ty::ExistentialTraitRef<I> {
             }))
         } else {
             let args = relate_args_invariantly(relation, a.args, b.args)?;
-            Ok(ty::ExistentialTraitRef { def_id: a.def_id, args })
+            Ok(ty::ExistentialTraitRef::new_from_args(relation.cx(), a.def_id, args))
         }
     }
 }
@@ -659,29 +650,18 @@ impl<I: Interner, T: Relate<I>> Relate<I> for ty::Binder<I, T> {
     }
 }
 
-impl<I: Interner> Relate<I> for ty::PredicatePolarity {
-    fn relate<R: TypeRelation<I>>(
-        _relation: &mut R,
-        a: ty::PredicatePolarity,
-        b: ty::PredicatePolarity,
-    ) -> RelateResult<I, ty::PredicatePolarity> {
-        if a != b {
-            Err(TypeError::PolarityMismatch(ExpectedFound::new(true, a, b)))
-        } else {
-            Ok(a)
-        }
-    }
-}
-
 impl<I: Interner> Relate<I> for ty::TraitPredicate<I> {
     fn relate<R: TypeRelation<I>>(
         relation: &mut R,
         a: ty::TraitPredicate<I>,
         b: ty::TraitPredicate<I>,
     ) -> RelateResult<I, ty::TraitPredicate<I>> {
-        Ok(ty::TraitPredicate {
-            trait_ref: relation.relate(a.trait_ref, b.trait_ref)?,
-            polarity: relation.relate(a.polarity, b.polarity)?,
-        })
+        let trait_ref = relation.relate(a.trait_ref, b.trait_ref)?;
+        if a.polarity != b.polarity {
+            return Err(TypeError::PolarityMismatch(ExpectedFound::new(
+                true, a.polarity, b.polarity,
+            )));
+        }
+        Ok(ty::TraitPredicate { trait_ref, polarity: a.polarity })
     }
 }
diff --git a/compiler/rustc_type_ir/src/solve/inspect.rs b/compiler/rustc_type_ir/src/solve/inspect.rs
index 138ba8bac88..d0e618dc6f9 100644
--- a/compiler/rustc_type_ir/src/solve/inspect.rs
+++ b/compiler/rustc_type_ir/src/solve/inspect.rs
@@ -23,9 +23,7 @@ use std::hash::Hash;
 use derive_where::derive_where;
 use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic};
 
-use crate::solve::{
-    CandidateSource, CanonicalInput, Certainty, Goal, GoalSource, QueryInput, QueryResult,
-};
+use crate::solve::{CandidateSource, CanonicalInput, Certainty, Goal, GoalSource, QueryResult};
 use crate::{Canonical, CanonicalVarValues, Interner};
 
 /// Some `data` together with information about how they relate to the input
@@ -69,15 +67,10 @@ pub struct CanonicalGoalEvaluation<I: Interner> {
 #[derive_where(PartialEq, Eq, Hash, Debug; I: Interner)]
 pub enum CanonicalGoalEvaluationKind<I: Interner> {
     Overflow,
-    Evaluation { final_revision: CanonicalGoalEvaluationStep<I> },
-}
-
-#[derive_where(PartialEq, Eq, Hash, Debug; I: Interner)]
-pub struct CanonicalGoalEvaluationStep<I: Interner> {
-    pub instantiated_goal: QueryInput<I, I::Predicate>,
-
-    /// The actual evaluation of the goal, always `ProbeKind::Root`.
-    pub evaluation: Probe<I>,
+    Evaluation {
+        /// This is always `ProbeKind::Root`.
+        final_revision: Probe<I>,
+    },
 }
 
 /// A self-contained computation during trait solving. This either
diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs
index b7f6ef4ffbb..499e6d3dd37 100644
--- a/compiler/rustc_type_ir/src/ty_kind.rs
+++ b/compiler/rustc_type_ir/src/ty_kind.rs
@@ -861,7 +861,11 @@ pub struct TypeAndMut<I: Interner> {
 pub struct FnSig<I: Interner> {
     pub inputs_and_output: I::Tys,
     pub c_variadic: bool,
+    #[type_visitable(ignore)]
+    #[type_foldable(identity)]
     pub safety: I::Safety,
+    #[type_visitable(ignore)]
+    #[type_foldable(identity)]
     pub abi: I::Abi,
 }
 
diff --git a/compiler/rustc_type_ir/src/ty_kind/closure.rs b/compiler/rustc_type_ir/src/ty_kind/closure.rs
index 09a43b17955..10b164eae02 100644
--- a/compiler/rustc_type_ir/src/ty_kind/closure.rs
+++ b/compiler/rustc_type_ir/src/ty_kind/closure.rs
@@ -372,8 +372,12 @@ pub struct CoroutineClosureSignature<I: Interner> {
     /// Always false
     pub c_variadic: bool,
     /// Always `Normal` (safe)
+    #[type_visitable(ignore)]
+    #[type_foldable(identity)]
     pub safety: I::Safety,
     /// Always `RustCall`
+    #[type_visitable(ignore)]
+    #[type_foldable(identity)]
     pub abi: I::Abi,
 }
 
diff --git a/compiler/rustc_type_ir_macros/src/lib.rs b/compiler/rustc_type_ir_macros/src/lib.rs
index 1a0a2479f6f..aaf69e2648d 100644
--- a/compiler/rustc_type_ir_macros/src/lib.rs
+++ b/compiler/rustc_type_ir_macros/src/lib.rs
@@ -1,18 +1,73 @@
-use quote::quote;
-use syn::parse_quote;
+use quote::{ToTokens, quote};
 use syn::visit_mut::VisitMut;
+use syn::{Attribute, parse_quote};
 use synstructure::decl_derive;
 
 decl_derive!(
-    [TypeFoldable_Generic] => type_foldable_derive
+    [TypeVisitable_Generic, attributes(type_visitable)] => type_visitable_derive
 );
 decl_derive!(
-    [TypeVisitable_Generic] => type_visitable_derive
+    [TypeFoldable_Generic, attributes(type_foldable)] => type_foldable_derive
 );
 decl_derive!(
     [Lift_Generic] => lift_derive
 );
 
+fn has_ignore_attr(attrs: &[Attribute], name: &'static str, meta: &'static str) -> bool {
+    let mut ignored = false;
+    attrs.iter().for_each(|attr| {
+        if !attr.path().is_ident(name) {
+            return;
+        }
+        let _ = attr.parse_nested_meta(|nested| {
+            if nested.path.is_ident(meta) {
+                ignored = true;
+            }
+            Ok(())
+        });
+    });
+
+    ignored
+}
+
+fn type_visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
+    if let syn::Data::Union(_) = s.ast().data {
+        panic!("cannot derive on union")
+    }
+
+    if !s.ast().generics.type_params().any(|ty| ty.ident == "I") {
+        s.add_impl_generic(parse_quote! { I });
+    }
+
+    s.filter(|bi| !has_ignore_attr(&bi.ast().attrs, "type_visitable", "ignore"));
+
+    s.add_where_predicate(parse_quote! { I: Interner });
+    s.add_bounds(synstructure::AddBounds::Fields);
+    let body_visit = s.each(|bind| {
+        quote! {
+            match ::rustc_ast_ir::visit::VisitorResult::branch(
+                ::rustc_type_ir::visit::TypeVisitable::visit_with(#bind, __visitor)
+            ) {
+                ::core::ops::ControlFlow::Continue(()) => {},
+                ::core::ops::ControlFlow::Break(r) => {
+                    return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
+                },
+            }
+        }
+    });
+    s.bind_with(|_| synstructure::BindStyle::Move);
+
+    s.bound_impl(quote!(::rustc_type_ir::visit::TypeVisitable<I>), quote! {
+        fn visit_with<__V: ::rustc_type_ir::visit::TypeVisitor<I>>(
+            &self,
+            __visitor: &mut __V
+        ) -> __V::Result {
+            match *self { #body_visit }
+            <__V::Result as ::rustc_ast_ir::visit::VisitorResult>::output()
+        }
+    })
+}
+
 fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
     if let syn::Data::Union(_) = s.ast().data {
         panic!("cannot derive on union")
@@ -29,12 +84,23 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke
         let bindings = vi.bindings();
         vi.construct(|_, index| {
             let bind = &bindings[index];
-            quote! {
-                ::rustc_type_ir::fold::TypeFoldable::try_fold_with(#bind, __folder)?
+
+            // retain value of fields with #[type_foldable(identity)]
+            if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") {
+                bind.to_token_stream()
+            } else {
+                quote! {
+                    ::rustc_type_ir::fold::TypeFoldable::try_fold_with(#bind, __folder)?
+                }
             }
         })
     });
 
+    // We filter fields which get ignored and don't require them to implement
+    // `TypeFoldable`. We do so after generating `body_fold` as we still need
+    // to generate code for them.
+    s.filter(|bi| !has_ignore_attr(&bi.ast().attrs, "type_foldable", "identity"));
+    s.add_bounds(synstructure::AddBounds::Fields);
     s.bound_impl(quote!(::rustc_type_ir::fold::TypeFoldable<I>), quote! {
         fn try_fold_with<__F: ::rustc_type_ir::fold::FallibleTypeFolder<I>>(
             self,
@@ -113,39 +179,3 @@ fn lift(mut ty: syn::Type) -> syn::Type {
 
     ty
 }
-
-fn type_visitable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
-    if let syn::Data::Union(_) = s.ast().data {
-        panic!("cannot derive on union")
-    }
-
-    if !s.ast().generics.type_params().any(|ty| ty.ident == "I") {
-        s.add_impl_generic(parse_quote! { I });
-    }
-
-    s.add_where_predicate(parse_quote! { I: Interner });
-    s.add_bounds(synstructure::AddBounds::Fields);
-    let body_visit = s.each(|bind| {
-        quote! {
-            match ::rustc_ast_ir::visit::VisitorResult::branch(
-                ::rustc_type_ir::visit::TypeVisitable::visit_with(#bind, __visitor)
-            ) {
-                ::core::ops::ControlFlow::Continue(()) => {},
-                ::core::ops::ControlFlow::Break(r) => {
-                    return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
-                },
-            }
-        }
-    });
-    s.bind_with(|_| synstructure::BindStyle::Move);
-
-    s.bound_impl(quote!(::rustc_type_ir::visit::TypeVisitable<I>), quote! {
-        fn visit_with<__V: ::rustc_type_ir::visit::TypeVisitor<I>>(
-            &self,
-            __visitor: &mut __V
-        ) -> __V::Result {
-            match *self { #body_visit }
-            <__V::Result as ::rustc_ast_ir::visit::VisitorResult>::output()
-        }
-    })
-}
diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs
index e98ae7c31ad..582d850e14b 100644
--- a/library/alloc/src/rc.rs
+++ b/library/alloc/src/rc.rs
@@ -2312,7 +2312,16 @@ impl<T: Default> Default for Rc<T> {
     /// ```
     #[inline]
     fn default() -> Rc<T> {
-        Rc::new(Default::default())
+        unsafe {
+            Self::from_inner(
+                Box::leak(Box::write(Box::new_uninit(), RcInner {
+                    strong: Cell::new(1),
+                    weak: Cell::new(1),
+                    value: T::default(),
+                }))
+                .into(),
+            )
+        }
     }
 }
 
diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs
index 52ceb8b45f9..26c1ba2a5c4 100644
--- a/library/alloc/src/str.rs
+++ b/library/alloc/src/str.rs
@@ -269,8 +269,7 @@ impl str {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[inline]
     pub fn replace<P: Pattern>(&self, from: P, to: &str) -> String {
-        // Fast path for ASCII to ASCII case.
-
+        // Fast path for replacing a single ASCII character with another.
         if let Some(from_byte) = match from.as_utf8_pattern() {
             Some(Utf8Pattern::StringPattern([from_byte])) => Some(*from_byte),
             Some(Utf8Pattern::CharPattern(c)) => c.as_ascii().map(|ascii_char| ascii_char.to_u8()),
@@ -280,8 +279,13 @@ impl str {
                 return unsafe { replace_ascii(self.as_bytes(), from_byte, *to_byte) };
             }
         }
-
-        let mut result = String::new();
+        // Set result capacity to self.len() when from.len() <= to.len()
+        let default_capacity = match from.as_utf8_pattern() {
+            Some(Utf8Pattern::StringPattern(s)) if s.len() <= to.len() => self.len(),
+            Some(Utf8Pattern::CharPattern(c)) if c.len_utf8() <= to.len() => self.len(),
+            _ => 0,
+        };
+        let mut result = String::with_capacity(default_capacity);
         let mut last_end = 0;
         for (start, part) in self.match_indices(from) {
             result.push_str(unsafe { self.get_unchecked(last_end..start) });
diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs
index acbc325a514..13677245e98 100644
--- a/library/alloc/src/sync.rs
+++ b/library/alloc/src/sync.rs
@@ -3447,13 +3447,16 @@ impl<T: Default> Default for Arc<T> {
     /// assert_eq!(*x, 0);
     /// ```
     fn default() -> Arc<T> {
-        let x = Box::into_raw(Box::write(Box::new_uninit(), ArcInner {
-            strong: atomic::AtomicUsize::new(1),
-            weak: atomic::AtomicUsize::new(1),
-            data: T::default(),
-        }));
-        // SAFETY: `Box::into_raw` consumes the `Box` and never returns null
-        unsafe { Self::from_inner(NonNull::new_unchecked(x)) }
+        unsafe {
+            Self::from_inner(
+                Box::leak(Box::write(Box::new_uninit(), ArcInner {
+                    strong: atomic::AtomicUsize::new(1),
+                    weak: atomic::AtomicUsize::new(1),
+                    data: T::default(),
+                }))
+                .into(),
+            )
+        }
     }
 }
 
diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs
index 1769612def0..9cb00644e64 100644
--- a/library/core/src/slice/cmp.rs
+++ b/library/core/src/slice/cmp.rs
@@ -257,3 +257,29 @@ impl SliceContains for i8 {
         memchr::memchr(byte, bytes).is_some()
     }
 }
+
+macro_rules! impl_slice_contains {
+    ($($t:ty),*) => {
+        $(
+            impl SliceContains for $t {
+                #[inline]
+                fn slice_contains(&self, arr: &[$t]) -> bool {
+                    // Make our LANE_COUNT 4x the normal lane count (aiming for 128 bit vectors).
+                    // The compiler will nicely unroll it.
+                    const LANE_COUNT: usize = 4 * (128 / (mem::size_of::<$t>() * 8));
+                    // SIMD
+                    let mut chunks = arr.chunks_exact(LANE_COUNT);
+                    for chunk in &mut chunks {
+                        if chunk.iter().fold(false, |acc, x| acc | (*x == *self)) {
+                            return true;
+                        }
+                    }
+                    // Scalar remainder
+                    return chunks.remainder().iter().any(|x| *x == *self);
+                }
+            }
+        )*
+    };
+}
+
+impl_slice_contains!(u16, u32, u64, i16, i32, i64, f32, f64, usize, isize);
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index 5d6921845d0..14603aa30e8 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -17,11 +17,14 @@
 #![feature(clone_to_uninit)]
 #![feature(const_align_of_val_raw)]
 #![feature(const_align_offset)]
+#![feature(const_bigint_helper_methods)]
 #![feature(const_black_box)]
+#![feature(const_eval_select)]
 #![feature(const_hash)]
 #![feature(const_heap)]
 #![feature(const_likely)]
 #![feature(const_nonnull_new)]
+#![feature(const_num_midpoint)]
 #![feature(const_option_ext)]
 #![feature(const_pin_2)]
 #![feature(const_pointer_is_aligned)]
@@ -46,6 +49,7 @@
 #![feature(get_many_mut)]
 #![feature(hasher_prefixfree_extras)]
 #![feature(hashmap_internals)]
+#![feature(inline_const_pat)]
 #![feature(int_roundings)]
 #![feature(ip)]
 #![feature(ip_from)]
@@ -104,6 +108,37 @@
 #![deny(fuzzy_provenance_casts)]
 #![deny(unsafe_op_in_unsafe_fn)]
 
+/// Version of `assert_matches` that ignores fancy runtime printing in const context and uses structural equality.
+macro_rules! assert_eq_const_safe {
+    ($left:expr, $right:expr$(, $($arg:tt)+)?) => {
+        {
+            fn runtime() {
+                assert_eq!($left, $right, $($arg)*);
+            }
+            const fn compiletime() {
+                assert!(matches!($left, const { $right }));
+            }
+            core::intrinsics::const_eval_select((), compiletime, runtime)
+        }
+    };
+}
+
+/// Creates a test for runtime and a test for constant-time.
+macro_rules! test_runtime_and_compiletime {
+    ($(
+        $(#[$attr:meta])*
+        fn $test:ident() $block:block
+    )*) => {
+        $(
+            $(#[$attr])*
+            #[test]
+            fn $test() $block
+            $(#[$attr])*
+            const _: () = $block;
+        )*
+    }
+}
+
 mod alloc;
 mod any;
 mod array;
diff --git a/library/core/tests/num/int_macros.rs b/library/core/tests/num/int_macros.rs
index 6a26bd15a66..7d3381ee504 100644
--- a/library/core/tests/num/int_macros.rs
+++ b/library/core/tests/num/int_macros.rs
@@ -18,42 +18,6 @@ macro_rules! int_module {
         }
 
         #[test]
-        fn test_rem_euclid() {
-            assert_eq!((-1 as $T).rem_euclid(MIN), MAX);
-        }
-
-        #[test]
-        pub fn test_abs() {
-            assert_eq!((1 as $T).abs(), 1 as $T);
-            assert_eq!((0 as $T).abs(), 0 as $T);
-            assert_eq!((-1 as $T).abs(), 1 as $T);
-        }
-
-        #[test]
-        fn test_signum() {
-            assert_eq!((1 as $T).signum(), 1 as $T);
-            assert_eq!((0 as $T).signum(), 0 as $T);
-            assert_eq!((-0 as $T).signum(), 0 as $T);
-            assert_eq!((-1 as $T).signum(), -1 as $T);
-        }
-
-        #[test]
-        fn test_is_positive() {
-            assert!((1 as $T).is_positive());
-            assert!(!(0 as $T).is_positive());
-            assert!(!(-0 as $T).is_positive());
-            assert!(!(-1 as $T).is_positive());
-        }
-
-        #[test]
-        fn test_is_negative() {
-            assert!(!(1 as $T).is_negative());
-            assert!(!(0 as $T).is_negative());
-            assert!(!(-0 as $T).is_negative());
-            assert!((-1 as $T).is_negative());
-        }
-
-        #[test]
         fn test_bitwise_operators() {
             assert_eq!(0b1110 as $T, (0b1100 as $T).bitor(0b1010 as $T));
             assert_eq!(0b1000 as $T, (0b1100 as $T).bitand(0b1010 as $T));
@@ -63,6 +27,40 @@ macro_rules! int_module {
             assert_eq!(-(0b11 as $T) - (1 as $T), (0b11 as $T).not());
         }
 
+        test_runtime_and_compiletime! {
+
+            fn test_rem_euclid() {
+                assert_eq_const_safe!((-1 as $T).rem_euclid(MIN), MAX);
+            }
+
+            fn test_abs() {
+                assert_eq_const_safe!((1 as $T).abs(), 1 as $T);
+                assert_eq_const_safe!((0 as $T).abs(), 0 as $T);
+                assert_eq_const_safe!((-1 as $T).abs(), 1 as $T);
+            }
+
+            fn test_signum() {
+                assert_eq_const_safe!((1 as $T).signum(), 1 as $T);
+                assert_eq_const_safe!((0 as $T).signum(), 0 as $T);
+                assert_eq_const_safe!((-0 as $T).signum(), 0 as $T);
+                assert_eq_const_safe!((-1 as $T).signum(), -1 as $T);
+            }
+
+            fn test_is_positive() {
+                assert!((1 as $T).is_positive());
+                assert!(!(0 as $T).is_positive());
+                assert!(!(-0 as $T).is_positive());
+                assert!(!(-1 as $T).is_positive());
+            }
+
+            fn test_is_negative() {
+                assert!(!(1 as $T).is_negative());
+                assert!(!(0 as $T).is_negative());
+                assert!(!(-0 as $T).is_negative());
+                assert!((-1 as $T).is_negative());
+            }
+        }
+
         const A: $T = 0b0101100;
         const B: $T = 0b0100001;
         const C: $T = 0b1111001;
@@ -70,134 +68,126 @@ macro_rules! int_module {
         const _0: $T = 0;
         const _1: $T = !0;
 
-        #[test]
-        fn test_count_ones() {
-            assert_eq!(A.count_ones(), 3);
-            assert_eq!(B.count_ones(), 2);
-            assert_eq!(C.count_ones(), 5);
-        }
+        test_runtime_and_compiletime! {
+            fn test_count_ones() {
+                assert_eq_const_safe!(A.count_ones(), 3);
+                assert_eq_const_safe!(B.count_ones(), 2);
+                assert_eq_const_safe!(C.count_ones(), 5);
+            }
 
-        #[test]
-        fn test_count_zeros() {
-            assert_eq!(A.count_zeros(), $T::BITS - 3);
-            assert_eq!(B.count_zeros(), $T::BITS - 2);
-            assert_eq!(C.count_zeros(), $T::BITS - 5);
-        }
+            fn test_count_zeros() {
+                assert_eq_const_safe!(A.count_zeros(), $T::BITS - 3);
+                assert_eq_const_safe!(B.count_zeros(), $T::BITS - 2);
+                assert_eq_const_safe!(C.count_zeros(), $T::BITS - 5);
+            }
 
-        #[test]
-        fn test_leading_trailing_ones() {
-            let a: $T = 0b0101_1111;
-            assert_eq!(a.trailing_ones(), 5);
-            assert_eq!((!a).leading_ones(), $T::BITS - 7);
+            fn test_leading_trailing_ones() {
+                const A: $T = 0b0101_1111;
+                assert_eq_const_safe!(A.trailing_ones(), 5);
+                assert_eq_const_safe!((!A).leading_ones(), $T::BITS - 7);
 
-            assert_eq!(a.reverse_bits().leading_ones(), 5);
+                assert_eq_const_safe!(A.reverse_bits().leading_ones(), 5);
 
-            assert_eq!(_1.leading_ones(), $T::BITS);
-            assert_eq!(_1.trailing_ones(), $T::BITS);
+                assert_eq_const_safe!(_1.leading_ones(), $T::BITS);
+                assert_eq_const_safe!(_1.trailing_ones(), $T::BITS);
 
-            assert_eq!((_1 << 1).trailing_ones(), 0);
-            assert_eq!(MAX.leading_ones(), 0);
+                assert_eq_const_safe!((_1 << 1).trailing_ones(), 0);
+                assert_eq_const_safe!(MAX.leading_ones(), 0);
 
-            assert_eq!((_1 << 1).leading_ones(), $T::BITS - 1);
-            assert_eq!(MAX.trailing_ones(), $T::BITS - 1);
+                assert_eq_const_safe!((_1 << 1).leading_ones(), $T::BITS - 1);
+                assert_eq_const_safe!(MAX.trailing_ones(), $T::BITS - 1);
 
-            assert_eq!(_0.leading_ones(), 0);
-            assert_eq!(_0.trailing_ones(), 0);
+                assert_eq_const_safe!(_0.leading_ones(), 0);
+                assert_eq_const_safe!(_0.trailing_ones(), 0);
 
-            let x: $T = 0b0010_1100;
-            assert_eq!(x.leading_ones(), 0);
-            assert_eq!(x.trailing_ones(), 0);
-        }
+                const X: $T = 0b0010_1100;
+                assert_eq_const_safe!(X.leading_ones(), 0);
+                assert_eq_const_safe!(X.trailing_ones(), 0);
+            }
 
-        #[test]
-        fn test_rotate() {
-            assert_eq!(A.rotate_left(6).rotate_right(2).rotate_right(4), A);
-            assert_eq!(B.rotate_left(3).rotate_left(2).rotate_right(5), B);
-            assert_eq!(C.rotate_left(6).rotate_right(2).rotate_right(4), C);
-
-            // Rotating these should make no difference
-            //
-            // We test using 124 bits because to ensure that overlong bit shifts do
-            // not cause undefined behaviour. See #10183.
-            assert_eq!(_0.rotate_left(124), _0);
-            assert_eq!(_1.rotate_left(124), _1);
-            assert_eq!(_0.rotate_right(124), _0);
-            assert_eq!(_1.rotate_right(124), _1);
-
-            // Rotating by 0 should have no effect
-            assert_eq!(A.rotate_left(0), A);
-            assert_eq!(B.rotate_left(0), B);
-            assert_eq!(C.rotate_left(0), C);
-            // Rotating by a multiple of word size should also have no effect
-            assert_eq!(A.rotate_left(128), A);
-            assert_eq!(B.rotate_left(128), B);
-            assert_eq!(C.rotate_left(128), C);
-        }
+            fn test_rotate() {
+                assert_eq_const_safe!(A.rotate_left(6).rotate_right(2).rotate_right(4), A);
+                assert_eq_const_safe!(B.rotate_left(3).rotate_left(2).rotate_right(5), B);
+                assert_eq_const_safe!(C.rotate_left(6).rotate_right(2).rotate_right(4), C);
+
+                // Rotating these should make no difference
+                //
+                // We test using 124 bits because to ensure that overlong bit shifts do
+                // not cause undefined behaviour. See #10183.
+                assert_eq_const_safe!(_0.rotate_left(124), _0);
+                assert_eq_const_safe!(_1.rotate_left(124), _1);
+                assert_eq_const_safe!(_0.rotate_right(124), _0);
+                assert_eq_const_safe!(_1.rotate_right(124), _1);
+
+                // Rotating by 0 should have no effect
+                assert_eq_const_safe!(A.rotate_left(0), A);
+                assert_eq_const_safe!(B.rotate_left(0), B);
+                assert_eq_const_safe!(C.rotate_left(0), C);
+                // Rotating by a multiple of word size should also have no effect
+                assert_eq_const_safe!(A.rotate_left(128), A);
+                assert_eq_const_safe!(B.rotate_left(128), B);
+                assert_eq_const_safe!(C.rotate_left(128), C);
+            }
 
-        #[test]
-        fn test_swap_bytes() {
-            assert_eq!(A.swap_bytes().swap_bytes(), A);
-            assert_eq!(B.swap_bytes().swap_bytes(), B);
-            assert_eq!(C.swap_bytes().swap_bytes(), C);
-
-            // Swapping these should make no difference
-            assert_eq!(_0.swap_bytes(), _0);
-            assert_eq!(_1.swap_bytes(), _1);
-        }
+            fn test_swap_bytes() {
+                assert_eq_const_safe!(A.swap_bytes().swap_bytes(), A);
+                assert_eq_const_safe!(B.swap_bytes().swap_bytes(), B);
+                assert_eq_const_safe!(C.swap_bytes().swap_bytes(), C);
 
-        #[test]
-        fn test_le() {
-            assert_eq!($T::from_le(A.to_le()), A);
-            assert_eq!($T::from_le(B.to_le()), B);
-            assert_eq!($T::from_le(C.to_le()), C);
-            assert_eq!($T::from_le(_0), _0);
-            assert_eq!($T::from_le(_1), _1);
-            assert_eq!(_0.to_le(), _0);
-            assert_eq!(_1.to_le(), _1);
-        }
+                // Swapping these should make no difference
+                assert_eq_const_safe!(_0.swap_bytes(), _0);
+                assert_eq_const_safe!(_1.swap_bytes(), _1);
+            }
 
-        #[test]
-        fn test_be() {
-            assert_eq!($T::from_be(A.to_be()), A);
-            assert_eq!($T::from_be(B.to_be()), B);
-            assert_eq!($T::from_be(C.to_be()), C);
-            assert_eq!($T::from_be(_0), _0);
-            assert_eq!($T::from_be(_1), _1);
-            assert_eq!(_0.to_be(), _0);
-            assert_eq!(_1.to_be(), _1);
-        }
+            fn test_le() {
+                assert_eq_const_safe!($T::from_le(A.to_le()), A);
+                assert_eq_const_safe!($T::from_le(B.to_le()), B);
+                assert_eq_const_safe!($T::from_le(C.to_le()), C);
+                assert_eq_const_safe!($T::from_le(_0), _0);
+                assert_eq_const_safe!($T::from_le(_1), _1);
+                assert_eq_const_safe!(_0.to_le(), _0);
+                assert_eq_const_safe!(_1.to_le(), _1);
+            }
 
-        #[test]
-        fn test_signed_checked_div() {
-            assert_eq!((10 as $T).checked_div(2), Some(5));
-            assert_eq!((5 as $T).checked_div(0), None);
-            assert_eq!(isize::MIN.checked_div(-1), None);
-        }
+            fn test_be() {
+                assert_eq_const_safe!($T::from_be(A.to_be()), A);
+                assert_eq_const_safe!($T::from_be(B.to_be()), B);
+                assert_eq_const_safe!($T::from_be(C.to_be()), C);
+                assert_eq_const_safe!($T::from_be(_0), _0);
+                assert_eq_const_safe!($T::from_be(_1), _1);
+                assert_eq_const_safe!(_0.to_be(), _0);
+                assert_eq_const_safe!(_1.to_be(), _1);
+            }
 
-        #[test]
-        fn test_saturating_abs() {
-            assert_eq!((0 as $T).saturating_abs(), 0);
-            assert_eq!((123 as $T).saturating_abs(), 123);
-            assert_eq!((-123 as $T).saturating_abs(), 123);
-            assert_eq!((MAX - 2).saturating_abs(), MAX - 2);
-            assert_eq!((MAX - 1).saturating_abs(), MAX - 1);
-            assert_eq!(MAX.saturating_abs(), MAX);
-            assert_eq!((MIN + 2).saturating_abs(), MAX - 1);
-            assert_eq!((MIN + 1).saturating_abs(), MAX);
-            assert_eq!(MIN.saturating_abs(), MAX);
-        }
+            fn test_signed_checked_div() {
+                assert_eq_const_safe!((10 as $T).checked_div(2), Some(5));
+                assert_eq_const_safe!((5 as $T).checked_div(0), None);
+                assert_eq_const_safe!(isize::MIN.checked_div(-1), None);
+            }
 
-        #[test]
-        fn test_saturating_neg() {
-            assert_eq!((0 as $T).saturating_neg(), 0);
-            assert_eq!((123 as $T).saturating_neg(), -123);
-            assert_eq!((-123 as $T).saturating_neg(), 123);
-            assert_eq!((MAX - 2).saturating_neg(), MIN + 3);
-            assert_eq!((MAX - 1).saturating_neg(), MIN + 2);
-            assert_eq!(MAX.saturating_neg(), MIN + 1);
-            assert_eq!((MIN + 2).saturating_neg(), MAX - 1);
-            assert_eq!((MIN + 1).saturating_neg(), MAX);
-            assert_eq!(MIN.saturating_neg(), MAX);
+            fn test_saturating_abs() {
+                assert_eq_const_safe!((0 as $T).saturating_abs(), 0);
+                assert_eq_const_safe!((123 as $T).saturating_abs(), 123);
+                assert_eq_const_safe!((-123 as $T).saturating_abs(), 123);
+                assert_eq_const_safe!((MAX - 2).saturating_abs(), MAX - 2);
+                assert_eq_const_safe!((MAX - 1).saturating_abs(), MAX - 1);
+                assert_eq_const_safe!(MAX.saturating_abs(), MAX);
+                assert_eq_const_safe!((MIN + 2).saturating_abs(), MAX - 1);
+                assert_eq_const_safe!((MIN + 1).saturating_abs(), MAX);
+                assert_eq_const_safe!(MIN.saturating_abs(), MAX);
+            }
+
+            fn test_saturating_neg() {
+                assert_eq_const_safe!((0 as $T).saturating_neg(), 0);
+                assert_eq_const_safe!((123 as $T).saturating_neg(), -123);
+                assert_eq_const_safe!((-123 as $T).saturating_neg(), 123);
+                assert_eq_const_safe!((MAX - 2).saturating_neg(), MIN + 3);
+                assert_eq_const_safe!((MAX - 1).saturating_neg(), MIN + 2);
+                assert_eq_const_safe!(MAX.saturating_neg(), MIN + 1);
+                assert_eq_const_safe!((MIN + 2).saturating_neg(), MAX - 1);
+                assert_eq_const_safe!((MIN + 1).saturating_neg(), MAX);
+                assert_eq_const_safe!(MIN.saturating_neg(), MAX);
+            }
         }
 
         #[test]
@@ -222,173 +212,173 @@ macro_rules! int_module {
             assert_eq!(from_str::<$T>("x"), None);
         }
 
-        #[test]
-        fn test_from_str_radix() {
-            assert_eq!($T::from_str_radix("123", 10), Ok(123 as $T));
-            assert_eq!($T::from_str_radix("1001", 2), Ok(9 as $T));
-            assert_eq!($T::from_str_radix("123", 8), Ok(83 as $T));
-            assert_eq!(i32::from_str_radix("123", 16), Ok(291 as i32));
-            assert_eq!(i32::from_str_radix("ffff", 16), Ok(65535 as i32));
-            assert_eq!(i32::from_str_radix("FFFF", 16), Ok(65535 as i32));
-            assert_eq!($T::from_str_radix("z", 36), Ok(35 as $T));
-            assert_eq!($T::from_str_radix("Z", 36), Ok(35 as $T));
-
-            assert_eq!($T::from_str_radix("-123", 10), Ok(-123 as $T));
-            assert_eq!($T::from_str_radix("-1001", 2), Ok(-9 as $T));
-            assert_eq!($T::from_str_radix("-123", 8), Ok(-83 as $T));
-            assert_eq!(i32::from_str_radix("-123", 16), Ok(-291 as i32));
-            assert_eq!(i32::from_str_radix("-ffff", 16), Ok(-65535 as i32));
-            assert_eq!(i32::from_str_radix("-FFFF", 16), Ok(-65535 as i32));
-            assert_eq!($T::from_str_radix("-z", 36), Ok(-35 as $T));
-            assert_eq!($T::from_str_radix("-Z", 36), Ok(-35 as $T));
-
-            assert_eq!($T::from_str_radix("Z", 35).ok(), None::<$T>);
-            assert_eq!($T::from_str_radix("-9", 2).ok(), None::<$T>);
-            assert_eq!($T::from_str_radix("10_0", 10).ok(), None::<$T>);
-            assert_eq!(u32::from_str_radix("-9", 10).ok(), None::<u32>);
-        }
+        test_runtime_and_compiletime! {
+            fn test_from_str_radix() {
+                assert_eq_const_safe!($T::from_str_radix("123", 10), Ok(123 as $T));
+                assert_eq_const_safe!($T::from_str_radix("1001", 2), Ok(9 as $T));
+                assert_eq_const_safe!($T::from_str_radix("123", 8), Ok(83 as $T));
+                assert_eq_const_safe!(i32::from_str_radix("123", 16), Ok(291 as i32));
+                assert_eq_const_safe!(i32::from_str_radix("ffff", 16), Ok(65535 as i32));
+                assert_eq_const_safe!(i32::from_str_radix("FFFF", 16), Ok(65535 as i32));
+                assert_eq_const_safe!($T::from_str_radix("z", 36), Ok(35 as $T));
+                assert_eq_const_safe!($T::from_str_radix("Z", 36), Ok(35 as $T));
+
+                assert_eq_const_safe!($T::from_str_radix("-123", 10), Ok(-123 as $T));
+                assert_eq_const_safe!($T::from_str_radix("-1001", 2), Ok(-9 as $T));
+                assert_eq_const_safe!($T::from_str_radix("-123", 8), Ok(-83 as $T));
+                assert_eq_const_safe!(i32::from_str_radix("-123", 16), Ok(-291 as i32));
+                assert_eq_const_safe!(i32::from_str_radix("-ffff", 16), Ok(-65535 as i32));
+                assert_eq_const_safe!(i32::from_str_radix("-FFFF", 16), Ok(-65535 as i32));
+                assert_eq_const_safe!($T::from_str_radix("-z", 36), Ok(-35 as $T));
+                assert_eq_const_safe!($T::from_str_radix("-Z", 36), Ok(-35 as $T));
+
+                assert!($T::from_str_radix("Z", 35).is_err());
+                assert!($T::from_str_radix("-9", 2).is_err());
+                assert!($T::from_str_radix("10_0", 10).is_err());
+                assert!(u32::from_str_radix("-9", 10).is_err());
+            }
 
-        #[test]
-        fn test_pow() {
-            let mut r = 2 as $T;
-            assert_eq!(r.pow(2), 4 as $T);
-            assert_eq!(r.pow(0), 1 as $T);
-            assert_eq!(r.wrapping_pow(2), 4 as $T);
-            assert_eq!(r.wrapping_pow(0), 1 as $T);
-            assert_eq!(r.checked_pow(2), Some(4 as $T));
-            assert_eq!(r.checked_pow(0), Some(1 as $T));
-            assert_eq!(r.overflowing_pow(2), (4 as $T, false));
-            assert_eq!(r.overflowing_pow(0), (1 as $T, false));
-            assert_eq!(r.saturating_pow(2), 4 as $T);
-            assert_eq!(r.saturating_pow(0), 1 as $T);
-
-            r = MAX;
-            // use `^` to represent .pow() with no overflow.
-            // if itest::MAX == 2^j-1, then itest is a `j` bit int,
-            // so that `itest::MAX*itest::MAX == 2^(2*j)-2^(j+1)+1`,
-            // thussaturating_pow the overflowing result is exactly 1.
-            assert_eq!(r.wrapping_pow(2), 1 as $T);
-            assert_eq!(r.checked_pow(2), None);
-            assert_eq!(r.overflowing_pow(2), (1 as $T, true));
-            assert_eq!(r.saturating_pow(2), MAX);
-            //test for negative exponent.
-            r = -2 as $T;
-            assert_eq!(r.pow(2), 4 as $T);
-            assert_eq!(r.pow(3), -8 as $T);
-            assert_eq!(r.pow(0), 1 as $T);
-            assert_eq!(r.wrapping_pow(2), 4 as $T);
-            assert_eq!(r.wrapping_pow(3), -8 as $T);
-            assert_eq!(r.wrapping_pow(0), 1 as $T);
-            assert_eq!(r.checked_pow(2), Some(4 as $T));
-            assert_eq!(r.checked_pow(3), Some(-8 as $T));
-            assert_eq!(r.checked_pow(0), Some(1 as $T));
-            assert_eq!(r.overflowing_pow(2), (4 as $T, false));
-            assert_eq!(r.overflowing_pow(3), (-8 as $T, false));
-            assert_eq!(r.overflowing_pow(0), (1 as $T, false));
-            assert_eq!(r.saturating_pow(2), 4 as $T);
-            assert_eq!(r.saturating_pow(3), -8 as $T);
-            assert_eq!(r.saturating_pow(0), 1 as $T);
-        }
+            fn test_pow() {
+                {
+                    const R: $T = 2;
+                    assert_eq_const_safe!(R.pow(2), 4 as $T);
+                    assert_eq_const_safe!(R.pow(0), 1 as $T);
+                    assert_eq_const_safe!(R.wrapping_pow(2), 4 as $T);
+                    assert_eq_const_safe!(R.wrapping_pow(0), 1 as $T);
+                    assert_eq_const_safe!(R.checked_pow(2), Some(4 as $T));
+                    assert_eq_const_safe!(R.checked_pow(0), Some(1 as $T));
+                    assert_eq_const_safe!(R.overflowing_pow(2), (4 as $T, false));
+                    assert_eq_const_safe!(R.overflowing_pow(0), (1 as $T, false));
+                    assert_eq_const_safe!(R.saturating_pow(2), 4 as $T);
+                    assert_eq_const_safe!(R.saturating_pow(0), 1 as $T);
+                }
+
+                {
+                    const R: $T = MAX;
+                    // use `^` to represent .pow() with no overflow.
+                    // if itest::MAX == 2^j-1, then itest is a `j` bit int,
+                    // so that `itest::MAX*itest::MAX == 2^(2*j)-2^(j+1)+1`,
+                    // thussaturating_pow the overflowing result is exactly 1.
+                    assert_eq_const_safe!(R.wrapping_pow(2), 1 as $T);
+                    assert_eq_const_safe!(R.checked_pow(2), None);
+                    assert_eq_const_safe!(R.overflowing_pow(2), (1 as $T, true));
+                    assert_eq_const_safe!(R.saturating_pow(2), MAX);
+                }
+
+                {
+                    // test for negative exponent.
+                    const R: $T = -2;
+                    assert_eq_const_safe!(R.pow(2), 4 as $T);
+                    assert_eq_const_safe!(R.pow(3), -8 as $T);
+                    assert_eq_const_safe!(R.pow(0), 1 as $T);
+                    assert_eq_const_safe!(R.wrapping_pow(2), 4 as $T);
+                    assert_eq_const_safe!(R.wrapping_pow(3), -8 as $T);
+                    assert_eq_const_safe!(R.wrapping_pow(0), 1 as $T);
+                    assert_eq_const_safe!(R.checked_pow(2), Some(4 as $T));
+                    assert_eq_const_safe!(R.checked_pow(3), Some(-8 as $T));
+                    assert_eq_const_safe!(R.checked_pow(0), Some(1 as $T));
+                    assert_eq_const_safe!(R.overflowing_pow(2), (4 as $T, false));
+                    assert_eq_const_safe!(R.overflowing_pow(3), (-8 as $T, false));
+                    assert_eq_const_safe!(R.overflowing_pow(0), (1 as $T, false));
+                    assert_eq_const_safe!(R.saturating_pow(2), 4 as $T);
+                    assert_eq_const_safe!(R.saturating_pow(3), -8 as $T);
+                    assert_eq_const_safe!(R.saturating_pow(0), 1 as $T);
+                }
+            }
 
-        #[test]
-        fn test_div_floor() {
-            let a: $T = 8;
-            let b = 3;
-            assert_eq!(a.div_floor(b), 2);
-            assert_eq!(a.div_floor(-b), -3);
-            assert_eq!((-a).div_floor(b), -3);
-            assert_eq!((-a).div_floor(-b), 2);
-        }
+            fn test_div_floor() {
+                const A: $T = 8;
+                const B: $T = 3;
+                assert_eq_const_safe!(A.div_floor(B), 2);
+                assert_eq_const_safe!(A.div_floor(-B), -3);
+                assert_eq_const_safe!((-A).div_floor(B), -3);
+                assert_eq_const_safe!((-A).div_floor(-B), 2);
+            }
 
-        #[test]
-        fn test_div_ceil() {
-            let a: $T = 8;
-            let b = 3;
-            assert_eq!(a.div_ceil(b), 3);
-            assert_eq!(a.div_ceil(-b), -2);
-            assert_eq!((-a).div_ceil(b), -2);
-            assert_eq!((-a).div_ceil(-b), 3);
-        }
+            fn test_div_ceil() {
+                const A: $T = 8;
+                const B: $T = 3;
+                assert_eq_const_safe!(A.div_ceil(B), 3);
+                assert_eq_const_safe!(A.div_ceil(-B), -2);
+                assert_eq_const_safe!((-A).div_ceil(B), -2);
+                assert_eq_const_safe!((-A).div_ceil(-B), 3);
+            }
 
-        #[test]
-        fn test_next_multiple_of() {
-            assert_eq!((16 as $T).next_multiple_of(8), 16);
-            assert_eq!((23 as $T).next_multiple_of(8), 24);
-            assert_eq!((16 as $T).next_multiple_of(-8), 16);
-            assert_eq!((23 as $T).next_multiple_of(-8), 16);
-            assert_eq!((-16 as $T).next_multiple_of(8), -16);
-            assert_eq!((-23 as $T).next_multiple_of(8), -16);
-            assert_eq!((-16 as $T).next_multiple_of(-8), -16);
-            assert_eq!((-23 as $T).next_multiple_of(-8), -24);
-            assert_eq!(MIN.next_multiple_of(-1), MIN);
-        }
+            fn test_next_multiple_of() {
+                assert_eq_const_safe!((16 as $T).next_multiple_of(8), 16);
+                assert_eq_const_safe!((23 as $T).next_multiple_of(8), 24);
+                assert_eq_const_safe!((16 as $T).next_multiple_of(-8), 16);
+                assert_eq_const_safe!((23 as $T).next_multiple_of(-8), 16);
+                assert_eq_const_safe!((-16 as $T).next_multiple_of(8), -16);
+                assert_eq_const_safe!((-23 as $T).next_multiple_of(8), -16);
+                assert_eq_const_safe!((-16 as $T).next_multiple_of(-8), -16);
+                assert_eq_const_safe!((-23 as $T).next_multiple_of(-8), -24);
+                assert_eq_const_safe!(MIN.next_multiple_of(-1), MIN);
+            }
 
-        #[test]
-        fn test_checked_next_multiple_of() {
-            assert_eq!((16 as $T).checked_next_multiple_of(8), Some(16));
-            assert_eq!((23 as $T).checked_next_multiple_of(8), Some(24));
-            assert_eq!((16 as $T).checked_next_multiple_of(-8), Some(16));
-            assert_eq!((23 as $T).checked_next_multiple_of(-8), Some(16));
-            assert_eq!((-16 as $T).checked_next_multiple_of(8), Some(-16));
-            assert_eq!((-23 as $T).checked_next_multiple_of(8), Some(-16));
-            assert_eq!((-16 as $T).checked_next_multiple_of(-8), Some(-16));
-            assert_eq!((-23 as $T).checked_next_multiple_of(-8), Some(-24));
-            assert_eq!((1 as $T).checked_next_multiple_of(0), None);
-            assert_eq!(MAX.checked_next_multiple_of(2), None);
-            assert_eq!(MIN.checked_next_multiple_of(-3), None);
-            assert_eq!(MIN.checked_next_multiple_of(-1), Some(MIN));
-        }
+            fn test_checked_next_multiple_of() {
+                assert_eq_const_safe!((16 as $T).checked_next_multiple_of(8), Some(16));
+                assert_eq_const_safe!((23 as $T).checked_next_multiple_of(8), Some(24));
+                assert_eq_const_safe!((16 as $T).checked_next_multiple_of(-8), Some(16));
+                assert_eq_const_safe!((23 as $T).checked_next_multiple_of(-8), Some(16));
+                assert_eq_const_safe!((-16 as $T).checked_next_multiple_of(8), Some(-16));
+                assert_eq_const_safe!((-23 as $T).checked_next_multiple_of(8), Some(-16));
+                assert_eq_const_safe!((-16 as $T).checked_next_multiple_of(-8), Some(-16));
+                assert_eq_const_safe!((-23 as $T).checked_next_multiple_of(-8), Some(-24));
+                assert_eq_const_safe!((1 as $T).checked_next_multiple_of(0), None);
+                assert_eq_const_safe!(MAX.checked_next_multiple_of(2), None);
+                assert_eq_const_safe!(MIN.checked_next_multiple_of(-3), None);
+                assert_eq_const_safe!(MIN.checked_next_multiple_of(-1), Some(MIN));
+            }
 
-        #[test]
-        fn test_carrying_add() {
-            assert_eq!($T::MAX.carrying_add(1, false), ($T::MIN, true));
-            assert_eq!($T::MAX.carrying_add(0, true), ($T::MIN, true));
-            assert_eq!($T::MAX.carrying_add(1, true), ($T::MIN + 1, true));
-            assert_eq!($T::MAX.carrying_add(-1, false), ($T::MAX - 1, false));
-            assert_eq!($T::MAX.carrying_add(-1, true), ($T::MAX, false)); // no intermediate overflow
-            assert_eq!($T::MIN.carrying_add(-1, false), ($T::MAX, true));
-            assert_eq!($T::MIN.carrying_add(-1, true), ($T::MIN, false)); // no intermediate overflow
-            assert_eq!((0 as $T).carrying_add($T::MAX, true), ($T::MIN, true));
-            assert_eq!((0 as $T).carrying_add($T::MIN, true), ($T::MIN + 1, false));
-        }
+            fn test_carrying_add() {
+                assert_eq_const_safe!(MAX.carrying_add(1, false), (MIN, true));
+                assert_eq_const_safe!(MAX.carrying_add(0, true), (MIN, true));
+                assert_eq_const_safe!(MAX.carrying_add(1, true), (MIN + 1, true));
+                assert_eq_const_safe!(MAX.carrying_add(-1, false), (MAX - 1, false));
+                assert_eq_const_safe!(MAX.carrying_add(-1, true), (MAX, false)); // no intermediate overflow
+                assert_eq_const_safe!(MIN.carrying_add(-1, false), (MAX, true));
+                assert_eq_const_safe!(MIN.carrying_add(-1, true), (MIN, false)); // no intermediate overflow
+                assert_eq_const_safe!((0 as $T).carrying_add(MAX, true), (MIN, true));
+                assert_eq_const_safe!((0 as $T).carrying_add(MIN, true), (MIN + 1, false));
+            }
 
-        #[test]
-        fn test_borrowing_sub() {
-            assert_eq!($T::MIN.borrowing_sub(1, false), ($T::MAX, true));
-            assert_eq!($T::MIN.borrowing_sub(0, true), ($T::MAX, true));
-            assert_eq!($T::MIN.borrowing_sub(1, true), ($T::MAX - 1, true));
-            assert_eq!($T::MIN.borrowing_sub(-1, false), ($T::MIN + 1, false));
-            assert_eq!($T::MIN.borrowing_sub(-1, true), ($T::MIN, false)); // no intermediate overflow
-            assert_eq!($T::MAX.borrowing_sub(-1, false), ($T::MIN, true));
-            assert_eq!($T::MAX.borrowing_sub(-1, true), ($T::MAX, false)); // no intermediate overflow
-            assert_eq!((0 as $T).borrowing_sub($T::MIN, false), ($T::MIN, true));
-            assert_eq!((0 as $T).borrowing_sub($T::MIN, true), ($T::MAX, false));
-        }
+            fn test_borrowing_sub() {
+                assert_eq_const_safe!(MIN.borrowing_sub(1, false), (MAX, true));
+                assert_eq_const_safe!(MIN.borrowing_sub(0, true), (MAX, true));
+                assert_eq_const_safe!(MIN.borrowing_sub(1, true), (MAX - 1, true));
+                assert_eq_const_safe!(MIN.borrowing_sub(-1, false), (MIN + 1, false));
+                assert_eq_const_safe!(MIN.borrowing_sub(-1, true), (MIN, false)); // no intermediate overflow
+                assert_eq_const_safe!(MAX.borrowing_sub(-1, false), (MIN, true));
+                assert_eq_const_safe!(MAX.borrowing_sub(-1, true), (MAX, false)); // no intermediate overflow
+                assert_eq_const_safe!((0 as $T).borrowing_sub(MIN, false), (MIN, true));
+                assert_eq_const_safe!((0 as $T).borrowing_sub(MIN, true), (MAX, false));
+            }
 
-        #[test]
-        fn test_midpoint() {
-            assert_eq!(<$T>::midpoint(1, 3), 2);
-            assert_eq!(<$T>::midpoint(3, 1), 2);
-
-            assert_eq!(<$T>::midpoint(0, 0), 0);
-            assert_eq!(<$T>::midpoint(0, 2), 1);
-            assert_eq!(<$T>::midpoint(2, 0), 1);
-            assert_eq!(<$T>::midpoint(2, 2), 2);
-
-            assert_eq!(<$T>::midpoint(1, 4), 2);
-            assert_eq!(<$T>::midpoint(4, 1), 2);
-            assert_eq!(<$T>::midpoint(3, 4), 3);
-            assert_eq!(<$T>::midpoint(4, 3), 3);
-
-            assert_eq!(<$T>::midpoint(<$T>::MIN, <$T>::MAX), -1);
-            assert_eq!(<$T>::midpoint(<$T>::MAX, <$T>::MIN), -1);
-            assert_eq!(<$T>::midpoint(<$T>::MIN, <$T>::MIN), <$T>::MIN);
-            assert_eq!(<$T>::midpoint(<$T>::MAX, <$T>::MAX), <$T>::MAX);
-
-            assert_eq!(<$T>::midpoint(<$T>::MIN, 6), <$T>::MIN / 2 + 3);
-            assert_eq!(<$T>::midpoint(6, <$T>::MIN), <$T>::MIN / 2 + 3);
-            assert_eq!(<$T>::midpoint(<$T>::MAX, 6), <$T>::MAX / 2 + 3);
-            assert_eq!(<$T>::midpoint(6, <$T>::MAX), <$T>::MAX / 2 + 3);
+            fn test_midpoint() {
+                assert_eq_const_safe!(<$T>::midpoint(1, 3), 2);
+                assert_eq_const_safe!(<$T>::midpoint(3, 1), 2);
+
+                assert_eq_const_safe!(<$T>::midpoint(0, 0), 0);
+                assert_eq_const_safe!(<$T>::midpoint(0, 2), 1);
+                assert_eq_const_safe!(<$T>::midpoint(2, 0), 1);
+                assert_eq_const_safe!(<$T>::midpoint(2, 2), 2);
+
+                assert_eq_const_safe!(<$T>::midpoint(1, 4), 2);
+                assert_eq_const_safe!(<$T>::midpoint(4, 1), 2);
+                assert_eq_const_safe!(<$T>::midpoint(3, 4), 3);
+                assert_eq_const_safe!(<$T>::midpoint(4, 3), 3);
+
+                assert_eq_const_safe!(<$T>::midpoint(<$T>::MIN, <$T>::MAX), -1);
+                assert_eq_const_safe!(<$T>::midpoint(<$T>::MAX, <$T>::MIN), -1);
+                assert_eq_const_safe!(<$T>::midpoint(<$T>::MIN, <$T>::MIN), <$T>::MIN);
+                assert_eq_const_safe!(<$T>::midpoint(<$T>::MAX, <$T>::MAX), <$T>::MAX);
+
+                assert_eq_const_safe!(<$T>::midpoint(<$T>::MIN, 6), <$T>::MIN / 2 + 3);
+                assert_eq_const_safe!(<$T>::midpoint(6, <$T>::MIN), <$T>::MIN / 2 + 3);
+                assert_eq_const_safe!(<$T>::midpoint(<$T>::MAX, 6), <$T>::MAX / 2 + 3);
+                assert_eq_const_safe!(<$T>::midpoint(6, <$T>::MAX), <$T>::MAX / 2 + 3);
+            }
         }
     };
 }
diff --git a/library/core/tests/num/uint_macros.rs b/library/core/tests/num/uint_macros.rs
index f4fa789461e..105aad4522d 100644
--- a/library/core/tests/num/uint_macros.rs
+++ b/library/core/tests/num/uint_macros.rs
@@ -2,7 +2,6 @@ macro_rules! uint_module {
     ($T:ident) => {
         use core::ops::{BitAnd, BitOr, BitXor, Not, Shl, Shr};
         use core::$T::*;
-        use std::str::FromStr;
 
         use crate::num;
 
@@ -35,122 +34,115 @@ macro_rules! uint_module {
         const _0: $T = 0;
         const _1: $T = !0;
 
-        #[test]
-        fn test_count_ones() {
-            assert!(A.count_ones() == 3);
-            assert!(B.count_ones() == 2);
-            assert!(C.count_ones() == 5);
-        }
+        test_runtime_and_compiletime! {
+            fn test_count_ones() {
+                assert!(A.count_ones() == 3);
+                assert!(B.count_ones() == 2);
+                assert!(C.count_ones() == 5);
+            }
 
-        #[test]
-        fn test_count_zeros() {
-            assert!(A.count_zeros() == $T::BITS - 3);
-            assert!(B.count_zeros() == $T::BITS - 2);
-            assert!(C.count_zeros() == $T::BITS - 5);
-        }
+            fn test_count_zeros() {
+                assert!(A.count_zeros() == $T::BITS - 3);
+                assert!(B.count_zeros() == $T::BITS - 2);
+                assert!(C.count_zeros() == $T::BITS - 5);
+            }
 
-        #[test]
-        fn test_leading_trailing_ones() {
-            let a: $T = 0b0101_1111;
-            assert_eq!(a.trailing_ones(), 5);
-            assert_eq!((!a).leading_ones(), $T::BITS - 7);
+            fn test_leading_trailing_ones() {
+                const A: $T = 0b0101_1111;
+                assert_eq_const_safe!(A.trailing_ones(), 5);
+                assert_eq_const_safe!((!A).leading_ones(), $T::BITS - 7);
 
-            assert_eq!(a.reverse_bits().leading_ones(), 5);
+                assert_eq_const_safe!(A.reverse_bits().leading_ones(), 5);
 
-            assert_eq!(_1.leading_ones(), $T::BITS);
-            assert_eq!(_1.trailing_ones(), $T::BITS);
+                assert_eq_const_safe!(_1.leading_ones(), $T::BITS);
+                assert_eq_const_safe!(_1.trailing_ones(), $T::BITS);
 
-            assert_eq!((_1 << 1).trailing_ones(), 0);
-            assert_eq!((_1 >> 1).leading_ones(), 0);
+                assert_eq_const_safe!((_1 << 1).trailing_ones(), 0);
+                assert_eq_const_safe!((_1 >> 1).leading_ones(), 0);
 
-            assert_eq!((_1 << 1).leading_ones(), $T::BITS - 1);
-            assert_eq!((_1 >> 1).trailing_ones(), $T::BITS - 1);
+                assert_eq_const_safe!((_1 << 1).leading_ones(), $T::BITS - 1);
+                assert_eq_const_safe!((_1 >> 1).trailing_ones(), $T::BITS - 1);
 
-            assert_eq!(_0.leading_ones(), 0);
-            assert_eq!(_0.trailing_ones(), 0);
+                assert_eq_const_safe!(_0.leading_ones(), 0);
+                assert_eq_const_safe!(_0.trailing_ones(), 0);
 
-            let x: $T = 0b0010_1100;
-            assert_eq!(x.leading_ones(), 0);
-            assert_eq!(x.trailing_ones(), 0);
-        }
+                const X: $T = 0b0010_1100;
+                assert_eq_const_safe!(X.leading_ones(), 0);
+                assert_eq_const_safe!(X.trailing_ones(), 0);
+            }
 
-        #[test]
-        fn test_rotate() {
-            assert_eq!(A.rotate_left(6).rotate_right(2).rotate_right(4), A);
-            assert_eq!(B.rotate_left(3).rotate_left(2).rotate_right(5), B);
-            assert_eq!(C.rotate_left(6).rotate_right(2).rotate_right(4), C);
-
-            // Rotating these should make no difference
-            //
-            // We test using 124 bits because to ensure that overlong bit shifts do
-            // not cause undefined behaviour. See #10183.
-            assert_eq!(_0.rotate_left(124), _0);
-            assert_eq!(_1.rotate_left(124), _1);
-            assert_eq!(_0.rotate_right(124), _0);
-            assert_eq!(_1.rotate_right(124), _1);
-
-            // Rotating by 0 should have no effect
-            assert_eq!(A.rotate_left(0), A);
-            assert_eq!(B.rotate_left(0), B);
-            assert_eq!(C.rotate_left(0), C);
-            // Rotating by a multiple of word size should also have no effect
-            assert_eq!(A.rotate_left(128), A);
-            assert_eq!(B.rotate_left(128), B);
-            assert_eq!(C.rotate_left(128), C);
-        }
+            fn test_rotate() {
+                assert_eq_const_safe!(A.rotate_left(6).rotate_right(2).rotate_right(4), A);
+                assert_eq_const_safe!(B.rotate_left(3).rotate_left(2).rotate_right(5), B);
+                assert_eq_const_safe!(C.rotate_left(6).rotate_right(2).rotate_right(4), C);
+
+                // Rotating these should make no difference
+                //
+                // We test using 124 bits because to ensure that overlong bit shifts do
+                // not cause undefined behaviour. See #10183.
+                assert_eq_const_safe!(_0.rotate_left(124), _0);
+                assert_eq_const_safe!(_1.rotate_left(124), _1);
+                assert_eq_const_safe!(_0.rotate_right(124), _0);
+                assert_eq_const_safe!(_1.rotate_right(124), _1);
+
+                // Rotating by 0 should have no effect
+                assert_eq_const_safe!(A.rotate_left(0), A);
+                assert_eq_const_safe!(B.rotate_left(0), B);
+                assert_eq_const_safe!(C.rotate_left(0), C);
+                // Rotating by a multiple of word size should also have no effect
+                assert_eq_const_safe!(A.rotate_left(128), A);
+                assert_eq_const_safe!(B.rotate_left(128), B);
+                assert_eq_const_safe!(C.rotate_left(128), C);
+            }
 
-        #[test]
-        fn test_swap_bytes() {
-            assert_eq!(A.swap_bytes().swap_bytes(), A);
-            assert_eq!(B.swap_bytes().swap_bytes(), B);
-            assert_eq!(C.swap_bytes().swap_bytes(), C);
-
-            // Swapping these should make no difference
-            assert_eq!(_0.swap_bytes(), _0);
-            assert_eq!(_1.swap_bytes(), _1);
-        }
+            fn test_swap_bytes() {
+                assert_eq_const_safe!(A.swap_bytes().swap_bytes(), A);
+                assert_eq_const_safe!(B.swap_bytes().swap_bytes(), B);
+                assert_eq_const_safe!(C.swap_bytes().swap_bytes(), C);
 
-        #[test]
-        fn test_reverse_bits() {
-            assert_eq!(A.reverse_bits().reverse_bits(), A);
-            assert_eq!(B.reverse_bits().reverse_bits(), B);
-            assert_eq!(C.reverse_bits().reverse_bits(), C);
-
-            // Swapping these should make no difference
-            assert_eq!(_0.reverse_bits(), _0);
-            assert_eq!(_1.reverse_bits(), _1);
-        }
+                // Swapping these should make no difference
+                assert_eq_const_safe!(_0.swap_bytes(), _0);
+                assert_eq_const_safe!(_1.swap_bytes(), _1);
+            }
 
-        #[test]
-        fn test_le() {
-            assert_eq!($T::from_le(A.to_le()), A);
-            assert_eq!($T::from_le(B.to_le()), B);
-            assert_eq!($T::from_le(C.to_le()), C);
-            assert_eq!($T::from_le(_0), _0);
-            assert_eq!($T::from_le(_1), _1);
-            assert_eq!(_0.to_le(), _0);
-            assert_eq!(_1.to_le(), _1);
-        }
+            fn test_reverse_bits() {
+                assert_eq_const_safe!(A.reverse_bits().reverse_bits(), A);
+                assert_eq_const_safe!(B.reverse_bits().reverse_bits(), B);
+                assert_eq_const_safe!(C.reverse_bits().reverse_bits(), C);
 
-        #[test]
-        fn test_be() {
-            assert_eq!($T::from_be(A.to_be()), A);
-            assert_eq!($T::from_be(B.to_be()), B);
-            assert_eq!($T::from_be(C.to_be()), C);
-            assert_eq!($T::from_be(_0), _0);
-            assert_eq!($T::from_be(_1), _1);
-            assert_eq!(_0.to_be(), _0);
-            assert_eq!(_1.to_be(), _1);
-        }
+                // Swapping these should make no difference
+                assert_eq_const_safe!(_0.reverse_bits(), _0);
+                assert_eq_const_safe!(_1.reverse_bits(), _1);
+            }
 
-        #[test]
-        fn test_unsigned_checked_div() {
-            assert!((10 as $T).checked_div(2) == Some(5));
-            assert!((5 as $T).checked_div(0) == None);
+            fn test_le() {
+                assert_eq_const_safe!($T::from_le(A.to_le()), A);
+                assert_eq_const_safe!($T::from_le(B.to_le()), B);
+                assert_eq_const_safe!($T::from_le(C.to_le()), C);
+                assert_eq_const_safe!($T::from_le(_0), _0);
+                assert_eq_const_safe!($T::from_le(_1), _1);
+                assert_eq_const_safe!(_0.to_le(), _0);
+                assert_eq_const_safe!(_1.to_le(), _1);
+            }
+
+            fn test_be() {
+                assert_eq_const_safe!($T::from_be(A.to_be()), A);
+                assert_eq_const_safe!($T::from_be(B.to_be()), B);
+                assert_eq_const_safe!($T::from_be(C.to_be()), C);
+                assert_eq_const_safe!($T::from_be(_0), _0);
+                assert_eq_const_safe!($T::from_be(_1), _1);
+                assert_eq_const_safe!(_0.to_be(), _0);
+                assert_eq_const_safe!(_1.to_be(), _1);
+            }
+
+            fn test_unsigned_checked_div() {
+                assert_eq_const_safe!((10 as $T).checked_div(2), Some(5));
+                assert_eq_const_safe!((5 as $T).checked_div(0), None);
+            }
         }
 
-        fn from_str<T: FromStr>(t: &str) -> Option<T> {
-            FromStr::from_str(t).ok()
+        fn from_str<T: core::str::FromStr>(t: &str) -> Option<T> {
+            core::str::FromStr::from_str(t).ok()
         }
 
         #[test]
@@ -166,52 +158,55 @@ macro_rules! uint_module {
             assert_eq!(from_str::<$T>("x"), None);
         }
 
-        #[test]
-        pub fn test_parse_bytes() {
-            assert_eq!($T::from_str_radix("123", 10), Ok(123 as $T));
-            assert_eq!($T::from_str_radix("1001", 2), Ok(9 as $T));
-            assert_eq!($T::from_str_radix("123", 8), Ok(83 as $T));
-            assert_eq!(u16::from_str_radix("123", 16), Ok(291 as u16));
-            assert_eq!(u16::from_str_radix("ffff", 16), Ok(65535 as u16));
-            assert_eq!($T::from_str_radix("z", 36), Ok(35 as $T));
-
-            assert_eq!($T::from_str_radix("Z", 10).ok(), None::<$T>);
-            assert_eq!($T::from_str_radix("_", 2).ok(), None::<$T>);
-        }
+        test_runtime_and_compiletime! {
+            fn test_parse_bytes() {
+                assert_eq_const_safe!($T::from_str_radix("123", 10), Ok(123 as $T));
+                assert_eq_const_safe!($T::from_str_radix("1001", 2), Ok(9 as $T));
+                assert_eq_const_safe!($T::from_str_radix("123", 8), Ok(83 as $T));
+                assert_eq_const_safe!(u16::from_str_radix("123", 16), Ok(291 as u16));
+                assert_eq_const_safe!(u16::from_str_radix("ffff", 16), Ok(65535 as u16));
+                assert_eq_const_safe!($T::from_str_radix("z", 36), Ok(35 as $T));
+
+                assert!($T::from_str_radix("Z", 10).is_err());
+                assert!($T::from_str_radix("_", 2).is_err());
+            }
 
-        #[test]
-        fn test_pow() {
-            let mut r = 2 as $T;
-            assert_eq!(r.pow(2), 4 as $T);
-            assert_eq!(r.pow(0), 1 as $T);
-            assert_eq!(r.wrapping_pow(2), 4 as $T);
-            assert_eq!(r.wrapping_pow(0), 1 as $T);
-            assert_eq!(r.checked_pow(2), Some(4 as $T));
-            assert_eq!(r.checked_pow(0), Some(1 as $T));
-            assert_eq!(r.overflowing_pow(2), (4 as $T, false));
-            assert_eq!(r.overflowing_pow(0), (1 as $T, false));
-            assert_eq!(r.saturating_pow(2), 4 as $T);
-            assert_eq!(r.saturating_pow(0), 1 as $T);
-
-            r = MAX;
-            // use `^` to represent .pow() with no overflow.
-            // if itest::MAX == 2^j-1, then itest is a `j` bit int,
-            // so that `itest::MAX*itest::MAX == 2^(2*j)-2^(j+1)+1`,
-            // thussaturating_pow the overflowing result is exactly 1.
-            assert_eq!(r.wrapping_pow(2), 1 as $T);
-            assert_eq!(r.checked_pow(2), None);
-            assert_eq!(r.overflowing_pow(2), (1 as $T, true));
-            assert_eq!(r.saturating_pow(2), MAX);
-        }
+            fn test_pow() {
+                {
+                    const R: $T = 2;
+                    assert_eq_const_safe!(R.pow(2), 4 as $T);
+                    assert_eq_const_safe!(R.pow(0), 1 as $T);
+                    assert_eq_const_safe!(R.wrapping_pow(2), 4 as $T);
+                    assert_eq_const_safe!(R.wrapping_pow(0), 1 as $T);
+                    assert_eq_const_safe!(R.checked_pow(2), Some(4 as $T));
+                    assert_eq_const_safe!(R.checked_pow(0), Some(1 as $T));
+                    assert_eq_const_safe!(R.overflowing_pow(2), (4 as $T, false));
+                    assert_eq_const_safe!(R.overflowing_pow(0), (1 as $T, false));
+                    assert_eq_const_safe!(R.saturating_pow(2), 4 as $T);
+                    assert_eq_const_safe!(R.saturating_pow(0), 1 as $T);
+                }
+
+                {
+                    const R: $T = $T::MAX;
+                    // use `^` to represent .pow() with no overflow.
+                    // if itest::MAX == 2^j-1, then itest is a `j` bit int,
+                    // so that `itest::MAX*itest::MAX == 2^(2*j)-2^(j+1)+1`,
+                    // thussaturating_pow the overflowing result is exactly 1.
+                    assert_eq_const_safe!(R.wrapping_pow(2), 1 as $T);
+                    assert_eq_const_safe!(R.checked_pow(2), None);
+                    assert_eq_const_safe!(R.overflowing_pow(2), (1 as $T, true));
+                    assert_eq_const_safe!(R.saturating_pow(2), MAX);
+                }
+            }
 
-        #[test]
-        fn test_isqrt() {
-            assert_eq!((0 as $T).isqrt(), 0 as $T);
-            assert_eq!((1 as $T).isqrt(), 1 as $T);
-            assert_eq!((2 as $T).isqrt(), 1 as $T);
-            assert_eq!((99 as $T).isqrt(), 9 as $T);
-            assert_eq!((100 as $T).isqrt(), 10 as $T);
-            assert_eq!($T::MAX.isqrt(), (1 << ($T::BITS / 2)) - 1);
+            fn test_isqrt() {
+                assert_eq_const_safe!((0 as $T).isqrt(), 0 as $T);
+                assert_eq_const_safe!((1 as $T).isqrt(), 1 as $T);
+                assert_eq_const_safe!((2 as $T).isqrt(), 1 as $T);
+                assert_eq_const_safe!((99 as $T).isqrt(), 9 as $T);
+                assert_eq_const_safe!((100 as $T).isqrt(), 10 as $T);
+                assert_eq_const_safe!($T::MAX.isqrt(), (1 << ($T::BITS / 2)) - 1);
+            }
         }
 
         #[cfg(not(miri))] // Miri is too slow
@@ -233,85 +228,79 @@ macro_rules! uint_module {
             }
         }
 
-        #[test]
-        fn test_div_floor() {
-            assert_eq!((8 as $T).div_floor(3), 2);
-        }
+        test_runtime_and_compiletime! {
+            fn test_div_floor() {
+                assert_eq_const_safe!((8 as $T).div_floor(3), 2);
+            }
 
-        #[test]
-        fn test_div_ceil() {
-            assert_eq!((8 as $T).div_ceil(3), 3);
-        }
+            fn test_div_ceil() {
+                assert_eq_const_safe!((8 as $T).div_ceil(3), 3);
+            }
 
-        #[test]
-        fn test_next_multiple_of() {
-            assert_eq!((16 as $T).next_multiple_of(8), 16);
-            assert_eq!((23 as $T).next_multiple_of(8), 24);
-            assert_eq!(MAX.next_multiple_of(1), MAX);
-        }
+            fn test_next_multiple_of() {
+                assert_eq_const_safe!((16 as $T).next_multiple_of(8), 16);
+                assert_eq_const_safe!((23 as $T).next_multiple_of(8), 24);
+                assert_eq_const_safe!(MAX.next_multiple_of(1), MAX);
+            }
 
-        #[test]
-        fn test_checked_next_multiple_of() {
-            assert_eq!((16 as $T).checked_next_multiple_of(8), Some(16));
-            assert_eq!((23 as $T).checked_next_multiple_of(8), Some(24));
-            assert_eq!((1 as $T).checked_next_multiple_of(0), None);
-            assert_eq!(MAX.checked_next_multiple_of(2), None);
-        }
+            fn test_checked_next_multiple_of() {
+                assert_eq_const_safe!((16 as $T).checked_next_multiple_of(8), Some(16));
+                assert_eq_const_safe!((23 as $T).checked_next_multiple_of(8), Some(24));
+                assert_eq_const_safe!((1 as $T).checked_next_multiple_of(0), None);
+                assert_eq_const_safe!(MAX.checked_next_multiple_of(2), None);
+            }
 
-        #[test]
-        fn test_is_next_multiple_of() {
-            assert!((12 as $T).is_multiple_of(4));
-            assert!(!(12 as $T).is_multiple_of(5));
-            assert!((0 as $T).is_multiple_of(0));
-            assert!(!(12 as $T).is_multiple_of(0));
-        }
+            fn test_is_next_multiple_of() {
+                assert!((12 as $T).is_multiple_of(4));
+                assert!(!(12 as $T).is_multiple_of(5));
+                assert!((0 as $T).is_multiple_of(0));
+                assert!(!(12 as $T).is_multiple_of(0));
+            }
 
-        #[test]
-        fn test_carrying_add() {
-            assert_eq!($T::MAX.carrying_add(1, false), (0, true));
-            assert_eq!($T::MAX.carrying_add(0, true), (0, true));
-            assert_eq!($T::MAX.carrying_add(1, true), (1, true));
-
-            assert_eq!($T::MIN.carrying_add($T::MAX, false), ($T::MAX, false));
-            assert_eq!($T::MIN.carrying_add(0, true), (1, false));
-            assert_eq!($T::MIN.carrying_add($T::MAX, true), (0, true));
-        }
+            fn test_carrying_add() {
+                assert_eq_const_safe!($T::MAX.carrying_add(1, false), (0, true));
+                assert_eq_const_safe!($T::MAX.carrying_add(0, true), (0, true));
+                assert_eq_const_safe!($T::MAX.carrying_add(1, true), (1, true));
 
-        #[test]
-        fn test_borrowing_sub() {
-            assert_eq!($T::MIN.borrowing_sub(1, false), ($T::MAX, true));
-            assert_eq!($T::MIN.borrowing_sub(0, true), ($T::MAX, true));
-            assert_eq!($T::MIN.borrowing_sub(1, true), ($T::MAX - 1, true));
-
-            assert_eq!($T::MAX.borrowing_sub($T::MAX, false), (0, false));
-            assert_eq!($T::MAX.borrowing_sub(0, true), ($T::MAX - 1, false));
-            assert_eq!($T::MAX.borrowing_sub($T::MAX, true), ($T::MAX, true));
-        }
+                assert_eq_const_safe!($T::MIN.carrying_add($T::MAX, false), ($T::MAX, false));
+                assert_eq_const_safe!($T::MIN.carrying_add(0, true), (1, false));
+                assert_eq_const_safe!($T::MIN.carrying_add($T::MAX, true), (0, true));
+            }
 
-        #[test]
-        fn test_midpoint() {
-            assert_eq!(<$T>::midpoint(1, 3), 2);
-            assert_eq!(<$T>::midpoint(3, 1), 2);
-
-            assert_eq!(<$T>::midpoint(0, 0), 0);
-            assert_eq!(<$T>::midpoint(0, 2), 1);
-            assert_eq!(<$T>::midpoint(2, 0), 1);
-            assert_eq!(<$T>::midpoint(2, 2), 2);
-
-            assert_eq!(<$T>::midpoint(1, 4), 2);
-            assert_eq!(<$T>::midpoint(4, 1), 2);
-            assert_eq!(<$T>::midpoint(3, 4), 3);
-            assert_eq!(<$T>::midpoint(4, 3), 3);
-
-            assert_eq!(<$T>::midpoint(<$T>::MIN, <$T>::MAX), (<$T>::MAX - <$T>::MIN) / 2);
-            assert_eq!(<$T>::midpoint(<$T>::MAX, <$T>::MIN), (<$T>::MAX - <$T>::MIN) / 2);
-            assert_eq!(<$T>::midpoint(<$T>::MIN, <$T>::MIN), <$T>::MIN);
-            assert_eq!(<$T>::midpoint(<$T>::MAX, <$T>::MAX), <$T>::MAX);
-
-            assert_eq!(<$T>::midpoint(<$T>::MIN, 6), <$T>::MIN / 2 + 3);
-            assert_eq!(<$T>::midpoint(6, <$T>::MIN), <$T>::MIN / 2 + 3);
-            assert_eq!(<$T>::midpoint(<$T>::MAX, 6), (<$T>::MAX - <$T>::MIN) / 2 + 3);
-            assert_eq!(<$T>::midpoint(6, <$T>::MAX), (<$T>::MAX - <$T>::MIN) / 2 + 3);
+            fn test_borrowing_sub() {
+                assert_eq_const_safe!($T::MIN.borrowing_sub(1, false), ($T::MAX, true));
+                assert_eq_const_safe!($T::MIN.borrowing_sub(0, true), ($T::MAX, true));
+                assert_eq_const_safe!($T::MIN.borrowing_sub(1, true), ($T::MAX - 1, true));
+
+                assert_eq_const_safe!($T::MAX.borrowing_sub($T::MAX, false), (0, false));
+                assert_eq_const_safe!($T::MAX.borrowing_sub(0, true), ($T::MAX - 1, false));
+                assert_eq_const_safe!($T::MAX.borrowing_sub($T::MAX, true), ($T::MAX, true));
+            }
+
+            fn test_midpoint() {
+                assert_eq_const_safe!(<$T>::midpoint(1, 3), 2);
+                assert_eq_const_safe!(<$T>::midpoint(3, 1), 2);
+
+                assert_eq_const_safe!(<$T>::midpoint(0, 0), 0);
+                assert_eq_const_safe!(<$T>::midpoint(0, 2), 1);
+                assert_eq_const_safe!(<$T>::midpoint(2, 0), 1);
+                assert_eq_const_safe!(<$T>::midpoint(2, 2), 2);
+
+                assert_eq_const_safe!(<$T>::midpoint(1, 4), 2);
+                assert_eq_const_safe!(<$T>::midpoint(4, 1), 2);
+                assert_eq_const_safe!(<$T>::midpoint(3, 4), 3);
+                assert_eq_const_safe!(<$T>::midpoint(4, 3), 3);
+
+                assert_eq_const_safe!(<$T>::midpoint(<$T>::MIN, <$T>::MAX), (<$T>::MAX - <$T>::MIN) / 2);
+                assert_eq_const_safe!(<$T>::midpoint(<$T>::MAX, <$T>::MIN), (<$T>::MAX - <$T>::MIN) / 2);
+                assert_eq_const_safe!(<$T>::midpoint(<$T>::MIN, <$T>::MIN), <$T>::MIN);
+                assert_eq_const_safe!(<$T>::midpoint(<$T>::MAX, <$T>::MAX), <$T>::MAX);
+
+                assert_eq_const_safe!(<$T>::midpoint(<$T>::MIN, 6), <$T>::MIN / 2 + 3);
+                assert_eq_const_safe!(<$T>::midpoint(6, <$T>::MIN), <$T>::MIN / 2 + 3);
+                assert_eq_const_safe!(<$T>::midpoint(<$T>::MAX, 6), (<$T>::MAX - <$T>::MIN) / 2 + 3);
+                assert_eq_const_safe!(<$T>::midpoint(6, <$T>::MAX), (<$T>::MAX - <$T>::MIN) / 2 + 3);
+            }
         }
     };
 }
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index e25b571acba..27acaff8255 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -1734,13 +1734,15 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
             } else {
                 // We need to properly build cargo using the suitable stage compiler.
 
-                // HACK: currently tool stages are off-by-one compared to compiler stages, i.e. if
-                // you give `tool::Cargo` a stage 1 rustc, it will cause stage 2 rustc to be built
-                // and produce a cargo built with stage 2 rustc. To fix this, we need to chop off
-                // the compiler stage by 1 to align with expected `./x test run-make --stage N`
-                // behavior, i.e. we need to pass `N - 1` compiler stage to cargo. See also Miri
-                // which does a similar hack.
-                let compiler = builder.compiler(builder.top_stage - 1, compiler.host);
+                let compiler = builder.download_rustc().then_some(compiler).unwrap_or_else(||
+                    // HACK: currently tool stages are off-by-one compared to compiler stages, i.e. if
+                    // you give `tool::Cargo` a stage 1 rustc, it will cause stage 2 rustc to be built
+                    // and produce a cargo built with stage 2 rustc. To fix this, we need to chop off
+                    // the compiler stage by 1 to align with expected `./x test run-make --stage N`
+                    // behavior, i.e. we need to pass `N - 1` compiler stage to cargo. See also Miri
+                    // which does a similar hack.
+                    builder.compiler(builder.top_stage - 1, compiler.host));
+
                 builder.ensure(tool::Cargo { compiler, target: compiler.host })
             };
 
@@ -3549,9 +3551,7 @@ impl Step for TestFloatParse {
         let path = self.path.to_str().unwrap();
         let crate_name = self.path.components().last().unwrap().as_os_str().to_str().unwrap();
 
-        if !builder.download_rustc() {
-            builder.ensure(compile::Std::new(compiler, self.host));
-        }
+        builder.ensure(tool::TestFloatParse { host: self.host });
 
         // Run any unit tests in the crate
         let cargo_test = tool::prepare_tool_cargo(
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index f5afa6c4c6c..50f71e167db 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -1097,6 +1097,38 @@ tool_extended!((self, builder),
     Rustfmt, "src/tools/rustfmt", "rustfmt", stable=true, add_bins_to_sysroot = ["rustfmt", "cargo-fmt"];
 );
 
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct TestFloatParse {
+    pub host: TargetSelection,
+}
+
+impl Step for TestFloatParse {
+    type Output = ();
+    const ONLY_HOSTS: bool = true;
+    const DEFAULT: bool = false;
+
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.path("src/etc/test-float-parse")
+    }
+
+    fn run(self, builder: &Builder<'_>) {
+        let bootstrap_host = builder.config.build;
+        let compiler = builder.compiler(builder.top_stage, bootstrap_host);
+
+        builder.ensure(ToolBuild {
+            compiler,
+            target: bootstrap_host,
+            tool: "test-float-parse",
+            mode: Mode::ToolStd,
+            path: "src/etc/test-float-parse",
+            source_type: SourceType::InTree,
+            extra_features: Vec::new(),
+            allow_features: "",
+            cargo_args: Vec::new(),
+        });
+    }
+}
+
 impl Builder<'_> {
     /// Gets a `BootstrapCommand` which is ready to run `tool` in `stage` built for
     /// `host`.
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 7d4d8d8941d..bf168809e28 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -216,7 +216,7 @@ fn clean_generic_bound<'tcx>(
         hir::GenericBound::Outlives(lt) => GenericBound::Outlives(clean_lifetime(lt, cx)),
         hir::GenericBound::Trait(ref t) => {
             // `T: ~const Destruct` is hidden because `T: Destruct` is a no-op.
-            if t.modifiers == hir::TraitBoundModifier::MaybeConst
+            if let hir::BoundConstness::Maybe(_) = t.modifiers.constness
                 && cx.tcx.lang_items().destruct_trait() == Some(t.trait_ref.trait_def_id().unwrap())
             {
                 return None;
@@ -263,7 +263,7 @@ fn clean_poly_trait_ref_with_constraints<'tcx>(
             trait_: clean_trait_ref_with_constraints(cx, poly_trait_ref, constraints),
             generic_params: clean_bound_vars(poly_trait_ref.bound_vars()),
         },
-        hir::TraitBoundModifier::None,
+        hir::TraitBoundModifiers::NONE,
     )
 }
 
@@ -2189,7 +2189,7 @@ pub(crate) fn clean_middle_ty<'tcx>(
         }
 
         ty::Alias(ty::Weak, data) => {
-            if cx.tcx.features().lazy_type_alias {
+            if cx.tcx.features().lazy_type_alias() {
                 // Weak type alias `data` represents the `type X` in `type X = Y`. If we need `Y`,
                 // we need to use `type_of`.
                 let path = clean_middle_path(
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 675507a44c9..6090de16d55 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -966,8 +966,8 @@ pub(crate) trait AttributesExt {
 
     fn cfg(&self, tcx: TyCtxt<'_>, hidden_cfg: &FxHashSet<Cfg>) -> Option<Arc<Cfg>> {
         let sess = tcx.sess;
-        let doc_cfg_active = tcx.features().doc_cfg;
-        let doc_auto_cfg_active = tcx.features().doc_auto_cfg;
+        let doc_cfg_active = tcx.features().doc_cfg();
+        let doc_auto_cfg_active = tcx.features().doc_auto_cfg();
 
         fn single<T: IntoIterator>(it: T) -> Option<T::Item> {
             let mut iter = it.into_iter();
@@ -1257,7 +1257,7 @@ impl Eq for Attributes {}
 
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 pub(crate) enum GenericBound {
-    TraitBound(PolyTrait, hir::TraitBoundModifier),
+    TraitBound(PolyTrait, hir::TraitBoundModifiers),
     Outlives(Lifetime),
     /// `use<'a, T>` precise-capturing bound syntax
     Use(Vec<Symbol>),
@@ -1265,19 +1265,22 @@ pub(crate) enum GenericBound {
 
 impl GenericBound {
     pub(crate) fn sized(cx: &mut DocContext<'_>) -> GenericBound {
-        Self::sized_with(cx, hir::TraitBoundModifier::None)
+        Self::sized_with(cx, hir::TraitBoundModifiers::NONE)
     }
 
     pub(crate) fn maybe_sized(cx: &mut DocContext<'_>) -> GenericBound {
-        Self::sized_with(cx, hir::TraitBoundModifier::Maybe)
+        Self::sized_with(cx, hir::TraitBoundModifiers {
+            polarity: hir::BoundPolarity::Maybe(DUMMY_SP),
+            constness: hir::BoundConstness::Never,
+        })
     }
 
-    fn sized_with(cx: &mut DocContext<'_>, modifier: hir::TraitBoundModifier) -> GenericBound {
+    fn sized_with(cx: &mut DocContext<'_>, modifiers: hir::TraitBoundModifiers) -> GenericBound {
         let did = cx.tcx.require_lang_item(LangItem::Sized, None);
         let empty = ty::Binder::dummy(ty::GenericArgs::empty());
         let path = clean_middle_path(cx, did, false, ThinVec::new(), empty);
         inline::record_extern_fqn(cx, did, ItemType::Trait);
-        GenericBound::TraitBound(PolyTrait { trait_: path, generic_params: Vec::new() }, modifier)
+        GenericBound::TraitBound(PolyTrait { trait_: path, generic_params: Vec::new() }, modifiers)
     }
 
     pub(crate) fn is_trait_bound(&self) -> bool {
@@ -1285,8 +1288,10 @@ impl GenericBound {
     }
 
     pub(crate) fn is_sized_bound(&self, cx: &DocContext<'_>) -> bool {
-        use rustc_hir::TraitBoundModifier as TBM;
-        if let GenericBound::TraitBound(PolyTrait { ref trait_, .. }, TBM::None) = *self
+        if let GenericBound::TraitBound(
+            PolyTrait { ref trait_, .. },
+            rustc_hir::TraitBoundModifiers::NONE,
+        ) = *self
             && Some(trait_.def_id()) == cx.tcx.lang_items().sized_trait()
         {
             return true;
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 9bebe1fb4c1..0f7d4d3e8f3 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -8,7 +8,7 @@ use rustc_data_structures::unord::UnordSet;
 use rustc_errors::codes::*;
 use rustc_errors::emitter::{DynEmitter, HumanEmitter, stderr_destination};
 use rustc_errors::json::JsonEmitter;
-use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, TerminalUrl};
+use rustc_errors::{ErrorGuaranteed, TerminalUrl};
 use rustc_feature::UnstableFeatures;
 use rustc_hir::def::Res;
 use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId};
@@ -21,8 +21,8 @@ use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
 use rustc_session::config::{self, CrateType, ErrorOutputType, Input, ResolveDocLinks};
 pub(crate) use rustc_session::config::{Options, UnstableOptions};
 use rustc_session::{Session, lint};
+use rustc_span::source_map;
 use rustc_span::symbol::sym;
-use rustc_span::{Span, source_map};
 use tracing::{debug, info};
 
 use crate::clean::inline::build_external_trait;
@@ -381,45 +381,10 @@ pub(crate) fn run_global_ctxt(
         );
     }
 
-    fn report_deprecated_attr(name: &str, dcx: DiagCtxtHandle<'_>, sp: Span) {
-        let mut msg =
-            dcx.struct_span_warn(sp, format!("the `#![doc({name})]` attribute is deprecated"));
-        msg.note(
-            "see issue #44136 <https://github.com/rust-lang/rust/issues/44136> \
-            for more information",
-        );
-
-        if name == "no_default_passes" {
-            msg.help("`#![doc(no_default_passes)]` no longer functions; you may want to use `#![doc(document_private_items)]`");
-        } else if name.starts_with("passes") {
-            msg.help("`#![doc(passes = \"...\")]` no longer functions; you may want to use `#![doc(document_private_items)]`");
-        } else if name.starts_with("plugins") {
-            msg.warn("`#![doc(plugins = \"...\")]` no longer functions; see CVE-2018-1000622 <https://nvd.nist.gov/vuln/detail/CVE-2018-1000622>");
-        }
-
-        msg.emit();
-    }
-
     // Process all of the crate attributes, extracting plugin metadata along
     // with the passes which we are supposed to run.
     for attr in krate.module.attrs.lists(sym::doc) {
-        let dcx = ctxt.sess().dcx();
-
         let name = attr.name_or_empty();
-        // `plugins = "..."`, `no_default_passes`, and `passes = "..."` have no effect
-        if attr.is_word() && name == sym::no_default_passes {
-            report_deprecated_attr("no_default_passes", dcx, attr.span());
-        } else if attr.value_str().is_some() {
-            match name {
-                sym::passes => {
-                    report_deprecated_attr("passes = \"...\"", dcx, attr.span());
-                }
-                sym::plugins => {
-                    report_deprecated_attr("plugins = \"...\"", dcx, attr.span());
-                }
-                _ => (),
-            }
-        }
 
         if attr.is_word() && name == sym::document_private_items {
             ctxt.render_options.document_private = true;
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 2e70a8c080d..5c599f20f9f 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -399,13 +399,13 @@ impl clean::GenericBound {
     ) -> impl Display + 'a + Captures<'tcx> {
         display_fn(move |f| match self {
             clean::GenericBound::Outlives(lt) => write!(f, "{}", lt.print()),
-            clean::GenericBound::TraitBound(ty, modifier) => {
-                f.write_str(match modifier {
-                    hir::TraitBoundModifier::None => "",
-                    hir::TraitBoundModifier::Maybe => "?",
-                    hir::TraitBoundModifier::Negative => "!",
-                    // `const` and `~const` trait bounds are experimental; don't render them.
-                    hir::TraitBoundModifier::Const | hir::TraitBoundModifier::MaybeConst => "",
+            clean::GenericBound::TraitBound(ty, modifiers) => {
+                // `const` and `~const` trait bounds are experimental; don't render them.
+                let hir::TraitBoundModifiers { polarity, constness: _ } = modifiers;
+                f.write_str(match polarity {
+                    hir::BoundPolarity::Positive => "",
+                    hir::BoundPolarity::Maybe(_) => "?",
+                    hir::BoundPolarity::Negative(_) => "!",
                 })?;
                 ty.print(cx).fmt(f)
             }
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index 12b63460056..c82f7e9aaf9 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -112,7 +112,7 @@ pub(crate) fn write_shared(
                 md_opts.output = cx.dst.clone();
                 md_opts.external_html = cx.shared.layout.external_html.clone();
                 try_err!(
-                    crate::markdown::render(&index_page, md_opts, cx.shared.edition()),
+                    crate::markdown::render_and_write(&index_page, md_opts, cx.shared.edition()),
                     &index_page
                 );
             }
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 0130f2ce517..7270f170780 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -552,20 +552,18 @@ impl FromClean<clean::GenericBound> for GenericBound {
 }
 
 pub(crate) fn from_trait_bound_modifier(
-    modifier: rustc_hir::TraitBoundModifier,
+    modifiers: rustc_hir::TraitBoundModifiers,
 ) -> TraitBoundModifier {
-    use rustc_hir::TraitBoundModifier::*;
-    match modifier {
-        None => TraitBoundModifier::None,
-        Maybe => TraitBoundModifier::Maybe,
-        MaybeConst => TraitBoundModifier::MaybeConst,
-        // FIXME(const_trait_impl): Create rjt::TBM::Const and map to it once always-const bounds
-        // are less experimental.
-        Const => TraitBoundModifier::None,
-        // FIXME(negative-bounds): This bound should be rendered negative, but
-        // since that's experimental, maybe let's not add it to the rustdoc json
-        // API just now...
-        Negative => TraitBoundModifier::None,
+    use rustc_hir as hir;
+    let hir::TraitBoundModifiers { constness, polarity } = modifiers;
+    match (constness, polarity) {
+        (hir::BoundConstness::Never, hir::BoundPolarity::Positive) => TraitBoundModifier::None,
+        (hir::BoundConstness::Never, hir::BoundPolarity::Maybe(_)) => TraitBoundModifier::Maybe,
+        (hir::BoundConstness::Maybe(_), hir::BoundPolarity::Positive) => {
+            TraitBoundModifier::MaybeConst
+        }
+        // FIXME: Fill out the rest of this matrix.
+        _ => TraitBoundModifier::None,
     }
 }
 
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 10fc0ea9990..cb9b3985bf6 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -817,7 +817,7 @@ fn main_args(
             return wrap_return(
                 dcx,
                 interface::run_compiler(config, |_compiler| {
-                    markdown::render(&md_input, render_options, edition)
+                    markdown::render_and_write(&md_input, render_options, edition)
                 }),
             );
         }
diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs
index 978f96f38be..76eac084907 100644
--- a/src/librustdoc/markdown.rs
+++ b/src/librustdoc/markdown.rs
@@ -1,3 +1,13 @@
+//! Standalone markdown rendering.
+//!
+//! For the (much more common) case of rendering markdown in doc-comments, see
+//! [crate::html::markdown].
+//!
+//! This is used when [rendering a markdown file to an html file][docs], without processing
+//! rust source code.
+//!
+//! [docs]: https://doc.rust-lang.org/stable/rustdoc/#using-standalone-markdown-files
+
 use std::fmt::Write as _;
 use std::fs::{File, create_dir_all, read_to_string};
 use std::io::prelude::*;
@@ -33,7 +43,7 @@ fn extract_leading_metadata(s: &str) -> (Vec<&str>, &str) {
 /// (e.g., output = "bar" => "bar/foo.html").
 ///
 /// Requires session globals to be available, for symbol interning.
-pub(crate) fn render<P: AsRef<Path>>(
+pub(crate) fn render_and_write<P: AsRef<Path>>(
     input: P,
     options: RenderOptions,
     edition: Edition,
diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs
index f4579d85531..484bdb5627c 100644
--- a/src/librustdoc/passes/check_doc_test_visibility.rs
+++ b/src/librustdoc/passes/check_doc_test_visibility.rs
@@ -116,7 +116,7 @@ pub(crate) fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item
 
     find_testable_code(dox, &mut tests, ErrorCodes::No, false, None);
 
-    if tests.found_tests == 0 && cx.tcx.features().rustdoc_missing_doc_code_examples {
+    if tests.found_tests == 0 && cx.tcx.features().rustdoc_missing_doc_code_examples() {
         if should_have_doc_example(cx, item) {
             debug!("reporting error for {item:?} (hir_id={hir_id:?})");
             let sp = item.attr_span(cx.tcx);
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 2f0ea8d618c..140fda70918 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -1419,7 +1419,7 @@ impl LinkCollector<'_, '_> {
             && key.path_str.contains("::")
         // We only want to check this if this is an associated item.
         {
-            if key.item_id.is_local() && !self.cx.tcx.features().intra_doc_pointers {
+            if key.item_id.is_local() && !self.cx.tcx.features().intra_doc_pointers() {
                 self.report_rawptr_assoc_feature_gate(diag.dox, &diag.link_range, diag.item);
                 return None;
             } else {
diff --git a/src/tools/clippy/clippy_lints/src/empty_enum.rs b/src/tools/clippy/clippy_lints/src/empty_enum.rs
index 70eb81fa09c..b0389fd9a2f 100644
--- a/src/tools/clippy/clippy_lints/src/empty_enum.rs
+++ b/src/tools/clippy/clippy_lints/src/empty_enum.rs
@@ -64,7 +64,7 @@ impl LateLintPass<'_> for EmptyEnum {
     fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
         if let ItemKind::Enum(..) = item.kind
             // Only suggest the `never_type` if the feature is enabled
-            && cx.tcx.features().never_type
+            && cx.tcx.features().never_type()
             && let Some(adt) = cx.tcx.type_of(item.owner_id).instantiate_identity().ty_adt_def()
             && adt.variants().is_empty()
         {
diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
index 00f83237224..65fdc93e0ed 100644
--- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
@@ -3,7 +3,7 @@ use clippy_utils::source::snippet;
 use rustc_errors::{Applicability, SuggestionStyle};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{
-    AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifier, TyKind,
+    AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifiers, TyKind,
     WherePredicate,
 };
 use rustc_hir_analysis::lower_ty;
@@ -234,7 +234,7 @@ fn collect_supertrait_bounds<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds
         .iter()
         .filter_map(|bound| {
             if let GenericBound::Trait(poly_trait) = bound
-                && let TraitBoundModifier::None = poly_trait.modifiers
+                && let TraitBoundModifiers::NONE = poly_trait.modifiers
                 && let [.., path] = poly_trait.trait_ref.path.segments
                 && poly_trait.bound_generic_params.is_empty()
                 && let Some(trait_def_id) = path.res.opt_def_id()
@@ -300,7 +300,7 @@ fn check<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds<'tcx>) {
     // simply comparing trait `DefId`s won't be enough. We also need to compare the generics.
     for (index, bound) in bounds.iter().enumerate() {
         if let GenericBound::Trait(poly_trait) = bound
-            && let TraitBoundModifier::None = poly_trait.modifiers
+            && let TraitBoundModifiers::NONE = poly_trait.modifiers
             && let [.., path] = poly_trait.trait_ref.path.segments
             && let implied_args = path.args.map_or([].as_slice(), |a| a.args)
             && let implied_constraints = path.args.map_or([].as_slice(), |a| a.constraints)
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
index f9ffbc5dc0b..20984bc40ca 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
@@ -114,7 +114,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
     let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
     for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) {
         if matches!(arm2.pat.kind, PatKind::Wild) {
-            if !cx.tcx.features().non_exhaustive_omitted_patterns_lint
+            if !cx.tcx.features().non_exhaustive_omitted_patterns_lint()
                 || is_lint_allowed(cx, NON_EXHAUSTIVE_OMITTED_PATTERNS, arm2.hir_id)
             {
                 let arm_span = adjusted_arm_span(cx, arm1.span);
diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs
index 564c598a334..42d9efe4ff6 100644
--- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs
@@ -251,7 +251,7 @@ fn emit_redundant_guards<'tcx>(
 fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     for_each_expr_without_closures(expr, |expr| {
         if match expr.kind {
-            ExprKind::ConstBlock(..) => cx.tcx.features().inline_const_pat,
+            ExprKind::ConstBlock(..) => cx.tcx.features().inline_const_pat(),
             ExprKind::Call(c, ..) if let ExprKind::Path(qpath) = c.kind => {
                 // Allow ctors
                 matches!(cx.qpath_res(&qpath, c.hir_id), Res::Def(DefKind::Ctor(..), ..))
diff --git a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
index 68c9af07465..9a1c397b5b2 100644
--- a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_errors::Applicability;
 use rustc_hir::def_id::{DefId, DefIdMap};
-use rustc_hir::{GenericBound, Generics, PolyTraitRef, TraitBoundModifier, WherePredicate};
+use rustc_hir::{GenericBound, Generics, PolyTraitRef, TraitBoundModifiers, BoundPolarity, WherePredicate};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{ClauseKind, PredicatePolarity};
 use rustc_session::declare_lint_pass;
@@ -118,13 +118,13 @@ impl LateLintPass<'_> for NeedlessMaybeSized {
         let maybe_sized_params: DefIdMap<_> = type_param_bounds(generics)
             .filter(|bound| {
                 bound.trait_bound.trait_ref.trait_def_id() == Some(sized_trait)
-                    && bound.trait_bound.modifiers == TraitBoundModifier::Maybe
+                    && matches!(bound.trait_bound.modifiers.polarity, BoundPolarity::Maybe(_))
             })
             .map(|bound| (bound.param, bound))
             .collect();
 
         for bound in type_param_bounds(generics) {
-            if bound.trait_bound.modifiers == TraitBoundModifier::None
+            if bound.trait_bound.modifiers == TraitBoundModifiers::NONE
                 && let Some(sized_bound) = maybe_sized_params.get(&bound.param)
                 && let Some(path) = path_to_sized_bound(cx, bound.trait_bound)
             {
diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
index 7f528b9d17b..3da4bf67558 100644
--- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
@@ -11,7 +11,7 @@ use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::{
     GenericBound, Generics, Item, ItemKind, LangItem, Node, Path, PathSegment, PredicateOrigin, QPath,
-    TraitBoundModifier, TraitItem, TraitRef, Ty, TyKind, WherePredicate,
+    TraitBoundModifiers, TraitItem, TraitRef, Ty, TyKind, WherePredicate, BoundPolarity,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
@@ -233,7 +233,7 @@ impl TraitBounds {
     fn cannot_combine_maybe_bound(&self, cx: &LateContext<'_>, bound: &GenericBound<'_>) -> bool {
         if !self.msrv.meets(msrvs::MAYBE_BOUND_IN_WHERE)
             && let GenericBound::Trait(tr) = bound
-            && let TraitBoundModifier::Maybe = tr.modifiers
+            && let BoundPolarity::Maybe(_) = tr.modifiers.polarity
         {
             cx.tcx.lang_items().get(LangItem::Sized) == tr.trait_ref.path.res.opt_def_id()
         } else {
@@ -374,12 +374,12 @@ fn check_trait_bound_duplication<'tcx>(cx: &LateContext<'tcx>, generics: &'_ Gen
 struct ComparableTraitRef<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
     trait_ref: &'tcx TraitRef<'tcx>,
-    modifier: TraitBoundModifier,
+    modifiers: TraitBoundModifiers,
 }
 
 impl PartialEq for ComparableTraitRef<'_, '_> {
     fn eq(&self, other: &Self) -> bool {
-        self.modifier == other.modifier
+        SpanlessEq::new(self.cx).eq_modifiers(self.modifiers, other.modifiers)
             && SpanlessEq::new(self.cx)
                 .paths_by_resolution()
                 .eq_path(self.trait_ref.path, other.trait_ref.path)
@@ -390,8 +390,8 @@ impl Hash for ComparableTraitRef<'_, '_> {
     fn hash<H: Hasher>(&self, state: &mut H) {
         let mut s = SpanlessHash::new(self.cx).paths_by_resolution();
         s.hash_path(self.trait_ref.path);
+        s.hash_modifiers(self.modifiers);
         state.write_u64(s.finish());
-        self.modifier.hash(state);
     }
 }
 
@@ -400,7 +400,7 @@ fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'
         let trait_path = t.trait_ref.path;
         let trait_span = {
             let path_span = trait_path.span;
-            if let TraitBoundModifier::Maybe = t.modifiers {
+            if let BoundPolarity::Maybe(_) = t.modifiers.polarity {
                 path_span.with_lo(path_span.lo() - BytePos(1)) // include the `?`
             } else {
                 path_span
@@ -427,7 +427,7 @@ fn rollup_traits<'cx, 'tcx>(
                 ComparableTraitRef {
                     cx,
                     trait_ref: &t.trait_ref,
-                    modifier: t.modifiers,
+                    modifiers: t.modifiers,
                 },
                 t.span,
             ))
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index 27c57808ece..181d414cbbd 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -9,7 +9,8 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{
     ArrayLen, AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr,
     ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime,
-    LifetimeName, Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind,
+    LifetimeName, Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitBoundModifiers, Ty,
+    TyKind,
 };
 use rustc_lexer::{TokenKind, tokenize};
 use rustc_lint::LateContext;
@@ -126,6 +127,11 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
     pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
         self.inter_expr().eq_path_segments(left, right)
     }
+
+    pub fn eq_modifiers(&mut self, left: TraitBoundModifiers, right: TraitBoundModifiers) -> bool {
+        std::mem::discriminant(&left.constness) == std::mem::discriminant(&right.constness)
+            && std::mem::discriminant(&left.polarity) == std::mem::discriminant(&right.polarity)
+    }
 }
 
 pub struct HirEqInterExpr<'a, 'b, 'tcx> {
@@ -1143,6 +1149,12 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
         }
     }
 
+    pub fn hash_modifiers(&mut self, modifiers: TraitBoundModifiers) {
+        let TraitBoundModifiers { constness, polarity } = modifiers;
+        std::mem::discriminant(&polarity).hash(&mut self.s);
+        std::mem::discriminant(&constness).hash(&mut self.s);
+    }
+
     pub fn hash_stmt(&mut self, b: &Stmt<'_>) {
         std::mem::discriminant(&b.kind).hash(&mut self.s);
 
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 099e620ffe0..d75cdefe635 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -57,7 +57,7 @@ impl EarlyProps {
             &mut poisoned,
             testfile,
             rdr,
-            &mut |DirectiveLine { directive: ln, .. }| {
+            &mut |DirectiveLine { raw_directive: ln, .. }| {
                 parse_and_update_aux(config, ln, &mut props.aux);
                 config.parse_and_update_revisions(testfile, ln, &mut props.revisions);
             },
@@ -344,8 +344,8 @@ impl TestProps {
                 &mut poisoned,
                 testfile,
                 file,
-                &mut |DirectiveLine { header_revision, directive: ln, .. }| {
-                    if header_revision.is_some() && header_revision != test_revision {
+                &mut |directive @ DirectiveLine { raw_directive: ln, .. }| {
+                    if !directive.applies_to_test_revision(test_revision) {
                         return;
                     }
 
@@ -678,28 +678,35 @@ impl TestProps {
     }
 }
 
-/// Extract an `(Option<line_revision>, directive)` directive from a line if comment is present.
-///
-/// See [`DirectiveLine`] for a diagram.
-pub fn line_directive<'line>(
+/// If the given line begins with the appropriate comment prefix for a directive,
+/// returns a struct containing various parts of the directive.
+fn line_directive<'line>(
+    line_number: usize,
     comment: &str,
     original_line: &'line str,
-) -> Option<(Option<&'line str>, &'line str)> {
+) -> Option<DirectiveLine<'line>> {
     // Ignore lines that don't start with the comment prefix.
     let after_comment = original_line.trim_start().strip_prefix(comment)?.trim_start();
 
+    let revision;
+    let raw_directive;
+
     if let Some(after_open_bracket) = after_comment.strip_prefix('[') {
         // A comment like `//@[foo]` only applies to revision `foo`.
-        let Some((line_revision, directive)) = after_open_bracket.split_once(']') else {
+        let Some((line_revision, after_close_bracket)) = after_open_bracket.split_once(']') else {
             panic!(
                 "malformed condition directive: expected `{comment}[foo]`, found `{original_line}`"
             )
         };
 
-        Some((Some(line_revision), directive.trim_start()))
+        revision = Some(line_revision);
+        raw_directive = after_close_bracket.trim_start();
     } else {
-        Some((None, after_comment))
-    }
+        revision = None;
+        raw_directive = after_comment;
+    };
+
+    Some(DirectiveLine { line_number, revision, raw_directive })
 }
 
 // To prevent duplicating the list of commmands between `compiletest`,`htmldocck` and `jsondocck`,
@@ -730,28 +737,37 @@ const KNOWN_HTMLDOCCK_DIRECTIVE_NAMES: &[&str] = &[
 const KNOWN_JSONDOCCK_DIRECTIVE_NAMES: &[&str] =
     &["count", "!count", "has", "!has", "is", "!is", "ismany", "!ismany", "set", "!set"];
 
-/// The broken-down contents of a line containing a test header directive,
+/// The (partly) broken-down contents of a line containing a test directive,
 /// which [`iter_header`] passes to its callback function.
 ///
 /// For example:
 ///
 /// ```text
 /// //@ compile-flags: -O
-///     ^^^^^^^^^^^^^^^^^ directive
+///     ^^^^^^^^^^^^^^^^^ raw_directive
 ///
 /// //@ [foo] compile-flags: -O
-///      ^^^                    header_revision
-///           ^^^^^^^^^^^^^^^^^ directive
+///      ^^^                    revision
+///           ^^^^^^^^^^^^^^^^^ raw_directive
 /// ```
 struct DirectiveLine<'ln> {
     line_number: usize,
-    /// Some header directives start with a revision name in square brackets
+    /// Some test directives start with a revision name in square brackets
     /// (e.g. `[foo]`), and only apply to that revision of the test.
     /// If present, this field contains the revision name (e.g. `foo`).
-    header_revision: Option<&'ln str>,
-    /// The main part of the header directive, after removing the comment prefix
+    revision: Option<&'ln str>,
+    /// The main part of the directive, after removing the comment prefix
     /// and the optional revision specifier.
-    directive: &'ln str,
+    ///
+    /// This is "raw" because the directive's name and colon-separated value
+    /// (if present) have not yet been extracted or checked.
+    raw_directive: &'ln str,
+}
+
+impl<'ln> DirectiveLine<'ln> {
+    fn applies_to_test_revision(&self, test_revision: Option<&str>) -> bool {
+        self.revision.is_none() || self.revision == test_revision
+    }
 }
 
 pub(crate) struct CheckDirectiveResult<'ln> {
@@ -819,8 +835,8 @@ fn iter_header(
             "ignore-cross-compile",
         ];
         // Process the extra implied directives, with a dummy line number of 0.
-        for directive in extra_directives {
-            it(DirectiveLine { line_number: 0, header_revision: None, directive });
+        for raw_directive in extra_directives {
+            it(DirectiveLine { line_number: 0, revision: None, raw_directive });
         }
     }
 
@@ -847,24 +863,21 @@ fn iter_header(
             return;
         }
 
-        let Some((header_revision, non_revisioned_directive_line)) = line_directive(comment, ln)
-        else {
+        let Some(directive_line) = line_directive(line_number, comment, ln) else {
             continue;
         };
 
         // Perform unknown directive check on Rust files.
         if testfile.extension().map(|e| e == "rs").unwrap_or(false) {
-            let directive_ln = non_revisioned_directive_line.trim();
-
             let CheckDirectiveResult { is_known_directive, trailing_directive } =
-                check_directive(directive_ln, mode, ln);
+                check_directive(directive_line.raw_directive, mode, ln);
 
             if !is_known_directive {
                 *poisoned = true;
 
                 eprintln!(
                     "error: detected unknown compiletest test directive `{}` in {}:{}",
-                    directive_ln,
+                    directive_line.raw_directive,
                     testfile.display(),
                     line_number,
                 );
@@ -888,11 +901,7 @@ fn iter_header(
             }
         }
 
-        it(DirectiveLine {
-            line_number,
-            header_revision,
-            directive: non_revisioned_directive_line,
-        });
+        it(directive_line);
     }
 }
 
@@ -1292,8 +1301,8 @@ pub fn make_test_description<R: Read>(
         &mut local_poisoned,
         path,
         src,
-        &mut |DirectiveLine { header_revision, directive: ln, line_number }| {
-            if header_revision.is_some() && header_revision != test_revision {
+        &mut |directive @ DirectiveLine { line_number, raw_directive: ln, .. }| {
+            if !directive.applies_to_test_revision(test_revision) {
                 return;
             }
 
diff --git a/src/tools/compiletest/src/runtest/debugger.rs b/src/tools/compiletest/src/runtest/debugger.rs
index 985fe6381e8..c15422fb6f6 100644
--- a/src/tools/compiletest/src/runtest/debugger.rs
+++ b/src/tools/compiletest/src/runtest/debugger.rs
@@ -4,7 +4,6 @@ use std::io::{BufRead, BufReader};
 use std::path::{Path, PathBuf};
 
 use crate::common::Config;
-use crate::header::line_directive;
 use crate::runtest::ProcRes;
 
 /// Representation of information to invoke a debugger and check its output
@@ -24,7 +23,6 @@ impl DebuggerCommands {
         file: &Path,
         config: &Config,
         debugger_prefixes: &[&str],
-        rev: Option<&str>,
     ) -> Result<Self, String> {
         let directives = debugger_prefixes
             .iter()
@@ -39,18 +37,17 @@ impl DebuggerCommands {
         for (line_no, line) in reader.lines().enumerate() {
             counter += 1;
             let line = line.map_err(|e| format!("Error while parsing debugger commands: {}", e))?;
-            let (lnrev, line) = line_directive("//", &line).unwrap_or((None, &line));
-
-            // Skip any revision specific directive that doesn't match the current
-            // revision being tested
-            if lnrev.is_some() && lnrev != rev {
-                continue;
-            }
 
+            // Breakpoints appear on lines with actual code, typically at the end of the line.
             if line.contains("#break") {
                 breakpoint_lines.push(counter);
+                continue;
             }
 
+            let Some(line) = line.trim_start().strip_prefix("//").map(str::trim_start) else {
+                continue;
+            };
+
             for &(ref command_directive, ref check_directive) in &directives {
                 config
                     .parse_name_value_directive(&line, command_directive)
diff --git a/src/tools/compiletest/src/runtest/debuginfo.rs b/src/tools/compiletest/src/runtest/debuginfo.rs
index bd0845b4524..c621c22ac99 100644
--- a/src/tools/compiletest/src/runtest/debuginfo.rs
+++ b/src/tools/compiletest/src/runtest/debuginfo.rs
@@ -66,13 +66,8 @@ impl TestCx<'_> {
         };
 
         // Parse debugger commands etc from test files
-        let dbg_cmds = DebuggerCommands::parse_from(
-            &self.testpaths.file,
-            self.config,
-            prefixes,
-            self.revision,
-        )
-        .unwrap_or_else(|e| self.fatal(&e));
+        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, self.config, prefixes)
+            .unwrap_or_else(|e| self.fatal(&e));
 
         // https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-commands
         let mut script_str = String::with_capacity(2048);
@@ -142,13 +137,8 @@ impl TestCx<'_> {
     }
 
     fn run_debuginfo_gdb_test_no_opt(&self) {
-        let dbg_cmds = DebuggerCommands::parse_from(
-            &self.testpaths.file,
-            self.config,
-            &["gdb"],
-            self.revision,
-        )
-        .unwrap_or_else(|e| self.fatal(&e));
+        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, self.config, &["gdb"])
+            .unwrap_or_else(|e| self.fatal(&e));
         let mut cmds = dbg_cmds.commands.join("\n");
 
         // compile test file (it should have 'compile-flags:-g' in the header)
@@ -413,13 +403,8 @@ impl TestCx<'_> {
         }
 
         // Parse debugger commands etc from test files
-        let dbg_cmds = DebuggerCommands::parse_from(
-            &self.testpaths.file,
-            self.config,
-            &["lldb"],
-            self.revision,
-        )
-        .unwrap_or_else(|e| self.fatal(&e));
+        let dbg_cmds = DebuggerCommands::parse_from(&self.testpaths.file, self.config, &["lldb"])
+            .unwrap_or_else(|e| self.fatal(&e));
 
         // Write debugger script:
         // We don't want to hang when calling `quit` while the process is still running
diff --git a/tests/assembly/rust-abi-arg-attr.rs b/tests/assembly/rust-abi-arg-attr.rs
new file mode 100644
index 00000000000..2a113eed4ba
--- /dev/null
+++ b/tests/assembly/rust-abi-arg-attr.rs
@@ -0,0 +1,108 @@
+//@ assembly-output: emit-asm
+//@ revisions: riscv64 riscv64-zbb loongarch64
+//@ compile-flags: -C opt-level=3
+//@ [riscv64] compile-flags: --target riscv64gc-unknown-linux-gnu
+//@ [riscv64] needs-llvm-components: riscv
+//@ [riscv64-zbb] compile-flags: --target riscv64gc-unknown-linux-gnu
+//@ [riscv64-zbb] compile-flags: -C target-feature=+zbb
+//@ [riscv64-zbb] needs-llvm-components: riscv
+//@ [loongarch64] compile-flags: --target loongarch64-unknown-linux-gnu
+//@ [loongarch64] needs-llvm-components: loongarch
+
+#![feature(no_core, lang_items, intrinsics, rustc_attrs)]
+#![crate_type = "lib"]
+#![no_std]
+#![no_core]
+
+// FIXME: Migrate these code after PR #130693 is landed.
+// vvvvv core
+
+#[lang = "sized"]
+trait Sized {}
+
+#[lang = "copy"]
+trait Copy {}
+
+impl Copy for i8 {}
+impl Copy for u32 {}
+impl Copy for i32 {}
+
+#[lang = "neg"]
+trait Neg {
+    type Output;
+
+    fn neg(self) -> Self::Output;
+}
+
+impl Neg for i8 {
+    type Output = i8;
+
+    fn neg(self) -> Self::Output {
+        -self
+    }
+}
+
+#[lang = "Ordering"]
+#[repr(i8)]
+enum Ordering {
+    Less = -1,
+    Equal = 0,
+    Greater = 1,
+}
+
+extern "rust-intrinsic" {
+    #[rustc_safe_intrinsic]
+    fn three_way_compare<T: Copy>(lhs: T, rhs: T) -> Ordering;
+}
+
+// ^^^^^ core
+
+// Reimplementation of function `{integer}::max`.
+macro_rules! max {
+    ($a:expr, $b:expr) => {
+        match three_way_compare($a, $b) {
+            Ordering::Less | Ordering::Equal => $b,
+            Ordering::Greater => $a,
+        }
+    };
+}
+
+#[no_mangle]
+// CHECK-LABEL: issue_114508_u32:
+pub fn issue_114508_u32(a: u32, b: u32) -> u32 {
+    // CHECK-NEXT:       .cfi_startproc
+
+    // riscv64-NEXT:     bltu a1, a0, .[[RET:.+]]
+    // riscv64-NEXT:     mv a0, a1
+    // riscv64-NEXT: .[[RET]]:
+
+    // riscv64-zbb-NEXT: maxu a0, a0, a1
+
+    // loongarch64-NEXT: sltu $a2, $a1, $a0
+    // loongarch64-NEXT: masknez $a1, $a1, $a2
+    // loongarch64-NEXT: maskeqz $a0, $a0, $a2
+    // loongarch64-NEXT: or $a0, $a0, $a1
+
+    // CHECK-NEXT:       ret
+    max!(a, b)
+}
+
+#[no_mangle]
+// CHECK-LABEL: issue_114508_i32:
+pub fn issue_114508_i32(a: i32, b: i32) -> i32 {
+    // CHECK-NEXT:       .cfi_startproc
+
+    // riscv64-NEXT:     blt a1, a0, .[[RET:.+]]
+    // riscv64-NEXT:     mv a0, a1
+    // riscv64-NEXT: .[[RET]]:
+
+    // riscv64-zbb-NEXT: max a0, a0, a1
+
+    // loongarch64-NEXT: slt $a2, $a1, $a0
+    // loongarch64-NEXT: masknez $a1, $a1, $a2
+    // loongarch64-NEXT: maskeqz $a0, $a0, $a2
+    // loongarch64-NEXT: or $a0, $a0, $a1
+
+    // CHECK-NEXT:       ret
+    max!(a, b)
+}
diff --git a/tests/assembly/x86-return-float.rs b/tests/assembly/x86-return-float.rs
index 30c5e869633..acd1af8d38a 100644
--- a/tests/assembly/x86-return-float.rs
+++ b/tests/assembly/x86-return-float.rs
@@ -305,8 +305,10 @@ pub unsafe fn call_other_f64(x: &mut (usize, f64)) {
 // CHECK-LABEL: return_f16:
 #[no_mangle]
 pub fn return_f16(x: f16) -> f16 {
-    // CHECK: pinsrw $0, {{.*}}(%ebp), %xmm0
-    // CHECK-NOT: xmm0
+    // CHECK: pushl %ebp
+    // CHECK: movl %esp, %ebp
+    // CHECK: movzwl 8(%ebp), %eax
+    // CHECK: popl %ebp
     // CHECK: retl
     x
 }
diff --git a/tests/codegen/checked_ilog.rs b/tests/codegen/checked_ilog.rs
index 8f3c07119fe..d7dfc7c29e7 100644
--- a/tests/codegen/checked_ilog.rs
+++ b/tests/codegen/checked_ilog.rs
@@ -5,7 +5,7 @@
 // Ensure that when val < base, we do not divide or multiply.
 
 // CHECK-LABEL: @checked_ilog
-// CHECK-SAME: (i16 noundef %val, i16 noundef %base)
+// CHECK-SAME: (i16{{.*}} %val, i16{{.*}} %base)
 #[no_mangle]
 pub fn checked_ilog(val: u16, base: u16) -> Option<u32> {
     // CHECK-NOT: udiv
diff --git a/tests/codegen/checked_math.rs b/tests/codegen/checked_math.rs
index 75df5866d6e..63f5c3d34f7 100644
--- a/tests/codegen/checked_math.rs
+++ b/tests/codegen/checked_math.rs
@@ -8,7 +8,7 @@
 // Thanks to poison semantics, this doesn't even need branches.
 
 // CHECK-LABEL: @checked_sub_unsigned
-// CHECK-SAME: (i16 noundef %a, i16 noundef %b)
+// CHECK-SAME: (i16{{.*}} %a, i16{{.*}} %b)
 #[no_mangle]
 pub fn checked_sub_unsigned(a: u16, b: u16) -> Option<u16> {
     // CHECK-DAG: %[[IS_SOME:.+]] = icmp uge i16 %a, %b
@@ -26,7 +26,7 @@ pub fn checked_sub_unsigned(a: u16, b: u16) -> Option<u16> {
 // looking for no-wrap flags, we just need there to not be any masking.
 
 // CHECK-LABEL: @checked_shl_unsigned
-// CHECK-SAME: (i32 noundef %a, i32 noundef %b)
+// CHECK-SAME: (i32{{.*}} %a, i32{{.*}} %b)
 #[no_mangle]
 pub fn checked_shl_unsigned(a: u32, b: u32) -> Option<u32> {
     // CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32
@@ -41,7 +41,7 @@ pub fn checked_shl_unsigned(a: u32, b: u32) -> Option<u32> {
 }
 
 // CHECK-LABEL: @checked_shr_unsigned
-// CHECK-SAME: (i32 noundef %a, i32 noundef %b)
+// CHECK-SAME: (i32{{.*}} %a, i32{{.*}} %b)
 #[no_mangle]
 pub fn checked_shr_unsigned(a: u32, b: u32) -> Option<u32> {
     // CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32
@@ -56,7 +56,7 @@ pub fn checked_shr_unsigned(a: u32, b: u32) -> Option<u32> {
 }
 
 // CHECK-LABEL: @checked_shl_signed
-// CHECK-SAME: (i32 noundef %a, i32 noundef %b)
+// CHECK-SAME: (i32{{.*}} %a, i32{{.*}} %b)
 #[no_mangle]
 pub fn checked_shl_signed(a: i32, b: u32) -> Option<i32> {
     // CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32
@@ -71,7 +71,7 @@ pub fn checked_shl_signed(a: i32, b: u32) -> Option<i32> {
 }
 
 // CHECK-LABEL: @checked_shr_signed
-// CHECK-SAME: (i32 noundef %a, i32 noundef %b)
+// CHECK-SAME: (i32{{.*}} %a, i32{{.*}} %b)
 #[no_mangle]
 pub fn checked_shr_signed(a: i32, b: u32) -> Option<i32> {
     // CHECK-DAG: %[[IS_SOME:.+]] = icmp ult i32 %b, 32
@@ -86,7 +86,7 @@ pub fn checked_shr_signed(a: i32, b: u32) -> Option<i32> {
 }
 
 // CHECK-LABEL: @checked_add_one_unwrap_unsigned
-// CHECK-SAME: (i32 noundef %x)
+// CHECK-SAME: (i32{{.*}} %x)
 #[no_mangle]
 pub fn checked_add_one_unwrap_unsigned(x: u32) -> u32 {
     // CHECK: %[[IS_MAX:.+]] = icmp eq i32 %x, -1
diff --git a/tests/codegen/comparison-operators-newtype.rs b/tests/codegen/comparison-operators-newtype.rs
index d336c4e6ed3..acce0cb5946 100644
--- a/tests/codegen/comparison-operators-newtype.rs
+++ b/tests/codegen/comparison-operators-newtype.rs
@@ -12,7 +12,7 @@ use std::cmp::Ordering;
 pub struct Foo(u16);
 
 // CHECK-LABEL: @check_lt
-// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
+// CHECK-SAME: (i16{{.*}} %[[A:.+]], i16{{.*}} %[[B:.+]])
 #[no_mangle]
 pub fn check_lt(a: Foo, b: Foo) -> bool {
     // CHECK: %[[R:.+]] = icmp ult i16 %[[A]], %[[B]]
@@ -21,7 +21,7 @@ pub fn check_lt(a: Foo, b: Foo) -> bool {
 }
 
 // CHECK-LABEL: @check_le
-// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
+// CHECK-SAME: (i16{{.*}} %[[A:.+]], i16{{.*}} %[[B:.+]])
 #[no_mangle]
 pub fn check_le(a: Foo, b: Foo) -> bool {
     // CHECK: %[[R:.+]] = icmp ule i16 %[[A]], %[[B]]
@@ -30,7 +30,7 @@ pub fn check_le(a: Foo, b: Foo) -> bool {
 }
 
 // CHECK-LABEL: @check_gt
-// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
+// CHECK-SAME: (i16{{.*}} %[[A:.+]], i16{{.*}} %[[B:.+]])
 #[no_mangle]
 pub fn check_gt(a: Foo, b: Foo) -> bool {
     // CHECK: %[[R:.+]] = icmp ugt i16 %[[A]], %[[B]]
@@ -39,7 +39,7 @@ pub fn check_gt(a: Foo, b: Foo) -> bool {
 }
 
 // CHECK-LABEL: @check_ge
-// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
+// CHECK-SAME: (i16{{.*}} %[[A:.+]], i16{{.*}} %[[B:.+]])
 #[no_mangle]
 pub fn check_ge(a: Foo, b: Foo) -> bool {
     // CHECK: %[[R:.+]] = icmp uge i16 %[[A]], %[[B]]
diff --git a/tests/codegen/fewer-names.rs b/tests/codegen/fewer-names.rs
index b14dd30482c..a171629a076 100644
--- a/tests/codegen/fewer-names.rs
+++ b/tests/codegen/fewer-names.rs
@@ -6,11 +6,11 @@
 
 #[no_mangle]
 pub fn sum(x: u32, y: u32) -> u32 {
-    // YES-LABEL: define{{.*}}i32 @sum(i32 noundef %0, i32 noundef %1)
+    // YES-LABEL: define{{.*}}i32 @sum(i32{{.*}} %0, i32{{.*}} %1)
     // YES-NEXT:    %3 = add i32 %1, %0
     // YES-NEXT:    ret i32 %3
 
-    // NO-LABEL: define{{.*}}i32 @sum(i32 noundef %x, i32 noundef %y)
+    // NO-LABEL: define{{.*}}i32 @sum(i32{{.*}} %x, i32{{.*}} %y)
     // NO-NEXT:  start:
     // NO-NEXT:    %z = add i32 %y, %x
     // NO-NEXT:    ret i32 %z
diff --git a/tests/codegen/float/f128.rs b/tests/codegen/float/f128.rs
index 4af264101de..514d35433e1 100644
--- a/tests/codegen/float/f128.rs
+++ b/tests/codegen/float/f128.rs
@@ -1,4 +1,4 @@
-// 32-bit x86 returns `f32` and `f64` differently to avoid the x87 stack.
+// 32-bit x86 returns float types differently to avoid the x87 stack.
 // 32-bit systems will return 128bit values using a return area pointer.
 //@ revisions: x86 bit32 bit64
 //@[x86] only-x86
@@ -152,7 +152,9 @@ pub fn f128_rem_assign(a: &mut f128, b: f128) {
 
 /* float to float conversions */
 
-// CHECK-LABEL: half @f128_as_f16(
+// x86-LABEL: i16 @f128_as_f16(
+// bits32-LABEL: half @f128_as_f16(
+// bits64-LABEL: half @f128_as_f16(
 #[no_mangle]
 pub fn f128_as_f16(a: f128) -> f16 {
     // CHECK: fptrunc fp128 %{{.+}} to half
diff --git a/tests/codegen/float/f16.rs b/tests/codegen/float/f16.rs
index 80931051f18..5c3a5893b9d 100644
--- a/tests/codegen/float/f16.rs
+++ b/tests/codegen/float/f16.rs
@@ -1,4 +1,4 @@
-// 32-bit x86 returns `f32` and `f64` differently to avoid the x87 stack.
+// 32-bit x86 returns float types differently to avoid the x87 stack.
 // 32-bit systems will return 128bit values using a return area pointer.
 //@ revisions: x86 bit32 bit64
 //@[x86] only-x86
@@ -58,42 +58,44 @@ pub fn f16_le(a: f16, b: f16) -> bool {
     a <= b
 }
 
-// CHECK-LABEL: half @f16_neg(
+// This is where we check the argument and return ABI for f16.
+// other-LABEL: half @f16_neg(half
+// x86-LABEL: i16 @f16_neg(half
 #[no_mangle]
 pub fn f16_neg(a: f16) -> f16 {
     // CHECK: fneg half %{{.+}}
     -a
 }
 
-// CHECK-LABEL: half @f16_add(
+// CHECK-LABEL: @f16_add
 #[no_mangle]
 pub fn f16_add(a: f16, b: f16) -> f16 {
     // CHECK: fadd half %{{.+}}, %{{.+}}
     a + b
 }
 
-// CHECK-LABEL: half @f16_sub(
+// CHECK-LABEL: @f16_sub
 #[no_mangle]
 pub fn f16_sub(a: f16, b: f16) -> f16 {
     // CHECK: fsub half %{{.+}}, %{{.+}}
     a - b
 }
 
-// CHECK-LABEL: half @f16_mul(
+// CHECK-LABEL: @f16_mul
 #[no_mangle]
 pub fn f16_mul(a: f16, b: f16) -> f16 {
     // CHECK: fmul half %{{.+}}, %{{.+}}
     a * b
 }
 
-// CHECK-LABEL: half @f16_div(
+// CHECK-LABEL: @f16_div
 #[no_mangle]
 pub fn f16_div(a: f16, b: f16) -> f16 {
     // CHECK: fdiv half %{{.+}}, %{{.+}}
     a / b
 }
 
-// CHECK-LABEL: half @f16_rem(
+// CHECK-LABEL: @f16_rem
 #[no_mangle]
 pub fn f16_rem(a: f16, b: f16) -> f16 {
     // CHECK: frem half %{{.+}}, %{{.+}}
@@ -142,10 +144,13 @@ pub fn f16_rem_assign(a: &mut f16, b: f16) {
 
 /* float to float conversions */
 
-// CHECK-LABEL: half @f16_as_self(
+// other-LABEL: half @f16_as_self(
+// x86-LABEL: i16 @f16_as_self(
 #[no_mangle]
 pub fn f16_as_self(a: f16) -> f16 {
-    // CHECK: ret half %{{.+}}
+    // other-CHECK: ret half %{{.+}}
+    // x86-CHECK: bitcast half
+    // x86-CHECK: ret i16
     a as f16
 }
 
@@ -176,21 +181,21 @@ pub fn f16_as_f128(a: f16) -> f128 {
     a as f128
 }
 
-// CHECK-LABEL: half @f32_as_f16(
+// CHECK-LABEL: @f32_as_f16
 #[no_mangle]
 pub fn f32_as_f16(a: f32) -> f16 {
     // CHECK: fptrunc float %{{.+}} to half
     a as f16
 }
 
-// CHECK-LABEL: half @f64_as_f16(
+// CHECK-LABEL: @f64_as_f16
 #[no_mangle]
 pub fn f64_as_f16(a: f64) -> f16 {
     // CHECK: fptrunc double %{{.+}} to half
     a as f16
 }
 
-// CHECK-LABEL: half @f128_as_f16(
+// CHECK-LABEL: @f128_as_f16
 #[no_mangle]
 pub fn f128_as_f16(a: f128) -> f16 {
     // CHECK: fptrunc fp128 %{{.+}} to half
@@ -273,70 +278,70 @@ pub fn f16_as_i128(a: f16) -> i128 {
 
 /* int to float conversions */
 
-// CHECK-LABEL: half @u8_as_f16(
+// CHECK-LABEL: @u8_as_f16
 #[no_mangle]
 pub fn u8_as_f16(a: u8) -> f16 {
     // CHECK: uitofp i8 %{{.+}} to half
     a as f16
 }
 
-// CHECK-LABEL: half @u16_as_f16(
+// CHECK-LABEL: @u16_as_f16
 #[no_mangle]
 pub fn u16_as_f16(a: u16) -> f16 {
     // CHECK: uitofp i16 %{{.+}} to half
     a as f16
 }
 
-// CHECK-LABEL: half @u32_as_f16(
+// CHECK-LABEL: @u32_as_f16
 #[no_mangle]
 pub fn u32_as_f16(a: u32) -> f16 {
     // CHECK: uitofp i32 %{{.+}} to half
     a as f16
 }
 
-// CHECK-LABEL: half @u64_as_f16(
+// CHECK-LABEL: @u64_as_f16
 #[no_mangle]
 pub fn u64_as_f16(a: u64) -> f16 {
     // CHECK: uitofp i64 %{{.+}} to half
     a as f16
 }
 
-// CHECK-LABEL: half @u128_as_f16(
+// CHECK-LABEL: @u128_as_f16
 #[no_mangle]
 pub fn u128_as_f16(a: u128) -> f16 {
     // CHECK: uitofp i128 %{{.+}} to half
     a as f16
 }
 
-// CHECK-LABEL: half @i8_as_f16(
+// CHECK-LABEL: @i8_as_f16
 #[no_mangle]
 pub fn i8_as_f16(a: i8) -> f16 {
     // CHECK: sitofp i8 %{{.+}} to half
     a as f16
 }
 
-// CHECK-LABEL: half @i16_as_f16(
+// CHECK-LABEL: @i16_as_f16
 #[no_mangle]
 pub fn i16_as_f16(a: i16) -> f16 {
     // CHECK: sitofp i16 %{{.+}} to half
     a as f16
 }
 
-// CHECK-LABEL: half @i32_as_f16(
+// CHECK-LABEL: @i32_as_f16
 #[no_mangle]
 pub fn i32_as_f16(a: i32) -> f16 {
     // CHECK: sitofp i32 %{{.+}} to half
     a as f16
 }
 
-// CHECK-LABEL: half @i64_as_f16(
+// CHECK-LABEL: @i64_as_f16
 #[no_mangle]
 pub fn i64_as_f16(a: i64) -> f16 {
     // CHECK: sitofp i64 %{{.+}} to half
     a as f16
 }
 
-// CHECK-LABEL: half @i128_as_f16(
+// CHECK-LABEL: @i128_as_f16
 #[no_mangle]
 pub fn i128_as_f16(a: i128) -> f16 {
     // CHECK: sitofp i128 %{{.+}} to half
diff --git a/tests/codegen/function-arguments.rs b/tests/codegen/function-arguments.rs
index bf9f405192b..7fa1d659885 100644
--- a/tests/codegen/function-arguments.rs
+++ b/tests/codegen/function-arguments.rs
@@ -32,7 +32,7 @@ pub fn boolean(x: bool) -> bool {
     x
 }
 
-// CHECK: i8 @maybeuninit_boolean(i8 %x)
+// CHECK: i8 @maybeuninit_boolean(i8{{.*}} %x)
 #[no_mangle]
 pub fn maybeuninit_boolean(x: MaybeUninit<bool>) -> MaybeUninit<bool> {
     x
@@ -44,19 +44,19 @@ pub fn enum_bool(x: MyBool) -> MyBool {
     x
 }
 
-// CHECK: i8 @maybeuninit_enum_bool(i8 %x)
+// CHECK: i8 @maybeuninit_enum_bool(i8{{.*}} %x)
 #[no_mangle]
 pub fn maybeuninit_enum_bool(x: MaybeUninit<MyBool>) -> MaybeUninit<MyBool> {
     x
 }
 
-// CHECK: noundef{{( range\(i32 0, 1114112\))?}} i32 @char(i32 noundef{{( range\(i32 0, 1114112\))?}} %x)
+// CHECK: noundef{{( range\(i32 0, 1114112\))?}} i32 @char(i32{{.*}}{{( range\(i32 0, 1114112\))?}} %x)
 #[no_mangle]
 pub fn char(x: char) -> char {
     x
 }
 
-// CHECK: i32 @maybeuninit_char(i32 %x)
+// CHECK: i32 @maybeuninit_char(i32{{.*}} %x)
 #[no_mangle]
 pub fn maybeuninit_char(x: MaybeUninit<char>) -> MaybeUninit<char> {
     x
diff --git a/tests/codegen/intrinsics/three_way_compare.rs b/tests/codegen/intrinsics/three_way_compare.rs
index f3b631abc22..9a476abe891 100644
--- a/tests/codegen/intrinsics/three_way_compare.rs
+++ b/tests/codegen/intrinsics/three_way_compare.rs
@@ -10,8 +10,7 @@ use std::intrinsics::three_way_compare;
 
 #[no_mangle]
 // CHECK-LABEL: @signed_cmp
-// DEBUG-SAME: (i16 %a, i16 %b)
-// OPTIM-SAME: (i16 noundef %a, i16 noundef %b)
+// CHECK-SAME: (i16{{.*}} %a, i16{{.*}} %b)
 pub fn signed_cmp(a: i16, b: i16) -> std::cmp::Ordering {
     // DEBUG: %[[GT:.+]] = icmp sgt i16 %a, %b
     // DEBUG: %[[ZGT:.+]] = zext i1 %[[GT]] to i8
@@ -29,8 +28,7 @@ pub fn signed_cmp(a: i16, b: i16) -> std::cmp::Ordering {
 
 #[no_mangle]
 // CHECK-LABEL: @unsigned_cmp
-// DEBUG-SAME: (i16 %a, i16 %b)
-// OPTIM-SAME: (i16 noundef %a, i16 noundef %b)
+// CHECK-SAME: (i16{{.*}} %a, i16{{.*}} %b)
 pub fn unsigned_cmp(a: u16, b: u16) -> std::cmp::Ordering {
     // DEBUG: %[[GT:.+]] = icmp ugt i16 %a, %b
     // DEBUG: %[[ZGT:.+]] = zext i1 %[[GT]] to i8
diff --git a/tests/codegen/mir-aggregate-no-alloca.rs b/tests/codegen/mir-aggregate-no-alloca.rs
index 04ffb075538..37b024a55b3 100644
--- a/tests/codegen/mir-aggregate-no-alloca.rs
+++ b/tests/codegen/mir-aggregate-no-alloca.rs
@@ -9,7 +9,7 @@
 #[repr(transparent)]
 pub struct Transparent32(u32);
 
-// CHECK: i32 @make_transparent(i32 noundef %x)
+// CHECK: i32 @make_transparent(i32{{.*}} %x)
 #[no_mangle]
 pub fn make_transparent(x: u32) -> Transparent32 {
     // CHECK-NOT: alloca
@@ -18,7 +18,7 @@ pub fn make_transparent(x: u32) -> Transparent32 {
     a
 }
 
-// CHECK: i32 @make_closure(i32 noundef %x)
+// CHECK: i32 @make_closure(i32{{.*}} %x)
 #[no_mangle]
 pub fn make_closure(x: i32) -> impl Fn(i32) -> i32 {
     // CHECK-NOT: alloca
@@ -40,7 +40,7 @@ pub fn make_transparent_pair(x: (u16, u16)) -> TransparentPair {
     a
 }
 
-// CHECK-LABEL: { i32, i32 } @make_2_tuple(i32 noundef %x)
+// CHECK-LABEL: { i32, i32 } @make_2_tuple(i32{{.*}} %x)
 #[no_mangle]
 pub fn make_2_tuple(x: u32) -> (u32, u32) {
     // CHECK-NOT: alloca
@@ -59,7 +59,7 @@ pub fn make_cell_of_bool(b: bool) -> std::cell::Cell<bool> {
     std::cell::Cell::new(b)
 }
 
-// CHECK-LABEL: { i8, i16 } @make_cell_of_bool_and_short(i1 noundef zeroext %b, i16 noundef %s)
+// CHECK-LABEL: { i8, i16 } @make_cell_of_bool_and_short(i1 noundef zeroext %b, i16{{.*}} %s)
 #[no_mangle]
 pub fn make_cell_of_bool_and_short(b: bool, s: u16) -> std::cell::Cell<(bool, u16)> {
     // CHECK-NOT: alloca
@@ -92,7 +92,7 @@ pub fn make_struct_0() -> Struct0 {
 
 pub struct Struct1(i32);
 
-// CHECK-LABEL: i32 @make_struct_1(i32 noundef %a)
+// CHECK-LABEL: i32 @make_struct_1(i32{{.*}} %a)
 #[no_mangle]
 pub fn make_struct_1(a: i32) -> Struct1 {
     // CHECK: ret i32 %a
@@ -104,7 +104,7 @@ pub struct Struct2Asc(i16, i64);
 
 // bit32-LABEL: void @make_struct_2_asc({{.*}} sret({{[^,]*}}) {{.*}} %s,
 // bit64-LABEL: { i64, i16 } @make_struct_2_asc(
-// CHECK-SAME: i16 noundef %a, i64 noundef %b)
+// CHECK-SAME: i16{{.*}} %a, i64 noundef %b)
 #[no_mangle]
 pub fn make_struct_2_asc(a: i16, b: i64) -> Struct2Asc {
     // CHECK-NOT: alloca
@@ -122,7 +122,7 @@ pub struct Struct2Desc(i64, i16);
 
 // bit32-LABEL: void @make_struct_2_desc({{.*}} sret({{[^,]*}}) {{.*}} %s,
 // bit64-LABEL: { i64, i16 } @make_struct_2_desc(
-// CHECK-SAME: i64 noundef %a, i16 noundef %b)
+// CHECK-SAME: i64 noundef %a, i16{{.*}} %b)
 #[no_mangle]
 pub fn make_struct_2_desc(a: i64, b: i16) -> Struct2Desc {
     // CHECK-NOT: alloca
diff --git a/tests/codegen/placement-new.rs b/tests/codegen/placement-new.rs
index edb25df5eb4..0ec2b6a6f20 100644
--- a/tests/codegen/placement-new.rs
+++ b/tests/codegen/placement-new.rs
@@ -1,9 +1,11 @@
 //@ compile-flags: -O
+//@ compile-flags: -Zmerge-functions=disabled
 #![crate_type = "lib"]
 
 // Test to check that types with "complex" destructors, but trivial `Default` impls
 // are constructed directly into the allocation in `Box::default` and `Arc::default`.
 
+use std::rc::Rc;
 use std::sync::Arc;
 
 // CHECK-LABEL: @box_default_inplace
@@ -16,6 +18,16 @@ pub fn box_default_inplace() -> Box<(String, String)> {
     Box::default()
 }
 
+// CHECK-LABEL: @rc_default_inplace
+#[no_mangle]
+pub fn rc_default_inplace() -> Rc<(String, String)> {
+    // CHECK-NOT: alloca
+    // CHECK: [[RC:%.*]] = {{.*}}call {{.*}}__rust_alloc(
+    // CHECK-NOT: call void @llvm.memcpy
+    // CHECK: ret ptr [[RC]]
+    Rc::default()
+}
+
 // CHECK-LABEL: @arc_default_inplace
 #[no_mangle]
 pub fn arc_default_inplace() -> Arc<(String, String)> {
diff --git a/tests/codegen/range-attribute.rs b/tests/codegen/range-attribute.rs
index 8972fc76ca2..a44ec1026b1 100644
--- a/tests/codegen/range-attribute.rs
+++ b/tests/codegen/range-attribute.rs
@@ -24,7 +24,7 @@ pub fn nonzero_int(x: NonZero<u128>) -> NonZero<u128> {
     x
 }
 
-// CHECK: noundef range(i8 0, 3) i8 @optional_bool(i8 noundef range(i8 0, 3) %x)
+// CHECK: noundef range(i8 0, 3) i8 @optional_bool(i8{{.*}} range(i8 0, 3) %x)
 #[no_mangle]
 pub fn optional_bool(x: Option<bool>) -> Option<bool> {
     x
@@ -36,7 +36,7 @@ pub enum Enum0 {
     C,
 }
 
-// CHECK: noundef range(i8 0, 4) i8 @enum0_value(i8 noundef range(i8 0, 4) %x)
+// CHECK: noundef range(i8 0, 4) i8 @enum0_value(i8{{.*}} range(i8 0, 4) %x)
 #[no_mangle]
 pub fn enum0_value(x: Enum0) -> Enum0 {
     x
diff --git a/tests/codegen/rust-abi-arch-specific-adjustment.rs b/tests/codegen/rust-abi-arch-specific-adjustment.rs
new file mode 100644
index 00000000000..9da10f662b0
--- /dev/null
+++ b/tests/codegen/rust-abi-arch-specific-adjustment.rs
@@ -0,0 +1,111 @@
+//@ compile-flags: -O -C no-prepopulate-passes
+//@ revisions: riscv64 loongarch64
+
+//@[riscv64] only-riscv64
+//@[riscv64] compile-flags: --target riscv64gc-unknown-linux-gnu
+//@[riscv64] needs-llvm-components: riscv
+
+//@[loongarch64] only-loongarch64
+//@[loongarch64] compile-flags: --target loongarch64-unknown-linux-gnu
+//@[loongarch64] needs-llvm-components: loongarch
+
+#![crate_type = "lib"]
+
+#[no_mangle]
+// riscv64:     define noundef i8 @arg_attr_u8(i8 noundef zeroext %x)
+// loongarch64: define noundef i8 @arg_attr_u8(i8 noundef zeroext %x)
+pub fn arg_attr_u8(x: u8) -> u8 {
+    x
+}
+
+#[no_mangle]
+// riscv64:     define noundef i16 @arg_attr_u16(i16 noundef zeroext %x)
+// loongarch64: define noundef i16 @arg_attr_u16(i16 noundef zeroext %x)
+pub fn arg_attr_u16(x: u16) -> u16 {
+    x
+}
+
+#[no_mangle]
+// riscv64:     define noundef i32 @arg_attr_u32(i32 noundef signext %x)
+// loongarch64: define noundef i32 @arg_attr_u32(i32 noundef signext %x)
+pub fn arg_attr_u32(x: u32) -> u32 {
+    x
+}
+
+#[no_mangle]
+// riscv64:     define noundef i64 @arg_attr_u64(i64 noundef %x)
+// loongarch64: define noundef i64 @arg_attr_u64(i64 noundef %x)
+pub fn arg_attr_u64(x: u64) -> u64 {
+    x
+}
+
+#[no_mangle]
+// riscv64:     define noundef i128 @arg_attr_u128(i128 noundef %x)
+// loongarch64: define noundef i128 @arg_attr_u128(i128 noundef %x)
+pub fn arg_attr_u128(x: u128) -> u128 {
+    x
+}
+
+#[no_mangle]
+// riscv64:     define noundef i8 @arg_attr_i8(i8 noundef signext %x)
+// loongarch64: define noundef i8 @arg_attr_i8(i8 noundef signext %x)
+pub fn arg_attr_i8(x: i8) -> i8 {
+    x
+}
+
+#[no_mangle]
+// riscv64:     define noundef i16 @arg_attr_i16(i16 noundef signext %x)
+// loongarch64: define noundef i16 @arg_attr_i16(i16 noundef signext %x)
+pub fn arg_attr_i16(x: i16) -> i16 {
+    x
+}
+
+#[no_mangle]
+// riscv64:     define noundef i32 @arg_attr_i32(i32 noundef signext %x)
+// loongarch64: define noundef i32 @arg_attr_i32(i32 noundef signext %x)
+pub fn arg_attr_i32(x: i32) -> i32 {
+    x
+}
+
+#[no_mangle]
+// riscv64:     define noundef i64 @arg_attr_i64(i64 noundef %x)
+// loongarch64: define noundef i64 @arg_attr_i64(i64 noundef %x)
+pub fn arg_attr_i64(x: i64) -> i64 {
+    x
+}
+
+#[no_mangle]
+// riscv64:     define noundef i128 @arg_attr_i128(i128 noundef %x)
+// loongarch64: define noundef i128 @arg_attr_i128(i128 noundef %x)
+pub fn arg_attr_i128(x: i128) -> i128 {
+    x
+}
+
+#[no_mangle]
+// riscv64:     define noundef zeroext i1 @arg_attr_bool(i1 noundef zeroext %x)
+// loongarch64: define noundef zeroext i1 @arg_attr_bool(i1 noundef zeroext %x)
+pub fn arg_attr_bool(x: bool) -> bool {
+    x
+}
+
+#[no_mangle]
+// ignore-tidy-linelength
+// riscv64:     define noundef{{( range\(i32 0, 1114112\))?}} i32 @arg_attr_char(i32 noundef signext{{( range\(i32 0, 1114112\))?}} %x)
+// loongarch64: define noundef{{( range\(i32 0, 1114112\))?}} i32 @arg_attr_char(i32 noundef signext{{( range\(i32 0, 1114112\))?}} %x)
+pub fn arg_attr_char(x: char) -> char {
+    x
+}
+
+#[no_mangle]
+// riscv64:     define noundef float @arg_attr_f32(float noundef %x)
+// loongarch64: define noundef float @arg_attr_f32(float noundef %x)
+pub fn arg_attr_f32(x: f32) -> f32 {
+    x
+}
+
+#[no_mangle]
+// riscv64:     define noundef double @arg_attr_f64(double noundef %x)
+// loongarch64: define noundef double @arg_attr_f64(double noundef %x)
+pub fn arg_attr_f64(x: f64) -> f64 {
+    x
+}
diff --git a/tests/codegen/sanitizer/cfi/emit-type-checks-attr-no-sanitize.rs b/tests/codegen/sanitizer/cfi/emit-type-checks-attr-no-sanitize.rs
index 259967e8918..71ccdc8ca62 100644
--- a/tests/codegen/sanitizer/cfi/emit-type-checks-attr-no-sanitize.rs
+++ b/tests/codegen/sanitizer/cfi/emit-type-checks-attr-no-sanitize.rs
@@ -12,7 +12,7 @@ pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 {
     // CHECK:       Function Attrs: {{.*}}
     // CHECK-LABEL: define{{.*}}foo{{.*}}!type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}}
     // CHECK:       start:
-    // CHECK-NEXT:  {{%.+}} = call i32 %f(i32 %arg)
+    // CHECK-NEXT:  {{%.+}} = call i32 %f(i32{{.*}} %arg)
     // CHECK-NEXT:  ret i32 {{%.+}}
     f(arg)
 }
diff --git a/tests/codegen/sanitizer/cfi/emit-type-checks.rs b/tests/codegen/sanitizer/cfi/emit-type-checks.rs
index 37edbefee56..ebc66a015df 100644
--- a/tests/codegen/sanitizer/cfi/emit-type-checks.rs
+++ b/tests/codegen/sanitizer/cfi/emit-type-checks.rs
@@ -11,7 +11,7 @@ pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 {
     // CHECK:       [[TT:%.+]] = call i1 @llvm.type.test(ptr {{%f|%0}}, metadata !"{{[[:print:]]+}}")
     // CHECK-NEXT:  br i1 [[TT]], label %type_test.pass, label %type_test.fail
     // CHECK:       type_test.pass:
-    // CHECK-NEXT:  {{%.+}} = call i32 %f(i32 %arg)
+    // CHECK-NEXT:  {{%.+}} = call i32 %f(i32{{.*}} %arg)
     // CHECK:       type_test.fail:
     // CHECK-NEXT:  call void @llvm.trap()
     // CHECK-NEXT:  unreachable
diff --git a/tests/codegen/transmute-scalar.rs b/tests/codegen/transmute-scalar.rs
index caaa70962d5..43da7c1781e 100644
--- a/tests/codegen/transmute-scalar.rs
+++ b/tests/codegen/transmute-scalar.rs
@@ -25,7 +25,7 @@ pub fn bool_to_byte(b: bool) -> u8 {
     unsafe { std::mem::transmute(b) }
 }
 
-// CHECK-LABEL: define{{.*}}zeroext i1 @byte_to_bool(i8 %byte)
+// CHECK-LABEL: define{{.*}}zeroext i1 @byte_to_bool(i8{{.*}} %byte)
 // CHECK: %_0 = trunc i8 %byte to i1
 // CHECK-NEXT: ret i1 %_0
 #[no_mangle]
diff --git a/tests/codegen/union-abi.rs b/tests/codegen/union-abi.rs
index b3c67a59730..2f14682dfa5 100644
--- a/tests/codegen/union-abi.rs
+++ b/tests/codegen/union-abi.rs
@@ -131,7 +131,7 @@ pub fn test_CUnionU128(_: CUnionU128) {
 pub union UnionBool {
     b: bool,
 }
-// CHECK: define {{(dso_local )?}}noundef zeroext i1 @test_UnionBool(i8 %b)
+// CHECK: define {{(dso_local )?}}noundef zeroext i1 @test_UnionBool(i8{{.*}} %b)
 #[no_mangle]
 pub fn test_UnionBool(b: UnionBool) -> bool {
     unsafe { b.b }
diff --git a/tests/codegen/var-names.rs b/tests/codegen/var-names.rs
index fd163a55551..4ea5b3b436d 100644
--- a/tests/codegen/var-names.rs
+++ b/tests/codegen/var-names.rs
@@ -2,7 +2,7 @@
 
 #![crate_type = "lib"]
 
-// CHECK-LABEL: define{{.*}}i32 @test(i32 noundef %a, i32 noundef %b)
+// CHECK-LABEL: define{{.*}}i32 @test(i32{{.*}} %a, i32{{.*}} %b)
 #[no_mangle]
 pub fn test(a: u32, b: u32) -> u32 {
     let c = a + b;
diff --git a/tests/run-make/import-macro-verbatim/include/include.txt b/tests/run-make/import-macro-verbatim/include/include.txt
new file mode 100644
index 00000000000..63d71b14c1d
--- /dev/null
+++ b/tests/run-make/import-macro-verbatim/include/include.txt
@@ -0,0 +1 @@
+static TEST: &str = "Hello World!";
diff --git a/tests/run-make/import-macro-verbatim/rmake.rs b/tests/run-make/import-macro-verbatim/rmake.rs
new file mode 100644
index 00000000000..d2bf626e0aa
--- /dev/null
+++ b/tests/run-make/import-macro-verbatim/rmake.rs
@@ -0,0 +1,8 @@
+//@ only-windows other platforms do not have Windows verbatim paths
+use run_make_support::rustc;
+fn main() {
+    // Canonicalizing the path ensures that it's verbatim (i.e. starts with `\\?\`)
+    let mut path = std::fs::canonicalize(file!()).unwrap();
+    path.pop();
+    rustc().input("verbatim.rs").env("VERBATIM_DIR", path).run();
+}
diff --git a/tests/run-make/import-macro-verbatim/verbatim.rs b/tests/run-make/import-macro-verbatim/verbatim.rs
new file mode 100644
index 00000000000..56a83673c1f
--- /dev/null
+++ b/tests/run-make/import-macro-verbatim/verbatim.rs
@@ -0,0 +1,12 @@
+//! Include a file by concating the verbatim path using `/` instead of `\`
+
+include!(concat!(env!("VERBATIM_DIR"), "/include/include.txt"));
+fn main() {
+    assert_eq!(TEST, "Hello World!");
+
+    let s = include_str!(concat!(env!("VERBATIM_DIR"), "/include/include.txt"));
+    assert_eq!(s, "static TEST: &str = \"Hello World!\";\n");
+
+    let b = include_bytes!(concat!(env!("VERBATIM_DIR"), "/include/include.txt"));
+    assert_eq!(b, b"static TEST: &str = \"Hello World!\";\n");
+}
diff --git a/tests/run-make/rust-lld-link-script-provide/main.rs b/tests/run-make/rust-lld-link-script-provide/main.rs
new file mode 100644
index 00000000000..5c19e7a4bbf
--- /dev/null
+++ b/tests/run-make/rust-lld-link-script-provide/main.rs
@@ -0,0 +1,7 @@
+#[no_mangle]
+fn foo() {}
+
+#[no_mangle]
+fn bar() {}
+
+fn main() {}
diff --git a/tests/run-make/rust-lld-link-script-provide/rmake.rs b/tests/run-make/rust-lld-link-script-provide/rmake.rs
new file mode 100644
index 00000000000..e78a411bc15
--- /dev/null
+++ b/tests/run-make/rust-lld-link-script-provide/rmake.rs
@@ -0,0 +1,18 @@
+// This test ensures that the “symbol not found” error does not occur
+// when the symbols in the `PROVIDE` of the link script can be eliminated.
+// This is a regression test for #131164.
+
+//@ needs-rust-lld
+//@ only-x86_64-unknown-linux-gnu
+
+use run_make_support::rustc;
+
+fn main() {
+    rustc()
+        .input("main.rs")
+        .arg("-Zlinker-features=+lld")
+        .arg("-Clink-self-contained=+linker")
+        .arg("-Zunstable-options")
+        .link_arg("-Tscript.t")
+        .run();
+}
diff --git a/tests/run-make/rust-lld-link-script-provide/script.t b/tests/run-make/rust-lld-link-script-provide/script.t
new file mode 100644
index 00000000000..4c4c6ddfc36
--- /dev/null
+++ b/tests/run-make/rust-lld-link-script-provide/script.t
@@ -0,0 +1 @@
+PROVIDE(foo = bar);
diff --git a/tests/rustdoc-ui/deprecated-attrs.rs b/tests/rustdoc-ui/deprecated-attrs.rs
index e4802ee2518..3b59e05a012 100644
--- a/tests/rustdoc-ui/deprecated-attrs.rs
+++ b/tests/rustdoc-ui/deprecated-attrs.rs
@@ -1,16 +1,21 @@
-//@ check-pass
 //@ compile-flags: --passes unknown-pass
 //@ error-pattern: the `passes` flag no longer functions
 
 #![doc(no_default_passes)]
-//~^ WARNING attribute is deprecated
+//~^ ERROR unknown `doc` attribute `no_default_passes`
+//~| NOTE no longer functions
 //~| NOTE see issue #44136
-//~| HELP no longer functions; you may want to use `#![doc(document_private_items)]`
+//~| HELP you may want to use `doc(document_private_items)`
+//~| NOTE `doc(no_default_passes)` is now a no-op
+//~| NOTE `#[deny(invalid_doc_attributes)]` on by default
 #![doc(passes = "collapse-docs unindent-comments")]
-//~^ WARNING attribute is deprecated
+//~^ ERROR unknown `doc` attribute `passes`
+//~| NOTE no longer functions
 //~| NOTE see issue #44136
-//~| HELP no longer functions; you may want to use `#![doc(document_private_items)]`
+//~| HELP you may want to use `doc(document_private_items)`
+//~| NOTE `doc(passes)` is now a no-op
 #![doc(plugins = "xxx")]
-//~^ WARNING attribute is deprecated
+//~^ ERROR unknown `doc` attribute `plugins`
 //~| NOTE see issue #44136
-//~| WARNING no longer functions; see CVE
+//~| NOTE no longer functions
+//~| NOTE `doc(plugins)` is now a no-op
diff --git a/tests/rustdoc-ui/deprecated-attrs.stderr b/tests/rustdoc-ui/deprecated-attrs.stderr
index 45b20ce70ef..a30523e7329 100644
--- a/tests/rustdoc-ui/deprecated-attrs.stderr
+++ b/tests/rustdoc-ui/deprecated-attrs.stderr
@@ -3,32 +3,35 @@ warning: the `passes` flag no longer functions
    = note: see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information
    = help: you may want to use --document-private-items
 
-warning: the `#![doc(no_default_passes)]` attribute is deprecated
-  --> $DIR/deprecated-attrs.rs:5:8
+error: unknown `doc` attribute `no_default_passes`
+  --> $DIR/deprecated-attrs.rs:4:8
    |
 LL | #![doc(no_default_passes)]
-   |        ^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^^ no longer functions
    |
-   = note: see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information
-   = help: `#![doc(no_default_passes)]` no longer functions; you may want to use `#![doc(document_private_items)]`
+   = note: `doc` attribute `no_default_passes` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136>
+   = help: you may want to use `doc(document_private_items)`
+   = note: `doc(no_default_passes)` is now a no-op
+   = note: `#[deny(invalid_doc_attributes)]` on by default
 
-warning: the `#![doc(passes = "...")]` attribute is deprecated
-  --> $DIR/deprecated-attrs.rs:9:8
+error: unknown `doc` attribute `passes`
+  --> $DIR/deprecated-attrs.rs:11:8
    |
 LL | #![doc(passes = "collapse-docs unindent-comments")]
-   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no longer functions
    |
-   = note: see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information
-   = help: `#![doc(passes = "...")]` no longer functions; you may want to use `#![doc(document_private_items)]`
+   = note: `doc` attribute `passes` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136>
+   = help: you may want to use `doc(document_private_items)`
+   = note: `doc(passes)` is now a no-op
 
-warning: the `#![doc(plugins = "...")]` attribute is deprecated
-  --> $DIR/deprecated-attrs.rs:13:8
+error: unknown `doc` attribute `plugins`
+  --> $DIR/deprecated-attrs.rs:17:8
    |
 LL | #![doc(plugins = "xxx")]
-   |        ^^^^^^^^^^^^^^^
+   |        ^^^^^^^^^^^^^^^ no longer functions
    |
-   = note: see issue #44136 <https://github.com/rust-lang/rust/issues/44136> for more information
-   = warning: `#![doc(plugins = "...")]` no longer functions; see CVE-2018-1000622 <https://nvd.nist.gov/vuln/detail/CVE-2018-1000622>
+   = note: `doc` attribute `plugins` no longer functions; see issue #44136 <https://github.com/rust-lang/rust/issues/44136> and CVE-2018-1000622 <https://nvd.nist.gov/vuln/detail/CVE-2018-1000622>
+   = note: `doc(plugins)` is now a no-op
 
-warning: 3 warnings emitted
+error: aborting due to 3 previous errors
 
diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs
index 28c8063a923..33d67360ff1 100644
--- a/tests/ui/abi/compatibility.rs
+++ b/tests/ui/abi/compatibility.rs
@@ -211,6 +211,15 @@ impl Clone for Zst {
     }
 }
 
+enum Either<T, U> {
+    Left(T),
+    Right(U),
+}
+enum Either2<T, U> {
+    Left(T),
+    Right(U, ()),
+}
+
 #[repr(C)]
 enum ReprCEnum<T> {
     Variant1,
@@ -328,7 +337,8 @@ mod unsized_ {
     test_transparent_unsized!(dyn_trait, dyn Any);
 }
 
-// RFC 3391 <https://rust-lang.github.io/rfcs/3391-result_ffi_guarantees.html>.
+// RFC 3391 <https://rust-lang.github.io/rfcs/3391-result_ffi_guarantees.html>, including the
+// extension ratified at <https://github.com/rust-lang/rust/pull/130628#issuecomment-2402761599>.
 macro_rules! test_nonnull {
     ($name:ident, $t:ty) => {
         mod $name {
@@ -340,6 +350,12 @@ macro_rules! test_nonnull {
             test_abi_compatible!(result_ok_zst, Result<Zst, $t>, $t);
             test_abi_compatible!(result_err_arr, Result<$t, [i8; 0]>, $t);
             test_abi_compatible!(result_ok_arr, Result<[i8; 0], $t>, $t);
+            test_abi_compatible!(result_err_void, Result<$t, Void>, $t);
+            test_abi_compatible!(result_ok_void, Result<Void, $t>, $t);
+            test_abi_compatible!(either_err_zst, Either<$t, Zst>, $t);
+            test_abi_compatible!(either_ok_zst, Either<Zst, $t>, $t);
+            test_abi_compatible!(either2_err_zst, Either2<$t, Zst>, $t);
+            test_abi_compatible!(either2_err_arr, Either2<$t, [i8; 0]>, $t);
         }
     }
 }
diff --git a/tests/ui/async-await/field-in-sync.rs b/tests/ui/async-await/field-in-sync.rs
new file mode 100644
index 00000000000..586980c6e2b
--- /dev/null
+++ b/tests/ui/async-await/field-in-sync.rs
@@ -0,0 +1,13 @@
+//@ edition: 2021
+
+struct S {
+    field: (),
+}
+
+async fn foo() -> S { todo!() }
+
+fn main() -> Result<(), ()> {
+    foo().field;
+    //~^ ERROR no field `field` on type `impl Future<Output = S>`
+    Ok(())
+}
diff --git a/tests/ui/async-await/field-in-sync.stderr b/tests/ui/async-await/field-in-sync.stderr
new file mode 100644
index 00000000000..7be30339c27
--- /dev/null
+++ b/tests/ui/async-await/field-in-sync.stderr
@@ -0,0 +1,17 @@
+error[E0609]: no field `field` on type `impl Future<Output = S>`
+  --> $DIR/field-in-sync.rs:10:11
+   |
+LL |     foo().field;
+   |           ^^^^^ field not available in `impl Future`, but it is available in its `Output`
+   |
+note: this implements `Future` and its output type has the field, but the future cannot be awaited in a synchronous function
+  --> $DIR/field-in-sync.rs:10:5
+   |
+LL | fn main() -> Result<(), ()> {
+   | --------------------------- this is not `async`
+LL |     foo().field;
+   |     ^^^^^
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0609`.
diff --git a/tests/ui/async-await/issue-61076.stderr b/tests/ui/async-await/issue-61076.stderr
index 44de282988b..b8478c8d138 100644
--- a/tests/ui/async-await/issue-61076.stderr
+++ b/tests/ui/async-await/issue-61076.stderr
@@ -28,7 +28,7 @@ error[E0609]: no field `0` on type `impl Future<Output = Tuple>`
 LL |     let _: i32 = tuple().0;
    |                          ^ field not available in `impl Future`, but it is available in its `Output`
    |
-help: consider `await`ing on the `Future` and access the field of its `Output`
+help: consider `await`ing on the `Future` to access the field
    |
 LL |     let _: i32 = tuple().await.0;
    |                         ++++++
@@ -39,7 +39,7 @@ error[E0609]: no field `a` on type `impl Future<Output = Struct>`
 LL |     let _: i32 = struct_().a;
    |                            ^ field not available in `impl Future`, but it is available in its `Output`
    |
-help: consider `await`ing on the `Future` and access the field of its `Output`
+help: consider `await`ing on the `Future` to access the field
    |
 LL |     let _: i32 = struct_().await.a;
    |                           ++++++
diff --git a/tests/ui/async-await/try-in-sync.rs b/tests/ui/async-await/try-in-sync.rs
new file mode 100644
index 00000000000..81d72c3fb9a
--- /dev/null
+++ b/tests/ui/async-await/try-in-sync.rs
@@ -0,0 +1,9 @@
+//@ edition: 2021
+
+async fn foo() -> Result<(), ()> { todo!() }
+
+fn main() -> Result<(), ()> {
+    foo()?;
+    //~^ ERROR the `?` operator can only be applied to values that implement `Try`
+    Ok(())
+}
diff --git a/tests/ui/async-await/try-in-sync.stderr b/tests/ui/async-await/try-in-sync.stderr
new file mode 100644
index 00000000000..bc7a6bd0151
--- /dev/null
+++ b/tests/ui/async-await/try-in-sync.stderr
@@ -0,0 +1,18 @@
+error[E0277]: the `?` operator can only be applied to values that implement `Try`
+  --> $DIR/try-in-sync.rs:6:5
+   |
+LL |     foo()?;
+   |     ^^^^^^ the `?` operator cannot be applied to type `impl Future<Output = Result<(), ()>>`
+   |
+   = help: the trait `Try` is not implemented for `impl Future<Output = Result<(), ()>>`
+note: this implements `Future` and its output type supports `?`, but the future cannot be awaited in a synchronous function
+  --> $DIR/try-in-sync.rs:6:10
+   |
+LL | fn main() -> Result<(), ()> {
+   | --------------------------- this is not `async`
+LL |     foo()?;
+   |          ^
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/stats/hir-stats.stderr b/tests/ui/stats/hir-stats.stderr
index 3a2d4df3bee..bd0c8cbc3b1 100644
--- a/tests/ui/stats/hir-stats.stderr
+++ b/tests/ui/stats/hir-stats.stderr
@@ -141,10 +141,10 @@ hir-stats FnDecl                   120 ( 1.3%)             3            40
 hir-stats Attribute                128 ( 1.4%)             4            32
 hir-stats GenericArgs              144 ( 1.6%)             3            48
 hir-stats Variant                  144 ( 1.6%)             2            72
-hir-stats GenericBound             192 ( 2.1%)             4            48
-hir-stats - Trait                    192 ( 2.1%)             4
 hir-stats WherePredicate           192 ( 2.1%)             3            64
 hir-stats - BoundPredicate           192 ( 2.1%)             3
+hir-stats GenericBound             256 ( 2.8%)             4            64
+hir-stats - Trait                    256 ( 2.8%)             4
 hir-stats Block                    288 ( 3.2%)             6            48
 hir-stats GenericParam             360 ( 4.0%)             5            72
 hir-stats Pat                      360 ( 4.0%)             5            72
@@ -155,15 +155,15 @@ hir-stats Generics                 560 ( 6.2%)            10            56
 hir-stats Ty                       720 ( 8.0%)            15            48
 hir-stats - Ref                       48 ( 0.5%)             1
 hir-stats - Ptr                       48 ( 0.5%)             1
-hir-stats - Path                     624 ( 7.0%)            13
-hir-stats Expr                     768 ( 8.6%)            12            64
+hir-stats - Path                     624 ( 6.9%)            13
+hir-stats Expr                     768 ( 8.5%)            12            64
 hir-stats - Path                      64 ( 0.7%)             1
 hir-stats - Match                     64 ( 0.7%)             1
 hir-stats - Struct                    64 ( 0.7%)             1
 hir-stats - InlineAsm                 64 ( 0.7%)             1
 hir-stats - Lit                      128 ( 1.4%)             2
 hir-stats - Block                    384 ( 4.3%)             6
-hir-stats Item                     968 (10.8%)            11            88
+hir-stats Item                     968 (10.7%)            11            88
 hir-stats - Enum                      88 ( 1.0%)             1
 hir-stats - Trait                     88 ( 1.0%)             1
 hir-stats - Impl                      88 ( 1.0%)             1
@@ -171,8 +171,8 @@ hir-stats - ExternCrate               88 ( 1.0%)             1
 hir-stats - ForeignMod                88 ( 1.0%)             1
 hir-stats - Fn                       176 ( 2.0%)             2
 hir-stats - Use                      352 ( 3.9%)             4
-hir-stats Path                   1_240 (13.8%)            31            40
-hir-stats PathSegment            1_920 (21.4%)            40            48
+hir-stats Path                   1_240 (13.7%)            31            40
+hir-stats PathSegment            1_920 (21.3%)            40            48
 hir-stats ----------------------------------------------------------------
-hir-stats Total                  8_960
+hir-stats Total                  9_024
 hir-stats