about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-07-27 18:22:33 +0000
committerbors <bors@rust-lang.org>2022-07-27 18:22:33 +0000
commit2643b16468fda787470340890212591d8bc832b7 (patch)
treedfb7655d89e2758e346ed903c3cf258e79b4fcec
parentda5b546d2e563747b16a16dae83bacf49aa0bf3b (diff)
parent2d52aa05d1786b32f7d6e944a733352d31675acc (diff)
downloadrust-2643b16468fda787470340890212591d8bc832b7.tar.gz
rust-2643b16468fda787470340890212591d8bc832b7.zip
Auto merge of #99816 - GuillaumeGomez:rollup-tyobksa, r=GuillaumeGomez
Rollup of 7 pull requests

Successful merges:

 - #94247 (Fix slice::ChunksMut aliasing)
 - #99358 (Allow `ValTree::try_to_raw_bytes` on `u8` array)
 - #99651 (Deeply deny fn and raw ptrs in const generics)
 - #99710 (lint: add bad opt access internal lint)
 - #99717 (Add some comments to the docs issue template to clarify)
 - #99728 (Clean up HIR-based lifetime resolution)
 - #99812 (Fix headings colors)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--.github/ISSUE_TEMPLATE/documentation.md17
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs24
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs3
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs2
-rw-r--r--compiler/rustc_driver/src/lib.rs5
-rw-r--r--compiler/rustc_error_messages/locales/en-US/passes.ftl6
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs6
-rw-r--r--compiler/rustc_hir/src/def.rs9
-rw-r--r--compiler/rustc_hir/src/hir.rs21
-rw-r--r--compiler/rustc_hir/src/intravisit.rs3
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs31
-rw-r--r--compiler/rustc_interface/src/interface.rs2
-rw-r--r--compiler/rustc_interface/src/passes.rs2
-rw-r--r--compiler/rustc_interface/src/tests.rs1
-rw-r--r--compiler/rustc_interface/src/util.rs2
-rw-r--r--compiler/rustc_lint/src/internal.rs49
-rw-r--r--compiler/rustc_lint/src/lib.rs7
-rw-r--r--compiler/rustc_middle/src/middle/resolve_lifetime.rs1
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs2
-rw-r--r--compiler/rustc_middle/src/ty/consts/valtree.rs34
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs60
-rw-r--r--compiler/rustc_mir_transform/src/lower_slice_len.rs2
-rw-r--r--compiler/rustc_mir_transform/src/reveal_all.rs2
-rw-r--r--compiler/rustc_passes/src/check_attr.rs33
-rw-r--r--compiler/rustc_passes/src/errors.rs18
-rw-r--r--compiler/rustc_resolve/src/late.rs239
-rw-r--r--compiler/rustc_resolve/src/late/lifetimes.rs568
-rw-r--r--compiler/rustc_session/src/config.rs4
-rw-r--r--compiler/rustc_session/src/options.rs75
-rw-r--r--compiler/rustc_session/src/session.rs577
-rw-r--r--compiler/rustc_span/src/symbol.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_match.rs131
-rw-r--r--compiler/rustc_typeck/src/astconv/mod.rs8
-rw-r--r--compiler/rustc_typeck/src/check/wfcheck.rs42
-rw-r--r--compiler/rustc_typeck/src/collect.rs12
-rw-r--r--library/core/src/slice/iter.rs194
-rw-r--r--library/core/tests/slice.rs44
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css4
-rw-r--r--src/librustdoc/html/static/css/themes/dark.css4
-rw-r--r--src/librustdoc/lib.rs2
-rw-r--r--src/test/mir-opt/issue-99325.rs12
-rw-r--r--src/test/mir-opt/issue_99325.main.mir_map.0.mir295
-rw-r--r--src/test/rustdoc-gui/docblock-details.goml4
-rw-r--r--src/test/rustdoc-gui/headings.goml100
-rw-r--r--src/test/rustdoc-gui/src/test_docs/lib.rs4
-rw-r--r--src/test/ui-fulldeps/internal-lints/bad_opt_access.rs22
-rw-r--r--src/test/ui-fulldeps/internal-lints/bad_opt_access.stderr20
-rw-r--r--src/test/ui/const-generics/fn-const-param-call.full.stderr5
-rw-r--r--src/test/ui/const-generics/fn-const-param-infer.full.stderr3
-rw-r--r--src/test/ui/const-generics/issues/issue-71381.full.stderr7
-rw-r--r--src/test/ui/const-generics/issues/issue-71382.full.stderr3
-rw-r--r--src/test/ui/const-generics/issues/issue-71611.full.stderr5
-rw-r--r--src/test/ui/const-generics/issues/issue-72352.full.stderr3
-rw-r--r--src/test/ui/const-generics/issues/issue-99641.rs18
-rw-r--r--src/test/ui/const-generics/issues/issue-99641.stderr15
-rw-r--r--src/test/ui/const-generics/raw-ptr-const-param-deref.full.stderr5
-rw-r--r--src/test/ui/const-generics/raw-ptr-const-param.full.stderr3
-rw-r--r--src/test/ui/consts/refs_check_const_eq-issue-88384.rs4
-rw-r--r--src/test/ui/consts/refs_check_const_eq-issue-88384.stderr17
-rw-r--r--src/test/ui/impl-header-lifetime-elision/constant-used-as-arraylen.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/lifetimes.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_async_fn.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr.rs2
-rw-r--r--src/tools/clippy/src/driver.rs2
66 files changed, 1526 insertions, 1306 deletions
diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md
index 1d93939e233..9ccda17a6ef 100644
--- a/.github/ISSUE_TEMPLATE/documentation.md
+++ b/.github/ISSUE_TEMPLATE/documentation.md
@@ -4,13 +4,28 @@ about: Create a report for a documentation problem.
 labels: A-docs
 ---
 <!--
+
 Thank you for finding a documentation problem! 📚
 
 Documentation problems might be grammatical issues, typos, or unclear wording, please provide details regarding the documentation including where it is present.
 
+Note: If your issue is for one of these, please use their dedicated issue tracker instead:
+
+- The Rust Book: https://github.com/rust-lang/book/issues
+- Rust by Example: https://github.com/rust-lang/rust-by-example/issues
+- The Edition Guide: https://github.com/rust-lang/edition-guide/issues
+- The Cargo Book: https://github.com/rust-lang/cargo/issues
+- The Clippy Book: https://github.com/rust-lang/rust-clippy/issues
+- The Reference: https://github.com/rust-lang/reference/issues
+- The Rustonomicon: https://github.com/rust-lang/nomicon/issues
+- The Embedded Book: https://github.com/rust-embedded/book/issues
+
+All other documentation issues should be filed here.
+
+Or, if you find an issue related to rustdoc (e.g. doctest, rustdoc UI), please use the bug report or blank issue template instead.
+
 -->
 
 ### Location
 
 ### Summary
-
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index d4b41aad08c..a1bf0f94964 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1883,29 +1883,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 }
                 hir::LifetimeName::Param(param, ParamName::Fresh)
             }
-            LifetimeRes::Anonymous { binder, elided } => {
-                let mut l_name = None;
-                if let Some(mut captured_lifetimes) = self.captured_lifetimes.take() {
-                    if !captured_lifetimes.binders_to_ignore.contains(&binder) {
-                        let p_id = self.next_node_id();
-                        let p_def_id = self.create_def(
-                            captured_lifetimes.parent_def_id,
-                            p_id,
-                            DefPathData::LifetimeNs(kw::UnderscoreLifetime),
-                        );
-                        captured_lifetimes
-                            .captures
-                            .insert(p_def_id, (span, p_id, ParamName::Fresh, res));
-                        l_name = Some(hir::LifetimeName::Param(p_def_id, ParamName::Fresh));
-                    }
-                    self.captured_lifetimes = Some(captured_lifetimes);
-                };
-                l_name.unwrap_or(if elided {
-                    hir::LifetimeName::Implicit
-                } else {
-                    hir::LifetimeName::Underscore
-                })
-            }
+            LifetimeRes::Infer => hir::LifetimeName::Infer,
             LifetimeRes::Static => hir::LifetimeName::Static,
             LifetimeRes::Error => hir::LifetimeName::Error,
             res => panic!("Unexpected lifetime resolution {:?} for {:?} at {:?}", res, ident, span),
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index 4cf1ac4d7ab..0662d4d882f 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -589,8 +589,7 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
 
             hir::LifetimeName::Param(_, hir::ParamName::Fresh)
             | hir::LifetimeName::ImplicitObjectLifetimeDefault
-            | hir::LifetimeName::Implicit
-            | hir::LifetimeName::Underscore => {
+            | hir::LifetimeName::Infer => {
                 // In this case, the user left off the lifetime; so
                 // they wrote something like:
                 //
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
index 29464cf8c4e..e0994451172 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
@@ -226,7 +226,7 @@ impl Qualif for CustomEq {
         // because that component may be part of an enum variant (e.g.,
         // `Option::<NonStructuralMatchTy>::Some`), in which case some values of this type may be
         // structural-match (`Option::None`).
-        traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty, true).is_some()
+        traits::search_for_structural_match_violation(cx.body.span, cx.tcx, ty).is_some()
     }
 
     fn in_adt_inherently<'tcx>(
diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs
index f5b059793cf..53ae913f94f 100644
--- a/compiler/rustc_driver/src/lib.rs
+++ b/compiler/rustc_driver/src/lib.rs
@@ -123,8 +123,7 @@ impl Callbacks for TimePassesCallbacks {
     fn config(&mut self, config: &mut interface::Config) {
         // If a --prints=... option has been given, we don't print the "total"
         // time because it will mess up the --prints output. See #64339.
-        self.time_passes = config.opts.prints.is_empty()
-            && (config.opts.unstable_opts.time_passes || config.opts.unstable_opts.time);
+        self.time_passes = config.opts.prints.is_empty() && config.opts.time_passes();
         config.opts.trimmed_def_paths = TrimmedDefPaths::GoodPath;
     }
 }
@@ -249,7 +248,7 @@ fn run_compiler(
                     if sopts.describe_lints {
                         let mut lint_store = rustc_lint::new_lint_store(
                             sopts.unstable_opts.no_interleave_lints,
-                            compiler.session().unstable_options(),
+                            compiler.session().enable_internal_lints(),
                         );
                         let registered_lints =
                             if let Some(register_lints) = compiler.register_lints() {
diff --git a/compiler/rustc_error_messages/locales/en-US/passes.ftl b/compiler/rustc_error_messages/locales/en-US/passes.ftl
index f95e33cd16a..b17eb9c2d26 100644
--- a/compiler/rustc_error_messages/locales/en-US/passes.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/passes.ftl
@@ -256,3 +256,9 @@ passes-unused-duplicate = unused attribute
 passes-unused-multiple = multiple `{$name}` attributes
     .suggestion = remove this attribute
     .note = attribute also specified here
+
+passes-rustc-lint-opt-ty = `#[rustc_lint_opt_ty]` should be applied to a struct
+    .label = not a struct
+
+passes-rustc-lint-opt-deny-field-access = `#[rustc_lint_opt_deny_field_access]` should be applied to a field
+    .label = not a field
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 7b540e67aab..0e73d8fd7f6 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -619,6 +619,12 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     // Used by the `rustc::untranslatable_diagnostic` and `rustc::diagnostic_outside_of_impl` lints
     // to assist in changes to diagnostic APIs.
     rustc_attr!(rustc_lint_diagnostics, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE),
+    // Used by the `rustc::bad_opt_access` lint to identify `DebuggingOptions` and `CodegenOptions`
+    // types (as well as any others in future).
+    rustc_attr!(rustc_lint_opt_ty, Normal, template!(Word), WarnFollowing, INTERNAL_UNSTABLE),
+    // Used by the `rustc::bad_opt_access` lint on fields
+    // types (as well as any others in future).
+    rustc_attr!(rustc_lint_opt_deny_field_access, Normal, template!(List: "message"), WarnFollowing, INTERNAL_UNSTABLE),
 
     // ==========================================================================
     // Internal attributes, Const related:
diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs
index c0d5d2bc46d..be5b7eccbaf 100644
--- a/compiler/rustc_hir/src/def.rs
+++ b/compiler/rustc_hir/src/def.rs
@@ -738,13 +738,8 @@ pub enum LifetimeRes {
         binder: NodeId,
     },
     /// This variant is used for anonymous lifetimes that we did not resolve during
-    /// late resolution.  Shifting the work to the HIR lifetime resolver.
-    Anonymous {
-        /// Id of the introducing place. See `Param`.
-        binder: NodeId,
-        /// Whether this lifetime was spelled or elided.
-        elided: bool,
-    },
+    /// late resolution.  Those lifetimes will be inferred by typechecking.
+    Infer,
     /// Explicit `'static` lifetime.
     Static,
     /// Resolution failure.
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 18ffc227fed..f71400898e6 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -90,9 +90,6 @@ pub enum LifetimeName {
     /// User-given names or fresh (synthetic) names.
     Param(LocalDefId, ParamName),
 
-    /// User wrote nothing (e.g., the lifetime in `&u32`).
-    Implicit,
-
     /// Implicit lifetime in a context like `dyn Foo`. This is
     /// distinguished from implicit lifetimes elsewhere because the
     /// lifetime that they default to must appear elsewhere within the
@@ -110,8 +107,9 @@ pub enum LifetimeName {
     /// that was already reported.
     Error,
 
-    /// User wrote specifies `'_`.
-    Underscore,
+    /// User wrote an anonymous lifetime, either `'_` or nothing.
+    /// The semantics of this lifetime should be inferred by typechecking code.
+    Infer,
 
     /// User wrote `'static`.
     Static,
@@ -120,10 +118,8 @@ pub enum LifetimeName {
 impl LifetimeName {
     pub fn ident(&self) -> Ident {
         match *self {
-            LifetimeName::ImplicitObjectLifetimeDefault
-            | LifetimeName::Implicit
-            | LifetimeName::Error => Ident::empty(),
-            LifetimeName::Underscore => Ident::with_dummy_span(kw::UnderscoreLifetime),
+            LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Error => Ident::empty(),
+            LifetimeName::Infer => Ident::with_dummy_span(kw::UnderscoreLifetime),
             LifetimeName::Static => Ident::with_dummy_span(kw::StaticLifetime),
             LifetimeName::Param(_, param_name) => param_name.ident(),
         }
@@ -132,8 +128,7 @@ impl LifetimeName {
     pub fn is_anonymous(&self) -> bool {
         match *self {
             LifetimeName::ImplicitObjectLifetimeDefault
-            | LifetimeName::Implicit
-            | LifetimeName::Underscore
+            | LifetimeName::Infer
             | LifetimeName::Param(_, ParamName::Fresh)
             | LifetimeName::Error => true,
             LifetimeName::Static | LifetimeName::Param(..) => false,
@@ -142,9 +137,7 @@ impl LifetimeName {
 
     pub fn is_elided(&self) -> bool {
         match self {
-            LifetimeName::ImplicitObjectLifetimeDefault
-            | LifetimeName::Implicit
-            | LifetimeName::Underscore => true,
+            LifetimeName::ImplicitObjectLifetimeDefault | LifetimeName::Infer => true,
 
             // It might seem surprising that `Fresh` counts as
             // *not* elided -- but this is because, as far as the code
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index d00b65da7e6..640974115b9 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -496,9 +496,8 @@ pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime
         | LifetimeName::Param(_, ParamName::Error)
         | LifetimeName::Static
         | LifetimeName::Error
-        | LifetimeName::Implicit
         | LifetimeName::ImplicitObjectLifetimeDefault
-        | LifetimeName::Underscore => {}
+        | LifetimeName::Infer => {}
     }
 }
 
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs
index e5ae835e813..c1b201da691 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs
@@ -100,23 +100,6 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
                 // the lifetime of the TyRptr
                 let hir_id = lifetime.hir_id;
                 match (self.tcx.named_region(hir_id), self.bound_region) {
-                    // Find the index of the anonymous region that was part of the
-                    // error. We will then search the function parameters for a bound
-                    // region at the right depth with the same index
-                    (
-                        Some(rl::Region::LateBoundAnon(debruijn_index, _, anon_index)),
-                        ty::BrAnon(br_index),
-                    ) => {
-                        debug!(
-                            "LateBoundAnon depth = {:?} anon_index = {:?} br_index={:?}",
-                            debruijn_index, anon_index, br_index
-                        );
-                        if debruijn_index == self.current_index && anon_index == br_index {
-                            self.found_type = Some(arg);
-                            return; // we can stop visiting now
-                        }
-                    }
-
                     // Find the index of the named region that was part of the
                     // error. We will then search the function parameters for a bound
                     // region at the right depth with the same index
@@ -151,8 +134,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> {
                             rl::Region::Static
                             | rl::Region::Free(_, _)
                             | rl::Region::EarlyBound(_, _)
-                            | rl::Region::LateBound(_, _, _)
-                            | rl::Region::LateBoundAnon(_, _, _),
+                            | rl::Region::LateBound(_, _, _),
                         )
                         | None,
                         _,
@@ -206,16 +188,6 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> {
     fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) {
         match (self.tcx.named_region(lifetime.hir_id), self.bound_region) {
             // the lifetime of the TyPath!
-            (
-                Some(rl::Region::LateBoundAnon(debruijn_index, _, anon_index)),
-                ty::BrAnon(br_index),
-            ) => {
-                if debruijn_index == self.current_index && anon_index == br_index {
-                    self.found_it = true;
-                    return;
-                }
-            }
-
             (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => {
                 debug!("EarlyBound id={:?} def_id={:?}", id, def_id);
                 if id == def_id {
@@ -239,7 +211,6 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> {
                     rl::Region::Static
                     | rl::Region::EarlyBound(_, _)
                     | rl::Region::LateBound(_, _, _)
-                    | rl::Region::LateBoundAnon(_, _, _)
                     | rl::Region::Free(_, _),
                 )
                 | None,
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index 6c7ddb4531e..94f81b66077 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -329,6 +329,8 @@ pub fn create_compiler_and_run<R>(config: Config, f: impl FnOnce(&Compiler) -> R
     })
 }
 
+// JUSTIFICATION: before session exists, only config
+#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
 pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R {
     tracing::trace!("run_compiler");
     util::run_in_thread_pool_with_globals(
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 334a595a88a..8f083591786 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -210,7 +210,7 @@ pub fn register_plugins<'a>(
 
     let mut lint_store = rustc_lint::new_lint_store(
         sess.opts.unstable_opts.no_interleave_lints,
-        sess.unstable_options(),
+        sess.enable_internal_lints(),
     );
     register_lints(sess, &mut lint_store);
 
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 0a0eb99cd92..21d9eaccf67 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -1,3 +1,4 @@
+#![cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
 use crate::interface::parse_cfgspecs;
 
 use rustc_data_structures::fx::FxHashSet;
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 97856ecf22c..4c64e679b95 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -559,6 +559,8 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<C
     // Only check command line flags if present. If no types are specified by
     // command line, then reuse the empty `base` Vec to hold the types that
     // will be found in crate attributes.
+    // JUSTIFICATION: before wrapper fn is available
+    #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
     let mut base = session.opts.crate_types.clone();
     if base.is_empty() {
         base.extend(attr_types);
diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs
index 738f475983e..c26d7824758 100644
--- a/compiler/rustc_lint/src/internal.rs
+++ b/compiler/rustc_lint/src/internal.rs
@@ -51,20 +51,6 @@ fn typeck_results_of_method_fn<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &Expr<'_>,
 ) -> Option<(Span, DefId, ty::subst::SubstsRef<'tcx>)> {
-    // FIXME(rustdoc): Lints which use this function use typecheck results which can cause
-    // `rustdoc` to error if there are resolution failures.
-    //
-    // As internal lints are currently always run if there are `unstable_options`, they are added
-    // to the lint store of rustdoc. Internal lints are also not used via the `lint_mod` query.
-    // Crate lints run outside of a query so rustdoc currently doesn't disable them.
-    //
-    // Instead of relying on this, either change crate lints to a query disabled by rustdoc, only
-    // run internal lints if the user is explicitly opting in or figure out a different way to
-    // avoid running lints for rustdoc.
-    if cx.tcx.sess.opts.actually_rustdoc {
-        return None;
-    }
-
     match expr.kind {
         ExprKind::MethodCall(segment, _, _)
             if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) =>
@@ -446,3 +432,38 @@ impl LateLintPass<'_> for Diagnostics {
         }
     }
 }
+
+declare_tool_lint! {
+    pub rustc::BAD_OPT_ACCESS,
+    Deny,
+    "prevent using options by field access when there is a wrapper function",
+    report_in_external_macro: true
+}
+
+declare_lint_pass!(BadOptAccess => [ BAD_OPT_ACCESS ]);
+
+impl LateLintPass<'_> for BadOptAccess {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        let ExprKind::Field(base, target) = expr.kind else { return };
+        let Some(adt_def) = cx.typeck_results().expr_ty(base).ty_adt_def() else { return };
+        // Skip types without `#[rustc_lint_opt_ty]` - only so that the rest of the lint can be
+        // avoided.
+        if !cx.tcx.has_attr(adt_def.did(), sym::rustc_lint_opt_ty) {
+            return;
+        }
+
+        for field in adt_def.all_fields() {
+            if field.name == target.name &&
+                let Some(attr) = cx.tcx.get_attr(field.did, sym::rustc_lint_opt_deny_field_access) &&
+                let Some(items) = attr.meta_item_list()  &&
+                let Some(item) = items.first()  &&
+                let Some(literal) = item.literal()  &&
+                let ast::LitKind::Str(val, _) = literal.kind
+            {
+                cx.struct_span_lint(BAD_OPT_ACCESS, expr.span, |lint| {
+                    lint.build(val.as_str()).emit(); }
+                );
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 8726d36498b..7b0702dad75 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -509,8 +509,14 @@ fn register_internals(store: &mut LintStore) {
     store.register_late_pass(|| Box::new(TyTyKind));
     store.register_lints(&Diagnostics::get_lints());
     store.register_late_pass(|| Box::new(Diagnostics));
+    store.register_lints(&BadOptAccess::get_lints());
+    store.register_late_pass(|| Box::new(BadOptAccess));
     store.register_lints(&PassByValue::get_lints());
     store.register_late_pass(|| Box::new(PassByValue));
+    // FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and
+    // `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and
+    // these lints will trigger all of the time - change this once migration to diagnostic structs
+    // and translation is completed
     store.register_group(
         false,
         "rustc::internal",
@@ -523,6 +529,7 @@ fn register_internals(store: &mut LintStore) {
             LintId::of(LINT_PASS_IMPL_WITHOUT_MACRO),
             LintId::of(USAGE_OF_QUALIFIED_TY),
             LintId::of(EXISTING_DOC_KEYWORD),
+            LintId::of(BAD_OPT_ACCESS),
         ],
     );
 }
diff --git a/compiler/rustc_middle/src/middle/resolve_lifetime.rs b/compiler/rustc_middle/src/middle/resolve_lifetime.rs
index c71ba7b1753..9b2f4456705 100644
--- a/compiler/rustc_middle/src/middle/resolve_lifetime.rs
+++ b/compiler/rustc_middle/src/middle/resolve_lifetime.rs
@@ -12,7 +12,6 @@ pub enum Region {
     Static,
     EarlyBound(/* index */ u32, /* lifetime decl */ DefId),
     LateBound(ty::DebruijnIndex, /* late-bound index */ u32, /* lifetime decl */ DefId),
-    LateBoundAnon(ty::DebruijnIndex, /* late-bound index */ u32, /* anon index */ u32),
     Free(DefId, /* lifetime decl */ DefId),
 }
 
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index a4e7a12bba3..f8792edc017 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -203,7 +203,7 @@ impl<'tcx> Const<'tcx> {
     pub fn to_valtree(self) -> ty::ValTree<'tcx> {
         match self.kind() {
             ty::ConstKind::Value(valtree) => valtree,
-            _ => bug!("expected ConstKind::Value"),
+            _ => bug!("expected ConstKind::Value, got {:?}", self.kind()),
         }
     }
 
diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs
index c7653bdbe84..93707bb18ce 100644
--- a/compiler/rustc_middle/src/ty/consts/valtree.rs
+++ b/compiler/rustc_middle/src/ty/consts/valtree.rs
@@ -80,31 +80,25 @@ impl<'tcx> ValTree<'tcx> {
     }
 
     /// Get the values inside the ValTree as a slice of bytes. This only works for
-    /// constants with types &str and &[u8].
+    /// constants with types &str, &[u8], or [u8; _].
     pub fn try_to_raw_bytes(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx [u8]> {
         match ty.kind() {
             ty::Ref(_, inner_ty, _) => match inner_ty.kind() {
-                ty::Str => {
-                    let leafs = self
-                        .unwrap_branch()
-                        .into_iter()
-                        .map(|v| v.unwrap_leaf().try_to_u8().unwrap());
-
-                    return Some(tcx.arena.alloc_from_iter(leafs));
-                }
-                ty::Slice(slice_ty) if *slice_ty == tcx.types.u8 => {
-                    let leafs = self
-                        .unwrap_branch()
-                        .into_iter()
-                        .map(|v| v.unwrap_leaf().try_to_u8().unwrap());
-
-                    return Some(tcx.arena.alloc_from_iter(leafs));
-                }
-                _ => {}
+                // `&str` can be interpreted as raw bytes
+                ty::Str => {}
+                // `&[u8]` can be interpreted as raw bytes
+                ty::Slice(slice_ty) if *slice_ty == tcx.types.u8 => {}
+                // other `&_` can't be interpreted as raw bytes
+                _ => return None,
             },
-            _ => {}
+            // `[u8; N]` can be interpreted as raw bytes
+            ty::Array(array_ty, _) if *array_ty == tcx.types.u8 => {}
+            // Otherwise, type cannot be interpreted as raw bytes
+            _ => return None,
         }
 
-        None
+        Some(tcx.arena.alloc_from_iter(
+            self.unwrap_branch().into_iter().map(|v| v.unwrap_leaf().try_to_u8().unwrap()),
+        ))
     }
 }
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 819513884ce..7f2e81a71a9 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -1452,7 +1452,7 @@ pub trait PrettyPrinter<'tcx>:
                 }
             },
             (ty::ValTree::Branch(_), ty::Array(t, _)) if *t == u8_type => {
-                let bytes = valtree.try_to_raw_bytes(self.tcx(), *t).unwrap_or_else(|| {
+                let bytes = valtree.try_to_raw_bytes(self.tcx(), ty).unwrap_or_else(|| {
                     bug!("expected to convert valtree to raw bytes for type {:?}", t)
                 });
                 p!("*");
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
index e32e0b11ba4..d6dd0f01794 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -120,37 +120,35 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
     }
 
     fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option<String> {
-        traits::search_for_structural_match_violation(self.span, self.tcx(), ty, true).map(
-            |non_sm_ty| {
-                with_no_trimmed_paths!(match non_sm_ty.kind {
-                    traits::NonStructuralMatchTyKind::Adt(adt) => self.adt_derive_msg(adt),
-                    traits::NonStructuralMatchTyKind::Dynamic => {
-                        "trait objects cannot be used in patterns".to_string()
-                    }
-                    traits::NonStructuralMatchTyKind::Opaque => {
-                        "opaque types cannot be used in patterns".to_string()
-                    }
-                    traits::NonStructuralMatchTyKind::Closure => {
-                        "closures cannot be used in patterns".to_string()
-                    }
-                    traits::NonStructuralMatchTyKind::Generator => {
-                        "generators cannot be used in patterns".to_string()
-                    }
-                    traits::NonStructuralMatchTyKind::Float => {
-                        "floating-point numbers cannot be used in patterns".to_string()
-                    }
-                    traits::NonStructuralMatchTyKind::Param => {
-                        bug!("use of a constant whose type is a parameter inside a pattern")
-                    }
-                    traits::NonStructuralMatchTyKind::Projection => {
-                        bug!("use of a constant whose type is a projection inside a pattern")
-                    }
-                    traits::NonStructuralMatchTyKind::Foreign => {
-                        bug!("use of a value of a foreign type inside a pattern")
-                    }
-                })
-            },
-        )
+        traits::search_for_structural_match_violation(self.span, self.tcx(), ty).map(|non_sm_ty| {
+            with_no_trimmed_paths!(match non_sm_ty.kind() {
+                ty::Adt(adt, _) => self.adt_derive_msg(*adt),
+                ty::Dynamic(..) => {
+                    "trait objects cannot be used in patterns".to_string()
+                }
+                ty::Opaque(..) => {
+                    "opaque types cannot be used in patterns".to_string()
+                }
+                ty::Closure(..) => {
+                    "closures cannot be used in patterns".to_string()
+                }
+                ty::Generator(..) | ty::GeneratorWitness(..) => {
+                    "generators cannot be used in patterns".to_string()
+                }
+                ty::Float(..) => {
+                    "floating-point numbers cannot be used in patterns".to_string()
+                }
+                ty::FnPtr(..) => {
+                    "function pointers cannot be used in patterns".to_string()
+                }
+                ty::RawPtr(..) => {
+                    "raw pointers cannot be used in patterns".to_string()
+                }
+                _ => {
+                    bug!("use of a value of `{non_sm_ty}` inside a pattern")
+                }
+            })
+        })
     }
 
     fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs
index 47848cfa497..2f02d00ec9f 100644
--- a/compiler/rustc_mir_transform/src/lower_slice_len.rs
+++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs
@@ -11,7 +11,7 @@ pub struct LowerSliceLenCalls;
 
 impl<'tcx> MirPass<'tcx> for LowerSliceLenCalls {
     fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
-        sess.opts.mir_opt_level() > 0
+        sess.mir_opt_level() > 0
     }
 
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
diff --git a/compiler/rustc_mir_transform/src/reveal_all.rs b/compiler/rustc_mir_transform/src/reveal_all.rs
index 8ea550fa123..4919ad40098 100644
--- a/compiler/rustc_mir_transform/src/reveal_all.rs
+++ b/compiler/rustc_mir_transform/src/reveal_all.rs
@@ -9,7 +9,7 @@ pub struct RevealAll;
 
 impl<'tcx> MirPass<'tcx> for RevealAll {
     fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
-        sess.opts.mir_opt_level() >= 3 || super::inline::Inline.is_enabled(sess)
+        sess.mir_opt_level() >= 3 || super::inline::Inline.is_enabled(sess)
     }
 
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index f3ccbbb5679..fde12b9eee6 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -121,6 +121,10 @@ impl CheckAttrVisitor<'_> {
                 sym::rustc_lint_diagnostics => {
                     self.check_rustc_lint_diagnostics(&attr, span, target)
                 }
+                sym::rustc_lint_opt_ty => self.check_rustc_lint_opt_ty(&attr, span, target),
+                sym::rustc_lint_opt_deny_field_access => {
+                    self.check_rustc_lint_opt_deny_field_access(&attr, span, target)
+                }
                 sym::rustc_clean
                 | sym::rustc_dirty
                 | sym::rustc_if_this_changed
@@ -1382,6 +1386,35 @@ impl CheckAttrVisitor<'_> {
         self.check_applied_to_fn_or_method(attr, span, target)
     }
 
+    /// Checks that the `#[rustc_lint_opt_ty]` attribute is only applied to a struct.
+    fn check_rustc_lint_opt_ty(&self, attr: &Attribute, span: Span, target: Target) -> bool {
+        match target {
+            Target::Struct => true,
+            _ => {
+                self.tcx.sess.emit_err(errors::RustcLintOptTy { attr_span: attr.span, span });
+                false
+            }
+        }
+    }
+
+    /// Checks that the `#[rustc_lint_opt_deny_field_access]` attribute is only applied to a field.
+    fn check_rustc_lint_opt_deny_field_access(
+        &self,
+        attr: &Attribute,
+        span: Span,
+        target: Target,
+    ) -> bool {
+        match target {
+            Target::Field => true,
+            _ => {
+                self.tcx
+                    .sess
+                    .emit_err(errors::RustcLintOptDenyFieldAccess { attr_span: attr.span, span });
+                false
+            }
+        }
+    }
+
     /// Checks that the dep-graph debugging attributes are only present when the query-dep-graph
     /// option is passed to the compiler.
     fn check_rustc_dirty_clean(&self, attr: &Attribute) -> bool {
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 0d4317f6b88..5feb0e2956b 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -625,3 +625,21 @@ pub struct UnusedMultiple {
     pub other: Span,
     pub name: Symbol,
 }
+
+#[derive(SessionDiagnostic)]
+#[error(passes::rustc_lint_opt_ty)]
+pub struct RustcLintOptTy {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(passes::rustc_lint_opt_deny_field_access)]
+pub struct RustcLintOptDenyFieldAccess {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub span: Span,
+}
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 9b5fd4ea6d1..ed65100ae77 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -262,9 +262,6 @@ enum LifetimeRibKind {
     /// error on default object bounds (e.g., `Box<dyn Foo>`).
     AnonymousReportError,
 
-    /// Pass responsibility to `resolve_lifetime` code for all cases.
-    AnonymousPassThrough(NodeId),
-
     /// Replace all anonymous lifetimes by provided lifetime.
     Elided(LifetimeRes),
 
@@ -698,14 +695,25 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                     },
                     |this| {
                         this.visit_generic_params(&bare_fn.generic_params, false);
-                        this.resolve_fn_signature(
-                            ty.id,
-                            None,
-                            false,
-                            // We don't need to deal with patterns in parameters, because
-                            // they are not possible for foreign or bodiless functions.
-                            bare_fn.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)),
-                            &bare_fn.decl.output,
+                        this.with_lifetime_rib(
+                            LifetimeRibKind::AnonymousCreateParameter {
+                                binder: ty.id,
+                                report_in_path: false,
+                            },
+                            |this| {
+                                this.resolve_fn_signature(
+                                    ty.id,
+                                    false,
+                                    // We don't need to deal with patterns in parameters, because
+                                    // they are not possible for foreign or bodiless functions.
+                                    bare_fn
+                                        .decl
+                                        .inputs
+                                        .iter()
+                                        .map(|Param { ty, .. }| (None, &**ty)),
+                                    &bare_fn.decl.output,
+                                )
+                            },
                         );
                     },
                 )
@@ -785,12 +793,19 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
             | FnKind::Fn(_, _, sig, _, generics, None) => {
                 self.visit_fn_header(&sig.header);
                 self.visit_generics(generics);
-                self.resolve_fn_signature(
-                    fn_id,
-                    None,
-                    sig.decl.has_self(),
-                    sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)),
-                    &sig.decl.output,
+                self.with_lifetime_rib(
+                    LifetimeRibKind::AnonymousCreateParameter {
+                        binder: fn_id,
+                        report_in_path: false,
+                    },
+                    |this| {
+                        this.resolve_fn_signature(
+                            fn_id,
+                            sig.decl.has_self(),
+                            sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)),
+                            &sig.decl.output,
+                        )
+                    },
                 );
                 return;
             }
@@ -815,15 +830,22 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                         let declaration = &sig.decl;
                         let async_node_id = sig.header.asyncness.opt_return_id();
 
-                        this.resolve_fn_signature(
-                            fn_id,
-                            async_node_id,
-                            declaration.has_self(),
-                            declaration
-                                .inputs
-                                .iter()
-                                .map(|Param { pat, ty, .. }| (Some(&**pat), &**ty)),
-                            &declaration.output,
+                        this.with_lifetime_rib(
+                            LifetimeRibKind::AnonymousCreateParameter {
+                                binder: fn_id,
+                                report_in_path: async_node_id.is_some(),
+                            },
+                            |this| {
+                                this.resolve_fn_signature(
+                                    fn_id,
+                                    declaration.has_self(),
+                                    declaration
+                                        .inputs
+                                        .iter()
+                                        .map(|Param { pat, ty, .. }| (Some(&**pat), &**ty)),
+                                    &declaration.output,
+                                )
+                            },
                         );
 
                         // Construct the list of in-scope lifetime parameters for async lowering.
@@ -868,7 +890,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                             let previous_state = replace(&mut this.in_func_body, true);
                             // Resolve the function body, potentially inside the body of an async closure
                             this.with_lifetime_rib(
-                                LifetimeRibKind::AnonymousPassThrough(fn_id),
+                                LifetimeRibKind::Elided(LifetimeRes::Infer),
                                 |this| this.visit_block(body),
                             );
 
@@ -896,7 +918,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                         this.with_lifetime_rib(
                             match binder {
                                 ClosureBinder::NotPresent => {
-                                    LifetimeRibKind::AnonymousPassThrough(fn_id)
+                                    LifetimeRibKind::Elided(LifetimeRes::Infer)
                                 }
                                 ClosureBinder::For { .. } => LifetimeRibKind::AnonymousReportError,
                             },
@@ -908,7 +930,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                         let previous_state = replace(&mut this.in_func_body, true);
                         // Resolve the function body, potentially inside the body of an async closure
                         this.with_lifetime_rib(
-                            LifetimeRibKind::AnonymousPassThrough(fn_id),
+                            LifetimeRibKind::Elided(LifetimeRes::Infer),
                             |this| this.visit_expr(body),
                         );
 
@@ -1038,12 +1060,19 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                                 kind: LifetimeBinderKind::PolyTrait,
                                 ..
                             } => {
-                                self.resolve_fn_signature(
-                                    binder,
-                                    None,
-                                    false,
-                                    p_args.inputs.iter().map(|ty| (None, &**ty)),
-                                    &p_args.output,
+                                self.with_lifetime_rib(
+                                    LifetimeRibKind::AnonymousCreateParameter {
+                                        binder,
+                                        report_in_path: false,
+                                    },
+                                    |this| {
+                                        this.resolve_fn_signature(
+                                            binder,
+                                            false,
+                                            p_args.inputs.iter().map(|ty| (None, &**ty)),
+                                            &p_args.output,
+                                        )
+                                    },
                                 );
                                 break;
                             }
@@ -1053,8 +1082,7 @@ impl<'a: 'ast, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                                 visit::walk_generic_args(self, path_span, args);
                                 break;
                             }
-                            LifetimeRibKind::AnonymousPassThrough(..)
-                            | LifetimeRibKind::AnonymousCreateParameter { .. }
+                            LifetimeRibKind::AnonymousCreateParameter { .. }
                             | LifetimeRibKind::AnonymousReportError
                             | LifetimeRibKind::Elided(_)
                             | LifetimeRibKind::ElisionFailure
@@ -1415,8 +1443,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                                     | LifetimeRibKind::AnonymousReportError
                                     | LifetimeRibKind::ElisionFailure => Some(LifetimeUseSet::Many),
                                     // An anonymous lifetime is legal here, go ahead.
-                                    LifetimeRibKind::AnonymousPassThrough(_)
-                                    | LifetimeRibKind::AnonymousCreateParameter { .. } => {
+                                    LifetimeRibKind::AnonymousCreateParameter { .. } => {
                                         Some(LifetimeUseSet::One { use_span: ident.span, use_ctxt })
                                     }
                                     // Only report if eliding the lifetime would have the same
@@ -1527,14 +1554,6 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     self.record_lifetime_res(lifetime.id, LifetimeRes::Error, elision_candidate);
                     return;
                 }
-                LifetimeRibKind::AnonymousPassThrough(node_id) => {
-                    self.record_lifetime_res(
-                        lifetime.id,
-                        LifetimeRes::Anonymous { binder: node_id, elided },
-                        elision_candidate,
-                    );
-                    return;
-                }
                 LifetimeRibKind::Elided(res) => {
                     self.record_lifetime_res(lifetime.id, res, elision_candidate);
                     return;
@@ -1632,6 +1651,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                 | PathSource::Struct
                 | PathSource::TupleStruct(..) => false,
             };
+            if !missing && !segment.has_generic_args {
+                continue;
+            }
 
             let elided_lifetime_span = if segment.has_generic_args {
                 // If there are brackets, but not generic arguments, then use the opening bracket
@@ -1653,37 +1675,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
 
             if !missing {
                 // Do not create a parameter for patterns and expressions.
-                for rib in self.lifetime_ribs.iter().rev() {
-                    match rib.kind {
-                        LifetimeRibKind::AnonymousPassThrough(binder) => {
-                            let res = LifetimeRes::Anonymous { binder, elided: true };
-                            for id in node_ids {
-                                self.record_lifetime_res(id, res, LifetimeElisionCandidate::Named);
-                            }
-                            break;
-                        }
-                        // `LifetimeRes::Error`, which would usually be used in the case of
-                        // `ReportError`, is unsuitable here, as we don't emit an error yet.  Instead,
-                        // we simply resolve to an implicit lifetime, which will be checked later, at
-                        // which point a suitable error will be emitted.
-                        LifetimeRibKind::AnonymousReportError | LifetimeRibKind::Item => {
-                            // FIXME(cjgillot) This resolution is wrong, but this does not matter
-                            // since these cases are erroneous anyway.  Lifetime resolution should
-                            // emit a "missing lifetime specifier" diagnostic.
-                            let res =
-                                LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided: true };
-                            for id in node_ids {
-                                self.record_lifetime_res(id, res, LifetimeElisionCandidate::Named);
-                            }
-                            break;
-                        }
-                        LifetimeRibKind::AnonymousCreateParameter { .. }
-                        | LifetimeRibKind::Elided(_)
-                        | LifetimeRibKind::ElisionFailure
-                        | LifetimeRibKind::Generics { .. }
-                        | LifetimeRibKind::ConstGeneric
-                        | LifetimeRibKind::AnonConst => {}
-                    }
+                for id in node_ids {
+                    self.record_lifetime_res(
+                        id,
+                        LifetimeRes::Infer,
+                        LifetimeElisionCandidate::Named,
+                    );
                 }
                 continue;
             }
@@ -1750,19 +1747,6 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                         }
                         break;
                     }
-                    // `PassThrough` is the normal case.
-                    LifetimeRibKind::AnonymousPassThrough(binder) => {
-                        let res = LifetimeRes::Anonymous { binder, elided: true };
-                        let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime);
-                        for id in node_ids {
-                            self.record_lifetime_res(
-                                id,
-                                res,
-                                replace(&mut candidate, LifetimeElisionCandidate::Ignore),
-                            );
-                        }
-                        break;
-                    }
                     LifetimeRibKind::Elided(res) => {
                         let mut candidate = LifetimeElisionCandidate::Missing(missing_lifetime);
                         for id in node_ids {
@@ -1837,15 +1821,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             )
         }
         match res {
-            LifetimeRes::Param { .. }
-            | LifetimeRes::Fresh { .. }
-            | LifetimeRes::Anonymous { .. }
-            | LifetimeRes::Static => {
+            LifetimeRes::Param { .. } | LifetimeRes::Fresh { .. } | LifetimeRes::Static => {
                 if let Some(ref mut candidates) = self.lifetime_elision_candidates {
                     candidates.insert(res, candidate);
                 }
             }
-            LifetimeRes::Error | LifetimeRes::ElidedAnchor { .. } => {}
+            LifetimeRes::Infer | LifetimeRes::Error | LifetimeRes::ElidedAnchor { .. } => {}
         }
     }
 
@@ -1864,18 +1845,12 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
     fn resolve_fn_signature(
         &mut self,
         fn_id: NodeId,
-        async_node_id: Option<NodeId>,
         has_self: bool,
         inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)> + Clone,
         output_ty: &'ast FnRetTy,
     ) {
         // Add each argument to the rib.
-        let parameter_rib = LifetimeRibKind::AnonymousCreateParameter {
-            binder: fn_id,
-            report_in_path: async_node_id.is_some(),
-        };
-        let elision_lifetime =
-            self.with_lifetime_rib(parameter_rib, |this| this.resolve_fn_params(has_self, inputs));
+        let elision_lifetime = self.resolve_fn_params(has_self, inputs);
         debug!(?elision_lifetime);
 
         let outer_failures = take(&mut self.diagnostic_metadata.current_elision_failures);
@@ -2268,26 +2243,23 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| {
                         this.visit_ty(ty);
                     });
-                    this.with_lifetime_rib(
-                        LifetimeRibKind::AnonymousPassThrough(item.id),
-                        |this| {
-                            if let Some(expr) = expr {
-                                let constant_item_kind = match item.kind {
-                                    ItemKind::Const(..) => ConstantItemKind::Const,
-                                    ItemKind::Static(..) => ConstantItemKind::Static,
-                                    _ => unreachable!(),
-                                };
-                                // We already forbid generic params because of the above item rib,
-                                // so it doesn't matter whether this is a trivial constant.
-                                this.with_constant_rib(
-                                    IsRepeatExpr::No,
-                                    HasGenericParams::Yes,
-                                    Some((item.ident, constant_item_kind)),
-                                    |this| this.visit_expr(expr),
-                                );
-                            }
-                        },
-                    );
+                    this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
+                        if let Some(expr) = expr {
+                            let constant_item_kind = match item.kind {
+                                ItemKind::Const(..) => ConstantItemKind::Const,
+                                ItemKind::Static(..) => ConstantItemKind::Static,
+                                _ => unreachable!(),
+                            };
+                            // We already forbid generic params because of the above item rib,
+                            // so it doesn't matter whether this is a trivial constant.
+                            this.with_constant_rib(
+                                IsRepeatExpr::No,
+                                HasGenericParams::Yes,
+                                Some((item.ident, constant_item_kind)),
+                                |this| this.visit_expr(expr),
+                            );
+                        }
+                    });
                 });
             }
 
@@ -2544,7 +2516,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                         // Type parameters can already be used and as associated consts are
                         // not used as part of the type system, this is far less surprising.
                         self.with_lifetime_rib(
-                            LifetimeRibKind::AnonymousPassThrough(item.id),
+                            LifetimeRibKind::Elided(LifetimeRes::Infer),
                             |this| {
                                 this.with_constant_rib(
                                     IsRepeatExpr::No,
@@ -2717,17 +2689,14 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                     //
                     // Type parameters can already be used and as associated consts are
                     // not used as part of the type system, this is far less surprising.
-                    self.with_lifetime_rib(
-                        LifetimeRibKind::AnonymousPassThrough(item.id),
-                        |this| {
-                            this.with_constant_rib(
-                                IsRepeatExpr::No,
-                                HasGenericParams::Yes,
-                                None,
-                                |this| this.visit_expr(expr),
-                            )
-                        },
-                    );
+                    self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
+                        this.with_constant_rib(
+                            IsRepeatExpr::No,
+                            HasGenericParams::Yes,
+                            None,
+                            |this| this.visit_expr(expr),
+                        )
+                    });
                 }
             }
             AssocItemKind::Fn(box Fn { generics, .. }) => {
diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs
index a7fd7c427c7..94460e33d8b 100644
--- a/compiler/rustc_resolve/src/late/lifetimes.rs
+++ b/compiler/rustc_resolve/src/late/lifetimes.rs
@@ -1,4 +1,3 @@
-// ignore-tidy-filelength
 //! Resolution of early vs late bound lifetimes.
 //!
 //! Name resolution for lifetimes is performed on the AST and embedded into HIR.  From this
@@ -21,10 +20,9 @@ use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::resolve_lifetime::*;
 use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
 use rustc_span::def_id::DefId;
-use rustc_span::symbol::{kw, sym, Ident};
+use rustc_span::symbol::{sym, Ident};
 use rustc_span::Span;
 use std::borrow::Cow;
-use std::cell::Cell;
 use std::fmt;
 use std::mem::take;
 
@@ -33,8 +31,6 @@ trait RegionExt {
 
     fn late(index: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region);
 
-    fn late_anon(named_late_bound_vars: u32, index: &Cell<u32>) -> Region;
-
     fn id(&self) -> Option<DefId>;
 
     fn shifted(self, amount: u32) -> Region;
@@ -65,16 +61,9 @@ impl RegionExt for Region {
         (def_id, Region::LateBound(depth, idx, def_id.to_def_id()))
     }
 
-    fn late_anon(named_late_bound_vars: u32, index: &Cell<u32>) -> Region {
-        let i = index.get();
-        index.set(i + 1);
-        let depth = ty::INNERMOST;
-        Region::LateBoundAnon(depth, named_late_bound_vars + i, i)
-    }
-
     fn id(&self) -> Option<DefId> {
         match *self {
-            Region::Static | Region::LateBoundAnon(..) => None,
+            Region::Static => None,
 
             Region::EarlyBound(_, id) | Region::LateBound(_, _, id) | Region::Free(_, id) => {
                 Some(id)
@@ -87,9 +76,6 @@ impl RegionExt for Region {
             Region::LateBound(debruijn, idx, id) => {
                 Region::LateBound(debruijn.shifted_in(amount), idx, id)
             }
-            Region::LateBoundAnon(debruijn, index, anon_index) => {
-                Region::LateBoundAnon(debruijn.shifted_in(amount), index, anon_index)
-            }
             _ => self,
         }
     }
@@ -99,9 +85,6 @@ impl RegionExt for Region {
             Region::LateBound(debruijn, index, id) => {
                 Region::LateBound(debruijn.shifted_out_to_binder(binder), index, id)
             }
-            Region::LateBoundAnon(debruijn, index, anon_index) => {
-                Region::LateBoundAnon(debruijn.shifted_out_to_binder(binder), index, anon_index)
-            }
             _ => self,
         }
     }
@@ -193,10 +176,6 @@ enum Scope<'a> {
 
         s: ScopeRef<'a>,
 
-        /// In some cases not allowing late bounds allows us to avoid ICEs.
-        /// This is almost ways set to true.
-        allow_late_bound: bool,
-
         /// If this binder comes from a where clause, specify how it was created.
         /// This is used to diagnose inaccessible lifetimes in APIT:
         /// ```ignore (illustrative)
@@ -215,9 +194,8 @@ enum Scope<'a> {
     },
 
     /// A scope which either determines unspecified lifetimes or errors
-    /// on them (e.g., due to ambiguity). For more details, see `Elide`.
+    /// on them (e.g., due to ambiguity).
     Elision {
-        elide: Elide,
         s: ScopeRef<'a>,
     },
 
@@ -273,7 +251,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
                 opaque_type_parent,
                 scope_type,
                 hir_id,
-                allow_late_bound,
                 where_bound_origin,
                 s: _,
             } => f
@@ -283,16 +260,13 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
                 .field("opaque_type_parent", opaque_type_parent)
                 .field("scope_type", scope_type)
                 .field("hir_id", hir_id)
-                .field("allow_late_bound", allow_late_bound)
                 .field("where_bound_origin", where_bound_origin)
                 .field("s", &"..")
                 .finish(),
             Scope::Body { id, s: _ } => {
                 f.debug_struct("Body").field("id", id).field("s", &"..").finish()
             }
-            Scope::Elision { elide, s: _ } => {
-                f.debug_struct("Elision").field("elide", elide).field("s", &"..").finish()
-            }
+            Scope::Elision { s: _ } => f.debug_struct("Elision").field("s", &"..").finish(),
             Scope::ObjectLifetimeDefault { lifetime, s: _ } => f
                 .debug_struct("ObjectLifetimeDefault")
                 .field("lifetime", lifetime)
@@ -309,21 +283,6 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
     }
 }
 
-#[derive(Clone, Debug)]
-enum Elide {
-    /// Use a fresh anonymous late-bound lifetime each time, by
-    /// incrementing the counter to generate sequential indices. All
-    /// anonymous lifetimes must start *after* named bound vars.
-    FreshLateAnon(u32, Cell<u32>),
-    /// Always use this one lifetime.
-    Exact(Region),
-    /// Less or more than one lifetime were found, error on unspecified.
-    Error,
-    /// Forbid lifetime elision inside of a larger scope where it would be
-    /// permitted. For example, in let position impl trait.
-    Forbid,
-}
-
 type ScopeRef<'a> = &'a Scope<'a>;
 
 const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root;
@@ -486,9 +445,6 @@ fn late_region_as_bound_region<'tcx>(tcx: TyCtxt<'tcx>, region: &Region) -> ty::
             let name = tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id.expect_local()));
             ty::BoundVariableKind::Region(ty::BrNamed(*def_id, name))
         }
-        Region::LateBoundAnon(_, _, anon_idx) => {
-            ty::BoundVariableKind::Region(ty::BrAnon(*anon_idx))
-        }
         _ => bug!("{:?} is not a late region", region),
     }
 }
@@ -623,7 +579,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                 next_early_index,
                 opaque_type_parent: false,
                 scope_type: BinderScopeType::Normal,
-                allow_late_bound: true,
                 where_bound_origin: None,
             };
 
@@ -664,8 +619,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
             }
             hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => {
                 // No lifetime parameters, but implied 'static.
-                let scope = Scope::Elision { elide: Elide::Exact(Region::Static), s: ROOT_SCOPE };
-                self.with(scope, |this| intravisit::walk_item(this, item));
+                self.with(Scope::Elision { s: self.scope }, |this| {
+                    intravisit::walk_item(this, item)
+                });
             }
             hir::ItemKind::OpaqueTy(hir::OpaqueTy { .. }) => {
                 // Opaque types are visited when we visit the
@@ -741,7 +697,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     opaque_type_parent: true,
                     scope_type: BinderScopeType::Normal,
                     s: ROOT_SCOPE,
-                    allow_late_bound: false,
                     where_bound_origin: None,
                 };
                 self.with(scope, |this| {
@@ -794,7 +749,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     next_early_index,
                     opaque_type_parent: false,
                     scope_type: BinderScopeType::Normal,
-                    allow_late_bound: true,
                     where_bound_origin: None,
                 };
                 self.with(scope, |this| {
@@ -819,13 +773,12 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                         // `Box<dyn Debug + 'static>`.
                         self.resolve_object_lifetime_default(lifetime)
                     }
-                    LifetimeName::Implicit | LifetimeName::Underscore => {
+                    LifetimeName::Infer => {
                         // If the user writes `'_`, we use the *ordinary* elision
                         // rules. So the `'_` in e.g., `Box<dyn Debug + '_>` will be
                         // resolved the same as the `'_` in `&'_ Foo`.
                         //
                         // cc #48468
-                        self.resolve_elided_lifetimes(&[lifetime])
                     }
                     LifetimeName::Param(..) | LifetimeName::Static => {
                         // If the user wrote an explicit name, use that.
@@ -860,7 +813,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                         // position impl Trait
                         let scope = Scope::TraitRefBoundary { s: self.scope };
                         self.with(scope, |this| {
-                            let scope = Scope::Elision { elide: Elide::Forbid, s: this.scope };
+                            let scope = Scope::Elision { s: this.scope };
                             this.with(scope, |this| {
                                 intravisit::walk_item(this, opaque_ty);
                             })
@@ -936,7 +889,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                 let mut index = self.next_early_index_for_opaque_type();
                 debug!(?index);
 
-                let mut elision = None;
                 let mut lifetimes = FxIndexMap::default();
                 let mut non_lifetime_count = 0;
                 debug!(?generics.params);
@@ -945,15 +897,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                         GenericParamKind::Lifetime { .. } => {
                             let (def_id, reg) = Region::early(self.tcx.hir(), &mut index, &param);
                             lifetimes.insert(def_id, reg);
-                            if let hir::ParamName::Plain(Ident {
-                                name: kw::UnderscoreLifetime,
-                                ..
-                            }) = param.name
-                            {
-                                // Pick the elided lifetime "definition" if one exists
-                                // and use it to make an elision scope.
-                                elision = Some(reg);
-                            }
                         }
                         GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
                             non_lifetime_count += 1;
@@ -963,51 +906,24 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                 let next_early_index = index + non_lifetime_count;
                 self.map.late_bound_vars.insert(ty.hir_id, vec![]);
 
-                if let Some(elision_region) = elision {
-                    let scope =
-                        Scope::Elision { elide: Elide::Exact(elision_region), s: self.scope };
-                    self.with(scope, |this| {
-                        let scope = Scope::Binder {
-                            hir_id: ty.hir_id,
-                            lifetimes,
-                            next_early_index,
-                            s: this.scope,
-                            opaque_type_parent: false,
-                            scope_type: BinderScopeType::Normal,
-                            allow_late_bound: false,
-                            where_bound_origin: None,
-                        };
-                        this.with(scope, |this| {
-                            this.visit_generics(generics);
-                            let scope = Scope::TraitRefBoundary { s: this.scope };
-                            this.with(scope, |this| {
-                                for bound in bounds {
-                                    this.visit_param_bound(bound);
-                                }
-                            })
-                        });
-                    });
-                } else {
-                    let scope = Scope::Binder {
-                        hir_id: ty.hir_id,
-                        lifetimes,
-                        next_early_index,
-                        s: self.scope,
-                        opaque_type_parent: false,
-                        scope_type: BinderScopeType::Normal,
-                        allow_late_bound: false,
-                        where_bound_origin: None,
-                    };
-                    self.with(scope, |this| {
-                        let scope = Scope::TraitRefBoundary { s: this.scope };
-                        this.with(scope, |this| {
-                            this.visit_generics(generics);
-                            for bound in bounds {
-                                this.visit_param_bound(bound);
-                            }
-                        })
-                    });
-                }
+                let scope = Scope::Binder {
+                    hir_id: ty.hir_id,
+                    lifetimes,
+                    next_early_index,
+                    s: self.scope,
+                    opaque_type_parent: false,
+                    scope_type: BinderScopeType::Normal,
+                    where_bound_origin: None,
+                };
+                self.with(scope, |this| {
+                    let scope = Scope::TraitRefBoundary { s: this.scope };
+                    this.with(scope, |this| {
+                        this.visit_generics(generics);
+                        for bound in bounds {
+                            this.visit_param_bound(bound);
+                        }
+                    })
+                });
             }
             _ => intravisit::walk_ty(self, ty),
         }
@@ -1051,7 +967,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     s: self.scope,
                     opaque_type_parent: true,
                     scope_type: BinderScopeType::Normal,
-                    allow_late_bound: false,
                     where_bound_origin: None,
                 };
                 self.with(scope, |this| {
@@ -1113,7 +1028,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     s: self.scope,
                     opaque_type_parent: true,
                     scope_type: BinderScopeType::Normal,
-                    allow_late_bound: true,
                     where_bound_origin: None,
                 };
                 self.with(scope, |this| {
@@ -1135,15 +1049,14 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
     #[tracing::instrument(level = "debug", skip(self))]
     fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
         match lifetime_ref.name {
-            hir::LifetimeName::ImplicitObjectLifetimeDefault
-            | hir::LifetimeName::Implicit
-            | hir::LifetimeName::Underscore => self.resolve_elided_lifetimes(&[lifetime_ref]),
             hir::LifetimeName::Static => self.insert_lifetime(lifetime_ref, Region::Static),
             hir::LifetimeName::Param(param_def_id, _) => {
                 self.resolve_lifetime_ref(param_def_id, lifetime_ref)
             }
             // If we've already reported an error, just ignore `lifetime_ref`.
             hir::LifetimeName::Error => {}
+            // Those will be resolved by typechecking.
+            hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Infer => {}
         }
     }
 
@@ -1156,12 +1069,21 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
         }
     }
 
-    fn visit_fn_decl(&mut self, fd: &'tcx hir::FnDecl<'tcx>) {
+    fn visit_fn(
+        &mut self,
+        fk: intravisit::FnKind<'tcx>,
+        fd: &'tcx hir::FnDecl<'tcx>,
+        body_id: hir::BodyId,
+        _: Span,
+        _: hir::HirId,
+    ) {
         let output = match fd.output {
             hir::FnRetTy::DefaultReturn(_) => None,
             hir::FnRetTy::Return(ref ty) => Some(&**ty),
         };
-        self.visit_fn_like_elision(&fd.inputs, output);
+        self.visit_fn_like_elision(&fd.inputs, output, matches!(fk, intravisit::FnKind::Closure));
+        intravisit::walk_fn_kind(self, fk);
+        self.visit_nested_body(body_id)
     }
 
     fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) {
@@ -1219,7 +1141,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                             next_early_index,
                             opaque_type_parent: false,
                             scope_type: BinderScopeType::Normal,
-                            allow_late_bound: true,
                             where_bound_origin: Some(origin),
                         };
                         this.with(scope, |this| {
@@ -1292,7 +1213,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     next_early_index: self.next_early_index(),
                     opaque_type_parent: false,
                     scope_type,
-                    allow_late_bound: true,
                     where_bound_origin: None,
                 };
                 self.with(scope, |this| {
@@ -1343,7 +1263,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
             next_early_index,
             opaque_type_parent: false,
             scope_type,
-            allow_late_bound: true,
             where_bound_origin: None,
         };
         self.with(scope, |this| {
@@ -1597,7 +1516,6 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
             s: self.scope,
             opaque_type_parent: true,
             scope_type: BinderScopeType::Normal,
-            allow_late_bound: true,
             where_bound_origin: None,
         };
         self.with(scope, walk);
@@ -1773,30 +1691,18 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
         );
 
         if generic_args.parenthesized {
-            self.visit_fn_like_elision(generic_args.inputs(), Some(generic_args.bindings[0].ty()));
+            self.visit_fn_like_elision(
+                generic_args.inputs(),
+                Some(generic_args.bindings[0].ty()),
+                false,
+            );
             return;
         }
 
-        let mut elide_lifetimes = true;
-        let lifetimes: Vec<_> = generic_args
-            .args
-            .iter()
-            .filter_map(|arg| match arg {
-                hir::GenericArg::Lifetime(lt) => {
-                    if !lt.is_elided() {
-                        elide_lifetimes = false;
-                    }
-                    Some(lt)
-                }
-                _ => None,
-            })
-            .collect();
-        // We short-circuit here if all are elided in order to pluralize
-        // possible errors
-        if elide_lifetimes {
-            self.resolve_elided_lifetimes(&lifetimes);
-        } else {
-            lifetimes.iter().for_each(|lt| self.visit_lifetime(lt));
+        for arg in generic_args.args {
+            if let hir::GenericArg::Lifetime(lt) = arg {
+                self.visit_lifetime(lt);
+            }
         }
 
         // Figure out if this is a type/trait segment,
@@ -2052,380 +1958,18 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
         &mut self,
         inputs: &'tcx [hir::Ty<'tcx>],
         output: Option<&'tcx hir::Ty<'tcx>>,
+        in_closure: bool,
     ) {
-        debug!("visit_fn_like_elision: enter");
-        let mut scope = &*self.scope;
-        let hir_id = loop {
-            match scope {
-                Scope::Binder { hir_id, allow_late_bound: true, .. } => {
-                    break *hir_id;
-                }
-                Scope::ObjectLifetimeDefault { ref s, .. }
-                | Scope::Elision { ref s, .. }
-                | Scope::Supertrait { ref s, .. }
-                | Scope::TraitRefBoundary { ref s, .. } => {
-                    scope = *s;
-                }
-                Scope::Root
-                | Scope::Body { .. }
-                | Scope::Binder { allow_late_bound: false, .. } => {
-                    // See issues #83907 and #83693. Just bail out from looking inside.
-                    // See the issue #95023 for not allowing late bound
-                    self.tcx.sess.delay_span_bug(
-                        rustc_span::DUMMY_SP,
-                        "In fn_like_elision without appropriate scope above",
-                    );
-                    return;
-                }
-            }
-        };
-        // While not strictly necessary, we gather anon lifetimes *before* actually
-        // visiting the argument types.
-        let mut gather = GatherAnonLifetimes { anon_count: 0 };
-        for input in inputs {
-            gather.visit_ty(input);
-        }
-        trace!(?gather.anon_count);
-        let late_bound_vars = self.map.late_bound_vars.entry(hir_id).or_default();
-        let named_late_bound_vars = late_bound_vars.len() as u32;
-        late_bound_vars.extend(
-            (0..gather.anon_count).map(|var| ty::BoundVariableKind::Region(ty::BrAnon(var))),
-        );
-        let arg_scope = Scope::Elision {
-            elide: Elide::FreshLateAnon(named_late_bound_vars, Cell::new(0)),
-            s: self.scope,
-        };
-        self.with(arg_scope, |this| {
+        self.with(Scope::Elision { s: self.scope }, |this| {
             for input in inputs {
                 this.visit_ty(input);
             }
-        });
-
-        let Some(output) = output else { return };
-
-        debug!("determine output");
-
-        // Figure out if there's a body we can get argument names from,
-        // and whether there's a `self` argument (treated specially).
-        let mut assoc_item_kind = None;
-        let mut impl_self = None;
-        let parent = self.tcx.hir().get_parent_node(output.hir_id);
-        match self.tcx.hir().get(parent) {
-            // `fn` definitions and methods.
-            Node::Item(&hir::Item { kind: hir::ItemKind::Fn(..), .. }) => {}
-
-            Node::TraitItem(&hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. }) => {
-                if let hir::ItemKind::Trait(.., ref trait_items) =
-                    self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind
-                {
-                    assoc_item_kind =
-                        trait_items.iter().find(|ti| ti.id.hir_id() == parent).map(|ti| ti.kind);
-                }
-            }
-
-            Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, _), .. }) => {
-                if let hir::ItemKind::Impl(hir::Impl { ref self_ty, ref items, .. }) =
-                    self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(parent)).kind
-                {
-                    impl_self = Some(self_ty);
-                    assoc_item_kind =
-                        items.iter().find(|ii| ii.id.hir_id() == parent).map(|ii| ii.kind);
-                }
-            }
-
-            // Foreign functions, `fn(...) -> R` and `Trait(...) -> R` (both types and bounds).
-            Node::ForeignItem(_) | Node::Ty(_) | Node::TraitRef(_) => {},
-
-            Node::TypeBinding(_) if let Node::TraitRef(_) = self.tcx.hir().get(self.tcx.hir().get_parent_node(parent)) => {},
-
-            // Everything else (only closures?) doesn't
-            // actually enjoy elision in return types.
-            _ => {
-                self.visit_ty(output);
-                return;
-            }
-        };
-
-        let has_self = match assoc_item_kind {
-            Some(hir::AssocItemKind::Fn { has_self }) => has_self,
-            _ => false,
-        };
-
-        // In accordance with the rules for lifetime elision, we can determine
-        // what region to use for elision in the output type in two ways.
-        // First (determined here), if `self` is by-reference, then the
-        // implied output region is the region of the self parameter.
-        if has_self {
-            struct SelfVisitor<'a> {
-                map: &'a NamedRegionMap,
-                impl_self: Option<&'a hir::TyKind<'a>>,
-                lifetime: Set1<Region>,
-            }
-
-            impl SelfVisitor<'_> {
-                // Look for `self: &'a Self` - also desugared from `&'a self`,
-                // and if that matches, use it for elision and return early.
-                fn is_self_ty(&self, res: Res) -> bool {
-                    if let Res::SelfTy { .. } = res {
-                        return true;
-                    }
-
-                    // Can't always rely on literal (or implied) `Self` due
-                    // to the way elision rules were originally specified.
-                    if let Some(&hir::TyKind::Path(hir::QPath::Resolved(None, ref path))) =
-                        self.impl_self
-                    {
-                        match path.res {
-                            // Permit the types that unambiguously always
-                            // result in the same type constructor being used
-                            // (it can't differ between `Self` and `self`).
-                            Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, _)
-                            | Res::PrimTy(_) => return res == path.res,
-                            _ => {}
-                        }
-                    }
-
-                    false
-                }
-            }
-
-            impl<'a> Visitor<'a> for SelfVisitor<'a> {
-                fn visit_ty(&mut self, ty: &'a hir::Ty<'a>) {
-                    if let hir::TyKind::Rptr(lifetime_ref, ref mt) = ty.kind {
-                        if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = mt.ty.kind
-                        {
-                            if self.is_self_ty(path.res) {
-                                if let Some(lifetime) = self.map.defs.get(&lifetime_ref.hir_id) {
-                                    self.lifetime.insert(*lifetime);
-                                }
-                            }
-                        }
-                    }
-                    intravisit::walk_ty(self, ty)
-                }
-            }
-
-            let mut visitor = SelfVisitor {
-                map: self.map,
-                impl_self: impl_self.map(|ty| &ty.kind),
-                lifetime: Set1::Empty,
-            };
-            visitor.visit_ty(&inputs[0]);
-            if let Set1::One(lifetime) = visitor.lifetime {
-                let scope = Scope::Elision { elide: Elide::Exact(lifetime), s: self.scope };
-                self.with(scope, |this| this.visit_ty(output));
-                return;
+            if !in_closure && let Some(output) = output {
+                this.visit_ty(output);
             }
-        }
-
-        // Second, if there was exactly one lifetime (either a substitution or a
-        // reference) in the arguments, then any anonymous regions in the output
-        // have that lifetime.
-        let mut possible_implied_output_region = None;
-        let mut lifetime_count = 0;
-        for input in inputs.iter().skip(has_self as usize) {
-            let mut gather = GatherLifetimes {
-                map: self.map,
-                outer_index: ty::INNERMOST,
-                have_bound_regions: false,
-                lifetimes: Default::default(),
-            };
-            gather.visit_ty(input);
-
-            lifetime_count += gather.lifetimes.len();
-
-            if lifetime_count == 1 && gather.lifetimes.len() == 1 {
-                // there's a chance that the unique lifetime of this
-                // iteration will be the appropriate lifetime for output
-                // parameters, so lets store it.
-                possible_implied_output_region = gather.lifetimes.iter().cloned().next();
-            }
-        }
-
-        let elide = if lifetime_count == 1 {
-            Elide::Exact(possible_implied_output_region.unwrap())
-        } else {
-            Elide::Error
-        };
-
-        debug!(?elide);
-
-        let scope = Scope::Elision { elide, s: self.scope };
-        self.with(scope, |this| this.visit_ty(output));
-
-        struct GatherLifetimes<'a> {
-            map: &'a NamedRegionMap,
-            outer_index: ty::DebruijnIndex,
-            have_bound_regions: bool,
-            lifetimes: FxHashSet<Region>,
-        }
-
-        impl<'v, 'a> Visitor<'v> for GatherLifetimes<'a> {
-            fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
-                if let hir::TyKind::BareFn(_) = ty.kind {
-                    self.outer_index.shift_in(1);
-                }
-                match ty.kind {
-                    hir::TyKind::TraitObject(bounds, ref lifetime, _) => {
-                        for bound in bounds {
-                            self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None);
-                        }
-
-                        // Stay on the safe side and don't include the object
-                        // lifetime default (which may not end up being used).
-                        if !lifetime.is_elided() {
-                            self.visit_lifetime(lifetime);
-                        }
-                    }
-                    _ => {
-                        intravisit::walk_ty(self, ty);
-                    }
-                }
-                if let hir::TyKind::BareFn(_) = ty.kind {
-                    self.outer_index.shift_out(1);
-                }
-            }
-
-            fn visit_generic_param(&mut self, param: &hir::GenericParam<'_>) {
-                if let hir::GenericParamKind::Lifetime { .. } = param.kind {
-                    // FIXME(eddyb) Do we want this? It only makes a difference
-                    // if this `for<'a>` lifetime parameter is never used.
-                    self.have_bound_regions = true;
-                }
-
-                intravisit::walk_generic_param(self, param);
-            }
-
-            fn visit_poly_trait_ref(
-                &mut self,
-                trait_ref: &hir::PolyTraitRef<'_>,
-                modifier: hir::TraitBoundModifier,
-            ) {
-                self.outer_index.shift_in(1);
-                intravisit::walk_poly_trait_ref(self, trait_ref, modifier);
-                self.outer_index.shift_out(1);
-            }
-
-            fn visit_param_bound(&mut self, bound: &hir::GenericBound<'_>) {
-                if let hir::GenericBound::LangItemTrait { .. } = bound {
-                    self.outer_index.shift_in(1);
-                    intravisit::walk_param_bound(self, bound);
-                    self.outer_index.shift_out(1);
-                } else {
-                    intravisit::walk_param_bound(self, bound);
-                }
-            }
-
-            fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) {
-                if let Some(&lifetime) = self.map.defs.get(&lifetime_ref.hir_id) {
-                    match lifetime {
-                        Region::LateBound(debruijn, _, _)
-                        | Region::LateBoundAnon(debruijn, _, _)
-                            if debruijn < self.outer_index =>
-                        {
-                            self.have_bound_regions = true;
-                        }
-                        _ => {
-                            // FIXME(jackh726): nested trait refs?
-                            self.lifetimes.insert(lifetime.shifted_out_to_binder(self.outer_index));
-                        }
-                    }
-                }
-            }
-        }
-
-        struct GatherAnonLifetimes {
-            anon_count: u32,
-        }
-        impl<'v> Visitor<'v> for GatherAnonLifetimes {
-            #[instrument(skip(self), level = "trace")]
-            fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
-                // If we enter a `BareFn`, then we enter a *new* binding scope
-                if let hir::TyKind::BareFn(_) = ty.kind {
-                    return;
-                }
-                intravisit::walk_ty(self, ty);
-            }
-
-            fn visit_generic_args(
-                &mut self,
-                path_span: Span,
-                generic_args: &'v hir::GenericArgs<'v>,
-            ) {
-                // parenthesized args enter a new elision scope
-                if generic_args.parenthesized {
-                    return;
-                }
-                intravisit::walk_generic_args(self, path_span, generic_args)
-            }
-
-            #[instrument(skip(self), level = "trace")]
-            fn visit_lifetime(&mut self, lifetime_ref: &hir::Lifetime) {
-                if lifetime_ref.is_elided() {
-                    self.anon_count += 1;
-                }
-            }
-        }
-    }
-
-    fn resolve_elided_lifetimes(&mut self, lifetime_refs: &[&'tcx hir::Lifetime]) {
-        debug!("resolve_elided_lifetimes(lifetime_refs={:?})", lifetime_refs);
-
-        if lifetime_refs.is_empty() {
-            return;
-        }
-
-        let mut late_depth = 0;
-        let mut scope = self.scope;
-        loop {
-            match *scope {
-                // Do not assign any resolution, it will be inferred.
-                Scope::Body { .. } => return,
-
-                Scope::Root => break,
-
-                Scope::Binder { s, scope_type, .. } => {
-                    match scope_type {
-                        BinderScopeType::Normal => late_depth += 1,
-                        BinderScopeType::Concatenating => {}
-                    }
-                    scope = s;
-                }
-
-                Scope::Elision {
-                    elide: Elide::FreshLateAnon(named_late_bound_vars, ref counter),
-                    ..
-                } => {
-                    for lifetime_ref in lifetime_refs {
-                        let lifetime =
-                            Region::late_anon(named_late_bound_vars, counter).shifted(late_depth);
-
-                        self.insert_lifetime(lifetime_ref, lifetime);
-                    }
-                    return;
-                }
-
-                Scope::Elision { elide: Elide::Exact(l), .. } => {
-                    let lifetime = l.shifted(late_depth);
-                    for lifetime_ref in lifetime_refs {
-                        self.insert_lifetime(lifetime_ref, lifetime);
-                    }
-                    return;
-                }
-
-                Scope::Elision { elide: Elide::Error, .. }
-                | Scope::Elision { elide: Elide::Forbid, .. } => break,
-
-                Scope::ObjectLifetimeDefault { s, .. }
-                | Scope::Supertrait { s, .. }
-                | Scope::TraitRefBoundary { s, .. } => {
-                    scope = s;
-                }
-            }
-        }
-
-        for lt in lifetime_refs {
-            self.tcx.sess.delay_span_bug(lt.span, "Missing lifetime specifier");
+        });
+        if in_closure && let Some(output) = output {
+            self.visit_ty(output);
         }
     }
 
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 55307b9cebb..fe9ef604541 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -948,6 +948,8 @@ fn default_configuration(sess: &Session) -> CrateConfig {
     if sess.opts.debug_assertions {
         ret.insert((sym::debug_assertions, None));
     }
+    // JUSTIFICATION: before wrapper fn is available
+    #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
     if sess.opts.crate_types.contains(&CrateType::ProcMacro) {
         ret.insert((sym::proc_macro, None));
     }
@@ -2196,6 +2198,8 @@ fn parse_remap_path_prefix(
     mapping
 }
 
+// JUSTIFICATION: before wrapper fn is available
+#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
 pub fn build_session_options(matches: &getopts::Matches) -> Options {
     let color = parse_color(matches);
 
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 28e2e0db89a..501997679f4 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -102,28 +102,6 @@ macro_rules! top_level_options {
     );
 }
 
-impl Options {
-    pub fn mir_opt_level(&self) -> usize {
-        self.unstable_opts
-            .mir_opt_level
-            .unwrap_or_else(|| if self.optimize != OptLevel::No { 2 } else { 1 })
-    }
-
-    pub fn instrument_coverage(&self) -> bool {
-        self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) != InstrumentCoverage::Off
-    }
-
-    pub fn instrument_coverage_except_unused_generics(&self) -> bool {
-        self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off)
-            == InstrumentCoverage::ExceptUnusedGenerics
-    }
-
-    pub fn instrument_coverage_except_unused_functions(&self) -> bool {
-        self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off)
-            == InstrumentCoverage::ExceptUnusedFunctions
-    }
-}
-
 top_level_options!(
     /// The top-level command-line options struct.
     ///
@@ -149,9 +127,11 @@ top_level_options!(
     /// `CodegenOptions`, think about how it influences incremental compilation. If in
     /// doubt, specify `[TRACKED]`, which is always "correct" but might lead to
     /// unnecessary re-compilation.
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_ty)]
     pub struct Options {
         /// The crate config requested for the session, which may be combined
         /// with additional crate configurations during the compile process.
+        #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::crate_types` instead of this field"))]
         crate_types: Vec<CrateType> [TRACKED],
         optimize: OptLevel [TRACKED],
         /// Include the `debug_assertions` flag in dependency tracking, since it
@@ -198,7 +178,9 @@ top_level_options!(
         /// what rustc was invoked with, but massaged a bit to agree with
         /// commands like `--emit llvm-ir` which they're often incompatible with
         /// if we otherwise use the defaults of rustc.
+        #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::codegen_units` instead of this field"))]
         cli_forced_codegen_units: Option<usize> [UNTRACKED],
+        #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))]
         cli_forced_thinlto_off: bool [UNTRACKED],
 
         /// Remap source path prefixes in all output (messages, object files, debug, etc.).
@@ -249,11 +231,12 @@ macro_rules! options {
      ),* ,) =>
 (
     #[derive(Clone)]
-    pub struct $struct_name { $(pub $opt: $t),* }
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_ty)]
+    pub struct $struct_name { $( $( #[$attr] )* pub $opt: $t),* }
 
     impl Default for $struct_name {
         fn default() -> $struct_name {
-            $struct_name { $( $( #[$attr] )* $opt: $init),* }
+            $struct_name { $($opt: $init),* }
         }
     }
 
@@ -297,6 +280,22 @@ macro_rules! options {
 
 ) }
 
+impl Options {
+    // JUSTIFICATION: defn of the suggested wrapper fn
+    #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
+    pub fn time_passes(&self) -> bool {
+        self.unstable_opts.time_passes || self.unstable_opts.time
+    }
+}
+
+impl CodegenOptions {
+    // JUSTIFICATION: defn of the suggested wrapper fn
+    #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
+    pub fn instrument_coverage(&self) -> InstrumentCoverage {
+        self.instrument_coverage.unwrap_or(InstrumentCoverage::Off)
+    }
+}
+
 // Sometimes different options need to build a common structure.
 // That structure can be kept in one of the options' fields, the others become dummy.
 macro_rules! redirect_field {
@@ -1076,6 +1075,7 @@ options! {
 
     ar: String = (String::new(), parse_string, [UNTRACKED],
         "this option is deprecated and does nothing"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::code_model` instead of this field"))]
     code_model: Option<CodeModel> = (None, parse_code_model, [TRACKED],
         "choose the code model to use (`rustc --print code-models` for details)"),
     codegen_units: Option<usize> = (None, parse_opt_number, [UNTRACKED],
@@ -1095,12 +1095,14 @@ options! {
         "extra data to put in each output filename"),
     force_frame_pointers: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "force use of the frame pointers"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::must_emit_unwind_tables` instead of this field"))]
     force_unwind_tables: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "force use of unwind tables"),
     incremental: Option<String> = (None, parse_opt_string, [UNTRACKED],
         "enable incremental compilation"),
     inline_threshold: Option<u32> = (None, parse_opt_number, [TRACKED],
         "set the threshold for inlining a function"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field"))]
     instrument_coverage: Option<InstrumentCoverage> = (None, parse_instrument_coverage, [TRACKED],
         "instrument the generated code to support LLVM source-based code coverage \
         reports (note, the compiler build config must include `profiler = true`); \
@@ -1113,6 +1115,7 @@ options! {
         "a single extra argument to append to the linker invocation (can be used several times)"),
     link_args: Vec<String> = (Vec::new(), parse_list, [UNTRACKED],
         "extra arguments to append to the linker invocation (space separated)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::link_dead_code` instead of this field"))]
     link_dead_code: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "keep dead code at link time (useful for code coverage) (default: no)"),
     link_self_contained: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
@@ -1127,6 +1130,7 @@ options! {
         "generate build artifacts that are compatible with linker-based LTO"),
     llvm_args: Vec<String> = (Vec::new(), parse_list, [TRACKED],
         "a list of arguments to pass to LLVM (space separated)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))]
     lto: LtoCli = (LtoCli::Unspecified, parse_lto, [TRACKED],
         "perform LLVM link-time optimizations"),
     metadata: Vec<String> = (Vec::new(), parse_list, [TRACKED],
@@ -1143,8 +1147,10 @@ options! {
         "disable LLVM's SLP vectorization pass"),
     opt_level: String = ("0".to_string(), parse_string, [TRACKED],
         "optimization level (0-3, s, or z; default: 0)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::overflow_checks` instead of this field"))]
     overflow_checks: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "use overflow checks for integer arithmetic"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::panic_strategy` instead of this field"))]
     panic: Option<PanicStrategy> = (None, parse_opt_panic_strategy, [TRACKED],
         "panic strategy to compile crate with"),
     passes: Vec<String> = (Vec::new(), parse_list, [TRACKED],
@@ -1156,6 +1162,7 @@ options! {
         "compile the program with profiling instrumentation"),
     profile_use: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
         "use the given `.profdata` file for profile-guided optimization"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::relocation_model` instead of this field"))]
     relocation_model: Option<RelocModel> = (None, parse_relocation_model, [TRACKED],
         "control generation of position-independent code (PIC) \
         (`rustc --print relocation-models` for details)"),
@@ -1167,6 +1174,7 @@ options! {
         "save all temporary output files during compilation (default: no)"),
     soft_float: bool = (false, parse_bool, [TRACKED],
         "use soft float ABI (*eabihf targets only) (default: no)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::split_debuginfo` instead of this field"))]
     split_debuginfo: Option<SplitDebuginfo> = (None, parse_split_debuginfo, [TRACKED],
         "how to handle split-debuginfo, a platform-specific option"),
     strip: Strip = (Strip::None, parse_strip, [UNTRACKED],
@@ -1202,11 +1210,13 @@ options! {
         "encode MIR of all functions into the crate metadata (default: no)"),
     assume_incomplete_release: bool = (false, parse_bool, [TRACKED],
         "make cfg(version) treat the current version as incomplete (default: no)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::asm_comments` instead of this field"))]
     asm_comments: bool = (false, parse_bool, [TRACKED],
         "generate comments into the assembly (may change behavior) (default: no)"),
     assert_incr_state: Option<String> = (None, parse_opt_string, [UNTRACKED],
         "assert that the incremental cache is in given state: \
          either `loaded` or `not-loaded`."),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::binary_dep_depinfo` instead of this field"))]
     binary_dep_depinfo: bool = (false, parse_bool, [TRACKED],
         "include artifacts (sysroot, crate dependencies) used during compilation in dep-info \
         (default: no)"),
@@ -1284,6 +1294,7 @@ options! {
         "emit the bc module with thin LTO info (default: yes)"),
     export_executable_symbols: bool = (false, parse_bool, [TRACKED],
         "export symbols from executables, as if they were dynamic libraries"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::fewer_names` instead of this field"))]
     fewer_names: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) \
         (default: no)"),
@@ -1326,6 +1337,7 @@ options! {
         "control whether `#[inline]` functions are in all CGUs"),
     input_stats: bool = (false, parse_bool, [UNTRACKED],
         "gather statistics about the input (default: no)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field"))]
     instrument_coverage: Option<InstrumentCoverage> = (None, parse_instrument_coverage, [TRACKED],
         "instrument the generated code to support LLVM source-based code coverage \
         reports (note, the compiler build config must include `profiler = true`); \
@@ -1334,6 +1346,7 @@ options! {
         `=except-unused-generics`
         `=except-unused-functions`
         `=off` (default)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::instrument_mcount` instead of this field"))]
     instrument_mcount: bool = (false, parse_bool, [TRACKED],
         "insert function instrument code for mcount-based tracing (default: no)"),
     keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED],
@@ -1356,6 +1369,7 @@ options! {
     merge_functions: Option<MergeFunctions> = (None, parse_merge_functions, [TRACKED],
         "control the operation of the MergeFunctions LLVM pass, taking \
         the same values as the target option of the same name"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::meta_stats` instead of this field"))]
     meta_stats: bool = (false, parse_bool, [UNTRACKED],
         "gather metadata statistics (default: no)"),
     mir_emit_retag: bool = (false, parse_bool, [TRACKED],
@@ -1365,6 +1379,7 @@ options! {
         "use like `-Zmir-enable-passes=+DestProp,-InstCombine`. Forces the specified passes to be \
         enabled, overriding all other checks. Passes that are not specified are enabled or \
         disabled by other flags as usual."),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::mir_opt_level` instead of this field"))]
     mir_opt_level: Option<usize> = (None, parse_opt_number, [TRACKED],
         "MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"),
     move_size_limit: Option<usize> = (None, parse_opt_number, [TRACKED],
@@ -1431,6 +1446,7 @@ options! {
         See #77382 and #74551."),
     print_fuel: Option<String> = (None, parse_opt_string, [TRACKED],
         "make rustc print the total optimization fuel used by a crate"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::print_llvm_passes` instead of this field"))]
     print_llvm_passes: bool = (false, parse_bool, [UNTRACKED],
         "print the LLVM optimization passes being run (default: no)"),
     print_mono_items: Option<String> = (None, parse_opt_string, [UNTRACKED],
@@ -1505,6 +1521,7 @@ options! {
         "exclude spans when debug-printing compiler state (default: no)"),
     src_hash_algorithm: Option<SourceFileHashAlgorithm> = (None, parse_src_file_hash, [TRACKED],
         "hash algorithm of source files in debug info (`md5`, `sha1`, or `sha256`)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::stack_protector` instead of this field"))]
     stack_protector: StackProtector = (StackProtector::None, parse_stack_protector, [TRACKED],
         "control stack smash protection strategy (`rustc --print stack-protector-strategies` for details)"),
     strict_init_checks: bool = (false, parse_bool, [TRACKED],
@@ -1525,6 +1542,7 @@ options! {
     symbol_mangling_version: Option<SymbolManglingVersion> = (None,
         parse_symbol_mangling_version, [TRACKED],
         "which mangling version to use for symbol names ('legacy' (default) or 'v0')"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::teach` instead of this field"))]
     teach: bool = (false, parse_bool, [TRACKED],
         "show extended diagnostic help (default: no)"),
     temps_dir: Option<String> = (None, parse_opt_string, [UNTRACKED],
@@ -1540,6 +1558,7 @@ options! {
         "emit directionality isolation markers in translated diagnostics"),
     tune_cpu: Option<String> = (None, parse_opt_string, [TRACKED],
         "select processor to schedule for (`rustc --print target-cpus` for details)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::lto` instead of this field"))]
     thinlto: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "enable ThinLTO when possible"),
     thir_unsafeck: bool = (false, parse_bool, [TRACKED],
@@ -1548,14 +1567,19 @@ options! {
     /// a sequential compiler for now. This'll likely be adjusted
     /// in the future. Note that -Zthreads=0 is the way to get
     /// the num_cpus behavior.
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::threads` instead of this field"))]
     threads: usize = (1, parse_threads, [UNTRACKED],
         "use a thread pool with N threads"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_passes` instead of this field"))]
     time: bool = (false, parse_bool, [UNTRACKED],
         "measure time of rustc processes (default: no)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_llvm_passes` instead of this field"))]
     time_llvm_passes: bool = (false, parse_bool, [UNTRACKED],
         "measure time of each LLVM pass (default: no)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::time_passes` instead of this field"))]
     time_passes: bool = (false, parse_bool, [UNTRACKED],
         "measure time of each rustc pass (default: no)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::tls_model` instead of this field"))]
     tls_model: Option<TlsModel> = (None, parse_tls_model, [TRACKED],
         "choose the TLS model to use (`rustc --print tls-models` for details)"),
     trace_macros: bool = (false, parse_bool, [UNTRACKED],
@@ -1590,14 +1614,17 @@ options! {
         "enable unsound and buggy MIR optimizations (default: no)"),
     /// This name is kind of confusing: Most unstable options enable something themselves, while
     /// this just allows "normal" options to be feature-gated.
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::unstable_options` instead of this field"))]
     unstable_options: bool = (false, parse_bool, [UNTRACKED],
         "adds unstable command line options to rustc interface (default: no)"),
     use_ctors_section: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "use legacy .ctors section for initializers rather than .init_array"),
     validate_mir: bool = (false, parse_bool, [UNTRACKED],
         "validate MIR after each transformation"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::verbose` instead of this field"))]
     verbose: bool = (false, parse_bool, [UNTRACKED],
         "in general, enable more debug printouts (default: no)"),
+    #[cfg_attr(not(bootstrap), rustc_lint_opt_deny_field_access("use `Session::verify_llvm_ir` instead of this field"))]
     verify_llvm_ir: bool = (false, parse_bool, [TRACKED],
         "verify LLVM IR (default: no)"),
     virtual_function_elimination: bool = (false, parse_bool, [TRACKED],
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 854cad79a20..9669287b3f3 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -1,7 +1,7 @@
 use crate::cgu_reuse_tracker::CguReuseTracker;
 use crate::code_stats::CodeStats;
 pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo};
-use crate::config::{self, CrateType, OutputType, SwitchWithOptPath};
+use crate::config::{self, CrateType, InstrumentCoverage, OptLevel, OutputType, SwitchWithOptPath};
 use crate::parse::{add_feature_diagnostics, ParseSess};
 use crate::search_paths::{PathKind, SearchPath};
 use crate::{filesearch, lint};
@@ -583,35 +583,28 @@ impl Session {
     pub fn source_map(&self) -> &SourceMap {
         self.parse_sess.source_map()
     }
-    pub fn verbose(&self) -> bool {
-        self.opts.unstable_opts.verbose
-    }
+
     pub fn time_passes(&self) -> bool {
-        self.opts.unstable_opts.time_passes || self.opts.unstable_opts.time
+        self.opts.time_passes()
     }
-    pub fn instrument_mcount(&self) -> bool {
-        self.opts.unstable_opts.instrument_mcount
-    }
-    pub fn time_llvm_passes(&self) -> bool {
-        self.opts.unstable_opts.time_llvm_passes
-    }
-    pub fn meta_stats(&self) -> bool {
-        self.opts.unstable_opts.meta_stats
-    }
-    pub fn asm_comments(&self) -> bool {
-        self.opts.unstable_opts.asm_comments
-    }
-    pub fn verify_llvm_ir(&self) -> bool {
-        self.opts.unstable_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some()
+
+    /// Returns `true` if internal lints should be added to the lint store - i.e. if
+    /// `-Zunstable-options` is provided and this isn't rustdoc (internal lints can trigger errors
+    /// to be emitted under rustdoc).
+    pub fn enable_internal_lints(&self) -> bool {
+        self.unstable_options() && !self.opts.actually_rustdoc
     }
-    pub fn print_llvm_passes(&self) -> bool {
-        self.opts.unstable_opts.print_llvm_passes
+
+    pub fn instrument_coverage(&self) -> bool {
+        self.opts.cg.instrument_coverage() != InstrumentCoverage::Off
     }
-    pub fn binary_dep_depinfo(&self) -> bool {
-        self.opts.unstable_opts.binary_dep_depinfo
+
+    pub fn instrument_coverage_except_unused_generics(&self) -> bool {
+        self.opts.cg.instrument_coverage() == InstrumentCoverage::ExceptUnusedGenerics
     }
-    pub fn mir_opt_level(&self) -> usize {
-        self.opts.mir_opt_level()
+
+    pub fn instrument_coverage_except_unused_functions(&self) -> bool {
+        self.opts.cg.instrument_coverage() == InstrumentCoverage::ExceptUnusedFunctions
     }
 
     /// Gets the features enabled for the current compilation session.
@@ -629,103 +622,9 @@ impl Session {
         }
     }
 
-    /// Calculates the flavor of LTO to use for this compilation.
-    pub fn lto(&self) -> config::Lto {
-        // If our target has codegen requirements ignore the command line
-        if self.target.requires_lto {
-            return config::Lto::Fat;
-        }
-
-        // If the user specified something, return that. If they only said `-C
-        // lto` and we've for whatever reason forced off ThinLTO via the CLI,
-        // then ensure we can't use a ThinLTO.
-        match self.opts.cg.lto {
-            config::LtoCli::Unspecified => {
-                // The compiler was invoked without the `-Clto` flag. Fall
-                // through to the default handling
-            }
-            config::LtoCli::No => {
-                // The user explicitly opted out of any kind of LTO
-                return config::Lto::No;
-            }
-            config::LtoCli::Yes | config::LtoCli::Fat | config::LtoCli::NoParam => {
-                // All of these mean fat LTO
-                return config::Lto::Fat;
-            }
-            config::LtoCli::Thin => {
-                return if self.opts.cli_forced_thinlto_off {
-                    config::Lto::Fat
-                } else {
-                    config::Lto::Thin
-                };
-            }
-        }
-
-        // Ok at this point the target doesn't require anything and the user
-        // hasn't asked for anything. Our next decision is whether or not
-        // we enable "auto" ThinLTO where we use multiple codegen units and
-        // then do ThinLTO over those codegen units. The logic below will
-        // either return `No` or `ThinLocal`.
-
-        // If processing command line options determined that we're incompatible
-        // with ThinLTO (e.g., `-C lto --emit llvm-ir`) then return that option.
-        if self.opts.cli_forced_thinlto_off {
-            return config::Lto::No;
-        }
-
-        // If `-Z thinlto` specified process that, but note that this is mostly
-        // a deprecated option now that `-C lto=thin` exists.
-        if let Some(enabled) = self.opts.unstable_opts.thinlto {
-            if enabled {
-                return config::Lto::ThinLocal;
-            } else {
-                return config::Lto::No;
-            }
-        }
-
-        // If there's only one codegen unit and LTO isn't enabled then there's
-        // no need for ThinLTO so just return false.
-        if self.codegen_units() == 1 {
-            return config::Lto::No;
-        }
-
-        // Now we're in "defaults" territory. By default we enable ThinLTO for
-        // optimized compiles (anything greater than O0).
-        match self.opts.optimize {
-            config::OptLevel::No => config::Lto::No,
-            _ => config::Lto::ThinLocal,
-        }
-    }
-
-    /// Returns the panic strategy for this compile session. If the user explicitly selected one
-    /// using '-C panic', use that, otherwise use the panic strategy defined by the target.
-    pub fn panic_strategy(&self) -> PanicStrategy {
-        self.opts.cg.panic.unwrap_or(self.target.panic_strategy)
-    }
-    pub fn fewer_names(&self) -> bool {
-        if let Some(fewer_names) = self.opts.unstable_opts.fewer_names {
-            fewer_names
-        } else {
-            let more_names = self.opts.output_types.contains_key(&OutputType::LlvmAssembly)
-                || self.opts.output_types.contains_key(&OutputType::Bitcode)
-                // AddressSanitizer and MemorySanitizer use alloca name when reporting an issue.
-                || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY);
-            !more_names
-        }
-    }
-
-    pub fn unstable_options(&self) -> bool {
-        self.opts.unstable_opts.unstable_options
-    }
-    pub fn is_nightly_build(&self) -> bool {
-        self.opts.unstable_features.is_nightly_build()
-    }
     pub fn is_sanitizer_cfi_enabled(&self) -> bool {
         self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI)
     }
-    pub fn overflow_checks(&self) -> bool {
-        self.opts.cg.overflow_checks.unwrap_or(self.opts.debug_assertions)
-    }
 
     /// Check whether this compile session and crate type use static crt.
     pub fn crt_static(&self, crate_type: Option<CrateType>) -> bool {
@@ -738,6 +637,8 @@ impl Session {
         let found_negative = requested_features.clone().any(|r| r == "-crt-static");
         let found_positive = requested_features.clone().any(|r| r == "+crt-static");
 
+        // JUSTIFICATION: necessary use of crate_types directly (see FIXME below)
+        #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
         if found_positive || found_negative {
             found_positive
         } else if crate_type == Some(CrateType::ProcMacro)
@@ -752,18 +653,6 @@ impl Session {
         }
     }
 
-    pub fn relocation_model(&self) -> RelocModel {
-        self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model)
-    }
-
-    pub fn code_model(&self) -> Option<CodeModel> {
-        self.opts.cg.code_model.or(self.target.code_model)
-    }
-
-    pub fn tls_model(&self) -> TlsModel {
-        self.opts.unstable_opts.tls_model.unwrap_or(self.target.tls_model)
-    }
-
     pub fn is_wasi_reactor(&self) -> bool {
         self.target.options.os == "wasi"
             && matches!(
@@ -772,49 +661,10 @@ impl Session {
             )
     }
 
-    pub fn split_debuginfo(&self) -> SplitDebuginfo {
-        self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo)
-    }
-
-    pub fn stack_protector(&self) -> StackProtector {
-        if self.target.options.supports_stack_protector {
-            self.opts.unstable_opts.stack_protector
-        } else {
-            StackProtector::None
-        }
-    }
-
     pub fn target_can_use_split_dwarf(&self) -> bool {
         !self.target.is_like_windows && !self.target.is_like_osx
     }
 
-    pub fn must_emit_unwind_tables(&self) -> bool {
-        // This is used to control the emission of the `uwtable` attribute on
-        // LLVM functions.
-        //
-        // Unwind tables are needed when compiling with `-C panic=unwind`, but
-        // LLVM won't omit unwind tables unless the function is also marked as
-        // `nounwind`, so users are allowed to disable `uwtable` emission.
-        // Historically rustc always emits `uwtable` attributes by default, so
-        // even they can be disabled, they're still emitted by default.
-        //
-        // On some targets (including windows), however, exceptions include
-        // other events such as illegal instructions, segfaults, etc. This means
-        // that on Windows we end up still needing unwind tables even if the `-C
-        // panic=abort` flag is passed.
-        //
-        // You can also find more info on why Windows needs unwind tables in:
-        //      https://bugzilla.mozilla.org/show_bug.cgi?id=1302078
-        //
-        // If a target requires unwind tables, then they must be emitted.
-        // Otherwise, we can defer to the `-C force-unwind-tables=<yes/no>`
-        // value, if it is provided, or disable them, if not.
-        self.target.requires_uwtable
-            || self.opts.cg.force_unwind_tables.unwrap_or(
-                self.panic_strategy() == PanicStrategy::Unwind || self.target.default_uwtable,
-            )
-    }
-
     pub fn generate_proc_macro_decls_symbol(&self, stable_crate_id: StableCrateId) -> String {
         format!("__rustc_proc_macro_decls_{:08x}__", stable_crate_id.to_u64())
     }
@@ -960,6 +810,280 @@ impl Session {
         ret
     }
 
+    pub fn rust_2015(&self) -> bool {
+        self.edition() == Edition::Edition2015
+    }
+
+    /// Are we allowed to use features from the Rust 2018 edition?
+    pub fn rust_2018(&self) -> bool {
+        self.edition() >= Edition::Edition2018
+    }
+
+    /// Are we allowed to use features from the Rust 2021 edition?
+    pub fn rust_2021(&self) -> bool {
+        self.edition() >= Edition::Edition2021
+    }
+
+    /// Are we allowed to use features from the Rust 2024 edition?
+    pub fn rust_2024(&self) -> bool {
+        self.edition() >= Edition::Edition2024
+    }
+
+    /// Returns `true` if we cannot skip the PLT for shared library calls.
+    pub fn needs_plt(&self) -> bool {
+        // Check if the current target usually needs PLT to be enabled.
+        // The user can use the command line flag to override it.
+        let needs_plt = self.target.needs_plt;
+
+        let dbg_opts = &self.opts.unstable_opts;
+
+        let relro_level = dbg_opts.relro_level.unwrap_or(self.target.relro_level);
+
+        // Only enable this optimization by default if full relro is also enabled.
+        // In this case, lazy binding was already unavailable, so nothing is lost.
+        // This also ensures `-Wl,-z,now` is supported by the linker.
+        let full_relro = RelroLevel::Full == relro_level;
+
+        // If user didn't explicitly forced us to use / skip the PLT,
+        // then try to skip it where possible.
+        dbg_opts.plt.unwrap_or(needs_plt || !full_relro)
+    }
+
+    /// Checks if LLVM lifetime markers should be emitted.
+    pub fn emit_lifetime_markers(&self) -> bool {
+        self.opts.optimize != config::OptLevel::No
+        // AddressSanitizer uses lifetimes to detect use after scope bugs.
+        // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables.
+        // HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future.
+        || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS)
+    }
+
+    pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool {
+        [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
+            .iter()
+            .any(|kind| attr.has_name(*kind))
+    }
+
+    pub fn contains_name(&self, attrs: &[Attribute], name: Symbol) -> bool {
+        attrs.iter().any(|item| item.has_name(name))
+    }
+
+    pub fn find_by_name<'a>(
+        &'a self,
+        attrs: &'a [Attribute],
+        name: Symbol,
+    ) -> Option<&'a Attribute> {
+        attrs.iter().find(|attr| attr.has_name(name))
+    }
+
+    pub fn filter_by_name<'a>(
+        &'a self,
+        attrs: &'a [Attribute],
+        name: Symbol,
+    ) -> impl Iterator<Item = &'a Attribute> {
+        attrs.iter().filter(move |attr| attr.has_name(name))
+    }
+
+    pub fn first_attr_value_str_by_name(
+        &self,
+        attrs: &[Attribute],
+        name: Symbol,
+    ) -> Option<Symbol> {
+        attrs.iter().find(|at| at.has_name(name)).and_then(|at| at.value_str())
+    }
+}
+
+// JUSTIFICATION: defn of the suggested wrapper fns
+#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
+impl Session {
+    pub fn verbose(&self) -> bool {
+        self.opts.unstable_opts.verbose
+    }
+
+    pub fn instrument_mcount(&self) -> bool {
+        self.opts.unstable_opts.instrument_mcount
+    }
+
+    pub fn time_llvm_passes(&self) -> bool {
+        self.opts.unstable_opts.time_llvm_passes
+    }
+
+    pub fn meta_stats(&self) -> bool {
+        self.opts.unstable_opts.meta_stats
+    }
+
+    pub fn asm_comments(&self) -> bool {
+        self.opts.unstable_opts.asm_comments
+    }
+
+    pub fn verify_llvm_ir(&self) -> bool {
+        self.opts.unstable_opts.verify_llvm_ir || option_env!("RUSTC_VERIFY_LLVM_IR").is_some()
+    }
+
+    pub fn print_llvm_passes(&self) -> bool {
+        self.opts.unstable_opts.print_llvm_passes
+    }
+
+    pub fn binary_dep_depinfo(&self) -> bool {
+        self.opts.unstable_opts.binary_dep_depinfo
+    }
+
+    pub fn mir_opt_level(&self) -> usize {
+        self.opts
+            .unstable_opts
+            .mir_opt_level
+            .unwrap_or_else(|| if self.opts.optimize != OptLevel::No { 2 } else { 1 })
+    }
+
+    /// Calculates the flavor of LTO to use for this compilation.
+    pub fn lto(&self) -> config::Lto {
+        // If our target has codegen requirements ignore the command line
+        if self.target.requires_lto {
+            return config::Lto::Fat;
+        }
+
+        // If the user specified something, return that. If they only said `-C
+        // lto` and we've for whatever reason forced off ThinLTO via the CLI,
+        // then ensure we can't use a ThinLTO.
+        match self.opts.cg.lto {
+            config::LtoCli::Unspecified => {
+                // The compiler was invoked without the `-Clto` flag. Fall
+                // through to the default handling
+            }
+            config::LtoCli::No => {
+                // The user explicitly opted out of any kind of LTO
+                return config::Lto::No;
+            }
+            config::LtoCli::Yes | config::LtoCli::Fat | config::LtoCli::NoParam => {
+                // All of these mean fat LTO
+                return config::Lto::Fat;
+            }
+            config::LtoCli::Thin => {
+                return if self.opts.cli_forced_thinlto_off {
+                    config::Lto::Fat
+                } else {
+                    config::Lto::Thin
+                };
+            }
+        }
+
+        // Ok at this point the target doesn't require anything and the user
+        // hasn't asked for anything. Our next decision is whether or not
+        // we enable "auto" ThinLTO where we use multiple codegen units and
+        // then do ThinLTO over those codegen units. The logic below will
+        // either return `No` or `ThinLocal`.
+
+        // If processing command line options determined that we're incompatible
+        // with ThinLTO (e.g., `-C lto --emit llvm-ir`) then return that option.
+        if self.opts.cli_forced_thinlto_off {
+            return config::Lto::No;
+        }
+
+        // If `-Z thinlto` specified process that, but note that this is mostly
+        // a deprecated option now that `-C lto=thin` exists.
+        if let Some(enabled) = self.opts.unstable_opts.thinlto {
+            if enabled {
+                return config::Lto::ThinLocal;
+            } else {
+                return config::Lto::No;
+            }
+        }
+
+        // If there's only one codegen unit and LTO isn't enabled then there's
+        // no need for ThinLTO so just return false.
+        if self.codegen_units() == 1 {
+            return config::Lto::No;
+        }
+
+        // Now we're in "defaults" territory. By default we enable ThinLTO for
+        // optimized compiles (anything greater than O0).
+        match self.opts.optimize {
+            config::OptLevel::No => config::Lto::No,
+            _ => config::Lto::ThinLocal,
+        }
+    }
+
+    /// Returns the panic strategy for this compile session. If the user explicitly selected one
+    /// using '-C panic', use that, otherwise use the panic strategy defined by the target.
+    pub fn panic_strategy(&self) -> PanicStrategy {
+        self.opts.cg.panic.unwrap_or(self.target.panic_strategy)
+    }
+
+    pub fn fewer_names(&self) -> bool {
+        if let Some(fewer_names) = self.opts.unstable_opts.fewer_names {
+            fewer_names
+        } else {
+            let more_names = self.opts.output_types.contains_key(&OutputType::LlvmAssembly)
+                || self.opts.output_types.contains_key(&OutputType::Bitcode)
+                // AddressSanitizer and MemorySanitizer use alloca name when reporting an issue.
+                || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY);
+            !more_names
+        }
+    }
+
+    pub fn unstable_options(&self) -> bool {
+        self.opts.unstable_opts.unstable_options
+    }
+
+    pub fn is_nightly_build(&self) -> bool {
+        self.opts.unstable_features.is_nightly_build()
+    }
+
+    pub fn overflow_checks(&self) -> bool {
+        self.opts.cg.overflow_checks.unwrap_or(self.opts.debug_assertions)
+    }
+
+    pub fn relocation_model(&self) -> RelocModel {
+        self.opts.cg.relocation_model.unwrap_or(self.target.relocation_model)
+    }
+
+    pub fn code_model(&self) -> Option<CodeModel> {
+        self.opts.cg.code_model.or(self.target.code_model)
+    }
+
+    pub fn tls_model(&self) -> TlsModel {
+        self.opts.unstable_opts.tls_model.unwrap_or(self.target.tls_model)
+    }
+
+    pub fn split_debuginfo(&self) -> SplitDebuginfo {
+        self.opts.cg.split_debuginfo.unwrap_or(self.target.split_debuginfo)
+    }
+
+    pub fn stack_protector(&self) -> StackProtector {
+        if self.target.options.supports_stack_protector {
+            self.opts.unstable_opts.stack_protector
+        } else {
+            StackProtector::None
+        }
+    }
+
+    pub fn must_emit_unwind_tables(&self) -> bool {
+        // This is used to control the emission of the `uwtable` attribute on
+        // LLVM functions.
+        //
+        // Unwind tables are needed when compiling with `-C panic=unwind`, but
+        // LLVM won't omit unwind tables unless the function is also marked as
+        // `nounwind`, so users are allowed to disable `uwtable` emission.
+        // Historically rustc always emits `uwtable` attributes by default, so
+        // even they can be disabled, they're still emitted by default.
+        //
+        // On some targets (including windows), however, exceptions include
+        // other events such as illegal instructions, segfaults, etc. This means
+        // that on Windows we end up still needing unwind tables even if the `-C
+        // panic=abort` flag is passed.
+        //
+        // You can also find more info on why Windows needs unwind tables in:
+        //      https://bugzilla.mozilla.org/show_bug.cgi?id=1302078
+        //
+        // If a target requires unwind tables, then they must be emitted.
+        // Otherwise, we can defer to the `-C force-unwind-tables=<yes/no>`
+        // value, if it is provided, or disable them, if not.
+        self.target.requires_uwtable
+            || self.opts.cg.force_unwind_tables.unwrap_or(
+                self.panic_strategy() == PanicStrategy::Unwind || self.target.default_uwtable,
+            )
+    }
+
     /// Returns the number of query threads that should be used for this
     /// compilation
     pub fn threads(&self) -> usize {
@@ -1040,109 +1164,17 @@ impl Session {
         self.opts.unstable_opts.teach && self.diagnostic().must_teach(code)
     }
 
-    pub fn rust_2015(&self) -> bool {
-        self.opts.edition == Edition::Edition2015
-    }
-
-    /// Are we allowed to use features from the Rust 2018 edition?
-    pub fn rust_2018(&self) -> bool {
-        self.opts.edition >= Edition::Edition2018
-    }
-
-    /// Are we allowed to use features from the Rust 2021 edition?
-    pub fn rust_2021(&self) -> bool {
-        self.opts.edition >= Edition::Edition2021
-    }
-
-    /// Are we allowed to use features from the Rust 2024 edition?
-    pub fn rust_2024(&self) -> bool {
-        self.opts.edition >= Edition::Edition2024
-    }
-
     pub fn edition(&self) -> Edition {
         self.opts.edition
     }
 
-    /// Returns `true` if we cannot skip the PLT for shared library calls.
-    pub fn needs_plt(&self) -> bool {
-        // Check if the current target usually needs PLT to be enabled.
-        // The user can use the command line flag to override it.
-        let needs_plt = self.target.needs_plt;
-
-        let dbg_opts = &self.opts.unstable_opts;
-
-        let relro_level = dbg_opts.relro_level.unwrap_or(self.target.relro_level);
-
-        // Only enable this optimization by default if full relro is also enabled.
-        // In this case, lazy binding was already unavailable, so nothing is lost.
-        // This also ensures `-Wl,-z,now` is supported by the linker.
-        let full_relro = RelroLevel::Full == relro_level;
-
-        // If user didn't explicitly forced us to use / skip the PLT,
-        // then try to skip it where possible.
-        dbg_opts.plt.unwrap_or(needs_plt || !full_relro)
-    }
-
-    /// Checks if LLVM lifetime markers should be emitted.
-    pub fn emit_lifetime_markers(&self) -> bool {
-        self.opts.optimize != config::OptLevel::No
-        // AddressSanitizer uses lifetimes to detect use after scope bugs.
-        // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables.
-        // HWAddressSanitizer will use lifetimes to detect use after scope bugs in the future.
-        || self.opts.unstable_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY | SanitizerSet::HWADDRESS)
-    }
-
     pub fn link_dead_code(&self) -> bool {
         self.opts.cg.link_dead_code.unwrap_or(false)
     }
-
-    pub fn instrument_coverage(&self) -> bool {
-        self.opts.instrument_coverage()
-    }
-
-    pub fn instrument_coverage_except_unused_generics(&self) -> bool {
-        self.opts.instrument_coverage_except_unused_generics()
-    }
-
-    pub fn instrument_coverage_except_unused_functions(&self) -> bool {
-        self.opts.instrument_coverage_except_unused_functions()
-    }
-
-    pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool {
-        [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
-            .iter()
-            .any(|kind| attr.has_name(*kind))
-    }
-
-    pub fn contains_name(&self, attrs: &[Attribute], name: Symbol) -> bool {
-        attrs.iter().any(|item| item.has_name(name))
-    }
-
-    pub fn find_by_name<'a>(
-        &'a self,
-        attrs: &'a [Attribute],
-        name: Symbol,
-    ) -> Option<&'a Attribute> {
-        attrs.iter().find(|attr| attr.has_name(name))
-    }
-
-    pub fn filter_by_name<'a>(
-        &'a self,
-        attrs: &'a [Attribute],
-        name: Symbol,
-    ) -> impl Iterator<Item = &'a Attribute> {
-        attrs.iter().filter(move |attr| attr.has_name(name))
-    }
-
-    pub fn first_attr_value_str_by_name(
-        &self,
-        attrs: &[Attribute],
-        name: Symbol,
-    ) -> Option<Symbol> {
-        attrs.iter().find(|at| at.has_name(name)).and_then(|at| at.value_str())
-    }
 }
 
+// JUSTIFICATION: part of session construction
+#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
 fn default_emitter(
     sopts: &config::Options,
     registry: rustc_errors::registry::Registry,
@@ -1227,6 +1259,8 @@ pub enum DiagnosticOutput {
     Raw(Box<dyn Write + Send>),
 }
 
+// JUSTIFICATION: literally session construction
+#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
 pub fn build_session(
     sopts: config::Options,
     local_crate_source_file: Option<PathBuf>,
@@ -1348,11 +1382,8 @@ pub fn build_session(
         CguReuseTracker::new_disabled()
     };
 
-    let prof = SelfProfilerRef::new(
-        self_profiler,
-        sopts.unstable_opts.time_passes || sopts.unstable_opts.time,
-        sopts.unstable_opts.time_passes,
-    );
+    let prof =
+        SelfProfilerRef::new(self_profiler, sopts.time_passes(), sopts.unstable_opts.time_passes);
 
     let ctfe_backtrace = Lock::new(match env::var("RUSTC_CTFE_BACKTRACE") {
         Ok(ref val) if val == "immediate" => CtfeBacktrace::Immediate,
@@ -1401,8 +1432,12 @@ pub fn build_session(
     sess
 }
 
-// If it is useful to have a Session available already for validating a
-// commandline argument, you can do so here.
+/// Validate command line arguments with a `Session`.
+///
+/// If it is useful to have a Session available already for validating a commandline argument, you
+/// can do so here.
+// JUSTIFICATION: needs to access args to validate them
+#[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
 fn validate_commandline_args_with_session_available(sess: &Session) {
     // Since we don't know if code in an rlib will be linked to statically or
     // dynamically downstream, rustc generates `__imp_` symbols that help linkers
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index c75b6772487..060e7a7b90a 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1238,6 +1238,8 @@ symbols! {
         rustc_layout_scalar_valid_range_start,
         rustc_legacy_const_generics,
         rustc_lint_diagnostics,
+        rustc_lint_opt_deny_field_access,
+        rustc_lint_opt_ty,
         rustc_lint_query_instability,
         rustc_macro_transparency,
         rustc_main,
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 3ef51b0c27a..d0a17f712d3 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -60,8 +60,9 @@ pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError
 pub use self::specialize::specialization_graph::FutureCompatOverlapError;
 pub use self::specialize::specialization_graph::FutureCompatOverlapErrorKind;
 pub use self::specialize::{specialization_graph, translate_substs, OverlapError};
-pub use self::structural_match::search_for_structural_match_violation;
-pub use self::structural_match::{NonStructuralMatchTy, NonStructuralMatchTyKind};
+pub use self::structural_match::{
+    search_for_adt_const_param_violation, search_for_structural_match_violation,
+};
 pub use self::util::{
     elaborate_obligations, elaborate_predicates, elaborate_predicates_with_span,
     elaborate_trait_ref, elaborate_trait_refs,
diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs
index 6c0b83fbd03..c278752e3d9 100644
--- a/compiler/rustc_trait_selection/src/traits/structural_match.rs
+++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs
@@ -6,29 +6,10 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
 use rustc_hir::lang_items::LangItem;
 use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
 use rustc_span::Span;
 use std::ops::ControlFlow;
 
-#[derive(Debug)]
-pub struct NonStructuralMatchTy<'tcx> {
-    pub ty: Ty<'tcx>,
-    pub kind: NonStructuralMatchTyKind<'tcx>,
-}
-
-#[derive(Debug)]
-pub enum NonStructuralMatchTyKind<'tcx> {
-    Adt(AdtDef<'tcx>),
-    Param,
-    Dynamic,
-    Foreign,
-    Opaque,
-    Closure,
-    Generator,
-    Projection,
-    Float,
-}
-
 /// This method traverses the structure of `ty`, trying to find an
 /// instance of an ADT (i.e. struct or enum) that doesn't implement
 /// the structural-match traits, or a generic type parameter
@@ -54,15 +35,28 @@ pub enum NonStructuralMatchTyKind<'tcx> {
 /// For more background on why Rust has this requirement, and issues
 /// that arose when the requirement was not enforced completely, see
 /// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307.
-///
-/// The floats_allowed flag is used to deny constants in floating point
 pub fn search_for_structural_match_violation<'tcx>(
     span: Span,
     tcx: TyCtxt<'tcx>,
     ty: Ty<'tcx>,
-    floats_allowed: bool,
-) -> Option<NonStructuralMatchTy<'tcx>> {
-    ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), floats_allowed })
+) -> Option<Ty<'tcx>> {
+    ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: false })
+        .break_value()
+}
+
+/// This method traverses the structure of `ty`, trying to find any
+/// types that are not allowed to be used in a const generic.
+///
+/// This is either because the type does not implement `StructuralEq`
+/// and `StructuralPartialEq`, or because the type is intentionally
+/// not supported in const generics (such as floats and raw pointers,
+/// which are allowed in match blocks).
+pub fn search_for_adt_const_param_violation<'tcx>(
+    span: Span,
+    tcx: TyCtxt<'tcx>,
+    ty: Ty<'tcx>,
+) -> Option<Ty<'tcx>> {
+    ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default(), adt_const_param: true })
         .break_value()
 }
 
@@ -125,7 +119,10 @@ struct Search<'tcx> {
     /// we will not recur on them again.
     seen: FxHashSet<hir::def_id::DefId>,
 
-    floats_allowed: bool,
+    // Additionally deny things that have been allowed in patterns,
+    // but are not allowed in adt const params, such as floats and
+    // fn ptrs.
+    adt_const_param: bool,
 }
 
 impl<'tcx> Search<'tcx> {
@@ -135,7 +132,7 @@ impl<'tcx> Search<'tcx> {
 }
 
 impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
-    type BreakTy = NonStructuralMatchTy<'tcx>;
+    type BreakTy = Ty<'tcx>;
 
     fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
         debug!("Search visiting ty: {:?}", ty);
@@ -143,51 +140,27 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
         let (adt_def, substs) = match *ty.kind() {
             ty::Adt(adt_def, substs) => (adt_def, substs),
             ty::Param(_) => {
-                let kind = NonStructuralMatchTyKind::Param;
-                return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
+                return ControlFlow::Break(ty);
             }
             ty::Dynamic(..) => {
-                let kind = NonStructuralMatchTyKind::Dynamic;
-                return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
+                return ControlFlow::Break(ty);
             }
             ty::Foreign(_) => {
-                let kind = NonStructuralMatchTyKind::Foreign;
-                return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
+                return ControlFlow::Break(ty);
             }
             ty::Opaque(..) => {
-                let kind = NonStructuralMatchTyKind::Opaque;
-                return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
+                return ControlFlow::Break(ty);
             }
             ty::Projection(..) => {
-                let kind = NonStructuralMatchTyKind::Projection;
-                return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
+                return ControlFlow::Break(ty);
             }
             ty::Closure(..) => {
-                let kind = NonStructuralMatchTyKind::Closure;
-                return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
+                return ControlFlow::Break(ty);
             }
             ty::Generator(..) | ty::GeneratorWitness(..) => {
-                let kind = NonStructuralMatchTyKind::Generator;
-                return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
-            }
-            ty::RawPtr(..) => {
-                // structural-match ignores substructure of
-                // `*const _`/`*mut _`, so skip `super_visit_with`.
-                //
-                // For example, if you have:
-                // ```
-                // struct NonStructural;
-                // #[derive(PartialEq, Eq)]
-                // struct T(*const NonStructural);
-                // const C: T = T(std::ptr::null());
-                // ```
-                //
-                // Even though `NonStructural` does not implement `PartialEq`,
-                // structural equality on `T` does not recur into the raw
-                // pointer. Therefore, one can still use `C` in a pattern.
-                return ControlFlow::CONTINUE;
+                return ControlFlow::Break(ty);
             }
-            ty::FnDef(..) | ty::FnPtr(..) => {
+            ty::FnDef(..) => {
                 // Types of formals and return in `fn(_) -> _` are also irrelevant;
                 // so we do not recur into them via `super_visit_with`
                 return ControlFlow::CONTINUE;
@@ -206,14 +179,41 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
                 return ControlFlow::CONTINUE;
             }
 
+            ty::FnPtr(..) => {
+                if !self.adt_const_param {
+                    return ControlFlow::CONTINUE;
+                } else {
+                    return ControlFlow::Break(ty);
+                }
+            }
+
+            ty::RawPtr(..) => {
+                if !self.adt_const_param {
+                    // structural-match ignores substructure of
+                    // `*const _`/`*mut _`, so skip `super_visit_with`.
+                    //
+                    // For example, if you have:
+                    // ```
+                    // struct NonStructural;
+                    // #[derive(PartialEq, Eq)]
+                    // struct T(*const NonStructural);
+                    // const C: T = T(std::ptr::null());
+                    // ```
+                    //
+                    // Even though `NonStructural` does not implement `PartialEq`,
+                    // structural equality on `T` does not recur into the raw
+                    // pointer. Therefore, one can still use `C` in a pattern.
+                    return ControlFlow::CONTINUE;
+                } else {
+                    return ControlFlow::Break(ty);
+                }
+            }
+
             ty::Float(_) => {
-                if self.floats_allowed {
+                if !self.adt_const_param {
                     return ControlFlow::CONTINUE;
                 } else {
-                    return ControlFlow::Break(NonStructuralMatchTy {
-                        ty,
-                        kind: NonStructuralMatchTyKind::Float,
-                    });
+                    return ControlFlow::Break(ty);
                 }
             }
 
@@ -239,8 +239,7 @@ impl<'tcx> TypeVisitor<'tcx> for Search<'tcx> {
 
         if !self.type_marked_structural(ty) {
             debug!("Search found ty: {:?}", ty);
-            let kind = NonStructuralMatchTyKind::Adt(adt_def);
-            return ControlFlow::Break(NonStructuralMatchTy { ty, kind });
+            return ControlFlow::Break(ty);
         }
 
         // structural-match does not care about the
diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs
index 58f4f02052f..08e8e6f7d0f 100644
--- a/compiler/rustc_typeck/src/astconv/mod.rs
+++ b/compiler/rustc_typeck/src/astconv/mod.rs
@@ -221,14 +221,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 tcx.mk_region(ty::ReLateBound(debruijn, br))
             }
 
-            Some(rl::Region::LateBoundAnon(debruijn, index, anon_index)) => {
-                let br = ty::BoundRegion {
-                    var: ty::BoundVar::from_u32(index),
-                    kind: ty::BrAnon(anon_index),
-                };
-                tcx.mk_region(ty::ReLateBound(debruijn, br))
-            }
-
             Some(rl::Region::EarlyBound(index, id)) => {
                 let name = lifetime_name(id.expect_local());
                 tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id: id, index, name }))
diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs
index e65955fe860..bae986de9a2 100644
--- a/compiler/rustc_typeck/src/check/wfcheck.rs
+++ b/compiler/rustc_typeck/src/check/wfcheck.rs
@@ -848,29 +848,13 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
             let ty = tcx.type_of(tcx.hir().local_def_id(param.hir_id));
 
             if tcx.features().adt_const_params {
-                let err = match ty.peel_refs().kind() {
-                    ty::FnPtr(_) => Some("function pointers"),
-                    ty::RawPtr(_) => Some("raw pointers"),
-                    _ => None,
-                };
-
-                if let Some(unsupported_type) = err {
-                    tcx.sess.span_err(
-                        hir_ty.span,
-                        &format!(
-                            "using {} as const generic parameters is forbidden",
-                            unsupported_type
-                        ),
-                    );
-                }
-
                 if let Some(non_structural_match_ty) =
-                    traits::search_for_structural_match_violation(param.span, tcx, ty, false)
+                    traits::search_for_adt_const_param_violation(param.span, tcx, ty)
                 {
                     // We use the same error code in both branches, because this is really the same
                     // issue: we just special-case the message for type parameters to make it
                     // clearer.
-                    match ty.peel_refs().kind() {
+                    match non_structural_match_ty.kind() {
                         ty::Param(_) => {
                             // Const parameters may not have type parameters as their types,
                             // because we cannot be sure that the type parameter derives `PartialEq`
@@ -902,6 +886,24 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
                             .note("floats do not derive `Eq` or `Ord`, which are required for const parameters")
                             .emit();
                         }
+                        ty::FnPtr(_) => {
+                            struct_span_err!(
+                                tcx.sess,
+                                hir_ty.span,
+                                E0741,
+                                "using function pointers as const generic parameters is forbidden",
+                            )
+                            .emit();
+                        }
+                        ty::RawPtr(_) => {
+                            struct_span_err!(
+                                tcx.sess,
+                                hir_ty.span,
+                                E0741,
+                                "using raw pointers as const generic parameters is forbidden",
+                            )
+                            .emit();
+                        }
                         _ => {
                             let mut diag = struct_span_err!(
                                 tcx.sess,
@@ -909,10 +911,10 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
                                 E0741,
                                 "`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \
                                 the type of a const parameter",
-                                non_structural_match_ty.ty,
+                                non_structural_match_ty,
                             );
 
-                            if ty == non_structural_match_ty.ty {
+                            if ty == non_structural_match_ty {
                                 diag.span_label(
                                     hir_ty.span,
                                     format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"),
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 0e78c60ca5b..60c0694ca0e 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -1346,16 +1346,8 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S
 
             match self.tcx.named_region(lt.hir_id) {
                 Some(rl::Region::Static | rl::Region::EarlyBound(..)) => {}
-                Some(
-                    rl::Region::LateBound(debruijn, _, _)
-                    | rl::Region::LateBoundAnon(debruijn, _, _),
-                ) if debruijn < self.outer_index => {}
-                Some(
-                    rl::Region::LateBound(..)
-                    | rl::Region::LateBoundAnon(..)
-                    | rl::Region::Free(..),
-                )
-                | None => {
+                Some(rl::Region::LateBound(debruijn, _, _)) if debruijn < self.outer_index => {}
+                Some(rl::Region::LateBound(..) | rl::Region::Free(..)) | None => {
                     self.has_late_bound_regions = Some(lt.span);
                 }
             }
diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs
index a1889e3d21c..76b4a534e5d 100644
--- a/library/core/src/slice/iter.rs
+++ b/library/core/src/slice/iter.rs
@@ -1628,14 +1628,21 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for Chunks<'a, T> {
 #[stable(feature = "rust1", since = "1.0.0")]
 #[must_use = "iterators are lazy and do nothing unless consumed"]
 pub struct ChunksMut<'a, T: 'a> {
-    v: &'a mut [T],
+    /// # Safety
+    /// This slice pointer must point at a valid region of `T` with at least length `v.len()`. Normally,
+    /// those requirements would mean that we could instead use a `&mut [T]` here, but we cannot
+    /// because `__iterator_get_unchecked` needs to return `&mut [T]`, which guarantees certain aliasing
+    /// properties that we cannot uphold if we hold on to the full original `&mut [T]`. Wrapping a raw
+    /// slice instead lets us hand out non-overlapping `&mut [T]` subslices of the slice we wrap.
+    v: *mut [T],
     chunk_size: usize,
+    _marker: PhantomData<&'a mut T>,
 }
 
 impl<'a, T: 'a> ChunksMut<'a, T> {
     #[inline]
     pub(super) fn new(slice: &'a mut [T], size: usize) -> Self {
-        Self { v: slice, chunk_size: size }
+        Self { v: slice, chunk_size: size, _marker: PhantomData }
     }
 }
 
@@ -1649,10 +1656,11 @@ impl<'a, T> Iterator for ChunksMut<'a, T> {
             None
         } else {
             let sz = cmp::min(self.v.len(), self.chunk_size);
-            let tmp = mem::replace(&mut self.v, &mut []);
-            let (head, tail) = tmp.split_at_mut(sz);
+            // SAFETY: The self.v contract ensures that any split_at_mut is valid.
+            let (head, tail) = unsafe { self.v.split_at_mut(sz) };
             self.v = tail;
-            Some(head)
+            // SAFETY: Nothing else points to or will point to the contents of this slice.
+            Some(unsafe { &mut *head })
         }
     }
 
@@ -1684,11 +1692,13 @@ impl<'a, T> Iterator for ChunksMut<'a, T> {
                 Some(sum) => cmp::min(self.v.len(), sum),
                 None => self.v.len(),
             };
-            let tmp = mem::replace(&mut self.v, &mut []);
-            let (head, tail) = tmp.split_at_mut(end);
-            let (_, nth) = head.split_at_mut(start);
+            // SAFETY: The self.v contract ensures that any split_at_mut is valid.
+            let (head, tail) = unsafe { self.v.split_at_mut(end) };
+            // SAFETY: The self.v contract ensures that any split_at_mut is valid.
+            let (_, nth) = unsafe { head.split_at_mut(start) };
             self.v = tail;
-            Some(nth)
+            // SAFETY: Nothing else points to or will point to the contents of this slice.
+            Some(unsafe { &mut *nth })
         }
     }
 
@@ -1698,13 +1708,14 @@ impl<'a, T> Iterator for ChunksMut<'a, T> {
             None
         } else {
             let start = (self.v.len() - 1) / self.chunk_size * self.chunk_size;
-            Some(&mut self.v[start..])
+            // SAFETY: Nothing else points to or will point to the contents of this slice.
+            Some(unsafe { &mut *self.v.get_unchecked_mut(start..) })
         }
     }
 
     unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
         let start = idx * self.chunk_size;
-        // SAFETY: see comments for `Chunks::__iterator_get_unchecked`.
+        // SAFETY: see comments for `Chunks::__iterator_get_unchecked` and `self.v`.
         //
         // Also note that the caller also guarantees that we're never called
         // with the same index again, and that no other methods that will
@@ -1726,12 +1737,12 @@ impl<'a, T> DoubleEndedIterator for ChunksMut<'a, T> {
         } else {
             let remainder = self.v.len() % self.chunk_size;
             let sz = if remainder != 0 { remainder } else { self.chunk_size };
-            let tmp = mem::replace(&mut self.v, &mut []);
-            let tmp_len = tmp.len();
+            let len = self.v.len();
             // SAFETY: Similar to `Chunks::next_back`
-            let (head, tail) = unsafe { tmp.split_at_mut_unchecked(tmp_len - sz) };
+            let (head, tail) = unsafe { self.v.split_at_mut_unchecked(len - sz) };
             self.v = head;
-            Some(tail)
+            // SAFETY: Nothing else points to or will point to the contents of this slice.
+            Some(unsafe { &mut *tail })
         }
     }
 
@@ -1747,10 +1758,13 @@ impl<'a, T> DoubleEndedIterator for ChunksMut<'a, T> {
                 Some(res) => cmp::min(self.v.len(), res),
                 None => self.v.len(),
             };
-            let (temp, _tail) = mem::replace(&mut self.v, &mut []).split_at_mut(end);
-            let (head, nth_back) = temp.split_at_mut(start);
+            // SAFETY: The self.v contract ensures that any split_at_mut is valid.
+            let (temp, _tail) = unsafe { self.v.split_at_mut(end) };
+            // SAFETY: The self.v contract ensures that any split_at_mut is valid.
+            let (head, nth_back) = unsafe { temp.split_at_mut(start) };
             self.v = head;
-            Some(nth_back)
+            // SAFETY: Nothing else points to or will point to the contents of this slice.
+            Some(unsafe { &mut *nth_back })
         }
     }
 }
@@ -1956,9 +1970,16 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for ChunksExact<'a, T> {
 #[stable(feature = "chunks_exact", since = "1.31.0")]
 #[must_use = "iterators are lazy and do nothing unless consumed"]
 pub struct ChunksExactMut<'a, T: 'a> {
-    v: &'a mut [T],
-    rem: &'a mut [T],
+    /// # Safety
+    /// This slice pointer must point at a valid region of `T` with at least length `v.len()`. Normally,
+    /// those requirements would mean that we could instead use a `&mut [T]` here, but we cannot
+    /// because `__iterator_get_unchecked` needs to return `&mut [T]`, which guarantees certain aliasing
+    /// properties that we cannot uphold if we hold on to the full original `&mut [T]`. Wrapping a raw
+    /// slice instead lets us hand out non-overlapping `&mut [T]` subslices of the slice we wrap.
+    v: *mut [T],
+    rem: &'a mut [T], // The iterator never yields from here, so this can be unique
     chunk_size: usize,
+    _marker: PhantomData<&'a mut T>,
 }
 
 impl<'a, T> ChunksExactMut<'a, T> {
@@ -1968,7 +1989,7 @@ impl<'a, T> ChunksExactMut<'a, T> {
         let fst_len = slice.len() - rem;
         // SAFETY: 0 <= fst_len <= slice.len() by construction above
         let (fst, snd) = unsafe { slice.split_at_mut_unchecked(fst_len) };
-        Self { v: fst, rem: snd, chunk_size }
+        Self { v: fst, rem: snd, chunk_size, _marker: PhantomData }
     }
 
     /// Returns the remainder of the original slice that is not going to be
@@ -1990,10 +2011,11 @@ impl<'a, T> Iterator for ChunksExactMut<'a, T> {
         if self.v.len() < self.chunk_size {
             None
         } else {
-            let tmp = mem::replace(&mut self.v, &mut []);
-            let (head, tail) = tmp.split_at_mut(self.chunk_size);
+            // SAFETY: self.chunk_size is inbounds because we compared above against self.v.len()
+            let (head, tail) = unsafe { self.v.split_at_mut(self.chunk_size) };
             self.v = tail;
-            Some(head)
+            // SAFETY: Nothing else points to or will point to the contents of this slice.
+            Some(unsafe { &mut *head })
         }
     }
 
@@ -2015,8 +2037,8 @@ impl<'a, T> Iterator for ChunksExactMut<'a, T> {
             self.v = &mut [];
             None
         } else {
-            let tmp = mem::replace(&mut self.v, &mut []);
-            let (_, snd) = tmp.split_at_mut(start);
+            // SAFETY: The self.v contract ensures that any split_at_mut is valid.
+            let (_, snd) = unsafe { self.v.split_at_mut(start) };
             self.v = snd;
             self.next()
         }
@@ -2029,7 +2051,7 @@ impl<'a, T> Iterator for ChunksExactMut<'a, T> {
 
     unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
         let start = idx * self.chunk_size;
-        // SAFETY: see comments for `ChunksMut::__iterator_get_unchecked`.
+        // SAFETY: see comments for `Chunks::__iterator_get_unchecked` and `self.v`.
         unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), self.chunk_size) }
     }
 }
@@ -2041,11 +2063,11 @@ impl<'a, T> DoubleEndedIterator for ChunksExactMut<'a, T> {
         if self.v.len() < self.chunk_size {
             None
         } else {
-            let tmp = mem::replace(&mut self.v, &mut []);
-            let tmp_len = tmp.len();
-            let (head, tail) = tmp.split_at_mut(tmp_len - self.chunk_size);
+            // SAFETY: This subtraction is inbounds because of the check above
+            let (head, tail) = unsafe { self.v.split_at_mut(self.v.len() - self.chunk_size) };
             self.v = head;
-            Some(tail)
+            // SAFETY: Nothing else points to or will point to the contents of this slice.
+            Some(unsafe { &mut *tail })
         }
     }
 
@@ -2058,10 +2080,13 @@ impl<'a, T> DoubleEndedIterator for ChunksExactMut<'a, T> {
         } else {
             let start = (len - 1 - n) * self.chunk_size;
             let end = start + self.chunk_size;
-            let (temp, _tail) = mem::replace(&mut self.v, &mut []).split_at_mut(end);
-            let (head, nth_back) = temp.split_at_mut(start);
+            // SAFETY: The self.v contract ensures that any split_at_mut is valid.
+            let (temp, _tail) = unsafe { mem::replace(&mut self.v, &mut []).split_at_mut(end) };
+            // SAFETY: The self.v contract ensures that any split_at_mut is valid.
+            let (head, nth_back) = unsafe { temp.split_at_mut(start) };
             self.v = head;
-            Some(nth_back)
+            // SAFETY: Nothing else points to or will point to the contents of this slice.
+            Some(unsafe { &mut *nth_back })
         }
     }
 }
@@ -2645,14 +2670,21 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for RChunks<'a, T> {
 #[stable(feature = "rchunks", since = "1.31.0")]
 #[must_use = "iterators are lazy and do nothing unless consumed"]
 pub struct RChunksMut<'a, T: 'a> {
-    v: &'a mut [T],
+    /// # Safety
+    /// This slice pointer must point at a valid region of `T` with at least length `v.len()`. Normally,
+    /// those requirements would mean that we could instead use a `&mut [T]` here, but we cannot
+    /// because `__iterator_get_unchecked` needs to return `&mut [T]`, which guarantees certain aliasing
+    /// properties that we cannot uphold if we hold on to the full original `&mut [T]`. Wrapping a raw
+    /// slice instead lets us hand out non-overlapping `&mut [T]` subslices of the slice we wrap.
+    v: *mut [T],
     chunk_size: usize,
+    _marker: PhantomData<&'a mut T>,
 }
 
 impl<'a, T: 'a> RChunksMut<'a, T> {
     #[inline]
     pub(super) fn new(slice: &'a mut [T], size: usize) -> Self {
-        Self { v: slice, chunk_size: size }
+        Self { v: slice, chunk_size: size, _marker: PhantomData }
     }
 }
 
@@ -2666,16 +2698,16 @@ impl<'a, T> Iterator for RChunksMut<'a, T> {
             None
         } else {
             let sz = cmp::min(self.v.len(), self.chunk_size);
-            let tmp = mem::replace(&mut self.v, &mut []);
-            let tmp_len = tmp.len();
+            let len = self.v.len();
             // SAFETY: split_at_mut_unchecked just requires the argument be less
             // than the length. This could only happen if the expression
-            // `tmp_len - sz` overflows. This could only happen if `sz >
-            // tmp_len`, which is impossible as we initialize it as the `min` of
-            // `self.v.len()` (e.g. `tmp_len`) and `self.chunk_size`.
-            let (head, tail) = unsafe { tmp.split_at_mut_unchecked(tmp_len - sz) };
+            // `len - sz` overflows. This could only happen if `sz >
+            // len`, which is impossible as we initialize it as the `min` of
+            // `self.v.len()` (e.g. `len`) and `self.chunk_size`.
+            let (head, tail) = unsafe { self.v.split_at_mut_unchecked(len - sz) };
             self.v = head;
-            Some(tail)
+            // SAFETY: Nothing else points to or will point to the contents of this slice.
+            Some(unsafe { &mut *tail })
         }
     }
 
@@ -2709,11 +2741,15 @@ impl<'a, T> Iterator for RChunksMut<'a, T> {
                 Some(sum) => sum,
                 None => 0,
             };
-            let tmp = mem::replace(&mut self.v, &mut []);
-            let (head, tail) = tmp.split_at_mut(start);
-            let (nth, _) = tail.split_at_mut(end - start);
+            // SAFETY: This type ensures that self.v is a valid pointer with a correct len.
+            // Therefore the bounds check in split_at_mut guarantess the split point is inbounds.
+            let (head, tail) = unsafe { self.v.split_at_mut(start) };
+            // SAFETY: This type ensures that self.v is a valid pointer with a correct len.
+            // Therefore the bounds check in split_at_mut guarantess the split point is inbounds.
+            let (nth, _) = unsafe { tail.split_at_mut(end - start) };
             self.v = head;
-            Some(nth)
+            // SAFETY: Nothing else points to or will point to the contents of this slice.
+            Some(unsafe { &mut *nth })
         }
     }
 
@@ -2724,7 +2760,8 @@ impl<'a, T> Iterator for RChunksMut<'a, T> {
         } else {
             let rem = self.v.len() % self.chunk_size;
             let end = if rem == 0 { self.chunk_size } else { rem };
-            Some(&mut self.v[0..end])
+            // SAFETY: Nothing else points to or will point to the contents of this slice.
+            Some(unsafe { &mut *self.v.get_unchecked_mut(0..end) })
         }
     }
 
@@ -2735,7 +2772,7 @@ impl<'a, T> Iterator for RChunksMut<'a, T> {
             Some(start) => start,
         };
         // SAFETY: see comments for `RChunks::__iterator_get_unchecked` and
-        // `ChunksMut::__iterator_get_unchecked`
+        // `ChunksMut::__iterator_get_unchecked`, `self.v`.
         unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), end - start) }
     }
 }
@@ -2749,11 +2786,11 @@ impl<'a, T> DoubleEndedIterator for RChunksMut<'a, T> {
         } else {
             let remainder = self.v.len() % self.chunk_size;
             let sz = if remainder != 0 { remainder } else { self.chunk_size };
-            let tmp = mem::replace(&mut self.v, &mut []);
             // SAFETY: Similar to `Chunks::next_back`
-            let (head, tail) = unsafe { tmp.split_at_mut_unchecked(sz) };
+            let (head, tail) = unsafe { self.v.split_at_mut_unchecked(sz) };
             self.v = tail;
-            Some(head)
+            // SAFETY: Nothing else points to or will point to the contents of this slice.
+            Some(unsafe { &mut *head })
         }
     }
 
@@ -2768,10 +2805,13 @@ impl<'a, T> DoubleEndedIterator for RChunksMut<'a, T> {
             let offset_from_end = (len - 1 - n) * self.chunk_size;
             let end = self.v.len() - offset_from_end;
             let start = end.saturating_sub(self.chunk_size);
-            let (tmp, tail) = mem::replace(&mut self.v, &mut []).split_at_mut(end);
-            let (_, nth_back) = tmp.split_at_mut(start);
+            // SAFETY: The self.v contract ensures that any split_at_mut is valid.
+            let (tmp, tail) = unsafe { self.v.split_at_mut(end) };
+            // SAFETY: The self.v contract ensures that any split_at_mut is valid.
+            let (_, nth_back) = unsafe { tmp.split_at_mut(start) };
             self.v = tail;
-            Some(nth_back)
+            // SAFETY: Nothing else points to or will point to the contents of this slice.
+            Some(unsafe { &mut *nth_back })
         }
     }
 }
@@ -2897,8 +2937,7 @@ impl<'a, T> Iterator for RChunksExact<'a, T> {
     unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
         let end = self.v.len() - idx * self.chunk_size;
         let start = end - self.chunk_size;
-        // SAFETY:
-        // SAFETY: mostmy identical to `Chunks::__iterator_get_unchecked`.
+        // SAFETY: mostly identical to `Chunks::__iterator_get_unchecked`.
         unsafe { from_raw_parts(self.v.as_ptr().add(start), self.chunk_size) }
     }
 }
@@ -2981,7 +3020,13 @@ unsafe impl<'a, T> TrustedRandomAccessNoCoerce for RChunksExact<'a, T> {
 #[stable(feature = "rchunks", since = "1.31.0")]
 #[must_use = "iterators are lazy and do nothing unless consumed"]
 pub struct RChunksExactMut<'a, T: 'a> {
-    v: &'a mut [T],
+    /// # Safety
+    /// This slice pointer must point at a valid region of `T` with at least length `v.len()`. Normally,
+    /// those requirements would mean that we could instead use a `&mut [T]` here, but we cannot
+    /// because `__iterator_get_unchecked` needs to return `&mut [T]`, which guarantees certain aliasing
+    /// properties that we cannot uphold if we hold on to the full original `&mut [T]`. Wrapping a raw
+    /// slice instead lets us hand out non-overlapping `&mut [T]` subslices of the slice we wrap.
+    v: *mut [T],
     rem: &'a mut [T],
     chunk_size: usize,
 }
@@ -3014,11 +3059,12 @@ impl<'a, T> Iterator for RChunksExactMut<'a, T> {
         if self.v.len() < self.chunk_size {
             None
         } else {
-            let tmp = mem::replace(&mut self.v, &mut []);
-            let tmp_len = tmp.len();
-            let (head, tail) = tmp.split_at_mut(tmp_len - self.chunk_size);
+            let len = self.v.len();
+            // SAFETY: The self.v contract ensures that any split_at_mut is valid.
+            let (head, tail) = unsafe { self.v.split_at_mut(len - self.chunk_size) };
             self.v = head;
-            Some(tail)
+            // SAFETY: Nothing else points to or will point to the contents of this slice.
+            Some(unsafe { &mut *tail })
         }
     }
 
@@ -3040,9 +3086,9 @@ impl<'a, T> Iterator for RChunksExactMut<'a, T> {
             self.v = &mut [];
             None
         } else {
-            let tmp = mem::replace(&mut self.v, &mut []);
-            let tmp_len = tmp.len();
-            let (fst, _) = tmp.split_at_mut(tmp_len - end);
+            let len = self.v.len();
+            // SAFETY: The self.v contract ensures that any split_at_mut is valid.
+            let (fst, _) = unsafe { self.v.split_at_mut(len - end) };
             self.v = fst;
             self.next()
         }
@@ -3056,7 +3102,7 @@ impl<'a, T> Iterator for RChunksExactMut<'a, T> {
     unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
         let end = self.v.len() - idx * self.chunk_size;
         let start = end - self.chunk_size;
-        // SAFETY: see comments for `RChunksMut::__iterator_get_unchecked`.
+        // SAFETY: see comments for `RChunksMut::__iterator_get_unchecked` and `self.v`.
         unsafe { from_raw_parts_mut(self.v.as_mut_ptr().add(start), self.chunk_size) }
     }
 }
@@ -3068,10 +3114,11 @@ impl<'a, T> DoubleEndedIterator for RChunksExactMut<'a, T> {
         if self.v.len() < self.chunk_size {
             None
         } else {
-            let tmp = mem::replace(&mut self.v, &mut []);
-            let (head, tail) = tmp.split_at_mut(self.chunk_size);
+            // SAFETY: The self.v contract ensures that any split_at_mut is valid.
+            let (head, tail) = unsafe { self.v.split_at_mut(self.chunk_size) };
             self.v = tail;
-            Some(head)
+            // SAFETY: Nothing else points to or will point to the contents of this slice.
+            Some(unsafe { &mut *head })
         }
     }
 
@@ -3087,10 +3134,13 @@ impl<'a, T> DoubleEndedIterator for RChunksExactMut<'a, T> {
             let offset = (len - n) * self.chunk_size;
             let start = self.v.len() - offset;
             let end = start + self.chunk_size;
-            let (tmp, tail) = mem::replace(&mut self.v, &mut []).split_at_mut(end);
-            let (_, nth_back) = tmp.split_at_mut(start);
+            // SAFETY: The self.v contract ensures that any split_at_mut is valid.
+            let (tmp, tail) = unsafe { self.v.split_at_mut(end) };
+            // SAFETY: The self.v contract ensures that any split_at_mut is valid.
+            let (_, nth_back) = unsafe { tmp.split_at_mut(start) };
             self.v = tail;
-            Some(nth_back)
+            // SAFETY: Nothing else points to or will point to the contents of this slice.
+            Some(unsafe { &mut *nth_back })
         }
     }
 }
diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs
index 5751a91721d..6d1516958f3 100644
--- a/library/core/tests/slice.rs
+++ b/library/core/tests/slice.rs
@@ -410,6 +410,50 @@ fn test_chunks_mut_zip() {
 }
 
 #[test]
+fn test_chunks_mut_zip_aliasing() {
+    let v1: &mut [i32] = &mut [0, 1, 2, 3, 4];
+    let v2: &[i32] = &[6, 7, 8, 9, 10];
+
+    let mut it = v1.chunks_mut(2).zip(v2.chunks(2));
+    let first = it.next().unwrap();
+    let _ = it.next().unwrap();
+    assert_eq!(first, (&mut [0, 1][..], &[6, 7][..]));
+}
+
+#[test]
+fn test_chunks_exact_mut_zip_aliasing() {
+    let v1: &mut [i32] = &mut [0, 1, 2, 3, 4];
+    let v2: &[i32] = &[6, 7, 8, 9, 10];
+
+    let mut it = v1.chunks_exact_mut(2).zip(v2.chunks(2));
+    let first = it.next().unwrap();
+    let _ = it.next().unwrap();
+    assert_eq!(first, (&mut [0, 1][..], &[6, 7][..]));
+}
+
+#[test]
+fn test_rchunks_mut_zip_aliasing() {
+    let v1: &mut [i32] = &mut [0, 1, 2, 3, 4];
+    let v2: &[i32] = &[6, 7, 8, 9, 10];
+
+    let mut it = v1.rchunks_mut(2).zip(v2.chunks(2));
+    let first = it.next().unwrap();
+    let _ = it.next().unwrap();
+    assert_eq!(first, (&mut [3, 4][..], &[6, 7][..]));
+}
+
+#[test]
+fn test_rchunks_exact_mut_zip_aliasing() {
+    let v1: &mut [i32] = &mut [0, 1, 2, 3, 4];
+    let v2: &[i32] = &[6, 7, 8, 9, 10];
+
+    let mut it = v1.rchunks_exact_mut(2).zip(v2.chunks(2));
+    let first = it.next().unwrap();
+    let _ = it.next().unwrap();
+    assert_eq!(first, (&mut [3, 4][..], &[6, 7][..]));
+}
+
+#[test]
 fn test_chunks_exact_count() {
     let v: &[i32] = &[0, 1, 2, 3, 4, 5];
     let c = v.chunks_exact(3);
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index 02515771349..aecc9aa879a 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -167,7 +167,7 @@ h2, h3, h4 {
 h2,
 .top-doc .docblock > h3,
 .top-doc .docblock > h4 {
-	border-bottom: 1px solid;
+	border-bottom: 1px solid var(--headings-border-bottom-color);
 }
 h3.code-header {
 	font-size: 1.125rem; /* 18px */
@@ -652,7 +652,7 @@ h2.location a {
 }
 
 .top-doc .docblock h2 { font-size: 1.375rem; }
-.top-doc .docblock h3 { font-size: 1.25; }
+.top-doc .docblock h3 { font-size: 1.25rem; }
 .top-doc .docblock h4,
 .top-doc .docblock h5 {
 	font-size: 1.125rem;
diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css
index 62d9eaa02e6..a550eb1c130 100644
--- a/src/librustdoc/html/static/css/themes/dark.css
+++ b/src/librustdoc/html/static/css/themes/dark.css
@@ -42,10 +42,6 @@ input:focus + .slider {
 	background-color: #0a042f !important;
 }
 
-.docblock h1, .docblock h2, .docblock h3, .docblock h4, .docblock h5, .docblock h6 {
-	border-bottom-color: #DDD;
-}
-
 .docblock table td, .docblock table th {
 	border-color: #ddd;
 }
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 7429f2b6ab1..3a96884d45d 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -788,7 +788,7 @@ fn main_options(options: config::Options) -> MainResult {
         if sess.opts.describe_lints {
             let mut lint_store = rustc_lint::new_lint_store(
                 sess.opts.unstable_opts.no_interleave_lints,
-                sess.unstable_options(),
+                sess.enable_internal_lints(),
             );
             let registered_lints = if let Some(register_lints) = compiler.register_lints() {
                 register_lints(sess, &mut lint_store);
diff --git a/src/test/mir-opt/issue-99325.rs b/src/test/mir-opt/issue-99325.rs
new file mode 100644
index 00000000000..b79946ea8b5
--- /dev/null
+++ b/src/test/mir-opt/issue-99325.rs
@@ -0,0 +1,12 @@
+#![feature(adt_const_params)]
+#![allow(incomplete_features)]
+
+pub fn function_with_bytes<const BYTES: &'static [u8; 4]>() -> &'static [u8] {
+    BYTES
+}
+
+// EMIT_MIR issue_99325.main.mir_map.0.mir
+pub fn main() {
+    assert_eq!(function_with_bytes::<b"AAAA">(), &[0x41, 0x41, 0x41, 0x41]);
+    assert_eq!(function_with_bytes::<{ &[0x41, 0x41, 0x41, 0x41] }>(), b"AAAA");
+}
diff --git a/src/test/mir-opt/issue_99325.main.mir_map.0.mir b/src/test/mir-opt/issue_99325.main.mir_map.0.mir
new file mode 100644
index 00000000000..175f244aa5d
--- /dev/null
+++ b/src/test/mir-opt/issue_99325.main.mir_map.0.mir
@@ -0,0 +1,295 @@
+// MIR for `main` 0 mir_map
+
+| User Type Annotations
+| 0: user_ty: Canonical { max_universe: U0, variables: [], value: TypeOf(DefId(0:3 ~ issue_99325[8f58]::function_with_bytes), UserSubsts { substs: [Const { ty: &'static [u8; 4], kind: Value(Branch([Leaf(0x41), Leaf(0x41), Leaf(0x41), Leaf(0x41)])) }], user_self_ty: None }) }, span: $DIR/issue-99325.rs:10:16: 10:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}
+| 1: user_ty: Canonical { max_universe: U0, variables: [], value: TypeOf(DefId(0:3 ~ issue_99325[8f58]::function_with_bytes), UserSubsts { substs: [Const { ty: &'static [u8; 4], kind: Unevaluated(Unevaluated { def: WithOptConstParam { did: DefId(0:8 ~ issue_99325[8f58]::main::{constant#1}), const_param_did: Some(DefId(0:4 ~ issue_99325[8f58]::function_with_bytes::BYTES)) }, substs: [], promoted: None }) }], user_self_ty: None }) }, span: $DIR/issue-99325.rs:11:16: 11:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}
+|
+fn main() -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/issue-99325.rs:9:15: 9:15
+    let _1: ();                          // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _2: (&&[u8], &&[u8; 4]);     // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _3: &&[u8];                  // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let _4: &[u8];                       // in scope 0 at $DIR/issue-99325.rs:10:16: 10:48
+    let mut _5: &&[u8; 4];               // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let _6: &[u8; 4];                    // in scope 0 at $DIR/issue-99325.rs:10:50: 10:75
+    let _7: [u8; 4];                     // in scope 0 at $DIR/issue-99325.rs:10:51: 10:75
+    let _8: &&[u8];                      // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let _9: &&[u8; 4];                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _10: bool;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _11: bool;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _12: &&[u8];                 // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _13: &&[u8; 4];              // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _14: !;                      // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let _16: !;                          // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _17: core::panicking::AssertKind; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _18: &&[u8];                 // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let _19: &&[u8];                     // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _20: &&[u8; 4];              // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let _21: &&[u8; 4];                  // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _22: std::option::Option<std::fmt::Arguments>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let _23: ();                         // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _24: (&&[u8], &&[u8; 4]);    // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _25: &&[u8];                 // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let _26: &[u8];                      // in scope 0 at $DIR/issue-99325.rs:11:16: 11:70
+    let mut _27: &&[u8; 4];              // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let _28: &[u8; 4];                   // in scope 0 at $DIR/issue-99325.rs:11:72: 11:79
+    let _29: &&[u8];                     // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let _30: &&[u8; 4];                  // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _31: bool;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _32: bool;                   // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _33: &&[u8];                 // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _34: &&[u8; 4];              // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _35: !;                      // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let _37: !;                          // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _38: core::panicking::AssertKind; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _39: &&[u8];                 // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let _40: &&[u8];                     // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _41: &&[u8; 4];              // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let _42: &&[u8; 4];                  // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    let mut _43: std::option::Option<std::fmt::Arguments>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    scope 1 {
+        debug left_val => _8;            // in scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        debug right_val => _9;           // in scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        let _15: core::panicking::AssertKind; // in scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        scope 2 {
+            debug kind => _15;           // in scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        }
+    }
+    scope 3 {
+        debug left_val => _29;           // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        debug right_val => _30;          // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        let _36: core::panicking::AssertKind; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        scope 4 {
+            debug kind => _36;           // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        }
+    }
+
+    bb0: {
+        StorageLive(_1);                 // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_2);                 // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_3);                 // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_4);                 // scope 0 at $DIR/issue-99325.rs:10:16: 10:48
+        _4 = function_with_bytes::<&*b"AAAA">() -> [return: bb1, unwind: bb19]; // scope 0 at $DIR/issue-99325.rs:10:16: 10:48
+                                         // mir::Constant
+                                         // + span: $DIR/issue-99325.rs:10:16: 10:46
+                                         // + user_ty: UserType(0)
+                                         // + literal: Const { ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}, val: Value(<ZST>) }
+    }
+
+    bb1: {
+        _3 = &_4;                        // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_5);                 // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_6);                 // scope 0 at $DIR/issue-99325.rs:10:50: 10:75
+        StorageLive(_7);                 // scope 0 at $DIR/issue-99325.rs:10:51: 10:75
+        _7 = [const 65_u8, const 65_u8, const 65_u8, const 65_u8]; // scope 0 at $DIR/issue-99325.rs:10:51: 10:75
+        _6 = &_7;                        // scope 0 at $DIR/issue-99325.rs:10:50: 10:75
+        _5 = &_6;                        // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _2 = (move _3, move _5);         // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_5);                 // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_3);                 // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        FakeRead(ForMatchedPlace(None), _2); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_8);                 // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _8 = (_2.0: &&[u8]);             // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_9);                 // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _9 = (_2.1: &&[u8; 4]);          // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_10);                // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_11);                // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_12);                // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _12 = &(*_8);                    // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_13);                // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _13 = &(*_9);                    // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _11 = <&[u8] as PartialEq<&[u8; 4]>>::eq(move _12, move _13) -> [return: bb2, unwind: bb19]; // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+                                         // mir::Constant
+                                         // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
+                                         // + literal: Const { ty: for<'r, 's> fn(&'r &[u8], &'s &[u8; 4]) -> bool {<&[u8] as PartialEq<&[u8; 4]>>::eq}, val: Value(<ZST>) }
+    }
+
+    bb2: {
+        StorageDead(_13);                // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_12);                // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _10 = Not(move _11);             // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_11);                // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        switchInt(move _10) -> [false: bb4, otherwise: bb3]; // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    }
+
+    bb3: {
+        StorageLive(_15);                // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _15 = core::panicking::AssertKind::Eq; // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        FakeRead(ForLet(None), _15);     // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_16);                // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_17);                // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _17 = move _15;                  // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_18);                // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_19);                // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _19 = &(*_8);                    // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _18 = &(*_19);                   // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_20);                // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_21);                // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _21 = &(*_9);                    // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _20 = &(*_21);                   // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_22);                // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _22 = Option::<Arguments>::None; // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _16 = core::panicking::assert_failed::<&[u8], &[u8; 4]>(move _17, move _18, move _20, move _22) -> bb19; // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+                                         // mir::Constant
+                                         // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
+                                         // + literal: Const { ty: for<'r, 's, 't0> fn(core::panicking::AssertKind, &'r &[u8], &'s &[u8; 4], Option<Arguments<'t0>>) -> ! {core::panicking::assert_failed::<&[u8], &[u8; 4]>}, val: Value(<ZST>) }
+    }
+
+    bb4: {
+        goto -> bb7;                     // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    }
+
+    bb5: {
+        StorageDead(_22);                // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_20);                // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_18);                // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_17);                // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_21);                // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_19);                // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_16);                // scope 2 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_15);                // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        unreachable;                     // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    }
+
+    bb6: {
+        goto -> bb8;                     // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    }
+
+    bb7: {
+        _1 = const ();                   // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        goto -> bb8;                     // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    }
+
+    bb8: {
+        StorageDead(_10);                // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_9);                 // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_8);                 // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        goto -> bb9;                     // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    }
+
+    bb9: {
+        StorageDead(_7);                 // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_6);                 // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_4);                 // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_2);                 // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_1);                 // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_23);                // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_24);                // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_25);                // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_26);                // scope 0 at $DIR/issue-99325.rs:11:16: 11:70
+        _26 = function_with_bytes::<&*b"AAAA">() -> [return: bb10, unwind: bb19]; // scope 0 at $DIR/issue-99325.rs:11:16: 11:70
+                                         // mir::Constant
+                                         // + span: $DIR/issue-99325.rs:11:16: 11:68
+                                         // + user_ty: UserType(1)
+                                         // + literal: Const { ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}, val: Value(<ZST>) }
+    }
+
+    bb10: {
+        _25 = &_26;                      // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_27);                // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_28);                // scope 0 at $DIR/issue-99325.rs:11:72: 11:79
+        _28 = const b"AAAA";             // scope 0 at $DIR/issue-99325.rs:11:72: 11:79
+                                         // mir::Constant
+                                         // + span: $DIR/issue-99325.rs:11:72: 11:79
+                                         // + literal: Const { ty: &[u8; 4], val: Value(Scalar(alloc4)) }
+        _27 = &_28;                      // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _24 = (move _25, move _27);      // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_27);                // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_25);                // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        FakeRead(ForMatchedPlace(None), _24); // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_29);                // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _29 = (_24.0: &&[u8]);           // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_30);                // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _30 = (_24.1: &&[u8; 4]);        // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_31);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_32);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_33);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _33 = &(*_29);                   // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_34);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _34 = &(*_30);                   // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _32 = <&[u8] as PartialEq<&[u8; 4]>>::eq(move _33, move _34) -> [return: bb11, unwind: bb19]; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+                                         // mir::Constant
+                                         // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
+                                         // + literal: Const { ty: for<'r, 's> fn(&'r &[u8], &'s &[u8; 4]) -> bool {<&[u8] as PartialEq<&[u8; 4]>>::eq}, val: Value(<ZST>) }
+    }
+
+    bb11: {
+        StorageDead(_34);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_33);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _31 = Not(move _32);             // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_32);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        switchInt(move _31) -> [false: bb13, otherwise: bb12]; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    }
+
+    bb12: {
+        StorageLive(_36);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _36 = core::panicking::AssertKind::Eq; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        FakeRead(ForLet(None), _36);     // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_37);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_38);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _38 = move _36;                  // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_39);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_40);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _40 = &(*_29);                   // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _39 = &(*_40);                   // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_41);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_42);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _42 = &(*_30);                   // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _41 = &(*_42);                   // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageLive(_43);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _43 = Option::<Arguments>::None; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _37 = core::panicking::assert_failed::<&[u8], &[u8; 4]>(move _38, move _39, move _41, move _43) -> bb19; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+                                         // mir::Constant
+                                         // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
+                                         // + literal: Const { ty: for<'r, 's, 't0> fn(core::panicking::AssertKind, &'r &[u8], &'s &[u8; 4], Option<Arguments<'t0>>) -> ! {core::panicking::assert_failed::<&[u8], &[u8; 4]>}, val: Value(<ZST>) }
+    }
+
+    bb13: {
+        goto -> bb16;                    // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    }
+
+    bb14: {
+        StorageDead(_43);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_41);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_39);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_38);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_42);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_40);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_37);                // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_36);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        unreachable;                     // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    }
+
+    bb15: {
+        goto -> bb17;                    // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    }
+
+    bb16: {
+        _23 = const ();                  // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        goto -> bb17;                    // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    }
+
+    bb17: {
+        StorageDead(_31);                // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_30);                // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_29);                // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        goto -> bb18;                    // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+    }
+
+    bb18: {
+        StorageDead(_28);                // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_26);                // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_24);                // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        StorageDead(_23);                // scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+        _0 = const ();                   // scope 0 at $DIR/issue-99325.rs:9:15: 12:2
+        return;                          // scope 0 at $DIR/issue-99325.rs:12:2: 12:2
+    }
+
+    bb19 (cleanup): {
+        resume;                          // scope 0 at $DIR/issue-99325.rs:9:1: 12:2
+    }
+}
+
+alloc4 (size: 4, align: 1) {
+    41 41 41 41                                     │ AAAA
+}
diff --git a/src/test/rustdoc-gui/docblock-details.goml b/src/test/rustdoc-gui/docblock-details.goml
index 2edbf1e4e2d..f6287ade2f2 100644
--- a/src/test/rustdoc-gui/docblock-details.goml
+++ b/src/test/rustdoc-gui/docblock-details.goml
@@ -9,12 +9,12 @@ reload:
 assert-text: (".top-doc .docblock > h3", "Hello")
 assert-css: (
     ".top-doc .docblock > h3",
-    {"border-bottom": "1px solid rgb(221, 221, 221)"},
+    {"border-bottom": "1px solid rgb(210, 210, 210)"},
 )
 // We now check that the `<summary>` doesn't have a bottom border and has the correct display.
 assert-css: (
     ".top-doc .docblock summary h4",
-    {"border-bottom": "0px none rgb(221, 221, 221)"},
+    {"border-bottom": "0px none rgb(210, 210, 210)"},
 )
 // This allows to ensure that summary is on one line only!
 assert-property: (".top-doc .docblock summary h4", {"offsetHeight": "33"})
diff --git a/src/test/rustdoc-gui/headings.goml b/src/test/rustdoc-gui/headings.goml
index cc3dd61e99a..8c2c3df1588 100644
--- a/src/test/rustdoc-gui/headings.goml
+++ b/src/test/rustdoc-gui/headings.goml
@@ -150,15 +150,109 @@ assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"})
 assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "20px"})
 assert-css: ("h3#top-doc-prose-sub-heading", {"border-bottom-width": "1px"})
 
-goto: file://|DOC_PATH|/staged_api/struct.Foo.html
+// Checking colors now.
 show-text: true
 local-storage: {"rustdoc-theme": "light", "rustdoc-use-system-theme": "false"}
+goto: file://|DOC_PATH|/test_docs/struct.HeavilyDocumentedStruct.html
+assert-css: (
+    ".top-doc .docblock h2",
+    {"color": "rgb(0, 0, 0)", "border-bottom": "1px solid rgb(221, 221, 221)"},
+)
+assert-css: (
+    ".top-doc .docblock h3",
+    {"color": "rgb(0, 0, 0)", "border-bottom": "1px solid rgb(221, 221, 221)"},
+)
+assert-css: (
+    ".top-doc .docblock h4",
+    {"color": "rgb(0, 0, 0)", "border-bottom": "1px solid rgb(221, 221, 221)"},
+)
+assert-css: (
+    ".top-doc .docblock h5",
+    {"color": "rgb(0, 0, 0)", "border-bottom": "0px none rgb(221, 221, 221)"},
+)
+assert-css: (
+    "#implementations-list .docblock h4",
+    {"color": "rgb(0, 0, 0)", "border-bottom": "0px none rgb(221, 221, 221)"},
+)
+assert-css: (
+    "#implementations-list .docblock h5",
+    {"color": "rgb(0, 0, 0)", "border-bottom": "0px none rgb(221, 221, 221)"},
+)
+assert-css: (
+    "#implementations-list .docblock h6",
+    {"color": "rgb(0, 0, 0)", "border-bottom": "0px none rgb(221, 221, 221)"},
+)
+
+local-storage: {"rustdoc-theme": "dark"}
+reload:
+assert-css: (
+    ".top-doc .docblock h2",
+    {"color": "rgb(221, 221, 221)", "border-bottom": "1px solid rgb(210, 210, 210)"},
+)
+assert-css: (
+    ".top-doc .docblock h3",
+    {"color": "rgb(221, 221, 221)", "border-bottom": "1px solid rgb(210, 210, 210)"},
+)
+assert-css: (
+    ".top-doc .docblock h4",
+    {"color": "rgb(221, 221, 221)", "border-bottom": "1px solid rgb(210, 210, 210)"},
+)
+assert-css: (
+    ".top-doc .docblock h5",
+    {"color": "rgb(221, 221, 221)", "border-bottom": "0px none rgb(210, 210, 210)"},
+)
+assert-css: (
+    "#implementations-list .docblock h4",
+    {"color": "rgb(221, 221, 221)", "border-bottom": "0px none rgb(210, 210, 210)"},
+)
+assert-css: (
+    "#implementations-list .docblock h5",
+    {"color": "rgb(221, 221, 221)", "border-bottom": "0px none rgb(210, 210, 210)"},
+)
+assert-css: (
+    "#implementations-list .docblock h6",
+    {"color": "rgb(221, 221, 221)", "border-bottom": "0px none rgb(210, 210, 210)"},
+)
+
+local-storage: {"rustdoc-theme": "ayu"}
+reload:
+assert-css: (
+    ".top-doc .docblock h2",
+    {"color": "rgb(255, 255, 255)", "border-bottom": "1px solid rgb(92, 103, 115)"},
+)
+assert-css: (
+    ".top-doc .docblock h2",
+    {"color": "rgb(255, 255, 255)", "border-bottom": "1px solid rgb(92, 103, 115)"},
+)
+assert-css: (
+    ".top-doc .docblock h4",
+    {"color": "rgb(255, 255, 255)", "border-bottom": "1px solid rgb(92, 103, 115)"},
+)
+assert-css: (
+    ".top-doc .docblock h5",
+    {"color": "rgb(197, 197, 197)", "border-bottom": "0px none rgb(92, 103, 115)"},
+)
+assert-css: (
+    "#implementations-list .docblock h4",
+    {"color": "rgb(255, 255, 255)", "border-bottom": "0px none rgb(92, 103, 115)"},
+)
+assert-css: (
+    "#implementations-list .docblock h5",
+    {"color": "rgb(197, 197, 197)", "border-bottom": "0px none rgb(92, 103, 115)"},
+)
+assert-css: (
+    "#implementations-list .docblock h6",
+    {"color": "rgb(197, 197, 197)", "border-bottom": "0px none rgb(92, 103, 115)"},
+)
+
+local-storage: {"rustdoc-theme": "light"}
+goto: file://|DOC_PATH|/staged_api/struct.Foo.html
 assert-css: (".since", {"color": "rgb(128, 128, 128)"})
 
-local-storage: {"rustdoc-theme": "dark", "rustdoc-use-system-theme": "false"}
+local-storage: {"rustdoc-theme": "dark"}
 reload:
 assert-css: (".since", {"color": "rgb(128, 128, 128)"})
 
-local-storage: {"rustdoc-theme": "ayu", "rustdoc-use-system-theme": "false"}
+local-storage: {"rustdoc-theme": "ayu"}
 reload:
 assert-css: (".since", {"color": "rgb(128, 128, 128)"})
diff --git a/src/test/rustdoc-gui/src/test_docs/lib.rs b/src/test/rustdoc-gui/src/test_docs/lib.rs
index aa2f78289be..1b26aaecb5e 100644
--- a/src/test/rustdoc-gui/src/test_docs/lib.rs
+++ b/src/test/rustdoc-gui/src/test_docs/lib.rs
@@ -146,6 +146,10 @@ pub use crate::repro as repro2;
 /// ### Top-doc Prose sub-sub-heading
 ///
 /// Text below sub-sub-heading
+///
+/// #### You know the drill.
+///
+/// More text.
 pub struct HeavilyDocumentedStruct {
     /// # Title for field
     /// ## Sub-heading for field
diff --git a/src/test/ui-fulldeps/internal-lints/bad_opt_access.rs b/src/test/ui-fulldeps/internal-lints/bad_opt_access.rs
new file mode 100644
index 00000000000..d6bd6945e15
--- /dev/null
+++ b/src/test/ui-fulldeps/internal-lints/bad_opt_access.rs
@@ -0,0 +1,22 @@
+// compile-flags: -Z unstable-options
+
+// Test that accessing command line options by field access triggers a lint for those fields
+// that have wrapper functions which should be used.
+
+#![crate_type = "lib"]
+#![feature(rustc_private)]
+#![deny(rustc::bad_opt_access)]
+
+extern crate rustc_session;
+use rustc_session::Session;
+
+pub fn access_bad_option(sess: Session) {
+    let _ = sess.opts.cg.split_debuginfo;
+    //~^ ERROR use `Session::split_debuginfo` instead of this field
+
+    let _ = sess.opts.crate_types;
+    //~^ ERROR use `Session::crate_types` instead of this field
+
+    let _ = sess.opts.crate_name;
+    // okay!
+}
diff --git a/src/test/ui-fulldeps/internal-lints/bad_opt_access.stderr b/src/test/ui-fulldeps/internal-lints/bad_opt_access.stderr
new file mode 100644
index 00000000000..e4145bff8be
--- /dev/null
+++ b/src/test/ui-fulldeps/internal-lints/bad_opt_access.stderr
@@ -0,0 +1,20 @@
+error: use `Session::split_debuginfo` instead of this field
+  --> $DIR/bad_opt_access.rs:14:13
+   |
+LL |     let _ = sess.opts.cg.split_debuginfo;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/bad_opt_access.rs:8:9
+   |
+LL | #![deny(rustc::bad_opt_access)]
+   |         ^^^^^^^^^^^^^^^^^^^^^
+
+error: use `Session::crate_types` instead of this field
+  --> $DIR/bad_opt_access.rs:17:13
+   |
+LL |     let _ = sess.opts.crate_types;
+   |             ^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/const-generics/fn-const-param-call.full.stderr b/src/test/ui/const-generics/fn-const-param-call.full.stderr
index d984449e6ca..b55c2449858 100644
--- a/src/test/ui/const-generics/fn-const-param-call.full.stderr
+++ b/src/test/ui/const-generics/fn-const-param-call.full.stderr
@@ -1,10 +1,10 @@
-error: using function pointers as const generic parameters is forbidden
+error[E0741]: using function pointers as const generic parameters is forbidden
   --> $DIR/fn-const-param-call.rs:11:25
    |
 LL | struct Wrapper<const F: fn() -> u32>;
    |                         ^^^^^^^^^^^
 
-error: using function pointers as const generic parameters is forbidden
+error[E0741]: using function pointers as const generic parameters is forbidden
   --> $DIR/fn-const-param-call.rs:13:15
    |
 LL | impl<const F: fn() -> u32> Wrapper<F> {
@@ -12,3 +12,4 @@ LL | impl<const F: fn() -> u32> Wrapper<F> {
 
 error: aborting due to 2 previous errors
 
+For more information about this error, try `rustc --explain E0741`.
diff --git a/src/test/ui/const-generics/fn-const-param-infer.full.stderr b/src/test/ui/const-generics/fn-const-param-infer.full.stderr
index f0767a10994..2d66a192332 100644
--- a/src/test/ui/const-generics/fn-const-param-infer.full.stderr
+++ b/src/test/ui/const-generics/fn-const-param-infer.full.stderr
@@ -1,4 +1,4 @@
-error: using function pointers as const generic parameters is forbidden
+error[E0741]: using function pointers as const generic parameters is forbidden
   --> $DIR/fn-const-param-infer.rs:6:25
    |
 LL | struct Checked<const F: fn(usize) -> bool>;
@@ -6,3 +6,4 @@ LL | struct Checked<const F: fn(usize) -> bool>;
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0741`.
diff --git a/src/test/ui/const-generics/issues/issue-71381.full.stderr b/src/test/ui/const-generics/issues/issue-71381.full.stderr
index 3950317b370..e17cf96aa3e 100644
--- a/src/test/ui/const-generics/issues/issue-71381.full.stderr
+++ b/src/test/ui/const-generics/issues/issue-71381.full.stderr
@@ -10,13 +10,13 @@ error[E0770]: the type of const parameters must not depend on other generic para
 LL |         const FN: unsafe extern "C" fn(Args),
    |                                        ^^^^ the type must not depend on the parameter `Args`
 
-error: using function pointers as const generic parameters is forbidden
+error[E0741]: using function pointers as const generic parameters is forbidden
   --> $DIR/issue-71381.rs:14:61
    |
 LL |     pub fn call_me<Args: Sized, const IDX: usize, const FN: unsafe extern "C" fn(Args)>(&self) {
    |                                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: using function pointers as const generic parameters is forbidden
+error[E0741]: using function pointers as const generic parameters is forbidden
   --> $DIR/issue-71381.rs:23:19
    |
 LL |         const FN: unsafe extern "C" fn(Args),
@@ -24,4 +24,5 @@ LL |         const FN: unsafe extern "C" fn(Args),
 
 error: aborting due to 4 previous errors
 
-For more information about this error, try `rustc --explain E0770`.
+Some errors have detailed explanations: E0741, E0770.
+For more information about an error, try `rustc --explain E0741`.
diff --git a/src/test/ui/const-generics/issues/issue-71382.full.stderr b/src/test/ui/const-generics/issues/issue-71382.full.stderr
index 715037bd5f1..ab2a4e64a83 100644
--- a/src/test/ui/const-generics/issues/issue-71382.full.stderr
+++ b/src/test/ui/const-generics/issues/issue-71382.full.stderr
@@ -1,4 +1,4 @@
-error: using function pointers as const generic parameters is forbidden
+error[E0741]: using function pointers as const generic parameters is forbidden
   --> $DIR/issue-71382.rs:16:23
    |
 LL |     fn test<const FN: fn()>(&self) {
@@ -6,3 +6,4 @@ LL |     fn test<const FN: fn()>(&self) {
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0741`.
diff --git a/src/test/ui/const-generics/issues/issue-71611.full.stderr b/src/test/ui/const-generics/issues/issue-71611.full.stderr
index 01a85b745ce..656aa29e19b 100644
--- a/src/test/ui/const-generics/issues/issue-71611.full.stderr
+++ b/src/test/ui/const-generics/issues/issue-71611.full.stderr
@@ -4,7 +4,7 @@ error[E0770]: the type of const parameters must not depend on other generic para
 LL | fn func<A, const F: fn(inner: A)>(outer: A) {
    |                               ^ the type must not depend on the parameter `A`
 
-error: using function pointers as const generic parameters is forbidden
+error[E0741]: using function pointers as const generic parameters is forbidden
   --> $DIR/issue-71611.rs:5:21
    |
 LL | fn func<A, const F: fn(inner: A)>(outer: A) {
@@ -12,4 +12,5 @@ LL | fn func<A, const F: fn(inner: A)>(outer: A) {
 
 error: aborting due to 2 previous errors
 
-For more information about this error, try `rustc --explain E0770`.
+Some errors have detailed explanations: E0741, E0770.
+For more information about an error, try `rustc --explain E0741`.
diff --git a/src/test/ui/const-generics/issues/issue-72352.full.stderr b/src/test/ui/const-generics/issues/issue-72352.full.stderr
index eedd73c4dcc..92580b33685 100644
--- a/src/test/ui/const-generics/issues/issue-72352.full.stderr
+++ b/src/test/ui/const-generics/issues/issue-72352.full.stderr
@@ -1,4 +1,4 @@
-error: using function pointers as const generic parameters is forbidden
+error[E0741]: using function pointers as const generic parameters is forbidden
   --> $DIR/issue-72352.rs:7:42
    |
 LL | unsafe fn unsafely_do_the_thing<const F: fn(&CStr) -> usize>(ptr: *const i8) -> usize {
@@ -6,3 +6,4 @@ LL | unsafe fn unsafely_do_the_thing<const F: fn(&CStr) -> usize>(ptr: *const i8
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0741`.
diff --git a/src/test/ui/const-generics/issues/issue-99641.rs b/src/test/ui/const-generics/issues/issue-99641.rs
new file mode 100644
index 00000000000..fae6d3fc41f
--- /dev/null
+++ b/src/test/ui/const-generics/issues/issue-99641.rs
@@ -0,0 +1,18 @@
+#![feature(adt_const_params)]
+#![allow(incomplete_features)]
+
+fn main() {
+    pub struct Color<const WHITE: (fn(),)>;
+    //~^ ERROR using function pointers
+
+    impl<const WHITE: (fn(),)> Color<WHITE> {
+        //~^ ERROR using function pointers
+        pub fn new() -> Self {
+            Color::<WHITE>
+        }
+    }
+
+    pub const D65: (fn(),) = (|| {},);
+
+    Color::<D65>::new();
+}
diff --git a/src/test/ui/const-generics/issues/issue-99641.stderr b/src/test/ui/const-generics/issues/issue-99641.stderr
new file mode 100644
index 00000000000..349ebba08d5
--- /dev/null
+++ b/src/test/ui/const-generics/issues/issue-99641.stderr
@@ -0,0 +1,15 @@
+error[E0741]: using function pointers as const generic parameters is forbidden
+  --> $DIR/issue-99641.rs:5:35
+   |
+LL |     pub struct Color<const WHITE: (fn(),)>;
+   |                                   ^^^^^^^
+
+error[E0741]: using function pointers as const generic parameters is forbidden
+  --> $DIR/issue-99641.rs:8:23
+   |
+LL |     impl<const WHITE: (fn(),)> Color<WHITE> {
+   |                       ^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0741`.
diff --git a/src/test/ui/const-generics/raw-ptr-const-param-deref.full.stderr b/src/test/ui/const-generics/raw-ptr-const-param-deref.full.stderr
index 04bc46cb4ab..657eee2be24 100644
--- a/src/test/ui/const-generics/raw-ptr-const-param-deref.full.stderr
+++ b/src/test/ui/const-generics/raw-ptr-const-param-deref.full.stderr
@@ -1,10 +1,10 @@
-error: using raw pointers as const generic parameters is forbidden
+error[E0741]: using raw pointers as const generic parameters is forbidden
   --> $DIR/raw-ptr-const-param-deref.rs:9:23
    |
 LL | struct Const<const P: *const u32>;
    |                       ^^^^^^^^^^
 
-error: using raw pointers as const generic parameters is forbidden
+error[E0741]: using raw pointers as const generic parameters is forbidden
   --> $DIR/raw-ptr-const-param-deref.rs:11:15
    |
 LL | impl<const P: *const u32> Const<P> {
@@ -12,3 +12,4 @@ LL | impl<const P: *const u32> Const<P> {
 
 error: aborting due to 2 previous errors
 
+For more information about this error, try `rustc --explain E0741`.
diff --git a/src/test/ui/const-generics/raw-ptr-const-param.full.stderr b/src/test/ui/const-generics/raw-ptr-const-param.full.stderr
index 310422aafcd..69f1aae5681 100644
--- a/src/test/ui/const-generics/raw-ptr-const-param.full.stderr
+++ b/src/test/ui/const-generics/raw-ptr-const-param.full.stderr
@@ -1,4 +1,4 @@
-error: using raw pointers as const generic parameters is forbidden
+error[E0741]: using raw pointers as const generic parameters is forbidden
   --> $DIR/raw-ptr-const-param.rs:6:23
    |
 LL | struct Const<const P: *const u32>;
@@ -6,3 +6,4 @@ LL | struct Const<const P: *const u32>;
 
 error: aborting due to previous error
 
+For more information about this error, try `rustc --explain E0741`.
diff --git a/src/test/ui/consts/refs_check_const_eq-issue-88384.rs b/src/test/ui/consts/refs_check_const_eq-issue-88384.rs
index 204d18ea25d..1496b28bd3e 100644
--- a/src/test/ui/consts/refs_check_const_eq-issue-88384.rs
+++ b/src/test/ui/consts/refs_check_const_eq-issue-88384.rs
@@ -1,5 +1,3 @@
-// check-pass
-
 #![feature(fn_traits)]
 #![feature(adt_const_params)]
 //~^ WARNING the feature `adt_const_params` is incomplete
@@ -10,8 +8,10 @@ struct CompileTimeSettings{
 }
 
 struct Foo<const T: CompileTimeSettings>;
+//~^ ERROR using function pointers as const generic parameters is forbidden
 
 impl<const T: CompileTimeSettings> Foo<T> {
+    //~^ ERROR using function pointers as const generic parameters is forbidden
     fn call_hooks(){
     }
 }
diff --git a/src/test/ui/consts/refs_check_const_eq-issue-88384.stderr b/src/test/ui/consts/refs_check_const_eq-issue-88384.stderr
index f2bad2f5527..4f2f5e244b6 100644
--- a/src/test/ui/consts/refs_check_const_eq-issue-88384.stderr
+++ b/src/test/ui/consts/refs_check_const_eq-issue-88384.stderr
@@ -1,5 +1,5 @@
 warning: the feature `adt_const_params` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/refs_check_const_eq-issue-88384.rs:4:12
+  --> $DIR/refs_check_const_eq-issue-88384.rs:2:12
    |
 LL | #![feature(adt_const_params)]
    |            ^^^^^^^^^^^^^^^^
@@ -7,5 +7,18 @@ LL | #![feature(adt_const_params)]
    = note: `#[warn(incomplete_features)]` on by default
    = note: see issue #95174 <https://github.com/rust-lang/rust/issues/95174> for more information
 
-warning: 1 warning emitted
+error[E0741]: using function pointers as const generic parameters is forbidden
+  --> $DIR/refs_check_const_eq-issue-88384.rs:10:21
+   |
+LL | struct Foo<const T: CompileTimeSettings>;
+   |                     ^^^^^^^^^^^^^^^^^^^
+
+error[E0741]: using function pointers as const generic parameters is forbidden
+  --> $DIR/refs_check_const_eq-issue-88384.rs:13:15
+   |
+LL | impl<const T: CompileTimeSettings> Foo<T> {
+   |               ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors; 1 warning emitted
 
+For more information about this error, try `rustc --explain E0741`.
diff --git a/src/test/ui/impl-header-lifetime-elision/constant-used-as-arraylen.rs b/src/test/ui/impl-header-lifetime-elision/constant-used-as-arraylen.rs
new file mode 100644
index 00000000000..929b82bfc43
--- /dev/null
+++ b/src/test/ui/impl-header-lifetime-elision/constant-used-as-arraylen.rs
@@ -0,0 +1,24 @@
+// check-pass
+// Verify that we do not ICE when anonymous lifetimes appear inside an AnonConst.
+
+pub struct EntriesBuffer(Box<[[u8; HashesEntry::LEN]; 5]>);
+
+impl EntriesBuffer {
+    pub fn iter_child_buffers(&mut self) -> impl Iterator<Item = &mut [u8; HashesEntry::LEN]> {
+        self.0.iter_mut()
+    }
+
+    pub fn iter_child_buffers_explicit(
+        &mut self,
+    ) -> impl Iterator<Item = &mut [u8; HashesEntry::<'_>::LEN]> {
+        self.0.iter_mut()
+    }
+}
+
+pub struct HashesEntry<'a>(&'a [u8]);
+
+impl HashesEntry<'_> {
+    pub const LEN: usize = 1;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs
index 826353aafc0..3f69cc20388 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -615,7 +615,7 @@ struct BodyLifetimeChecker {
 impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker {
     // for lifetimes as parameters of generics
     fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
-        if lifetime.name.ident().name != kw::Empty && lifetime.name.ident().name != kw::StaticLifetime {
+        if lifetime.name.ident().name != kw::UnderscoreLifetime && lifetime.name.ident().name != kw::StaticLifetime {
             self.lifetimes_used_in_body = true;
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
index 945880d2147..a0ca7e6ff1e 100644
--- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
@@ -166,7 +166,7 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName])
     // - There's only one output lifetime bound using `+ '_`
     // - All input lifetimes are explicitly bound to the output
     input_lifetimes.is_empty()
-        || (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Underscore))
+        || (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Infer))
         || input_lifetimes
             .iter()
             .all(|in_lt| output_lifetimes.iter().any(|out_lt| in_lt == out_lt))
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index 8534d8a29f1..3c5ea2d9414 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -351,7 +351,7 @@ impl fmt::Display for RefPrefix {
                 name.fmt(f)?;
                 f.write_char(' ')?;
             },
-            LifetimeName::Underscore => f.write_str("'_ ")?,
+            LifetimeName::Infer => f.write_str("'_ ")?,
             LifetimeName::Static => f.write_str("'static ")?,
             _ => (),
         }
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index c219c7de830..c1ec2bd5bd6 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -94,6 +94,8 @@ struct ClippyCallbacks {
 }
 
 impl rustc_driver::Callbacks for ClippyCallbacks {
+    // JUSTIFICATION: necessary in clippy driver to set `mir_opt_level`
+    #[cfg_attr(not(bootstrap), allow(rustc::bad_opt_access))]
     fn config(&mut self, config: &mut interface::Config) {
         let previous = config.register_lints.take();
         let clippy_args_var = self.clippy_args_var.take();