about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.mailmap2
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs16
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs8
-rw-r--r--compiler/rustc_builtin_macros/messages.ftl6
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs35
-rw-r--r--compiler/rustc_builtin_macros/src/format.rs37
-rw-r--r--compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs22
-rw-r--r--compiler/rustc_codegen_cranelift/src/abi/returning.rs24
-rw-r--r--compiler/rustc_codegen_gcc/src/abi.rs10
-rw-r--r--compiler/rustc_codegen_gcc/src/intrinsic/mod.rs10
-rw-r--r--compiler/rustc_codegen_llvm/src/abi.rs58
-rw-r--r--compiler/rustc_codegen_llvm/src/intrinsic.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs12
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/intrinsic.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/mod.rs4
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0691.md4
-rw-r--r--compiler/rustc_feature/src/active.rs2
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl12
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs58
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl14
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs65
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs46
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs8
-rw-r--r--compiler/rustc_infer/src/infer/free_regions.rs9
-rw-r--r--compiler/rustc_interface/src/util.rs20
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs11
-rw-r--r--compiler/rustc_metadata/src/fs.rs4
-rw-r--r--compiler/rustc_middle/src/infer/canonical.rs10
-rw-r--r--compiler/rustc_middle/src/infer/mod.rs2
-rw-r--r--compiler/rustc_middle/src/macros.rs4
-rw-r--r--compiler/rustc_middle/src/mir/basic_blocks.rs2
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs2
-rw-r--r--compiler/rustc_middle/src/mir/interpret/mod.rs2
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs6
-rw-r--r--compiler/rustc_middle/src/mir/query.rs2
-rw-r--r--compiler/rustc_middle/src/mir/type_foldable.rs2
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs35
-rw-r--r--compiler/rustc_middle/src/traits/query.rs21
-rw-r--r--compiler/rustc_middle/src/traits/select.rs2
-rw-r--r--compiler/rustc_middle/src/traits/solve/inspect.rs33
-rw-r--r--compiler/rustc_middle/src/traits/solve/inspect/format.rs34
-rw-r--r--compiler/rustc_middle/src/ty/abstract_const.rs2
-rw-r--r--compiler/rustc_middle/src/ty/adjustment.rs8
-rw-r--r--compiler/rustc_middle/src/ty/binding.rs2
-rw-r--r--compiler/rustc_middle/src/ty/consts/kind.rs2
-rw-r--r--compiler/rustc_middle/src/ty/context.rs10
-rw-r--r--compiler/rustc_middle/src/ty/error.rs4
-rw-r--r--compiler/rustc_middle/src/ty/generic_args.rs4
-rw-r--r--compiler/rustc_middle/src/ty/instance.rs3
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs20
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs29
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs149
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs4
-rw-r--r--compiler/rustc_middle/src/ty/typeck_results.rs4
-rw-r--r--compiler/rustc_parse_format/src/lib.rs54
-rw-r--r--compiler/rustc_passes/messages.ftl3
-rw-r--r--compiler/rustc_passes/src/check_attr.rs22
-rw-r--r--compiler/rustc_resolve/src/macros.rs5
-rw-r--r--compiler/rustc_session/src/config.rs12
-rw-r--r--compiler/rustc_session/src/output.rs19
-rw-r--r--compiler/rustc_target/src/abi/call/mod.rs58
-rw-r--r--compiler/rustc_target/src/abi/call/x86.rs6
-rw-r--r--compiler/rustc_trait_selection/messages.ftl2
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt.rs27
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs4
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect.rs138
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs10
-rw-r--r--compiler/rustc_trait_selection/src/solve/search_graph/mod.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs109
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs2
-rw-r--r--config.example.toml1
-rw-r--r--library/alloc/src/collections/vec_deque/mod.rs4
-rw-r--r--library/alloc/src/fmt.rs4
-rw-r--r--library/alloc/src/lib.rs1
-rw-r--r--library/alloc/src/macros.rs14
-rw-r--r--library/alloc/src/vec/mod.rs4
-rw-r--r--library/core/src/ascii/ascii_char.rs41
-rw-r--r--library/core/src/fmt/builders.rs8
-rw-r--r--library/core/src/fmt/mod.rs24
-rw-r--r--library/core/src/hint.rs41
-rw-r--r--library/core/src/iter/range.rs67
-rw-r--r--library/core/src/macros/mod.rs2
-rw-r--r--library/core/src/num/mod.rs3
-rw-r--r--library/core/src/num/saturating.rs437
-rw-r--r--library/core/src/primitive_docs.rs6
-rw-r--r--library/std/src/lib.rs27
-rw-r--r--library/std/src/num.rs2
-rw-r--r--library/std/src/os/unix/fs.rs2
-rw-r--r--library/std/src/primitive_docs.rs6
-rw-r--r--library/std/src/sync/mpsc/mod.rs15
-rw-r--r--library/std/src/thread/mod.rs82
-rw-r--r--src/bootstrap/README.md5
-rw-r--r--src/bootstrap/bin/_helper.rs24
-rw-r--r--src/bootstrap/bin/rustc.rs17
-rw-r--r--src/bootstrap/bin/rustdoc.rs19
-rw-r--r--src/bootstrap/bootstrap.py8
-rw-r--r--src/bootstrap/builder.rs2
-rw-r--r--src/bootstrap/compile.rs6
-rw-r--r--src/bootstrap/config.rs29
-rw-r--r--src/bootstrap/config/tests.rs14
-rw-r--r--src/bootstrap/download.rs2
-rw-r--r--src/bootstrap/lib.rs11
-rw-r--r--src/bootstrap/llvm.rs32
-rw-r--r--src/bootstrap/sanity.rs21
-rw-r--r--src/bootstrap/setup.rs1
-rw-r--r--src/bootstrap/test.rs20
-rw-r--r--src/ci/docker/host-x86_64/arm-android/Dockerfile2
-rw-r--r--src/ci/docker/host-x86_64/dist-android/Dockerfile2
-rwxr-xr-xsrc/ci/docker/host-x86_64/dist-various-1/install-x86_64-redox.sh2
-rw-r--r--src/etc/rust_analyzer_settings.json4
-rw-r--r--src/librustdoc/doctest.rs1
-rw-r--r--src/librustdoc/externalfiles.rs4
-rw-r--r--src/librustdoc/html/markdown.rs95
-rw-r--r--src/librustdoc/html/markdown/tests.rs7
-rw-r--r--src/librustdoc/html/render/mod.rs9
-rw-r--r--src/librustdoc/markdown.rs14
-rw-r--r--src/librustdoc/passes/calculate_doc_coverage.rs9
-rw-r--r--src/librustdoc/passes/check_custom_code_classes.rs31
-rw-r--r--src/librustdoc/passes/check_doc_test_visibility.rs9
-rw-r--r--src/librustdoc/passes/lint/check_code_block_syntax.rs4
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/tests/ui/arithmetic_side_effects.rs2
-rw-r--r--tests/mir-opt/issue_99325.main.built.after.32bit.mir4
-rw-r--r--tests/mir-opt/issue_99325.main.built.after.64bit.mir4
-rw-r--r--tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir2
-rw-r--r--tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir2
-rw-r--r--tests/run-make/metadata-dep-info/Makefile13
-rw-r--r--tests/run-make/metadata-dep-info/dash-separated.rs4
-rw-r--r--tests/run-make/metadata-dep-info/dash-separated_something-extra.normalized.d5
-rw-r--r--tests/rustdoc-gui/search-result-color.goml2
-rw-r--r--tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs7
-rw-r--r--tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr7
-rw-r--r--tests/ui/abi/debug.stderr4
-rw-r--r--tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.rs4
-rw-r--r--tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.stderr6
-rw-r--r--tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr2
-rw-r--r--tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs37
-rw-r--r--tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr110
-rw-r--r--tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.rs7
-rw-r--r--tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.stderr12
-rw-r--r--tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.rs11
-rw-r--r--tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.stderr24
-rw-r--r--tests/ui/error-codes/E0040.stderr10
-rw-r--r--tests/ui/explicit/explicit-call-to-dtor.stderr10
-rw-r--r--tests/ui/explicit/explicit-call-to-supertrait-dtor.stderr10
-rw-r--r--tests/ui/fmt/raw-idents.rs17
-rw-r--r--tests/ui/fmt/raw-idents.stderr44
-rw-r--r--tests/ui/range/range-1.stderr2
-rw-r--r--tests/ui/repr/repr-transparent.rs23
-rw-r--r--tests/ui/repr/repr-transparent.stderr82
-rw-r--r--tests/ui/span/send-is-not-static-std-sync.rs2
-rw-r--r--tests/ui/span/send-is-not-static-std-sync.stderr8
154 files changed, 1949 insertions, 1105 deletions
diff --git a/.mailmap b/.mailmap
index eb82cf4de8d..7d455ebd45f 100644
--- a/.mailmap
+++ b/.mailmap
@@ -549,6 +549,8 @@ Timothy Maloney <tmaloney@pdx.edu>
 Tomas Koutsky <tomas@stepnivlk.net>
 Torsten Weber <TorstenWeber12@gmail.com>
 Torsten Weber <TorstenWeber12@gmail.com> <torstenweber12@gmail.com>
+Trevor Gross <tmgross@umich.edu> <t.gross35@gmail.com>
+Trevor Gross <tmgross@umich.edu> <tgross@intrepidcs.com>
 Trevor Spiteri <tspiteri@ieee.org> <trevor.spiteri@um.edu.mt>
 Tshepang Mbambo <tshepang@gmail.com>
 Ty Overby <ty@pre-alpha.com>
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index 19a2b3017bc..db008ea139d 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -99,6 +99,22 @@ impl Attribute {
         }
     }
 
+    pub fn path_matches(&self, name: &[Symbol]) -> bool {
+        match &self.kind {
+            AttrKind::Normal(normal) => {
+                normal.item.path.segments.len() == name.len()
+                    && normal
+                        .item
+                        .path
+                        .segments
+                        .iter()
+                        .zip(name)
+                        .all(|(s, n)| s.args.is_none() && s.ident.name == *n)
+            }
+            AttrKind::DocComment(..) => false,
+        }
+    }
+
     pub fn is_word(&self) -> bool {
         if let AttrKind::Normal(normal) = &self.kind {
             matches!(normal.item.args, AttrArgs::Empty)
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index 337af89b21f..b40e89e471d 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -442,8 +442,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
         span: Span,
         counter: usize,
     ) -> RegionNameHighlight {
-        let mut highlight = RegionHighlightMode::new(self.infcx.tcx);
-        highlight.highlighting_region_vid(needle_fr, counter);
+        let mut highlight = RegionHighlightMode::default();
+        highlight.highlighting_region_vid(self.infcx.tcx, needle_fr, counter);
         let type_name =
             self.infcx.extract_inference_diagnostics_data(ty.into(), Some(highlight)).name;
 
@@ -804,8 +804,8 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
             return None;
         }
 
-        let mut highlight = RegionHighlightMode::new(tcx);
-        highlight.highlighting_region_vid(fr, *self.next_region_name.try_borrow().unwrap());
+        let mut highlight = RegionHighlightMode::default();
+        highlight.highlighting_region_vid(tcx, fr, *self.next_region_name.try_borrow().unwrap());
         let type_name =
             self.infcx.extract_inference_diagnostics_data(yield_ty.into(), Some(highlight)).name;
 
diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl
index 8d8db4c13fa..207ae8ad844 100644
--- a/compiler/rustc_builtin_macros/messages.ftl
+++ b/compiler/rustc_builtin_macros/messages.ftl
@@ -137,6 +137,8 @@ builtin_macros_format_positional_after_named = positional arguments cannot follo
     .label = positional arguments must be before named arguments
     .named_args = named argument
 
+builtin_macros_format_remove_raw_ident = remove the `r#`
+
 builtin_macros_format_requires_string = requires at least a format string argument
 
 builtin_macros_format_string_invalid = invalid format string: {$desc}
@@ -165,6 +167,8 @@ builtin_macros_format_unused_arg = {$named ->
 builtin_macros_format_unused_args = multiple unused formatting arguments
     .label = multiple missing formatting specifiers
 
+builtin_macros_format_use_positional = consider using a positional formatting argument instead
+
 builtin_macros_global_asm_clobber_abi = `clobber_abi` cannot be used with `global_asm!`
 
 builtin_macros_invalid_crate_attribute = invalid crate attribute
@@ -205,8 +209,6 @@ builtin_macros_requires_cfg_pattern =
 
 builtin_macros_should_panic = functions using `#[should_panic]` must return `()`
 
-builtin_macros_sugg = consider using a positional formatting argument instead
-
 builtin_macros_test_arg_non_lifetime = functions used as tests can not have any non-lifetime generic parameters
 
 builtin_macros_test_args = functions used as tests can not have any arguments
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index fbf0395bb05..1238773d58b 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -539,18 +539,29 @@ pub(crate) struct InvalidFormatStringLabel {
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(
-    builtin_macros_sugg,
-    style = "verbose",
-    applicability = "machine-applicable"
-)]
-pub(crate) struct InvalidFormatStringSuggestion {
-    #[suggestion_part(code = "{len}")]
-    pub(crate) captured: Span,
-    pub(crate) len: String,
-    #[suggestion_part(code = ", {arg}")]
-    pub(crate) span: Span,
-    pub(crate) arg: String,
+pub(crate) enum InvalidFormatStringSuggestion {
+    #[multipart_suggestion(
+        builtin_macros_format_use_positional,
+        style = "verbose",
+        applicability = "machine-applicable"
+    )]
+    UsePositional {
+        #[suggestion_part(code = "{len}")]
+        captured: Span,
+        len: String,
+        #[suggestion_part(code = ", {arg}")]
+        span: Span,
+        arg: String,
+    },
+    #[suggestion(
+        builtin_macros_format_remove_raw_ident,
+        code = "",
+        applicability = "machine-applicable"
+    )]
+    RemoveRawIdent {
+        #[primary_span]
+        span: Span,
+    },
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs
index ede95dbf897..8397b5e4221 100644
--- a/compiler/rustc_builtin_macros/src/format.rs
+++ b/compiler/rustc_builtin_macros/src/format.rs
@@ -260,20 +260,29 @@ fn make_format_args(
         if let Some((label, span)) = err.secondary_label && is_source_literal {
             e.label_ = Some(errors::InvalidFormatStringLabel { span: fmt_span.from_inner(InnerSpan::new(span.start, span.end)), label } );
         }
-        if err.should_be_replaced_with_positional_argument {
-            let captured_arg_span =
-                fmt_span.from_inner(InnerSpan::new(err.span.start, err.span.end));
-            if let Ok(arg) = ecx.source_map().span_to_snippet(captured_arg_span) {
-                let span = match args.unnamed_args().last() {
-                    Some(arg) => arg.expr.span,
-                    None => fmt_span,
-                };
-                e.sugg_ = Some(errors::InvalidFormatStringSuggestion {
-                    captured: captured_arg_span,
-                    len: args.unnamed_args().len().to_string(),
-                    span: span.shrink_to_hi(),
-                    arg,
-                });
+        match err.suggestion {
+            parse::Suggestion::None => {}
+            parse::Suggestion::UsePositional => {
+                let captured_arg_span =
+                    fmt_span.from_inner(InnerSpan::new(err.span.start, err.span.end));
+                if let Ok(arg) = ecx.source_map().span_to_snippet(captured_arg_span) {
+                    let span = match args.unnamed_args().last() {
+                        Some(arg) => arg.expr.span,
+                        None => fmt_span,
+                    };
+                    e.sugg_ = Some(errors::InvalidFormatStringSuggestion::UsePositional {
+                        captured: captured_arg_span,
+                        len: args.unnamed_args().len().to_string(),
+                        span: span.shrink_to_hi(),
+                        arg,
+                    });
+                }
+            }
+            parse::Suggestion::RemoveRawIdent(span) => {
+                if is_source_literal {
+                    let span = fmt_span.from_inner(InnerSpan::new(span.start, span.end));
+                    e.sugg_ = Some(errors::InvalidFormatStringSuggestion::RemoveRawIdent { span })
+                }
             }
         }
         ecx.emit_err(e);
diff --git a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs
index d847e524f8c..0d16da48067 100644
--- a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs
+++ b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs
@@ -100,11 +100,11 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
                 }
                 _ => unreachable!("{:?}", self.layout.abi),
             },
-            PassMode::Cast(ref cast, pad_i32) => {
+            PassMode::Cast { ref cast, pad_i32 } => {
                 assert!(!pad_i32, "padding support not yet implemented");
                 cast_target_to_abi_params(cast)
             }
-            PassMode::Indirect { attrs, extra_attrs: None, on_stack } => {
+            PassMode::Indirect { attrs, meta_attrs: None, on_stack } => {
                 if on_stack {
                     // Abi requires aligning struct size to pointer size
                     let size = self.layout.size.align_to(tcx.data_layout.pointer_align.abi);
@@ -117,11 +117,11 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
                     smallvec![apply_arg_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), attrs)]
                 }
             }
-            PassMode::Indirect { attrs, extra_attrs: Some(extra_attrs), on_stack } => {
+            PassMode::Indirect { attrs, meta_attrs: Some(meta_attrs), on_stack } => {
                 assert!(!on_stack);
                 smallvec![
                     apply_arg_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), attrs),
-                    apply_arg_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), extra_attrs),
+                    apply_arg_attrs_to_abi_param(AbiParam::new(pointer_ty(tcx)), meta_attrs),
                 ]
             }
         }
@@ -148,14 +148,14 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
                 }
                 _ => unreachable!("{:?}", self.layout.abi),
             },
-            PassMode::Cast(ref cast, _) => {
+            PassMode::Cast { ref cast, .. } => {
                 (None, cast_target_to_abi_params(cast).into_iter().collect())
             }
-            PassMode::Indirect { attrs: _, extra_attrs: None, on_stack } => {
+            PassMode::Indirect { attrs: _, meta_attrs: None, on_stack } => {
                 assert!(!on_stack);
                 (Some(AbiParam::special(pointer_ty(tcx), ArgumentPurpose::StructReturn)), vec![])
             }
-            PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => {
+            PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => {
                 unreachable!("unsized return value")
             }
         }
@@ -229,7 +229,7 @@ pub(super) fn adjust_arg_for_abi<'tcx>(
             let (a, b) = arg.load_scalar_pair(fx);
             smallvec![a, b]
         }
-        PassMode::Cast(ref cast, _) => to_casted_value(fx, arg, cast),
+        PassMode::Cast { ref cast, .. } => to_casted_value(fx, arg, cast),
         PassMode::Indirect { .. } => {
             if is_owned {
                 match arg.force_stack(fx) {
@@ -287,14 +287,14 @@ pub(super) fn cvalue_for_param<'tcx>(
             assert_eq!(block_params.len(), 2, "{:?}", block_params);
             Some(CValue::by_val_pair(block_params[0], block_params[1], arg_abi.layout))
         }
-        PassMode::Cast(ref cast, _) => {
+        PassMode::Cast { ref cast, .. } => {
             Some(from_casted_value(fx, &block_params, arg_abi.layout, cast))
         }
-        PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => {
+        PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => {
             assert_eq!(block_params.len(), 1, "{:?}", block_params);
             Some(CValue::by_ref(Pointer::new(block_params[0]), arg_abi.layout))
         }
-        PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => {
+        PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => {
             assert_eq!(block_params.len(), 2, "{:?}", block_params);
             Some(CValue::by_ref_unsized(
                 Pointer::new(block_params[0]),
diff --git a/compiler/rustc_codegen_cranelift/src/abi/returning.rs b/compiler/rustc_codegen_cranelift/src/abi/returning.rs
index 14e54d5ee38..646fb4a3cdc 100644
--- a/compiler/rustc_codegen_cranelift/src/abi/returning.rs
+++ b/compiler/rustc_codegen_cranelift/src/abi/returning.rs
@@ -13,7 +13,7 @@ pub(super) fn codegen_return_param<'tcx>(
     block_params_iter: &mut impl Iterator<Item = Value>,
 ) -> CPlace<'tcx> {
     let (ret_place, ret_param): (_, SmallVec<[_; 2]>) = match fx.fn_abi.as_ref().unwrap().ret.mode {
-        PassMode::Ignore | PassMode::Direct(_) | PassMode::Pair(_, _) | PassMode::Cast(..) => {
+        PassMode::Ignore | PassMode::Direct(_) | PassMode::Pair(_, _) | PassMode::Cast { .. } => {
             let is_ssa =
                 ssa_analyzed[RETURN_PLACE].is_ssa(fx, fx.fn_abi.as_ref().unwrap().ret.layout.ty);
             (
@@ -26,7 +26,7 @@ pub(super) fn codegen_return_param<'tcx>(
                 smallvec![],
             )
         }
-        PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => {
+        PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => {
             let ret_param = block_params_iter.next().unwrap();
             assert_eq!(fx.bcx.func.dfg.value_type(ret_param), fx.pointer_type);
             (
@@ -34,7 +34,7 @@ pub(super) fn codegen_return_param<'tcx>(
                 smallvec![ret_param],
             )
         }
-        PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => {
+        PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => {
             unreachable!("unsized return value")
         }
     };
@@ -62,7 +62,7 @@ pub(super) fn codegen_with_call_return_arg<'tcx>(
 ) {
     let (ret_temp_place, return_ptr) = match ret_arg_abi.mode {
         PassMode::Ignore => (None, None),
-        PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => {
+        PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => {
             if let Some(ret_ptr) = ret_place.try_to_ptr() {
                 // This is an optimization to prevent unnecessary copies of the return value when
                 // the return place is already a memory place as opposed to a register.
@@ -73,10 +73,10 @@ pub(super) fn codegen_with_call_return_arg<'tcx>(
                 (Some(place), Some(place.to_ptr().get_addr(fx)))
             }
         }
-        PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => {
+        PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => {
             unreachable!("unsized return value")
         }
-        PassMode::Direct(_) | PassMode::Pair(_, _) | PassMode::Cast(..) => (None, None),
+        PassMode::Direct(_) | PassMode::Pair(_, _) | PassMode::Cast { .. } => (None, None),
     };
 
     let call_inst = f(fx, return_ptr);
@@ -93,21 +93,21 @@ pub(super) fn codegen_with_call_return_arg<'tcx>(
             ret_place
                 .write_cvalue(fx, CValue::by_val_pair(ret_val_a, ret_val_b, ret_arg_abi.layout));
         }
-        PassMode::Cast(ref cast, _) => {
+        PassMode::Cast { ref cast, .. } => {
             let results =
                 fx.bcx.inst_results(call_inst).iter().copied().collect::<SmallVec<[Value; 2]>>();
             let result =
                 super::pass_mode::from_casted_value(fx, &results, ret_place.layout(), cast);
             ret_place.write_cvalue(fx, result);
         }
-        PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => {
+        PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => {
             if let Some(ret_temp_place) = ret_temp_place {
                 // If ret_temp_place is None, it is not necessary to copy the return value.
                 let ret_temp_value = ret_temp_place.to_cvalue(fx);
                 ret_place.write_cvalue(fx, ret_temp_value);
             }
         }
-        PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => {
+        PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => {
             unreachable!("unsized return value")
         }
     }
@@ -116,10 +116,10 @@ pub(super) fn codegen_with_call_return_arg<'tcx>(
 /// Codegen a return instruction with the right return value(s) if any.
 pub(crate) fn codegen_return(fx: &mut FunctionCx<'_, '_, '_>) {
     match fx.fn_abi.as_ref().unwrap().ret.mode {
-        PassMode::Ignore | PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => {
+        PassMode::Ignore | PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => {
             fx.bcx.ins().return_(&[]);
         }
-        PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => {
+        PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => {
             unreachable!("unsized return value")
         }
         PassMode::Direct(_) => {
@@ -132,7 +132,7 @@ pub(crate) fn codegen_return(fx: &mut FunctionCx<'_, '_, '_>) {
             let (ret_val_a, ret_val_b) = place.to_cvalue(fx).load_scalar_pair(fx);
             fx.bcx.ins().return_(&[ret_val_a, ret_val_b]);
         }
-        PassMode::Cast(ref cast, _) => {
+        PassMode::Cast { ref cast, .. } => {
             let place = fx.get_local_place(RETURN_PLACE);
             let ret_val = place.to_cvalue(fx);
             let ret_vals = super::pass_mode::to_casted_value(fx, ret_val, cast);
diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs
index 377dc753f68..a49530ebb4c 100644
--- a/compiler/rustc_codegen_gcc/src/abi.rs
+++ b/compiler/rustc_codegen_gcc/src/abi.rs
@@ -113,7 +113,7 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
             match self.ret.mode {
                 PassMode::Ignore => cx.type_void(),
                 PassMode::Direct(_) | PassMode::Pair(..) => self.ret.layout.immediate_gcc_type(cx),
-                PassMode::Cast(ref cast, _) => cast.gcc_type(cx),
+                PassMode::Cast { ref cast, .. } => cast.gcc_type(cx),
                 PassMode::Indirect { .. } => {
                     argument_tys.push(cx.type_ptr_to(self.ret.memory_ty(cx)));
                     cx.type_void()
@@ -129,21 +129,21 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
                     argument_tys.push(arg.layout.scalar_pair_element_gcc_type(cx, 1));
                     continue;
                 }
-                PassMode::Indirect { extra_attrs: Some(_), .. } => {
+                PassMode::Indirect { meta_attrs: Some(_), .. } => {
                     unimplemented!();
                 }
-                PassMode::Cast(ref cast, pad_i32) => {
+                PassMode::Cast { ref cast, pad_i32 } => {
                     // add padding
                     if pad_i32 {
                         argument_tys.push(Reg::i32().gcc_type(cx));
                     }
                     cast.gcc_type(cx)
                 }
-                PassMode::Indirect { extra_attrs: None, on_stack: true, .. } => {
+                PassMode::Indirect { meta_attrs: None, on_stack: true, .. } => {
                     on_stack_param_indices.insert(argument_tys.len());
                     arg.memory_ty(cx)
                 },
-                PassMode::Indirect { extra_attrs: None, on_stack: false, .. } => cx.type_ptr_to(arg.memory_ty(cx)),
+                PassMode::Indirect { meta_attrs: None, on_stack: false, .. } => cx.type_ptr_to(arg.memory_ty(cx)),
             };
             argument_tys.push(arg_ty);
         }
diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
index f8c32c6dbbb..68a087a1d7f 100644
--- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
+++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
@@ -144,7 +144,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
                 sym::volatile_load | sym::unaligned_volatile_load => {
                     let tp_ty = fn_args.type_at(0);
                     let mut ptr = args[0].immediate();
-                    if let PassMode::Cast(ty, _) = &fn_abi.ret.mode {
+                    if let PassMode::Cast { cast: ty, .. } = &fn_abi.ret.mode {
                         ptr = self.pointercast(ptr, self.type_ptr_to(ty.gcc_type(self)));
                     }
                     let load = self.volatile_load(ptr.get_type(), ptr);
@@ -353,7 +353,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
             };
 
         if !fn_abi.ret.is_ignore() {
-            if let PassMode::Cast(ty, _) = &fn_abi.ret.mode {
+            if let PassMode::Cast { cast: ty, .. } = &fn_abi.ret.mode {
                 let ptr_llty = self.type_ptr_to(ty.gcc_type(self));
                 let ptr = self.pointercast(result.llval, ptr_llty);
                 self.store(llval, ptr, result.align);
@@ -449,7 +449,7 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
         else if self.is_unsized_indirect() {
             bug!("unsized `ArgAbi` must be handled through `store_fn_arg`");
         }
-        else if let PassMode::Cast(ref cast, _) = self.mode {
+        else if let PassMode::Cast { ref cast, .. } = self.mode {
             // FIXME(eddyb): Figure out when the simpler Store is safe, clang
             // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}.
             let can_store_through_cast_ptr = false;
@@ -511,10 +511,10 @@ impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
             PassMode::Pair(..) => {
                 OperandValue::Pair(next(), next()).store(bx, dst);
             },
-            PassMode::Indirect { extra_attrs: Some(_), .. } => {
+            PassMode::Indirect { meta_attrs: Some(_), .. } => {
                 OperandValue::Ref(next(), Some(next()), self.layout.align.abi).store(bx, dst);
             },
-            PassMode::Direct(_) | PassMode::Indirect { extra_attrs: None, .. } | PassMode::Cast(..) => {
+            PassMode::Direct(_) | PassMode::Indirect { meta_attrs: None, .. } | PassMode::Cast { .. } => {
                 let next_arg = next();
                 self.store(bx, next_arg, dst);
             },
diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs
index 64587f98b8a..9e834b83df4 100644
--- a/compiler/rustc_codegen_llvm/src/abi.rs
+++ b/compiler/rustc_codegen_llvm/src/abi.rs
@@ -211,7 +211,7 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
             OperandValue::Ref(val, None, self.layout.align.abi).store(bx, dst)
         } else if self.is_unsized_indirect() {
             bug!("unsized `ArgAbi` must be handled through `store_fn_arg`");
-        } else if let PassMode::Cast(cast, _) = &self.mode {
+        } else if let PassMode::Cast { cast, pad_i32: _ } = &self.mode {
             // FIXME(eddyb): Figure out when the simpler Store is safe, clang
             // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}.
             let can_store_through_cast_ptr = false;
@@ -274,12 +274,12 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
             PassMode::Pair(..) => {
                 OperandValue::Pair(next(), next()).store(bx, dst);
             }
-            PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => {
+            PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => {
                 OperandValue::Ref(next(), Some(next()), self.layout.align.abi).store(bx, dst);
             }
             PassMode::Direct(_)
-            | PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ }
-            | PassMode::Cast(..) => {
+            | PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ }
+            | PassMode::Cast { .. } => {
                 let next_arg = next();
                 self.store(bx, next_arg, dst);
             }
@@ -332,7 +332,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
         let llreturn_ty = match &self.ret.mode {
             PassMode::Ignore => cx.type_void(),
             PassMode::Direct(_) | PassMode::Pair(..) => self.ret.layout.immediate_llvm_type(cx),
-            PassMode::Cast(cast, _) => cast.llvm_type(cx),
+            PassMode::Cast { cast, pad_i32: _ } => cast.llvm_type(cx),
             PassMode::Indirect { .. } => {
                 llargument_tys.push(cx.type_ptr());
                 cx.type_void()
@@ -351,6 +351,11 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
                     // guarnateeing that we generate ABI-compatible LLVM IR. Things get tricky for
                     // aggregates...
                     if matches!(arg.layout.abi, abi::Abi::Aggregate { .. }) {
+                        assert!(
+                            arg.layout.is_sized(),
+                            "`PassMode::Direct` for unsized type: {}",
+                            arg.layout.ty
+                        );
                         // This really shouldn't happen, since `immediate_llvm_type` will use
                         // `layout.fields` to turn this Rust type into an LLVM type. This means all
                         // sorts of Rust type details leak into the ABI. However wasm sadly *does*
@@ -378,8 +383,10 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
                     llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 1, true));
                     continue;
                 }
-                PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => {
-                    assert!(arg.layout.is_unsized());
+                PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack } => {
+                    // `Indirect` with metadata is only for unsized types, and doesn't work with
+                    // on-stack passing.
+                    assert!(arg.layout.is_unsized() && !on_stack);
                     // Construct the type of a (wide) pointer to `ty`, and pass its two fields.
                     // Any two ABI-compatible unsized types have the same metadata type and
                     // moreover the same metadata value leads to the same dynamic size and
@@ -390,7 +397,13 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
                     llargument_tys.push(ptr_layout.scalar_pair_element_llvm_type(cx, 1, true));
                     continue;
                 }
-                PassMode::Cast(cast, pad_i32) => {
+                PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => {
+                    assert!(arg.layout.is_sized());
+                    cx.type_ptr()
+                }
+                PassMode::Cast { cast, pad_i32 } => {
+                    // `Cast` means "transmute to `CastType`"; that only makes sense for sized types.
+                    assert!(arg.layout.is_sized());
                     // add padding
                     if *pad_i32 {
                         llargument_tys.push(Reg::i32().llvm_type(cx));
@@ -399,7 +412,6 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
                     // We assume here that ABI-compatible Rust types have the same cast type.
                     cast.llvm_type(cx)
                 }
-                PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => cx.type_ptr(),
             };
             llargument_tys.push(llarg_ty);
         }
@@ -442,13 +454,13 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
             PassMode::Direct(attrs) => {
                 attrs.apply_attrs_to_llfn(llvm::AttributePlace::ReturnValue, cx, llfn);
             }
-            PassMode::Indirect { attrs, extra_attrs: _, on_stack } => {
+            PassMode::Indirect { attrs, meta_attrs: _, on_stack } => {
                 assert!(!on_stack);
                 let i = apply(attrs);
                 let sret = llvm::CreateStructRetAttr(cx.llcx, self.ret.layout.llvm_type(cx));
                 attributes::apply_to_llfn(llfn, llvm::AttributePlace::Argument(i), &[sret]);
             }
-            PassMode::Cast(cast, _) => {
+            PassMode::Cast { cast, pad_i32: _ } => {
                 cast.attrs.apply_attrs_to_llfn(llvm::AttributePlace::ReturnValue, cx, llfn);
             }
             _ => {}
@@ -456,25 +468,25 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
         for arg in self.args.iter() {
             match &arg.mode {
                 PassMode::Ignore => {}
-                PassMode::Indirect { attrs, extra_attrs: None, on_stack: true } => {
+                PassMode::Indirect { attrs, meta_attrs: None, on_stack: true } => {
                     let i = apply(attrs);
                     let byval = llvm::CreateByValAttr(cx.llcx, arg.layout.llvm_type(cx));
                     attributes::apply_to_llfn(llfn, llvm::AttributePlace::Argument(i), &[byval]);
                 }
                 PassMode::Direct(attrs)
-                | PassMode::Indirect { attrs, extra_attrs: None, on_stack: false } => {
+                | PassMode::Indirect { attrs, meta_attrs: None, on_stack: false } => {
                     apply(attrs);
                 }
-                PassMode::Indirect { attrs, extra_attrs: Some(extra_attrs), on_stack } => {
+                PassMode::Indirect { attrs, meta_attrs: Some(meta_attrs), on_stack } => {
                     assert!(!on_stack);
                     apply(attrs);
-                    apply(extra_attrs);
+                    apply(meta_attrs);
                 }
                 PassMode::Pair(a, b) => {
                     apply(a);
                     apply(b);
                 }
-                PassMode::Cast(cast, pad_i32) => {
+                PassMode::Cast { cast, pad_i32 } => {
                     if *pad_i32 {
                         apply(&ArgAttributes::new());
                     }
@@ -504,13 +516,13 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
             PassMode::Direct(attrs) => {
                 attrs.apply_attrs_to_callsite(llvm::AttributePlace::ReturnValue, bx.cx, callsite);
             }
-            PassMode::Indirect { attrs, extra_attrs: _, on_stack } => {
+            PassMode::Indirect { attrs, meta_attrs: _, on_stack } => {
                 assert!(!on_stack);
                 let i = apply(bx.cx, attrs);
                 let sret = llvm::CreateStructRetAttr(bx.cx.llcx, self.ret.layout.llvm_type(bx));
                 attributes::apply_to_callsite(callsite, llvm::AttributePlace::Argument(i), &[sret]);
             }
-            PassMode::Cast(cast, _) => {
+            PassMode::Cast { cast, pad_i32: _ } => {
                 cast.attrs.apply_attrs_to_callsite(
                     llvm::AttributePlace::ReturnValue,
                     &bx.cx,
@@ -532,7 +544,7 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
         for arg in self.args.iter() {
             match &arg.mode {
                 PassMode::Ignore => {}
-                PassMode::Indirect { attrs, extra_attrs: None, on_stack: true } => {
+                PassMode::Indirect { attrs, meta_attrs: None, on_stack: true } => {
                     let i = apply(bx.cx, attrs);
                     let byval = llvm::CreateByValAttr(bx.cx.llcx, arg.layout.llvm_type(bx));
                     attributes::apply_to_callsite(
@@ -542,18 +554,18 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
                     );
                 }
                 PassMode::Direct(attrs)
-                | PassMode::Indirect { attrs, extra_attrs: None, on_stack: false } => {
+                | PassMode::Indirect { attrs, meta_attrs: None, on_stack: false } => {
                     apply(bx.cx, attrs);
                 }
-                PassMode::Indirect { attrs, extra_attrs: Some(extra_attrs), on_stack: _ } => {
+                PassMode::Indirect { attrs, meta_attrs: Some(meta_attrs), on_stack: _ } => {
                     apply(bx.cx, attrs);
-                    apply(bx.cx, extra_attrs);
+                    apply(bx.cx, meta_attrs);
                 }
                 PassMode::Pair(a, b) => {
                     apply(bx.cx, a);
                     apply(bx.cx, b);
                 }
-                PassMode::Cast(cast, pad_i32) => {
+                PassMode::Cast { cast, pad_i32 } => {
                     if *pad_i32 {
                         apply(bx.cx, &ArgAttributes::new());
                     }
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index a9b06030e70..9289c37d763 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -165,7 +165,7 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> {
             sym::volatile_load | sym::unaligned_volatile_load => {
                 let tp_ty = fn_args.type_at(0);
                 let ptr = args[0].immediate();
-                let load = if let PassMode::Cast(ty, _) = &fn_abi.ret.mode {
+                let load = if let PassMode::Cast { cast: ty, pad_i32: _ } = &fn_abi.ret.mode {
                     let llty = ty.llvm_type(self);
                     self.volatile_load(llty, ptr)
                 } else {
@@ -386,7 +386,7 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> {
         };
 
         if !fn_abi.ret.is_ignore() {
-            if let PassMode::Cast(_, _) = &fn_abi.ret.mode {
+            if let PassMode::Cast { .. } = &fn_abi.ret.mode {
                 self.store(llval, result.llval, result.align);
             } else {
                 OperandRef::from_immediate_or_packed_pair(self, llval, result.layout)
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index d8f6b4ed836..464278f9c5e 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -416,7 +416,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 }
             }
 
-            PassMode::Cast(cast_ty, _) => {
+            PassMode::Cast { cast: cast_ty, pad_i32: _ } => {
                 let op = match self.locals[mir::RETURN_PLACE] {
                     LocalRef::Operand(op) => op,
                     LocalRef::PendingOperand => bug!("use of return before def"),
@@ -1310,7 +1310,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     ) {
         match arg.mode {
             PassMode::Ignore => return,
-            PassMode::Cast(_, true) => {
+            PassMode::Cast { pad_i32: true, .. } => {
                 // Fill padding with undef value, where applicable.
                 llargs.push(bx.const_undef(bx.reg_backend_type(&Reg::i32())));
             }
@@ -1322,7 +1322,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 }
                 _ => bug!("codegen_argument: {:?} invalid for pair argument", op),
             },
-            PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => match op.val {
+            PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => match op.val {
                 Ref(a, Some(b), _) => {
                     llargs.push(a);
                     llargs.push(b);
@@ -1347,7 +1347,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     op.val.store(bx, scratch);
                     (scratch.llval, scratch.align, true)
                 }
-                PassMode::Cast(..) => {
+                PassMode::Cast { .. } => {
                     let scratch = PlaceRef::alloca(bx, arg.layout);
                     op.val.store(bx, scratch);
                     (scratch.llval, scratch.align, true)
@@ -1400,7 +1400,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
 
         if by_ref && !arg.is_indirect() {
             // Have to load the argument, maybe while casting it.
-            if let PassMode::Cast(ty, _) = &arg.mode {
+            if let PassMode::Cast { cast: ty, .. } = &arg.mode {
                 let llty = bx.cast_backend_type(ty);
                 llval = bx.load(llty, llval, align.min(arg.layout.align.abi));
             } else {
@@ -1744,7 +1744,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             }
             DirectOperand(index) => {
                 // If there is a cast, we have to store and reload.
-                let op = if let PassMode::Cast(..) = ret_abi.mode {
+                let op = if let PassMode::Cast { .. } = ret_abi.mode {
                     let tmp = PlaceRef::alloca(bx, ret_abi.layout);
                     tmp.storage_live(bx);
                     bx.store_arg(&ret_abi, llval, tmp);
diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
index 8821fb21fd0..8efef440522 100644
--- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
@@ -462,7 +462,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         };
 
         if !fn_abi.ret.is_ignore() {
-            if let PassMode::Cast(..) = &fn_abi.ret.mode {
+            if let PassMode::Cast { .. } = &fn_abi.ret.mode {
                 bx.store(llval, result.llval, result.align);
             } else {
                 OperandRef::from_immediate_or_packed_pair(bx, llval, result.layout)
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index c4408f2db4c..8445de6c678 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -327,7 +327,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
                 for i in 0..tupled_arg_tys.len() {
                     let arg = &fx.fn_abi.args[idx];
                     idx += 1;
-                    if let PassMode::Cast(_, true) = arg.mode {
+                    if let PassMode::Cast { pad_i32: true, .. } = arg.mode {
                         llarg_idx += 1;
                     }
                     let pr_field = place.project_field(bx, i);
@@ -351,7 +351,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
 
             let arg = &fx.fn_abi.args[idx];
             idx += 1;
-            if let PassMode::Cast(_, true) = arg.mode {
+            if let PassMode::Cast { pad_i32: true, .. } = arg.mode {
                 llarg_idx += 1;
             }
 
diff --git a/compiler/rustc_error_codes/src/error_codes/E0691.md b/compiler/rustc_error_codes/src/error_codes/E0691.md
index 483c74c0ff5..a5bedd61e92 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0691.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0691.md
@@ -1,9 +1,11 @@
+#### Note: this error code is no longer emitted by the compiler.
+
 A struct, enum, or union with the `repr(transparent)` representation hint
 contains a zero-sized field that requires non-trivial alignment.
 
 Erroneous code example:
 
-```compile_fail,E0691
+```ignore (error is no longer emitted)
 #![feature(repr_align)]
 
 #[repr(align(32))]
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index 6c338be99b6..f3b88f46bae 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -416,7 +416,7 @@ declare_features! (
     /// Allows having using `suggestion` in the `#[deprecated]` attribute.
     (active, deprecated_suggestion, "1.61.0", Some(94785), None),
     /// Allows using the `#[diagnostic]` attribute tool namespace
-    (active, diagnostic_namespace, "1.73.0", Some(94785), None),
+    (active, diagnostic_namespace, "1.73.0", Some(111996), None),
     /// Controls errors in trait implementations.
     (active, do_not_recommend, "1.67.0", Some(51992), None),
     /// Tells rustdoc to automatically generate `#[doc(cfg(...))]`.
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 1a38657dff4..18f7a18625e 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -304,13 +304,13 @@ hir_analysis_transparent_enum_variant = transparent enum needs exactly one varia
     .many_label = too many variants in `{$path}`
     .multi_label = variant here
 
-hir_analysis_transparent_non_zero_sized = transparent {$desc} needs at most one non-zero-sized field, but has {$field_count}
-    .label = needs at most one non-zero-sized field, but has {$field_count}
-    .labels = this field is non-zero-sized
+hir_analysis_transparent_non_zero_sized = transparent {$desc} needs at most one field with non-trivial size or alignment, but has {$field_count}
+    .label = needs at most one field with non-trivial size or alignment, but has {$field_count}
+    .labels = this field has non-zero size or requires alignment
 
-hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$desc} needs at most one non-zero-sized field, but has {$field_count}
-    .label = needs at most one non-zero-sized field, but has {$field_count}
-    .labels = this field is non-zero-sized
+hir_analysis_transparent_non_zero_sized_enum = the variant of a transparent {$desc} needs at most one field with non-trivial size or alignment, but has {$field_count}
+    .label = needs at most one field with non-trivial size or alignment, but has {$field_count}
+    .labels = this field has non-zero size or requires alignment
 
 hir_analysis_typeof_reserved_keyword_used =
     `typeof` is a reserved keyword but unimplemented
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 9a57cc6dbab..1d381175c75 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -1138,19 +1138,19 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
         return;
     }
 
-    // For each field, figure out if it's known to be a ZST and align(1), with "known"
-    // respecting #[non_exhaustive] attributes.
+    // For each field, figure out if it's known to have "trivial" layout (i.e., is a 1-ZST), with
+    // "known" respecting #[non_exhaustive] attributes.
     let field_infos = adt.all_fields().map(|field| {
         let ty = field.ty(tcx, GenericArgs::identity_for_item(tcx, field.did));
         let param_env = tcx.param_env(field.did);
         let layout = tcx.layout_of(param_env.and(ty));
         // We are currently checking the type this field came from, so it must be local
         let span = tcx.hir().span_if_local(field.did).unwrap();
-        let zst = layout.is_ok_and(|layout| layout.is_zst());
-        let align = layout.ok().map(|layout| layout.align.abi.bytes());
-        if !zst {
-            return (span, zst, align, None);
+        let trivial = layout.is_ok_and(|layout| layout.is_1zst());
+        if !trivial {
+            return (span, trivial, None);
         }
+        // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive`.
 
         fn check_non_exhaustive<'tcx>(
             tcx: TyCtxt<'tcx>,
@@ -1184,41 +1184,25 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
             }
         }
 
-        (span, zst, align, check_non_exhaustive(tcx, ty).break_value())
+        (span, trivial, check_non_exhaustive(tcx, ty).break_value())
     });
 
-    let non_zst_fields = field_infos
+    let non_trivial_fields = field_infos
         .clone()
-        .filter_map(|(span, zst, _align, _non_exhaustive)| if !zst { Some(span) } else { None });
-    let non_zst_count = non_zst_fields.clone().count();
-    if non_zst_count >= 2 {
-        bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, tcx.def_span(adt.did()));
+        .filter_map(|(span, trivial, _non_exhaustive)| if !trivial { Some(span) } else { None });
+    let non_trivial_count = non_trivial_fields.clone().count();
+    if non_trivial_count >= 2 {
+        bad_non_zero_sized_fields(
+            tcx,
+            adt,
+            non_trivial_count,
+            non_trivial_fields,
+            tcx.def_span(adt.did()),
+        );
+        return;
     }
-    let incompatible_zst_fields =
-        field_infos.clone().filter(|(_, _, _, opt)| opt.is_some()).count();
-    let incompat = incompatible_zst_fields + non_zst_count >= 2 && non_zst_count < 2;
-    for (span, zst, align, non_exhaustive) in field_infos {
-        if zst && align != Some(1) {
-            let mut err = struct_span_err!(
-                tcx.sess,
-                span,
-                E0691,
-                "zero-sized field in transparent {} has alignment larger than 1",
-                adt.descr(),
-            );
-
-            if let Some(align_bytes) = align {
-                err.span_label(
-                    span,
-                    format!("has alignment of {align_bytes}, which is larger than 1"),
-                );
-            } else {
-                err.span_label(span, "may have alignment larger than 1");
-            }
-
-            err.emit();
-        }
-        if incompat && let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive {
+    for (span, _trivial, non_exhaustive) in field_infos {
+        if let Some((descr, def_id, args, non_exhaustive)) = non_exhaustive {
             tcx.struct_span_lint_hir(
                 REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
                 tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()),
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl
index 034ffc17a7a..4be3ea890dc 100644
--- a/compiler/rustc_hir_typeck/messages.ftl
+++ b/compiler/rustc_hir_typeck/messages.ftl
@@ -33,6 +33,10 @@ hir_typeck_expected_default_return_type = expected `()` because of default retur
 
 hir_typeck_expected_return_type = expected `{$expected}` because of return type
 
+hir_typeck_explicit_destructor = explicit use of destructor method
+    .label = explicit destructor calls not allowed
+    .suggestion = consider using `drop` function
+
 hir_typeck_field_multiply_specified_in_initializer =
     field `{$ident}` specified more than once
     .label = used more than once
@@ -52,8 +56,10 @@ hir_typeck_functional_record_update_on_non_struct =
 
 hir_typeck_help_set_edition_cargo = set `edition = "{$edition}"` in `Cargo.toml`
 hir_typeck_help_set_edition_standalone = pass `--edition {$edition}` to `rustc`
-hir_typeck_lang_start_expected_sig_note = the `start` lang item should have the signature `fn(fn() -> T, isize, *const *const u8, u8) -> isize`
 
+hir_typeck_invalid_callee = expected function, found {$ty}
+
+hir_typeck_lang_start_expected_sig_note = the `start` lang item should have the signature `fn(fn() -> T, isize, *const *const u8, u8) -> isize`
 hir_typeck_lang_start_incorrect_number_params = incorrect number of parameters for the `start` lang item
 hir_typeck_lang_start_incorrect_number_params_note_expected_count = the `start` lang item should have four parameters, but found {$found_param_count}
 
@@ -66,6 +72,9 @@ hir_typeck_lang_start_incorrect_ret_ty = the return type of the `start` lang ite
 hir_typeck_method_call_on_unknown_raw_pointee =
     cannot call a method on a raw pointer with an unknown pointee type
 
+hir_typeck_missing_fn_lang_items = failed to find an overloaded call trait for closure call
+    .help = make sure the `fn`/`fn_mut`/`fn_once` lang items are defined and have correctly defined `call`/`call_mut`/`call_once` methods
+
 hir_typeck_missing_parentheses_in_range = can't call method `{$method_name}` on type `{$ty_str}`
 
 hir_typeck_no_associated_item = no {$item_kind} named `{$item_name}` found for {$ty_prefix} `{$ty_str}`{$trait_missing_method ->
@@ -92,6 +101,9 @@ hir_typeck_return_stmt_outside_of_fn_body =
     .encl_body_label = the {$statement_kind} is part of this body...
     .encl_fn_label = ...not the enclosing function body
 
+hir_typeck_rustcall_incorrect_args =
+    functions with the "rust-call" ABI must take a single non-self tuple argument
+
 hir_typeck_struct_expr_non_exhaustive =
     cannot create non-exhaustive {$what} using struct expression
 
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index 02371f85ac3..b39919ece71 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -2,9 +2,9 @@ use super::method::probe::ProbeScope;
 use super::method::MethodCallee;
 use super::{Expectation, FnCtxt, TupleArgumentsFlag};
 
-use crate::type_error_struct;
+use crate::errors;
 use rustc_ast::util::parser::PREC_POSTFIX;
-use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, StashKey};
+use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, StashKey};
 use rustc_hir as hir;
 use rustc_hir::def::{self, CtorKind, DefKind, Namespace, Res};
 use rustc_hir::def_id::DefId;
@@ -44,23 +44,15 @@ pub fn check_legal_trait_for_method_call(
     trait_id: DefId,
 ) {
     if tcx.lang_items().drop_trait() == Some(trait_id) {
-        let mut err = struct_span_err!(tcx.sess, span, E0040, "explicit use of destructor method");
-        err.span_label(span, "explicit destructor calls not allowed");
-
-        let (sp, suggestion) = receiver
-            .and_then(|s| tcx.sess.source_map().span_to_snippet(s).ok())
-            .filter(|snippet| !snippet.is_empty())
-            .map(|snippet| (expr_span, format!("drop({snippet})")))
-            .unwrap_or_else(|| (span, "drop".to_string()));
-
-        err.span_suggestion(
-            sp,
-            "consider using `drop` function",
-            suggestion,
-            Applicability::MaybeIncorrect,
-        );
-
-        err.emit();
+        let sugg = if let Some(receiver) = receiver.filter(|s| !s.is_empty()) {
+            errors::ExplicitDestructorCallSugg::Snippet {
+                lo: expr_span.shrink_to_lo(),
+                hi: receiver.shrink_to_hi().to(expr_span.shrink_to_hi()),
+            }
+        } else {
+            errors::ExplicitDestructorCallSugg::Empty(span)
+        };
+        tcx.sess.emit_err(errors::ExplicitDestructorCall { span, sugg });
     }
 }
 
@@ -387,6 +379,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // Unit testing: function items annotated with
                 // `#[rustc_evaluate_where_clauses]` trigger special output
                 // to let us test the trait evaluation system.
+                // Untranslatable diagnostics are okay for rustc internals
+                #[allow(rustc::untranslatable_diagnostic)]
+                #[allow(rustc::diagnostic_outside_of_impl)]
                 if self.tcx.has_attr(def_id, sym::rustc_evaluate_where_clauses) {
                     let predicates = self.tcx.predicates_of(def_id);
                     let predicates = predicates.instantiate(self.tcx, args);
@@ -478,10 +473,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 );
                 self.require_type_is_sized(ty, sp, traits::RustCall);
             } else {
-                self.tcx.sess.span_err(
-                        sp,
-                        "functions with the \"rust-call\" ABI must take a single non-self tuple argument",
-                    );
+                self.tcx.sess.emit_err(errors::RustCallIncorrectArgs { span: sp });
             }
         }
 
@@ -610,17 +602,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         let callee_ty = self.resolve_vars_if_possible(callee_ty);
-        let mut err = type_error_struct!(
-            self.tcx.sess,
-            callee_expr.span,
-            callee_ty,
-            E0618,
-            "expected function, found {}",
-            match &unit_variant {
+        let mut err = self.tcx.sess.create_err(errors::InvalidCallee {
+            span: callee_expr.span,
+            ty: match &unit_variant {
                 Some((_, kind, path)) => format!("{kind} `{path}`"),
                 None => format!("`{callee_ty}`"),
-            }
-        );
+            },
+        });
+        if callee_ty.references_error() {
+            err.downgrade_to_delayed_bug();
+        }
 
         self.identify_bad_closure_def_and_call(
             &mut err,
@@ -891,15 +882,7 @@ impl<'a, 'tcx> DeferredCallResolution<'tcx> {
             None => {
                 // This can happen if `#![no_core]` is used and the `fn/fn_mut/fn_once`
                 // lang items are not defined (issue #86238).
-                let mut err = fcx.inh.tcx.sess.struct_span_err(
-                    self.call_expr.span,
-                    "failed to find an overloaded call trait for closure call",
-                );
-                err.help(
-                    "make sure the `fn`/`fn_mut`/`fn_once` lang items are defined \
-                     and have correctly defined `call`/`call_mut`/`call_once` methods",
-                );
-                err.emit();
+                fcx.inh.tcx.sess.emit_err(errors::MissingFnLangItems { span: self.call_expr.span });
             }
         }
     }
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 255fe5e0206..c1d7056b1a0 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -55,6 +55,13 @@ impl IntoDiagnosticArg for ReturnLikeStatementKind {
 }
 
 #[derive(Diagnostic)]
+#[diag(hir_typeck_rustcall_incorrect_args)]
+pub struct RustCallIncorrectArgs {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(hir_typeck_yield_expr_outside_of_generator, code = "E0627")]
 pub struct YieldExprOutsideOfGenerator {
     #[primary_span]
@@ -77,6 +84,14 @@ pub struct MethodCallOnUnknownRawPointee {
 }
 
 #[derive(Diagnostic)]
+#[diag(hir_typeck_missing_fn_lang_items)]
+#[help]
+pub struct MissingFnLangItems {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(hir_typeck_functional_record_update_on_non_struct, code = "E0436")]
 pub struct FunctionalRecordUpdateOnNonStruct {
     #[primary_span]
@@ -130,6 +145,29 @@ pub enum ExpectedReturnTypeLabel<'tcx> {
 }
 
 #[derive(Diagnostic)]
+#[diag(hir_typeck_explicit_destructor, code = "E0040")]
+pub struct ExplicitDestructorCall {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[subdiagnostic]
+    pub sugg: ExplicitDestructorCallSugg,
+}
+
+#[derive(Subdiagnostic)]
+pub enum ExplicitDestructorCallSugg {
+    #[suggestion(hir_typeck_suggestion, code = "drop", applicability = "maybe-incorrect")]
+    Empty(#[primary_span] Span),
+    #[multipart_suggestion(hir_typeck_suggestion, style = "short")]
+    Snippet {
+        #[suggestion_part(code = "drop(")]
+        lo: Span,
+        #[suggestion_part(code = ")")]
+        hi: Span,
+    },
+}
+
+#[derive(Diagnostic)]
 #[diag(hir_typeck_missing_parentheses_in_range, code = "E0689")]
 pub struct MissingParenthesesInRange {
     #[primary_span]
@@ -252,6 +290,14 @@ impl HelpUseLatestEdition {
     }
 }
 
+#[derive(Diagnostic)]
+#[diag(hir_typeck_invalid_callee, code = "E0618")]
+pub struct InvalidCallee {
+    #[primary_span]
+    pub span: Span,
+    pub ty: String,
+}
+
 #[derive(Subdiagnostic)]
 pub enum OptionResultRefMismatch {
     #[suggestion(
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
index f903f7a49ef..4aec28b051f 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
@@ -385,7 +385,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
 
         let highlight_trait_ref = |trait_ref| Highlighted {
             tcx: self.tcx(),
-            highlight: RegionHighlightMode::new(self.tcx()),
+            highlight: RegionHighlightMode::default(),
             value: trait_ref,
         };
 
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs
index 12d38ced030..d2ba9966f03 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs
@@ -67,9 +67,9 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
         }
 
         impl<'tcx> HighlightBuilder<'tcx> {
-            fn build(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> RegionHighlightMode<'tcx> {
+            fn build(ty: Ty<'tcx>) -> RegionHighlightMode<'tcx> {
                 let mut builder =
-                    HighlightBuilder { highlight: RegionHighlightMode::new(tcx), counter: 1 };
+                    HighlightBuilder { highlight: RegionHighlightMode::default(), counter: 1 };
                 builder.visit_ty(ty);
                 builder.highlight
             }
@@ -85,12 +85,12 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
             }
         }
 
-        let expected_highlight = HighlightBuilder::build(self.tcx(), expected);
+        let expected_highlight = HighlightBuilder::build(expected);
         let expected = self
             .cx
             .extract_inference_diagnostics_data(expected.into(), Some(expected_highlight))
             .name;
-        let found_highlight = HighlightBuilder::build(self.tcx(), found);
+        let found_highlight = HighlightBuilder::build(found);
         let found =
             self.cx.extract_inference_diagnostics_data(found.into(), Some(found_highlight)).name;
 
diff --git a/compiler/rustc_infer/src/infer/free_regions.rs b/compiler/rustc_infer/src/infer/free_regions.rs
index 2402a7ea7c7..ed1a2a11719 100644
--- a/compiler/rustc_infer/src/infer/free_regions.rs
+++ b/compiler/rustc_infer/src/infer/free_regions.rs
@@ -4,7 +4,7 @@
 //! and use that to decide when one free region outlives another, and so forth.
 
 use rustc_data_structures::transitive_relation::TransitiveRelation;
-use rustc_middle::ty::{Lift, Region, TyCtxt};
+use rustc_middle::ty::{Region, TyCtxt};
 
 /// Combines a `FreeRegionMap` and a `TyCtxt`.
 ///
@@ -101,10 +101,3 @@ impl<'tcx> FreeRegionMap<'tcx> {
         result
     }
 }
-
-impl<'a, 'tcx> Lift<'tcx> for FreeRegionMap<'a> {
-    type Lifted = FreeRegionMap<'tcx>;
-    fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<FreeRegionMap<'tcx>> {
-        self.relation.maybe_map(|fr| tcx.lift(fr)).map(|relation| FreeRegionMap { relation })
-    }
-}
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 37e242c6e40..0634e44c562 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -568,6 +568,13 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu
     ) {
         sess.emit_fatal(errors::MultipleOutputTypesToStdout);
     }
+
+    let crate_name = sess
+        .opts
+        .crate_name
+        .clone()
+        .or_else(|| rustc_attr::find_crate_name(attrs).map(|n| n.to_string()));
+
     match sess.io.output_file {
         None => {
             // "-" as input file will cause the parser to read from stdin so we
@@ -576,15 +583,11 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu
             let dirpath = sess.io.output_dir.clone().unwrap_or_default();
 
             // If a crate name is present, we use it as the link name
-            let stem = sess
-                .opts
-                .crate_name
-                .clone()
-                .or_else(|| rustc_attr::find_crate_name(attrs).map(|n| n.to_string()))
-                .unwrap_or_else(|| sess.io.input.filestem().to_owned());
+            let stem = crate_name.clone().unwrap_or_else(|| sess.io.input.filestem().to_owned());
 
             OutputFilenames::new(
                 dirpath,
+                crate_name.unwrap_or_else(|| stem.replace('-', "_")),
                 stem,
                 None,
                 sess.io.temps_dir.clone(),
@@ -609,9 +612,12 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu
                 sess.emit_warning(errors::IgnoringOutDir);
             }
 
+            let out_filestem =
+                out_file.filestem().unwrap_or_default().to_str().unwrap().to_string();
             OutputFilenames::new(
                 out_file.parent().unwrap_or_else(|| Path::new("")).to_path_buf(),
-                out_file.filestem().unwrap_or_default().to_str().unwrap().to_string(),
+                crate_name.unwrap_or_else(|| out_filestem.replace('-', "_")),
+                out_filestem,
                 ofile,
                 sess.io.temps_dir.clone(),
                 sess.opts.cg.extra_filename.clone(),
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 672a7a20148..860366fdd61 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -3405,8 +3405,8 @@ declare_lint_pass! {
         UNFULFILLED_LINT_EXPECTATIONS,
         UNINHABITED_STATIC,
         UNKNOWN_CRATE_TYPES,
-        UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
         UNKNOWN_LINTS,
+        UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
         UNNAMEABLE_TEST_ITEMS,
         UNNAMEABLE_TYPES,
         UNREACHABLE_CODE,
@@ -4420,7 +4420,8 @@ declare_lint! {
 }
 
 declare_lint! {
-    /// The `unknown_diagnostic_attributes` lint detects unrecognized diagnostic attributes.
+    /// The `unknown_or_malformed_diagnostic_attributes` lint detects unrecognized or otherwise malformed
+    /// diagnostic attributes.
     ///
     /// ### Example
     ///
@@ -4432,15 +4433,17 @@ declare_lint! {
     ///
     /// {{produces}}
     ///
+    ///
     /// ### Explanation
     ///
     /// It is usually a mistake to specify a diagnostic attribute that does not exist. Check
     /// the spelling, and check the diagnostic attribute listing for the correct name. Also
     /// consider if you are using an old version of the compiler, and the attribute
     /// is only available in a newer version.
-    pub UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
+    pub UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
     Warn,
-    "unrecognized diagnostic attribute"
+    "unrecognized or malformed diagnostic attribute",
+    @feature_gate = sym::diagnostic_namespace;
 }
 
 declare_lint! {
diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs
index 2a9662b809a..7eb2a347db2 100644
--- a/compiler/rustc_metadata/src/fs.rs
+++ b/compiler/rustc_metadata/src/fs.rs
@@ -5,7 +5,6 @@ use crate::errors::{
 use crate::{encode_metadata, EncodedMetadata};
 
 use rustc_data_structures::temp_dir::MaybeTempDir;
-use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::config::{OutFileName, OutputType};
 use rustc_session::output::filename_for_metadata;
@@ -40,8 +39,7 @@ pub fn emit_wrapper_file(
 }
 
 pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) {
-    let crate_name = tcx.crate_name(LOCAL_CRATE);
-    let out_filename = filename_for_metadata(tcx.sess, crate_name, tcx.output_filenames(()));
+    let out_filename = filename_for_metadata(tcx.sess, tcx.output_filenames(()));
     // To avoid races with another rustc process scanning the output directory,
     // we need to write the file somewhere else and atomically move it to its
     // final destination, with an `fs::rename` call. In order for the rename to
diff --git a/compiler/rustc_middle/src/infer/canonical.rs b/compiler/rustc_middle/src/infer/canonical.rs
index b17f1512886..2f2597d6b22 100644
--- a/compiler/rustc_middle/src/infer/canonical.rs
+++ b/compiler/rustc_middle/src/infer/canonical.rs
@@ -34,7 +34,7 @@ use std::ops::Index;
 /// variables have been rewritten to "canonical vars". These are
 /// numbered starting from 0 in order of first appearance.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyDecodable, TyEncodable)]
-#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
+#[derive(HashStable, TypeFoldable, TypeVisitable)]
 pub struct Canonical<'tcx, V> {
     pub value: V,
     pub max_universe: ty::UniverseIndex,
@@ -72,7 +72,7 @@ impl<'tcx> ty::TypeFoldable<TyCtxt<'tcx>> for CanonicalVarInfos<'tcx> {
 /// variables. You will need to supply it later to instantiate the
 /// canonicalized query response.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyDecodable, TyEncodable)]
-#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
+#[derive(HashStable, TypeFoldable, TypeVisitable)]
 pub struct CanonicalVarValues<'tcx> {
     pub var_values: ty::GenericArgsRef<'tcx>,
 }
@@ -311,7 +311,7 @@ pub enum CanonicalTyVarKind {
 /// After we execute a query with a canonicalized key, we get back a
 /// `Canonical<QueryResponse<..>>`. You can use
 /// `instantiate_query_result` to access the data in this result.
-#[derive(Clone, Debug, HashStable, TypeFoldable, TypeVisitable, Lift)]
+#[derive(Clone, Debug, HashStable, TypeFoldable, TypeVisitable)]
 pub struct QueryResponse<'tcx, R> {
     pub var_values: CanonicalVarValues<'tcx>,
     pub region_constraints: QueryRegionConstraints<'tcx>,
@@ -326,7 +326,7 @@ pub struct QueryResponse<'tcx, R> {
 }
 
 #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
-#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
+#[derive(HashStable, TypeFoldable, TypeVisitable)]
 pub struct QueryRegionConstraints<'tcx> {
     pub outlives: Vec<QueryOutlivesConstraint<'tcx>>,
     pub member_constraints: Vec<MemberConstraint<'tcx>>,
@@ -432,7 +432,7 @@ impl<'tcx, V> Canonical<'tcx, V> {
 pub type QueryOutlivesConstraint<'tcx> =
     (ty::OutlivesPredicate<GenericArg<'tcx>, Region<'tcx>>, ConstraintCategory<'tcx>);
 
-TrivialTypeTraversalAndLiftImpls! {
+TrivialTypeTraversalImpls! {
     crate::infer::canonical::Certainty,
     crate::infer::canonical::CanonicalTyVarKind,
 }
diff --git a/compiler/rustc_middle/src/infer/mod.rs b/compiler/rustc_middle/src/infer/mod.rs
index 493bb8a6823..1384611e146 100644
--- a/compiler/rustc_middle/src/infer/mod.rs
+++ b/compiler/rustc_middle/src/infer/mod.rs
@@ -13,7 +13,7 @@ use rustc_span::Span;
 /// R0 member of [O1..On]
 /// ```
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
-#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
+#[derive(HashStable, TypeFoldable, TypeVisitable)]
 pub struct MemberConstraint<'tcx> {
     /// The `DefId` and args of the opaque type causing this constraint.
     /// Used for error reporting.
diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs
index fca16d8e509..c1884bb8068 100644
--- a/compiler/rustc_middle/src/macros.rs
+++ b/compiler/rustc_middle/src/macros.rs
@@ -42,7 +42,7 @@ macro_rules! span_bug {
 // the impls for you.
 
 #[macro_export]
-macro_rules! CloneLiftImpls {
+macro_rules! TrivialLiftImpls {
     ($($ty:ty),+ $(,)?) => {
         $(
             impl<'tcx> $crate::ty::Lift<'tcx> for $ty {
@@ -96,6 +96,6 @@ macro_rules! TrivialTypeTraversalImpls {
 macro_rules! TrivialTypeTraversalAndLiftImpls {
     ($($t:tt)*) => {
         TrivialTypeTraversalImpls! { $($t)* }
-        CloneLiftImpls! { $($t)* }
+        TrivialLiftImpls! { $($t)* }
     }
 }
diff --git a/compiler/rustc_middle/src/mir/basic_blocks.rs b/compiler/rustc_middle/src/mir/basic_blocks.rs
index 70d8f3bd54b..cd770c395e4 100644
--- a/compiler/rustc_middle/src/mir/basic_blocks.rs
+++ b/compiler/rustc_middle/src/mir/basic_blocks.rs
@@ -178,7 +178,7 @@ impl<'tcx> graph::WithPredecessors for BasicBlocks<'tcx> {
     }
 }
 
-TrivialTypeTraversalAndLiftImpls! { Cache }
+TrivialTypeTraversalImpls! { Cache }
 
 impl<S: Encoder> Encodable<S> for Cache {
     #[inline]
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index 7e3be0b5d5f..2194ef81cec 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -67,7 +67,7 @@ impl Into<ErrorGuaranteed> for ReportedErrorInfo {
     }
 }
 
-TrivialTypeTraversalAndLiftImpls! { ErrorHandled }
+TrivialTypeTraversalImpls! { ErrorHandled }
 
 pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
 pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs
index 44d1dcbbe17..8c00746a180 100644
--- a/compiler/rustc_middle/src/mir/interpret/mod.rs
+++ b/compiler/rustc_middle/src/mir/interpret/mod.rs
@@ -162,7 +162,7 @@ pub use self::pointer::{Pointer, PointerArithmetic, Provenance};
 /// - A constant
 /// - A static
 #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, TyEncodable, TyDecodable)]
-#[derive(HashStable, Lift, TypeFoldable, TypeVisitable)]
+#[derive(HashStable, TypeFoldable, TypeVisitable)]
 pub struct GlobalId<'tcx> {
     /// For a constant or static, the `Instance` of the item itself.
     /// For a promoted global, the `Instance` of the function they belong to.
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 8df3a79b4d4..5e749776992 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -744,7 +744,7 @@ pub enum BindingForm<'tcx> {
     RefForGuard,
 }
 
-TrivialTypeTraversalAndLiftImpls! { BindingForm<'tcx> }
+TrivialTypeTraversalImpls! { BindingForm<'tcx> }
 
 mod binding_form_impl {
     use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
@@ -2295,7 +2295,7 @@ pub struct Constant<'tcx> {
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable, Debug)]
-#[derive(Lift, TypeFoldable, TypeVisitable)]
+#[derive(TypeFoldable, TypeVisitable)]
 pub enum ConstantKind<'tcx> {
     /// This constant came from the type system.
     ///
@@ -2615,7 +2615,7 @@ impl<'tcx> ConstantKind<'tcx> {
 }
 
 /// An unevaluated (potentially generic) constant used in MIR.
-#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Lift)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
 #[derive(Hash, HashStable, TypeFoldable, TypeVisitable)]
 pub struct UnevaluatedConst<'tcx> {
     pub def: DefId,
diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs
index 71bec49af93..0c80610b308 100644
--- a/compiler/rustc_middle/src/mir/query.rs
+++ b/compiler/rustc_middle/src/mir/query.rs
@@ -334,7 +334,7 @@ rustc_data_structures::static_assert_size!(ConstraintCategory<'_>, 16);
 ///
 /// See also `rustc_const_eval::borrow_check::constraints`.
 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
-#[derive(TyEncodable, TyDecodable, HashStable, Lift, TypeVisitable, TypeFoldable)]
+#[derive(TyEncodable, TyDecodable, HashStable, TypeVisitable, TypeFoldable)]
 pub enum ConstraintCategory<'tcx> {
     Return(ReturnConstraint),
     Yield,
diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs
index 06874741bb0..8d427fdb6f5 100644
--- a/compiler/rustc_middle/src/mir/type_foldable.rs
+++ b/compiler/rustc_middle/src/mir/type_foldable.rs
@@ -5,7 +5,7 @@ use rustc_ast::InlineAsmTemplatePiece;
 use super::*;
 use crate::ty;
 
-TrivialTypeTraversalAndLiftImpls! {
+TrivialTypeTraversalImpls! {
     BlockTailInfo,
     MirPhase,
     SourceInfo,
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 3465759b913..1340e674568 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -13,7 +13,7 @@ use crate::infer::canonical::Canonical;
 use crate::mir::ConstraintCategory;
 use crate::ty::abstract_const::NotConstEvaluatable;
 use crate::ty::GenericArgsRef;
-use crate::ty::{self, AdtKind, Ty, TyCtxt};
+use crate::ty::{self, AdtKind, Ty};
 
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{Applicability, Diagnostic};
@@ -86,7 +86,7 @@ pub enum Reveal {
 ///
 /// We do not want to intern this as there are a lot of obligation causes which
 /// only live for a short period of time.
-#[derive(Clone, Debug, PartialEq, Eq, Lift, HashStable, TyEncodable, TyDecodable)]
+#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
 #[derive(TypeVisitable, TypeFoldable)]
 pub struct ObligationCause<'tcx> {
     pub span: Span,
@@ -194,7 +194,7 @@ impl<'tcx> ObligationCause<'tcx> {
     }
 }
 
-#[derive(Clone, Debug, PartialEq, Eq, Lift, HashStable, TyEncodable, TyDecodable)]
+#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
 #[derive(TypeVisitable, TypeFoldable)]
 pub struct UnifyReceiverContext<'tcx> {
     pub assoc_item: ty::AssocItem,
@@ -202,7 +202,7 @@ pub struct UnifyReceiverContext<'tcx> {
     pub args: GenericArgsRef<'tcx>,
 }
 
-#[derive(Clone, PartialEq, Eq, Lift, Default, HashStable)]
+#[derive(Clone, PartialEq, Eq, Default, HashStable)]
 #[derive(TypeVisitable, TypeFoldable, TyEncodable, TyDecodable)]
 pub struct InternedObligationCauseCode<'tcx> {
     /// `None` for `ObligationCauseCode::MiscObligation` (a common case, occurs ~60% of
@@ -238,7 +238,7 @@ impl<'tcx> std::ops::Deref for InternedObligationCauseCode<'tcx> {
     }
 }
 
-#[derive(Clone, Debug, PartialEq, Eq, Lift, HashStable, TyEncodable, TyDecodable)]
+#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
 #[derive(TypeVisitable, TypeFoldable)]
 pub enum ObligationCauseCode<'tcx> {
     /// Not well classified or should be obvious from the span.
@@ -470,7 +470,7 @@ pub enum WellFormedLoc {
     },
 }
 
-#[derive(Clone, Debug, PartialEq, Eq, Lift, HashStable, TyEncodable, TyDecodable)]
+#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
 #[derive(TypeVisitable, TypeFoldable)]
 pub struct ImplDerivedObligationCause<'tcx> {
     pub derived: DerivedObligationCause<'tcx>,
@@ -524,14 +524,7 @@ pub enum StatementAsExpression {
     NeedsBoxing,
 }
 
-impl<'tcx> ty::Lift<'tcx> for StatementAsExpression {
-    type Lifted = StatementAsExpression;
-    fn lift_to_tcx(self, _tcx: TyCtxt<'tcx>) -> Option<StatementAsExpression> {
-        Some(self)
-    }
-}
-
-#[derive(Clone, Debug, PartialEq, Eq, Lift, HashStable, TyEncodable, TyDecodable)]
+#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
 #[derive(TypeVisitable, TypeFoldable)]
 pub struct MatchExpressionArmCause<'tcx> {
     pub arm_block_id: Option<hir::HirId>,
@@ -547,7 +540,7 @@ pub struct MatchExpressionArmCause<'tcx> {
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
-#[derive(Lift, TypeFoldable, TypeVisitable, HashStable, TyEncodable, TyDecodable)]
+#[derive(TypeFoldable, TypeVisitable, HashStable, TyEncodable, TyDecodable)]
 pub struct IfExpressionCause<'tcx> {
     pub then_id: hir::HirId,
     pub else_id: hir::HirId,
@@ -557,7 +550,7 @@ pub struct IfExpressionCause<'tcx> {
     pub opt_suggest_box_span: Option<Span>,
 }
 
-#[derive(Clone, Debug, PartialEq, Eq, Lift, HashStable, TyEncodable, TyDecodable)]
+#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
 #[derive(TypeVisitable, TypeFoldable)]
 pub struct DerivedObligationCause<'tcx> {
     /// The trait predicate of the parent obligation that led to the
@@ -570,7 +563,7 @@ pub struct DerivedObligationCause<'tcx> {
     pub parent_code: InternedObligationCauseCode<'tcx>,
 }
 
-#[derive(Clone, Debug, TypeVisitable, Lift)]
+#[derive(Clone, Debug, TypeVisitable)]
 pub enum SelectionError<'tcx> {
     /// The trait is not implemented.
     Unimplemented,
@@ -593,7 +586,7 @@ pub enum SelectionError<'tcx> {
     OpaqueTypeAutoTraitLeakageUnknown(DefId),
 }
 
-#[derive(Clone, Debug, TypeVisitable, Lift)]
+#[derive(Clone, Debug, TypeVisitable)]
 pub struct SelectionOutputTypeParameterMismatch<'tcx> {
     pub found_trait_ref: ty::PolyTraitRef<'tcx>,
     pub expected_trait_ref: ty::PolyTraitRef<'tcx>,
@@ -638,7 +631,7 @@ pub type SelectionResult<'tcx, T> = Result<Option<T>, SelectionError<'tcx>>;
 /// ### The type parameter `N`
 ///
 /// See explanation on `ImplSourceUserDefinedData`.
-#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)]
+#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
 #[derive(TypeFoldable, TypeVisitable)]
 pub enum ImplSource<'tcx, N> {
     /// ImplSource identifying a particular impl.
@@ -704,7 +697,7 @@ impl<'tcx, N> ImplSource<'tcx, N> {
 /// is `Obligation`, as one might expect. During codegen, however, this
 /// is `()`, because codegen only requires a shallow resolution of an
 /// impl, and nested obligations are satisfied later.
-#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable, Lift)]
+#[derive(Clone, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
 #[derive(TypeFoldable, TypeVisitable)]
 pub struct ImplSourceUserDefinedData<'tcx, N> {
     pub impl_def_id: DefId,
@@ -736,7 +729,7 @@ pub enum BuiltinImplSource {
     TupleUnsizing,
 }
 
-TrivialTypeTraversalAndLiftImpls! { BuiltinImplSource }
+TrivialTypeTraversalImpls! { BuiltinImplSource }
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash, HashStable, PartialOrd, Ord)]
 pub enum ObjectSafetyViolation {
diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs
index 950a59e9695..975e3e3ac62 100644
--- a/compiler/rustc_middle/src/traits/query.rs
+++ b/compiler/rustc_middle/src/traits/query.rs
@@ -17,8 +17,7 @@ pub mod type_op {
     use crate::ty::{Predicate, Ty, TyCtxt, UserType};
     use std::fmt;
 
-    #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, Lift)]
-    #[derive(TypeFoldable, TypeVisitable)]
+    #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)]
     pub struct AscribeUserType<'tcx> {
         pub mir_ty: Ty<'tcx>,
         pub user_ty: UserType<'tcx>,
@@ -30,22 +29,19 @@ pub mod type_op {
         }
     }
 
-    #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, Lift)]
-    #[derive(TypeFoldable, TypeVisitable)]
+    #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)]
     pub struct Eq<'tcx> {
         pub a: Ty<'tcx>,
         pub b: Ty<'tcx>,
     }
 
-    #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, Lift)]
-    #[derive(TypeFoldable, TypeVisitable)]
+    #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)]
     pub struct Subtype<'tcx> {
         pub sub: Ty<'tcx>,
         pub sup: Ty<'tcx>,
     }
 
-    #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, Lift)]
-    #[derive(TypeFoldable, TypeVisitable)]
+    #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)]
     pub struct ProvePredicate<'tcx> {
         pub predicate: Predicate<'tcx>,
     }
@@ -56,8 +52,7 @@ pub mod type_op {
         }
     }
 
-    #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, Lift)]
-    #[derive(TypeFoldable, TypeVisitable)]
+    #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HashStable, TypeFoldable, TypeVisitable)]
     pub struct Normalize<T> {
         pub value: T,
     }
@@ -101,7 +96,7 @@ impl<'tcx> From<TypeError<'tcx>> for NoSolution {
     }
 }
 
-#[derive(Clone, Debug, Default, HashStable, TypeFoldable, TypeVisitable, Lift)]
+#[derive(Clone, Debug, Default, HashStable, TypeFoldable, TypeVisitable)]
 pub struct DropckOutlivesResult<'tcx> {
     pub kinds: Vec<GenericArg<'tcx>>,
     pub overflows: Vec<Ty<'tcx>>,
@@ -194,7 +189,7 @@ pub struct MethodAutoderefBadTy<'tcx> {
 }
 
 /// Result from the `normalize_projection_ty` query.
-#[derive(Clone, Debug, HashStable, TypeFoldable, TypeVisitable, Lift)]
+#[derive(Clone, Debug, HashStable, TypeFoldable, TypeVisitable)]
 pub struct NormalizationResult<'tcx> {
     /// Result of normalization.
     pub normalized_ty: Ty<'tcx>,
@@ -207,7 +202,7 @@ pub struct NormalizationResult<'tcx> {
 /// case they are called implied bounds). They are fed to the
 /// `OutlivesEnv` which in turn is supplied to the region checker and
 /// other parts of the inference system.
-#[derive(Clone, Debug, TypeFoldable, TypeVisitable, Lift, HashStable)]
+#[derive(Clone, Debug, TypeFoldable, TypeVisitable, HashStable)]
 pub enum OutlivesBound<'tcx> {
     RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>),
     RegionSubParam(ty::Region<'tcx>, ty::ParamTy),
diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs
index ffae3579889..90bc5dd8f69 100644
--- a/compiler/rustc_middle/src/traits/select.rs
+++ b/compiler/rustc_middle/src/traits/select.rs
@@ -305,7 +305,7 @@ impl From<ErrorGuaranteed> for OverflowError {
     }
 }
 
-TrivialTypeTraversalAndLiftImpls! { OverflowError }
+TrivialTypeTraversalImpls! { OverflowError }
 
 impl<'tcx> From<OverflowError> for SelectionError<'tcx> {
     fn from(overflow_error: OverflowError) -> SelectionError<'tcx> {
diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs
index d8b3a061b77..c3ed40867cf 100644
--- a/compiler/rustc_middle/src/traits/solve/inspect.rs
+++ b/compiler/rustc_middle/src/traits/solve/inspect.rs
@@ -15,9 +15,15 @@ pub enum CacheHit {
 }
 
 #[derive(Eq, PartialEq)]
+pub enum GoalEvaluationKind {
+    Root,
+    Nested { is_normalizes_to_hack: IsNormalizesToHack },
+}
+
+#[derive(Eq, PartialEq)]
 pub struct GoalEvaluation<'tcx> {
     pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>,
-    pub is_normalizes_to_hack: IsNormalizesToHack,
+    pub kind: GoalEvaluationKind,
     pub evaluation: CanonicalGoalEvaluation<'tcx>,
     pub returned_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
 }
@@ -25,12 +31,12 @@ pub struct GoalEvaluation<'tcx> {
 #[derive(Eq, PartialEq)]
 pub struct CanonicalGoalEvaluation<'tcx> {
     pub goal: CanonicalInput<'tcx>,
-    pub kind: GoalEvaluationKind<'tcx>,
+    pub kind: CanonicalGoalEvaluationKind<'tcx>,
     pub result: QueryResult<'tcx>,
 }
 
 #[derive(Eq, PartialEq)]
-pub enum GoalEvaluationKind<'tcx> {
+pub enum CanonicalGoalEvaluationKind<'tcx> {
     Overflow,
     CacheHit(CacheHit),
     Uncached { revisions: Vec<GoalEvaluationStep<'tcx>> },
@@ -52,22 +58,31 @@ pub struct GoalEvaluationStep<'tcx> {
     pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
 
     /// The actual evaluation of the goal, always `ProbeKind::Root`.
-    pub evaluation: GoalCandidate<'tcx>,
+    pub evaluation: Probe<'tcx>,
 }
 
+/// A self-contained computation during trait solving. This either
+/// corresponds to a `EvalCtxt::probe(_X)` call or the root evaluation
+/// of a goal.
 #[derive(Eq, PartialEq)]
-pub struct GoalCandidate<'tcx> {
-    pub added_goals_evaluations: Vec<AddedGoalsEvaluation<'tcx>>,
-    pub candidates: Vec<GoalCandidate<'tcx>>,
+pub struct Probe<'tcx> {
+    pub steps: Vec<ProbeStep<'tcx>>,
     pub kind: ProbeKind<'tcx>,
 }
 
-impl Debug for GoalCandidate<'_> {
+impl Debug for Probe<'_> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        ProofTreeFormatter::new(f).format_candidate(self)
+        ProofTreeFormatter::new(f).format_probe(self)
     }
 }
 
+#[derive(Eq, PartialEq)]
+pub enum ProbeStep<'tcx> {
+    AddGoal(Goal<'tcx, ty::Predicate<'tcx>>),
+    EvaluateGoals(AddedGoalsEvaluation<'tcx>),
+    NestedProbe(Probe<'tcx>),
+}
+
 #[derive(Debug, PartialEq, Eq)]
 pub enum ProbeKind<'tcx> {
     /// The root inference context while proving a goal.
diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs
index d916e80a625..d33e83ae1ed 100644
--- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs
+++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs
@@ -40,9 +40,12 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
     }
 
     pub(super) fn format_goal_evaluation(&mut self, eval: &GoalEvaluation<'_>) -> std::fmt::Result {
-        let goal_text = match eval.is_normalizes_to_hack {
-            IsNormalizesToHack::Yes => "NORMALIZES-TO HACK GOAL",
-            IsNormalizesToHack::No => "GOAL",
+        let goal_text = match eval.kind {
+            GoalEvaluationKind::Root => "ROOT GOAL",
+            GoalEvaluationKind::Nested { is_normalizes_to_hack } => match is_normalizes_to_hack {
+                IsNormalizesToHack::No => "GOAL",
+                IsNormalizesToHack::Yes => "NORMALIZES-TO HACK GOAL",
+            },
         };
         writeln!(self.f, "{}: {:?}", goal_text, eval.uncanonicalized_goal)?;
         self.nested(|this| this.format_canonical_goal_evaluation(&eval.evaluation))?;
@@ -68,16 +71,16 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
         writeln!(self.f, "GOAL: {:?}", eval.goal)?;
 
         match &eval.kind {
-            GoalEvaluationKind::Overflow => {
+            CanonicalGoalEvaluationKind::Overflow => {
                 writeln!(self.f, "OVERFLOW: {:?}", eval.result)
             }
-            GoalEvaluationKind::CacheHit(CacheHit::Global) => {
+            CanonicalGoalEvaluationKind::CacheHit(CacheHit::Global) => {
                 writeln!(self.f, "GLOBAL CACHE HIT: {:?}", eval.result)
             }
-            GoalEvaluationKind::CacheHit(CacheHit::Provisional) => {
+            CanonicalGoalEvaluationKind::CacheHit(CacheHit::Provisional) => {
                 writeln!(self.f, "PROVISIONAL CACHE HIT: {:?}", eval.result)
             }
-            GoalEvaluationKind::Uncached { revisions } => {
+            CanonicalGoalEvaluationKind::Uncached { revisions } => {
                 for (n, step) in revisions.iter().enumerate() {
                     writeln!(self.f, "REVISION {n}")?;
                     self.nested(|this| this.format_evaluation_step(step))?;
@@ -92,11 +95,11 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
         evaluation_step: &GoalEvaluationStep<'_>,
     ) -> std::fmt::Result {
         writeln!(self.f, "INSTANTIATED: {:?}", evaluation_step.instantiated_goal)?;
-        self.format_candidate(&evaluation_step.evaluation)
+        self.format_probe(&evaluation_step.evaluation)
     }
 
-    pub(super) fn format_candidate(&mut self, candidate: &GoalCandidate<'_>) -> std::fmt::Result {
-        match &candidate.kind {
+    pub(super) fn format_probe(&mut self, probe: &Probe<'_>) -> std::fmt::Result {
+        match &probe.kind {
             ProbeKind::Root { result } => {
                 writeln!(self.f, "ROOT RESULT: {result:?}")
             }
@@ -118,11 +121,12 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
         }?;
 
         self.nested(|this| {
-            for candidate in &candidate.candidates {
-                this.format_candidate(candidate)?;
-            }
-            for nested in &candidate.added_goals_evaluations {
-                this.format_added_goals_evaluation(nested)?;
+            for step in &probe.steps {
+                match step {
+                    ProbeStep::AddGoal(goal) => writeln!(this.f, "ADDED GOAL: {goal:?}")?,
+                    ProbeStep::EvaluateGoals(eval) => this.format_added_goals_evaluation(eval)?,
+                    ProbeStep::NestedProbe(probe) => this.format_probe(probe)?,
+                }
             }
             Ok(())
         })
diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs
index cdd8351499b..570f896ba29 100644
--- a/compiler/rustc_middle/src/ty/abstract_const.rs
+++ b/compiler/rustc_middle/src/ty/abstract_const.rs
@@ -27,7 +27,7 @@ impl From<ErrorGuaranteed> for NotConstEvaluatable {
     }
 }
 
-TrivialTypeTraversalAndLiftImpls! { NotConstEvaluatable }
+TrivialTypeTraversalImpls! { NotConstEvaluatable }
 
 pub type BoundAbstractConst<'tcx> = Result<Option<EarlyBinder<ty::Const<'tcx>>>, ErrorGuaranteed>;
 
diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs
index 76931ceaa69..c3e8991c63a 100644
--- a/compiler/rustc_middle/src/ty/adjustment.rs
+++ b/compiler/rustc_middle/src/ty/adjustment.rs
@@ -76,7 +76,7 @@ pub enum PointerCoercion {
 ///    At some point, of course, `Box` should move out of the compiler, in which
 ///    case this is analogous to transforming a struct. E.g., `Box<[i32; 4]>` ->
 ///    `Box<[i32]>` is an `Adjust::Unsize` with the target `Box<[i32]>`.
-#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable, Lift)]
+#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
 pub struct Adjustment<'tcx> {
     pub kind: Adjust<'tcx>,
     pub target: Ty<'tcx>,
@@ -88,7 +88,7 @@ impl<'tcx> Adjustment<'tcx> {
     }
 }
 
-#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable, Lift)]
+#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
 pub enum Adjust<'tcx> {
     /// Go from ! to any type.
     NeverToAny,
@@ -110,7 +110,7 @@ pub enum Adjust<'tcx> {
 /// The target type is `U` in both cases, with the region and mutability
 /// being those shared by both the receiver and the returned reference.
 #[derive(Copy, Clone, PartialEq, Debug, TyEncodable, TyDecodable, HashStable)]
-#[derive(TypeFoldable, TypeVisitable, Lift)]
+#[derive(TypeFoldable, TypeVisitable)]
 pub struct OverloadedDeref<'tcx> {
     pub region: ty::Region<'tcx>,
     pub mutbl: hir::Mutability,
@@ -182,7 +182,7 @@ impl From<AutoBorrowMutability> for hir::Mutability {
 }
 
 #[derive(Copy, Clone, PartialEq, Debug, TyEncodable, TyDecodable, HashStable)]
-#[derive(TypeFoldable, TypeVisitable, Lift)]
+#[derive(TypeFoldable, TypeVisitable)]
 pub enum AutoBorrow<'tcx> {
     /// Converts from T to &T.
     Ref(ty::Region<'tcx>, AutoBorrowMutability),
diff --git a/compiler/rustc_middle/src/ty/binding.rs b/compiler/rustc_middle/src/ty/binding.rs
index 2fec8ac9095..af594bc5f24 100644
--- a/compiler/rustc_middle/src/ty/binding.rs
+++ b/compiler/rustc_middle/src/ty/binding.rs
@@ -6,7 +6,7 @@ pub enum BindingMode {
     BindByValue(Mutability),
 }
 
-TrivialTypeTraversalAndLiftImpls! { BindingMode }
+TrivialTypeTraversalImpls! { BindingMode }
 
 impl BindingMode {
     pub fn convert(BindingAnnotation(by_ref, mutbl): BindingAnnotation) -> BindingMode {
diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs
index e25402fe0c2..749b54ca0be 100644
--- a/compiler/rustc_middle/src/ty/consts/kind.rs
+++ b/compiler/rustc_middle/src/ty/consts/kind.rs
@@ -8,7 +8,7 @@ use rustc_hir::def_id::DefId;
 use rustc_macros::HashStable;
 
 /// An unevaluated (potentially generic) constant used in the type-system.
-#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable, Lift)]
+#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, TyEncodable, TyDecodable)]
 #[derive(Hash, HashStable, TypeFoldable, TypeVisitable)]
 pub struct UnevaluatedConst<'tcx> {
     pub def: DefId,
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index fcb1a6971e9..e8d50a1c025 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -50,7 +50,7 @@ use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
 use rustc_hir::definitions::Definitions;
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::lang_items::LangItem;
-use rustc_hir::{Constness, HirId, Node, TraitCandidate};
+use rustc_hir::{HirId, Node, TraitCandidate};
 use rustc_index::IndexVec;
 use rustc_macros::HashStable;
 use rustc_query_system::dep_graph::DepNodeIndex;
@@ -1275,19 +1275,13 @@ nop_lift! {predicate; Clause<'a> => Clause<'tcx>}
 
 nop_list_lift! {type_lists; Ty<'a> => Ty<'tcx>}
 nop_list_lift! {poly_existential_predicates; PolyExistentialPredicate<'a> => PolyExistentialPredicate<'tcx>}
-nop_list_lift! {clauses; Clause<'a> => Clause<'tcx>}
-nop_list_lift! {canonical_var_infos; CanonicalVarInfo<'a> => CanonicalVarInfo<'tcx>}
-nop_list_lift! {projs; ProjectionKind => ProjectionKind}
 nop_list_lift! {bound_variable_kinds; ty::BoundVariableKind => ty::BoundVariableKind}
 
 // This is the impl for `&'a GenericArgs<'a>`.
 nop_list_lift! {args; GenericArg<'a> => GenericArg<'tcx>}
 
-CloneLiftImpls! {
-    Constness,
-    traits::WellFormedLoc,
+TrivialLiftImpls! {
     ImplPolarity,
-    crate::mir::ReturnConstraint,
 }
 
 macro_rules! sty_debug_print {
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index bf6f082c21c..f939d466078 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -11,7 +11,7 @@ use std::collections::hash_map::DefaultHasher;
 use std::hash::{Hash, Hasher};
 use std::path::PathBuf;
 
-#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable, TypeVisitable, Lift)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable, TypeVisitable)]
 pub struct ExpectedFound<T> {
     pub expected: T,
     pub found: T,
@@ -28,7 +28,7 @@ impl<T> ExpectedFound<T> {
 }
 
 // Data structures used in type unification
-#[derive(Copy, Clone, Debug, TypeVisitable, Lift, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, TypeVisitable, PartialEq, Eq)]
 #[rustc_pass_by_value]
 pub enum TypeError<'tcx> {
     Mismatch,
diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs
index e598ead791e..72390e4bbb0 100644
--- a/compiler/rustc_middle/src/ty/generic_args.rs
+++ b/compiler/rustc_middle/src/ty/generic_args.rs
@@ -1029,7 +1029,7 @@ impl<'a, 'tcx> ArgFolder<'a, 'tcx> {
 /// Stores the user-given args to reach some fully qualified path
 /// (e.g., `<T>::Item` or `<T as Trait>::Item`).
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
-#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
+#[derive(HashStable, TypeFoldable, TypeVisitable)]
 pub struct UserArgs<'tcx> {
     /// The args for the item as given by the user.
     pub args: GenericArgsRef<'tcx>,
@@ -1056,7 +1056,7 @@ pub struct UserArgs<'tcx> {
 /// the self type, giving `Foo<?A>`. Finally, we unify that with
 /// the self type here, which contains `?A` to be `&'static u32`
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
-#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
+#[derive(HashStable, TypeFoldable, TypeVisitable)]
 pub struct UserSelfTy<'tcx> {
     pub impl_def_id: DefId,
     pub self_ty: Ty<'tcx>,
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index e5b9203d12a..2b75f6c4e8f 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -18,6 +18,9 @@ use std::fmt;
 /// Monomorphization happens on-the-fly and no monomorphized MIR is ever created. Instead, this type
 /// simply couples a potentially generic `InstanceDef` with some args, and codegen and const eval
 /// will do all required substitution as they run.
+///
+/// Note: the `Lift` impl is currently not used by rustc, but is used by
+/// rustc_codegen_cranelift when the `jit` feature is enabled.
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)]
 #[derive(HashStable, Lift, TypeFoldable, TypeVisitable)]
 pub struct Instance<'tcx> {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 99b697d0ea5..eb8ea0bc114 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1510,7 +1510,7 @@ impl<'a, 'tcx> IntoIterator for &'a InstantiatedPredicates<'tcx> {
     }
 }
 
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable, Lift)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable)]
 #[derive(TypeFoldable, TypeVisitable)]
 pub struct OpaqueTypeKey<'tcx> {
     pub def_id: LocalDefId,
@@ -1793,7 +1793,7 @@ impl<'tcx> ParamEnv<'tcx> {
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TypeFoldable, TypeVisitable)]
-#[derive(HashStable, Lift)]
+#[derive(HashStable)]
 pub struct ParamEnvAnd<'tcx, T> {
     pub param_env: ParamEnv<'tcx>,
     pub value: T,
@@ -2408,6 +2408,22 @@ impl<'tcx> TyCtxt<'tcx> {
         }
     }
 
+    pub fn get_attrs_by_path<'attr>(
+        self,
+        did: DefId,
+        attr: &'attr [Symbol],
+    ) -> impl Iterator<Item = &'tcx ast::Attribute> + 'attr
+    where
+        'tcx: 'attr,
+    {
+        let filter_fn = move |a: &&ast::Attribute| a.path_matches(&attr);
+        if let Some(did) = did.as_local() {
+            self.hir().attrs(self.hir().local_def_id_to_hir_id(did)).iter().filter(filter_fn)
+        } else {
+            self.item_attrs(did).iter().filter(filter_fn)
+        }
+    }
+
     pub fn get_attr(self, did: impl Into<DefId>, attr: Symbol) -> Option<&'tcx ast::Attribute> {
         if cfg!(debug_assertions) && !rustc_feature::is_valid_for_get_attr(attr) {
             let did: DefId = did.into();
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index f24ac79323f..e1d4e43841d 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -136,10 +136,8 @@ define_helper!(
 ///
 /// Regions not selected by the region highlight mode are presently
 /// unaffected.
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Default)]
 pub struct RegionHighlightMode<'tcx> {
-    tcx: TyCtxt<'tcx>,
-
     /// If enabled, when we see the selected region, use "`'N`"
     /// instead of the ordinary behavior.
     highlight_regions: [Option<(ty::Region<'tcx>, usize)>; 3],
@@ -155,14 +153,6 @@ pub struct RegionHighlightMode<'tcx> {
 }
 
 impl<'tcx> RegionHighlightMode<'tcx> {
-    pub fn new(tcx: TyCtxt<'tcx>) -> Self {
-        Self {
-            tcx,
-            highlight_regions: Default::default(),
-            highlight_bound_region: Default::default(),
-        }
-    }
-
     /// If `region` and `number` are both `Some`, invokes
     /// `highlighting_region`.
     pub fn maybe_highlighting_region(
@@ -188,8 +178,13 @@ impl<'tcx> RegionHighlightMode<'tcx> {
     }
 
     /// Convenience wrapper for `highlighting_region`.
-    pub fn highlighting_region_vid(&mut self, vid: ty::RegionVid, number: usize) {
-        self.highlighting_region(ty::Region::new_var(self.tcx, vid), number)
+    pub fn highlighting_region_vid(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        vid: ty::RegionVid,
+        number: usize,
+    ) {
+        self.highlighting_region(ty::Region::new_var(tcx, vid), number)
     }
 
     /// Returns `Some(n)` with the number to use for the given region, if any.
@@ -1778,7 +1773,7 @@ impl<'a, 'tcx> FmtPrinter<'a, 'tcx> {
             printed_type_count: 0,
             type_length_limit,
             truncated: false,
-            region_highlight_mode: RegionHighlightMode::new(tcx),
+            region_highlight_mode: RegionHighlightMode::default(),
             ty_infer_name_resolver: None,
             const_infer_name_resolver: None,
         }))
@@ -2746,20 +2741,14 @@ forward_display_to_print! {
 
     // HACK(eddyb) these are exhaustive instead of generic,
     // because `for<'tcx>` isn't possible yet.
-    ty::PolyExistentialPredicate<'tcx>,
     ty::PolyExistentialProjection<'tcx>,
     ty::PolyExistentialTraitRef<'tcx>,
     ty::Binder<'tcx, ty::TraitRef<'tcx>>,
     ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>,
-    ty::Binder<'tcx, TraitRefPrintOnlyTraitName<'tcx>>,
     ty::Binder<'tcx, ty::FnSig<'tcx>>,
     ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
     ty::Binder<'tcx, TraitPredPrintModifiersAndPath<'tcx>>,
-    ty::Binder<'tcx, ty::SubtypePredicate<'tcx>>,
     ty::Binder<'tcx, ty::ProjectionPredicate<'tcx>>,
-    ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>,
-    ty::Binder<'tcx, ty::OutlivesPredicate<ty::Region<'tcx>, ty::Region<'tcx>>>,
-
     ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>,
     ty::OutlivesPredicate<ty::Region<'tcx>, ty::Region<'tcx>>
 }
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index 1b29a83f23e..f4158597d10 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -9,15 +9,13 @@ use crate::ty::print::{with_no_trimmed_paths, FmtPrinter, Printer};
 use crate::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
 use crate::ty::{self, AliasTy, InferConst, Lift, Term, TermKind, Ty, TyCtxt};
 use rustc_hir::def::Namespace;
-use rustc_index::{Idx, IndexVec};
 use rustc_target::abi::TyAndLayout;
 use rustc_type_ir::{ConstKind, DebugWithInfcx, InferCtxtLike, OptWithInfcx};
 
 use std::fmt::{self, Debug};
 use std::ops::ControlFlow;
-use std::rc::Rc;
-use std::sync::Arc;
 
+use super::print::PrettyPrinter;
 use super::{GenericArg, GenericArgKind, Region};
 
 impl fmt::Debug for ty::TraitDef {
@@ -343,14 +341,27 @@ impl<'tcx> DebugWithInfcx<TyCtxt<'tcx>> for ty::Const<'tcx> {
         this: OptWithInfcx<'_, TyCtxt<'tcx>, InfCtx, &Self>,
         f: &mut core::fmt::Formatter<'_>,
     ) -> core::fmt::Result {
-        // This reflects what `Const` looked liked before `Interned` was
-        // introduced. We print it like this to avoid having to update expected
-        // output in a lot of tests.
+        // If this is a value, we spend some effort to make it look nice.
+        if let ConstKind::Value(_) = this.data.kind() {
+            return ty::tls::with(move |tcx| {
+                // Somehow trying to lift the valtree results in lifetime errors, so we lift the
+                // entire constant.
+                let lifted = tcx.lift(*this.data).unwrap();
+                let ConstKind::Value(valtree) = lifted.kind() else {
+                    bug!("we checked that this is a valtree")
+                };
+                let cx = FmtPrinter::new(tcx, Namespace::ValueNS);
+                let cx =
+                    cx.pretty_print_const_valtree(valtree, lifted.ty(), /*print_ty*/ true)?;
+                f.write_str(&cx.into_buffer())
+            });
+        }
+        // Fall back to something verbose.
         write!(
             f,
-            "Const {{ ty: {:?}, kind: {:?} }}",
-            &this.map(|data| data.ty()),
-            &this.map(|data| data.kind())
+            "{kind:?}: {ty:?}",
+            ty = &this.map(|data| data.ty()),
+            kind = &this.map(|data| data.kind())
         )
     }
 }
@@ -442,22 +453,16 @@ impl<'tcx, T: DebugWithInfcx<TyCtxt<'tcx>>> DebugWithInfcx<TyCtxt<'tcx>> for ty:
 
 // For things for which the type library provides traversal implementations
 // for all Interners, we only need to provide a Lift implementation:
-CloneLiftImpls! {
-    (),
-    bool,
-    usize,
-    u16,
-    u32,
-    u64,
-    String,
-    rustc_type_ir::DebruijnIndex,
-}
-
-// For things about which the type library does not know, or does not
-// provide any traversal implementations, we need to provide both a Lift
-// implementation and traversal implementations (the latter only for
-// TyCtxt<'_> interners).
-TrivialTypeTraversalAndLiftImpls! {
+TrivialLiftImpls! {
+     (),
+     bool,
+     usize,
+}
+
+// For some things about which the type library does not know, or does not
+// provide any traversal implementations, we need to provide a traversal
+// implementation (only for TyCtxt<'_> interners).
+TrivialTypeTraversalImpls! {
     ::rustc_target::abi::FieldIdx,
     ::rustc_target::abi::VariantIdx,
     crate::middle::region::Scope,
@@ -467,14 +472,10 @@ TrivialTypeTraversalAndLiftImpls! {
     ::rustc_ast::NodeId,
     ::rustc_span::symbol::Symbol,
     ::rustc_hir::def::Res,
-    ::rustc_hir::def_id::DefId,
     ::rustc_hir::def_id::LocalDefId,
     ::rustc_hir::HirId,
     ::rustc_hir::MatchSource,
-    ::rustc_hir::Mutability,
-    ::rustc_hir::Unsafety,
     ::rustc_target::asm::InlineAsmRegOrRegClass,
-    ::rustc_target::spec::abi::Abi,
     crate::mir::coverage::CounterId,
     crate::mir::coverage::ExpressionId,
     crate::mir::coverage::MappedExpressionIndex,
@@ -492,16 +493,12 @@ TrivialTypeTraversalAndLiftImpls! {
     crate::ty::AssocItem,
     crate::ty::AssocKind,
     crate::ty::AliasKind,
-    crate::ty::AliasRelationDirection,
     crate::ty::Placeholder<crate::ty::BoundRegion>,
     crate::ty::Placeholder<crate::ty::BoundTy>,
     crate::ty::Placeholder<ty::BoundVar>,
-    crate::ty::ClosureKind,
     crate::ty::FreeRegion,
     crate::ty::InferTy,
     crate::ty::IntVarValue,
-    crate::ty::ParamConst,
-    crate::ty::ParamTy,
     crate::ty::adjustment::PointerCoercion,
     crate::ty::RegionVid,
     crate::ty::UniverseIndex,
@@ -509,33 +506,30 @@ TrivialTypeTraversalAndLiftImpls! {
     ::rustc_span::Span,
     ::rustc_span::symbol::Ident,
     ::rustc_errors::ErrorGuaranteed,
-    interpret::Scalar,
-    interpret::AllocId,
-    rustc_target::abi::Size,
     ty::BoundVar,
+    ty::ValTree<'tcx>,
 }
-
+// For some things about which the type library does not know, or does not
+// provide any traversal implementations, we need to provide a traversal
+// implementation and a lift implementation (the former only for TyCtxt<'_>
+// interners).
 TrivialTypeTraversalAndLiftImpls! {
-    ty::ValTree<'tcx>,
+    ::rustc_hir::def_id::DefId,
+    ::rustc_hir::Mutability,
+    ::rustc_hir::Unsafety,
+    ::rustc_target::spec::abi::Abi,
+    crate::ty::AliasRelationDirection,
+    crate::ty::ClosureKind,
+    crate::ty::ParamConst,
+    crate::ty::ParamTy,
+    interpret::Scalar,
+    interpret::AllocId,
+    rustc_target::abi::Size,
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // Lift implementations
 
-impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>> Lift<'tcx> for (A, B) {
-    type Lifted = (A::Lifted, B::Lifted);
-    fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
-        Some((tcx.lift(self.0)?, tcx.lift(self.1)?))
-    }
-}
-
-impl<'tcx, A: Lift<'tcx>, B: Lift<'tcx>, C: Lift<'tcx>> Lift<'tcx> for (A, B, C) {
-    type Lifted = (A::Lifted, B::Lifted, C::Lifted);
-    fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
-        Some((tcx.lift(self.0)?, tcx.lift(self.1)?, tcx.lift(self.2)?))
-    }
-}
-
 impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Option<T> {
     type Lifted = Option<T::Lifted>;
     fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
@@ -546,50 +540,6 @@ impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Option<T> {
     }
 }
 
-impl<'tcx, T: Lift<'tcx>, E: Lift<'tcx>> Lift<'tcx> for Result<T, E> {
-    type Lifted = Result<T::Lifted, E::Lifted>;
-    fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
-        match self {
-            Ok(x) => tcx.lift(x).map(Ok),
-            Err(e) => tcx.lift(e).map(Err),
-        }
-    }
-}
-
-impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Box<T> {
-    type Lifted = Box<T::Lifted>;
-    fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
-        Some(Box::new(tcx.lift(*self)?))
-    }
-}
-
-impl<'tcx, T: Lift<'tcx> + Clone> Lift<'tcx> for Rc<T> {
-    type Lifted = Rc<T::Lifted>;
-    fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
-        Some(Rc::new(tcx.lift(self.as_ref().clone())?))
-    }
-}
-
-impl<'tcx, T: Lift<'tcx> + Clone> Lift<'tcx> for Arc<T> {
-    type Lifted = Arc<T::Lifted>;
-    fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
-        Some(Arc::new(tcx.lift(self.as_ref().clone())?))
-    }
-}
-impl<'tcx, T: Lift<'tcx>> Lift<'tcx> for Vec<T> {
-    type Lifted = Vec<T::Lifted>;
-    fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
-        self.into_iter().map(|v| tcx.lift(v)).collect()
-    }
-}
-
-impl<'tcx, I: Idx, T: Lift<'tcx>> Lift<'tcx> for IndexVec<I, T> {
-    type Lifted = IndexVec<I, T::Lifted>;
-    fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
-        self.into_iter().map(|e| tcx.lift(e)).collect()
-    }
-}
-
 impl<'a, 'tcx> Lift<'tcx> for Term<'a> {
     type Lifted = ty::Term<'tcx>;
     fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
@@ -602,13 +552,6 @@ impl<'a, 'tcx> Lift<'tcx> for Term<'a> {
         )
     }
 }
-impl<'a, 'tcx> Lift<'tcx> for ty::ParamEnv<'a> {
-    type Lifted = ty::ParamEnv<'tcx>;
-    fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
-        tcx.lift(self.caller_bounds())
-            .map(|caller_bounds| ty::ParamEnv::new(caller_bounds, self.reveal()))
-    }
-}
 
 ///////////////////////////////////////////////////////////////////////////
 // Traversal implementations.
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 2502e303f7a..edab7fe58ba 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -351,7 +351,7 @@ impl<'tcx> ClosureArgs<'tcx> {
 }
 
 /// Similar to `ClosureArgs`; see the above documentation for more.
-#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable, Lift)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)]
 pub struct GeneratorArgs<'tcx> {
     pub args: GenericArgsRef<'tcx>,
 }
@@ -1305,7 +1305,7 @@ impl<'tcx> AliasTy<'tcx> {
     }
 }
 
-#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, Lift)]
+#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable)]
 pub struct GenSig<'tcx> {
     pub resume_ty: Ty<'tcx>,
     pub yield_ty: Ty<'tcx>,
diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs
index 159cbb72a3b..69c4c588c44 100644
--- a/compiler/rustc_middle/src/ty/typeck_results.rs
+++ b/compiler/rustc_middle/src/ty/typeck_results.rs
@@ -654,7 +654,7 @@ rustc_index::newtype_index! {
 pub type CanonicalUserTypeAnnotations<'tcx> =
     IndexVec<UserTypeAnnotationIndex, CanonicalUserTypeAnnotation<'tcx>>;
 
-#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable, Lift)]
+#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
 pub struct CanonicalUserTypeAnnotation<'tcx> {
     pub user_ty: Box<CanonicalUserType<'tcx>>,
     pub span: Span,
@@ -714,7 +714,7 @@ impl<'tcx> CanonicalUserType<'tcx> {
 /// from constants that are named via paths, like `Foo::<A>::new` and
 /// so forth.
 #[derive(Copy, Clone, Debug, PartialEq, TyEncodable, TyDecodable)]
-#[derive(Eq, Hash, HashStable, TypeFoldable, TypeVisitable, Lift)]
+#[derive(Eq, Hash, HashStable, TypeFoldable, TypeVisitable)]
 pub enum UserType<'tcx> {
     Ty(Ty<'tcx>),
 
diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs
index a6c7eb8d912..90ac436a91f 100644
--- a/compiler/rustc_parse_format/src/lib.rs
+++ b/compiler/rustc_parse_format/src/lib.rs
@@ -210,7 +210,17 @@ pub struct ParseError {
     pub label: string::String,
     pub span: InnerSpan,
     pub secondary_label: Option<(string::String, InnerSpan)>,
-    pub should_be_replaced_with_positional_argument: bool,
+    pub suggestion: Suggestion,
+}
+
+pub enum Suggestion {
+    None,
+    /// Replace inline argument with positional argument:
+    /// `format!("{foo.bar}")` -> `format!("{}", foo.bar)`
+    UsePositional,
+    /// Remove `r#` from identifier:
+    /// `format!("{r#foo}")` -> `format!("{foo}")`
+    RemoveRawIdent(InnerSpan),
 }
 
 /// The parser structure for interpreting the input format string. This is
@@ -365,7 +375,7 @@ impl<'a> Parser<'a> {
             label: label.into(),
             span,
             secondary_label: None,
-            should_be_replaced_with_positional_argument: false,
+            suggestion: Suggestion::None,
         });
     }
 
@@ -389,7 +399,7 @@ impl<'a> Parser<'a> {
             label: label.into(),
             span,
             secondary_label: None,
-            should_be_replaced_with_positional_argument: false,
+            suggestion: Suggestion::None,
         });
     }
 
@@ -493,7 +503,7 @@ impl<'a> Parser<'a> {
             label,
             span: pos.to(pos),
             secondary_label,
-            should_be_replaced_with_positional_argument: false,
+            suggestion: Suggestion::None,
         });
 
         None
@@ -573,7 +583,37 @@ impl<'a> Parser<'a> {
             Some(ArgumentIs(i))
         } else {
             match self.cur.peek() {
-                Some(&(_, c)) if rustc_lexer::is_id_start(c) => Some(ArgumentNamed(self.word())),
+                Some(&(lo, c)) if rustc_lexer::is_id_start(c) => {
+                    let word = self.word();
+
+                    // Recover from `r#ident` in format strings.
+                    // FIXME: use a let chain
+                    if word == "r" {
+                        if let Some((pos, '#')) = self.cur.peek() {
+                            if self.input[pos + 1..]
+                                .chars()
+                                .next()
+                                .is_some_and(rustc_lexer::is_id_start)
+                            {
+                                self.cur.next();
+                                let word = self.word();
+                                let prefix_span = self.span(lo, lo + 2);
+                                let full_span = self.span(lo, lo + 2 + word.len());
+                                self.errors.insert(0, ParseError {
+                                    description: "raw identifiers are not supported".to_owned(),
+                                    note: Some("identifiers in format strings can be keywords and don't need to be prefixed with `r#`".to_string()),
+                                    label: "raw identifier used here".to_owned(),
+                                    span: full_span,
+                                    secondary_label: None,
+                                    suggestion: Suggestion::RemoveRawIdent(prefix_span),
+                                });
+                                return Some(ArgumentNamed(word));
+                            }
+                        }
+                    }
+
+                    Some(ArgumentNamed(word))
+                }
 
                 // This is an `ArgumentNext`.
                 // Record the fact and do the resolution after parsing the
@@ -841,7 +881,7 @@ impl<'a> Parser<'a> {
                     label: "expected `?` to occur after `:`".to_owned(),
                     span: pos.to(pos),
                     secondary_label: None,
-                    should_be_replaced_with_positional_argument: false,
+                    suggestion: Suggestion::None,
                 },
             );
         }
@@ -867,7 +907,7 @@ impl<'a> Parser<'a> {
                             label: "not supported".to_string(),
                             span: InnerSpan::new(arg.position_span.start, field.position_span.end),
                             secondary_label: None,
-                            should_be_replaced_with_positional_argument: true,
+                            suggestion: Suggestion::UsePositional,
                         },
                     );
                 }
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index 56f4b387df8..4f8ce99417a 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -153,6 +153,9 @@ passes_deprecated_annotation_has_no_effect =
 passes_deprecated_attribute =
     deprecated attribute must be paired with either stable or unstable attribute
 
+passes_diagnostic_diagnostic_on_unimplemented_only_for_traits =
+    `#[diagnostic::on_unimplemented]` can only be applied to trait definitions
+
 passes_diagnostic_item_first_defined =
     the diagnostic item is first defined here
 
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index d10bc33db52..2d94354e3e1 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -16,6 +16,7 @@ use rustc_hir::{
     self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID, CRATE_OWNER_ID,
 };
 use rustc_hir::{MethodKind, Target, Unsafety};
+use rustc_macros::LintDiagnostic;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault;
 use rustc_middle::query::Providers;
@@ -24,7 +25,7 @@ use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_session::lint::builtin::{
     CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS,
-    UNUSED_ATTRIBUTES,
+    UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES,
 };
 use rustc_session::parse::feature_err;
 use rustc_span::symbol::{kw, sym, Symbol};
@@ -36,6 +37,10 @@ use rustc_trait_selection::traits::ObligationCtxt;
 use std::cell::Cell;
 use std::collections::hash_map::Entry;
 
+#[derive(LintDiagnostic)]
+#[diag(passes_diagnostic_diagnostic_on_unimplemented_only_for_traits)]
+pub struct DiagnosticOnUnimplementedOnlyForTraits;
+
 pub(crate) fn target_from_impl_item<'tcx>(
     tcx: TyCtxt<'tcx>,
     impl_item: &hir::ImplItem<'_>,
@@ -104,6 +109,9 @@ impl CheckAttrVisitor<'_> {
         let mut seen = FxHashMap::default();
         let attrs = self.tcx.hir().attrs(hir_id);
         for attr in attrs {
+            if attr.path_matches(&[sym::diagnostic, sym::on_unimplemented]) {
+                self.check_diagnostic_on_unimplemented(attr.span, hir_id, target);
+            }
             match attr.name_or_empty() {
                 sym::do_not_recommend => self.check_do_not_recommend(attr.span, target),
                 sym::inline => self.check_inline(hir_id, attr, span, target),
@@ -287,6 +295,18 @@ impl CheckAttrVisitor<'_> {
         }
     }
 
+    /// Checks if `#[diagnostic::on_unimplemented]` is applied to a trait definition
+    fn check_diagnostic_on_unimplemented(&self, attr_span: Span, hir_id: HirId, target: Target) {
+        if !matches!(target, Target::Trait) {
+            self.tcx.emit_spanned_lint(
+                UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                hir_id,
+                attr_span,
+                DiagnosticOnUnimplementedOnlyForTraits,
+            );
+        }
+    }
+
     /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
     fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
         match target {
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 82060716575..90ae08ce37c 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -26,7 +26,7 @@ use rustc_middle::middle::stability;
 use rustc_middle::ty::RegisteredTools;
 use rustc_middle::ty::{TyCtxt, Visibility};
 use rustc_session::lint::builtin::{
-    LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE, UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
+    LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE, UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
 };
 use rustc_session::lint::builtin::{UNUSED_MACROS, UNUSED_MACRO_RULES};
 use rustc_session::lint::BuiltinLintDiagnostics;
@@ -610,9 +610,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         if res == Res::NonMacroAttr(NonMacroAttrKind::Tool)
             && path.segments.len() >= 2
             && path.segments[0].ident.name == sym::diagnostic
+            && path.segments[1].ident.name != sym::on_unimplemented
         {
             self.tcx.sess.parse_sess.buffer_lint(
-                UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
+                UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
                 path.segments[1].span(),
                 node_id,
                 "unknown diagnostic attribute",
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index ba82ee95caa..956ae149e55 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -898,6 +898,9 @@ impl OutFileName {
 #[derive(Clone, Hash, Debug, HashStable_Generic)]
 pub struct OutputFilenames {
     pub out_directory: PathBuf,
+    /// Crate name. Never contains '-'.
+    crate_stem: String,
+    /// Typically based on `.rs` input file name. Any '-' is preserved.
     filestem: String,
     pub single_output_file: Option<OutFileName>,
     pub temps_directory: Option<PathBuf>,
@@ -911,6 +914,7 @@ pub const DWARF_OBJECT_EXT: &str = "dwo";
 impl OutputFilenames {
     pub fn new(
         out_directory: PathBuf,
+        out_crate_name: String,
         out_filestem: String,
         single_output_file: Option<OutFileName>,
         temps_directory: Option<PathBuf>,
@@ -922,6 +926,7 @@ impl OutputFilenames {
             single_output_file,
             temps_directory,
             outputs,
+            crate_stem: format!("{out_crate_name}{extra}"),
             filestem: format!("{out_filestem}{extra}"),
         }
     }
@@ -938,7 +943,12 @@ impl OutputFilenames {
     /// should be placed on disk.
     pub fn output_path(&self, flavor: OutputType) -> PathBuf {
         let extension = flavor.extension();
-        self.with_directory_and_extension(&self.out_directory, extension)
+        match flavor {
+            OutputType::Metadata => {
+                self.out_directory.join(format!("lib{}.{}", self.crate_stem, extension))
+            }
+            _ => self.with_directory_and_extension(&self.out_directory, extension),
+        }
     }
 
     /// Gets the path where a compilation artifact of the given type for the
diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs
index c0884fb21cd..7a57b0621cd 100644
--- a/compiler/rustc_session/src/output.rs
+++ b/compiler/rustc_session/src/output.rs
@@ -119,26 +119,11 @@ pub fn validate_crate_name(sess: &Session, s: Symbol, sp: Option<Span>) {
     }
 }
 
-pub fn filename_for_metadata(
-    sess: &Session,
-    crate_name: Symbol,
-    outputs: &OutputFilenames,
-) -> OutFileName {
-    // If the command-line specified the path, use that directly.
-    if let Some(Some(out_filename)) = sess.opts.output_types.get(&OutputType::Metadata) {
-        return out_filename.clone();
-    }
-
-    let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename);
-
-    let out_filename = outputs.single_output_file.clone().unwrap_or_else(|| {
-        OutFileName::Real(outputs.out_directory.join(&format!("lib{libname}.rmeta")))
-    });
-
+pub fn filename_for_metadata(sess: &Session, outputs: &OutputFilenames) -> OutFileName {
+    let out_filename = outputs.path(OutputType::Metadata);
     if let OutFileName::Real(ref path) = out_filename {
         check_file_is_writeable(path, sess);
     }
-
     out_filename
 }
 
diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs
index 56547bfb426..5efd171b9dd 100644
--- a/compiler/rustc_target/src/abi/call/mod.rs
+++ b/compiler/rustc_target/src/abi/call/mod.rs
@@ -46,17 +46,17 @@ pub enum PassMode {
     ///
     /// The argument has a layout abi of `ScalarPair`.
     Pair(ArgAttributes, ArgAttributes),
-    /// Pass the argument after casting it, to either a single uniform or a
-    /// pair of registers. The bool indicates if a `Reg::i32()` dummy argument
-    /// is emitted before the real argument.
-    Cast(Box<CastTarget>, bool),
+    /// Pass the argument after casting it. See the `CastTarget` docs for details. The bool
+    /// indicates if a `Reg::i32()` dummy argument is emitted before the real argument.
+    Cast { pad_i32: bool, cast: Box<CastTarget> },
     /// Pass the argument indirectly via a hidden pointer.
-    /// The `extra_attrs` value, if any, is for the extra data (vtable or length)
-    /// which indicates that it refers to an unsized rvalue.
-    /// `on_stack` defines that the value should be passed at a fixed
-    /// stack offset in accordance to the ABI rather than passed using a
-    /// pointer. This corresponds to the `byval` LLVM argument attribute.
-    Indirect { attrs: ArgAttributes, extra_attrs: Option<ArgAttributes>, on_stack: bool },
+    /// The `meta_attrs` value, if any, is for the metadata (vtable or length) of an unsized
+    /// argument. (This is the only mode that supports unsized arguments.)
+    /// `on_stack` defines that the value should be passed at a fixed stack offset in accordance to
+    /// the ABI rather than passed using a pointer. This corresponds to the `byval` LLVM argument
+    /// attribute (using the Rust type of this argument). `on_stack` cannot be true for unsized
+    /// arguments, i.e., when `meta_attrs` is `Some`.
+    Indirect { attrs: ArgAttributes, meta_attrs: Option<ArgAttributes>, on_stack: bool },
 }
 
 impl PassMode {
@@ -65,17 +65,20 @@ impl PassMode {
     /// so that needs to be compared as well!
     pub fn eq_abi(&self, other: &Self) -> bool {
         match (self, other) {
-            (PassMode::Ignore, PassMode::Ignore) => true, // can still be reached for the return type
+            (PassMode::Ignore, PassMode::Ignore) => true,
             (PassMode::Direct(a1), PassMode::Direct(a2)) => a1.eq_abi(a2),
             (PassMode::Pair(a1, b1), PassMode::Pair(a2, b2)) => a1.eq_abi(a2) && b1.eq_abi(b2),
-            (PassMode::Cast(c1, pad1), PassMode::Cast(c2, pad2)) => c1.eq_abi(c2) && pad1 == pad2,
             (
-                PassMode::Indirect { attrs: a1, extra_attrs: None, on_stack: s1 },
-                PassMode::Indirect { attrs: a2, extra_attrs: None, on_stack: s2 },
+                PassMode::Cast { cast: c1, pad_i32: pad1 },
+                PassMode::Cast { cast: c2, pad_i32: pad2 },
+            ) => c1.eq_abi(c2) && pad1 == pad2,
+            (
+                PassMode::Indirect { attrs: a1, meta_attrs: None, on_stack: s1 },
+                PassMode::Indirect { attrs: a2, meta_attrs: None, on_stack: s2 },
             ) => a1.eq_abi(a2) && s1 == s2,
             (
-                PassMode::Indirect { attrs: a1, extra_attrs: Some(e1), on_stack: s1 },
-                PassMode::Indirect { attrs: a2, extra_attrs: Some(e2), on_stack: s2 },
+                PassMode::Indirect { attrs: a1, meta_attrs: Some(e1), on_stack: s1 },
+                PassMode::Indirect { attrs: a2, meta_attrs: Some(e2), on_stack: s2 },
             ) => a1.eq_abi(a2) && e1.eq_abi(e2) && s1 == s2,
             _ => false,
         }
@@ -256,6 +259,13 @@ impl Uniform {
     }
 }
 
+/// Describes the type used for `PassMode::Cast`.
+///
+/// Passing arguments in this mode works as follows: the registers in the `prefix` (the ones that
+/// are `Some`) get laid out one after the other (using `repr(C)` layout rules). Then the
+/// `rest.unit` register type gets repeated often enough to cover `rest.size`. This describes the
+/// actual type used for the call; the Rust type of the argument is then transmuted to this ABI type
+/// (and all data in the padding between the registers is dropped).
 #[derive(Clone, PartialEq, Eq, Hash, Debug, HashStable_Generic)]
 pub struct CastTarget {
     pub prefix: [Option<Reg>; 8],
@@ -565,15 +575,15 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
         attrs.pointee_size = layout.size;
         attrs.pointee_align = Some(layout.align.abi);
 
-        let extra_attrs = layout.is_unsized().then_some(ArgAttributes::new());
+        let meta_attrs = layout.is_unsized().then_some(ArgAttributes::new());
 
-        PassMode::Indirect { attrs, extra_attrs, on_stack: false }
+        PassMode::Indirect { attrs, meta_attrs, on_stack: false }
     }
 
     pub fn make_indirect(&mut self) {
         match self.mode {
             PassMode::Direct(_) | PassMode::Pair(_, _) => {}
-            PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: false } => return,
+            PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: false } => return,
             _ => panic!("Tried to make {:?} indirect", self.mode),
         }
 
@@ -583,7 +593,7 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
     pub fn make_indirect_byval(&mut self, byval_align: Option<Align>) {
         self.make_indirect();
         match self.mode {
-            PassMode::Indirect { ref mut attrs, extra_attrs: _, ref mut on_stack } => {
+            PassMode::Indirect { ref mut attrs, meta_attrs: _, ref mut on_stack } => {
                 *on_stack = true;
 
                 // Some platforms, like 32-bit x86, change the alignment of the type when passing
@@ -616,11 +626,11 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
     }
 
     pub fn cast_to<T: Into<CastTarget>>(&mut self, target: T) {
-        self.mode = PassMode::Cast(Box::new(target.into()), false);
+        self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32: false };
     }
 
     pub fn cast_to_and_pad_i32<T: Into<CastTarget>>(&mut self, target: T, pad_i32: bool) {
-        self.mode = PassMode::Cast(Box::new(target.into()), pad_i32);
+        self.mode = PassMode::Cast { cast: Box::new(target.into()), pad_i32 };
     }
 
     pub fn is_indirect(&self) -> bool {
@@ -628,11 +638,11 @@ impl<'a, Ty> ArgAbi<'a, Ty> {
     }
 
     pub fn is_sized_indirect(&self) -> bool {
-        matches!(self.mode, PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ })
+        matches!(self.mode, PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ })
     }
 
     pub fn is_unsized_indirect(&self) -> bool {
-        matches!(self.mode, PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ })
+        matches!(self.mode, PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ })
     }
 
     pub fn is_ignore(&self) -> bool {
diff --git a/compiler/rustc_target/src/abi/call/x86.rs b/compiler/rustc_target/src/abi/call/x86.rs
index b738c3133d9..afa1b70efc2 100644
--- a/compiler/rustc_target/src/abi/call/x86.rs
+++ b/compiler/rustc_target/src/abi/call/x86.rs
@@ -142,13 +142,13 @@ where
         for arg in fn_abi.args.iter_mut() {
             let attrs = match arg.mode {
                 PassMode::Ignore
-                | PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => {
+                | PassMode::Indirect { attrs: _, meta_attrs: None, on_stack: _ } => {
                     continue;
                 }
                 PassMode::Direct(ref mut attrs) => attrs,
                 PassMode::Pair(..)
-                | PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ }
-                | PassMode::Cast(..) => {
+                | PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ }
+                | PassMode::Cast { .. } => {
                     unreachable!("x86 shouldn't be passing arguments by {:?}", arg.mode)
                 }
             };
diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl
index 2db24c43734..20253b32add 100644
--- a/compiler/rustc_trait_selection/messages.ftl
+++ b/compiler/rustc_trait_selection/messages.ftl
@@ -27,6 +27,8 @@ trait_selection_inherent_projection_normalization_overflow = overflow evaluating
 trait_selection_invalid_on_clause_in_rustc_on_unimplemented = invalid `on`-clause in `#[rustc_on_unimplemented]`
     .label = invalid on-clause here
 
+trait_selection_malformed_on_unimplemented_attr = malformed `on_unimplemented` attribute
+
 trait_selection_negative_positive_conflict = found both positive and negative implementation of trait `{$trait_desc}`{$self_desc ->
         [none] {""}
        *[default] {" "}for type `{$self_desc}`
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
index 307c0516f70..51cb192a1e8 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
@@ -28,8 +28,8 @@ use std::ops::ControlFlow;
 use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
 
 use super::inspect::ProofTreeBuilder;
-use super::search_graph;
 use super::SolverMode;
+use super::{search_graph, GoalEvaluationKind};
 use super::{search_graph::SearchGraph, Goal};
 pub use select::InferCtxtSelectExt;
 
@@ -85,7 +85,7 @@ pub struct EvalCtxt<'a, 'tcx> {
     // evaluation code.
     tainted: Result<(), NoSolution>,
 
-    inspect: ProofTreeBuilder<'tcx>,
+    pub(super) inspect: ProofTreeBuilder<'tcx>,
 }
 
 #[derive(Debug, Clone)]
@@ -164,7 +164,7 @@ impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
         Option<inspect::GoalEvaluation<'tcx>>,
     ) {
         EvalCtxt::enter_root(self, generate_proof_tree, |ecx| {
-            ecx.evaluate_goal(IsNormalizesToHack::No, goal)
+            ecx.evaluate_goal(GoalEvaluationKind::Root, goal)
         })
     }
 }
@@ -340,11 +340,11 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
     /// been constrained and the certainty of the result.
     fn evaluate_goal(
         &mut self,
-        is_normalizes_to_hack: IsNormalizesToHack,
+        goal_evaluation_kind: GoalEvaluationKind,
         goal: Goal<'tcx, ty::Predicate<'tcx>>,
     ) -> Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution> {
         let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
-        let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, is_normalizes_to_hack);
+        let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, goal_evaluation_kind);
         let encountered_overflow = self.search_graph.encountered_overflow();
         let canonical_response = EvalCtxt::evaluate_canonical_goal(
             self.tcx(),
@@ -389,7 +389,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
         // solver cycle.
         if cfg!(debug_assertions)
             && has_changed
-            && is_normalizes_to_hack == IsNormalizesToHack::No
+            && !matches!(
+                goal_evaluation_kind,
+                GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::Yes }
+            )
             && !self.search_graph.in_cycle()
         {
             // The nested evaluation has to happen with the original state
@@ -561,8 +564,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
                 },
             );
 
-            let (_, certainty, instantiate_goals) =
-                self.evaluate_goal(IsNormalizesToHack::Yes, unconstrained_goal)?;
+            let (_, certainty, instantiate_goals) = self.evaluate_goal(
+                GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::Yes },
+                unconstrained_goal,
+            )?;
             self.add_goals(instantiate_goals);
 
             // Finally, equate the goal's RHS with the unconstrained var.
@@ -596,8 +601,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
         }
 
         for goal in goals.goals.drain(..) {
-            let (has_changed, certainty, instantiate_goals) =
-                self.evaluate_goal(IsNormalizesToHack::No, goal)?;
+            let (has_changed, certainty, instantiate_goals) = self.evaluate_goal(
+                GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::No },
+                goal,
+            )?;
             self.add_goals(instantiate_goals);
             if has_changed {
                 unchanged_certainty = None;
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
index f88cfbac3f3..6087b916790 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
@@ -24,13 +24,13 @@ where
             search_graph: outer_ecx.search_graph,
             nested_goals: outer_ecx.nested_goals.clone(),
             tainted: outer_ecx.tainted,
-            inspect: outer_ecx.inspect.new_goal_candidate(),
+            inspect: outer_ecx.inspect.new_probe(),
         };
         let r = nested_ecx.infcx.probe(|_| f(&mut nested_ecx));
         if !outer_ecx.inspect.is_noop() {
             let probe_kind = probe_kind(&r);
             nested_ecx.inspect.probe_kind(probe_kind);
-            outer_ecx.inspect.goal_candidate(nested_ecx.inspect);
+            outer_ecx.inspect.finish_probe(nested_ecx.inspect);
         }
         r
     }
diff --git a/compiler/rustc_trait_selection/src/solve/inspect.rs b/compiler/rustc_trait_selection/src/solve/inspect.rs
index 46025da7683..749bba33c9b 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect.rs
@@ -7,13 +7,13 @@ use rustc_middle::ty::{self, TyCtxt};
 use rustc_session::config::DumpSolverProofTree;
 
 use super::eval_ctxt::UseGlobalCache;
-use super::GenerateProofTree;
+use super::{GenerateProofTree, GoalEvaluationKind};
 
 #[derive(Eq, PartialEq, Debug)]
 pub struct WipGoalEvaluation<'tcx> {
     pub uncanonicalized_goal: Goal<'tcx, ty::Predicate<'tcx>>,
+    pub kind: WipGoalEvaluationKind,
     pub evaluation: Option<WipCanonicalGoalEvaluation<'tcx>>,
-    pub is_normalizes_to_hack: IsNormalizesToHack,
     pub returned_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
 }
 
@@ -21,8 +21,13 @@ impl<'tcx> WipGoalEvaluation<'tcx> {
     pub fn finalize(self) -> inspect::GoalEvaluation<'tcx> {
         inspect::GoalEvaluation {
             uncanonicalized_goal: self.uncanonicalized_goal,
+            kind: match self.kind {
+                WipGoalEvaluationKind::Root => inspect::GoalEvaluationKind::Root,
+                WipGoalEvaluationKind::Nested { is_normalizes_to_hack } => {
+                    inspect::GoalEvaluationKind::Nested { is_normalizes_to_hack }
+                }
+            },
             evaluation: self.evaluation.unwrap().finalize(),
-            is_normalizes_to_hack: self.is_normalizes_to_hack,
             returned_goals: self.returned_goals,
         }
     }
@@ -30,6 +35,12 @@ impl<'tcx> WipGoalEvaluation<'tcx> {
 
 #[derive(Eq, PartialEq, Debug)]
 pub enum WipGoalEvaluationKind {
+    Root,
+    Nested { is_normalizes_to_hack: IsNormalizesToHack },
+}
+
+#[derive(Eq, PartialEq, Debug)]
+pub enum WipCanonicalGoalEvaluationKind {
     Overflow,
     CacheHit(CacheHit),
 }
@@ -37,7 +48,7 @@ pub enum WipGoalEvaluationKind {
 #[derive(Eq, PartialEq, Debug)]
 pub struct WipCanonicalGoalEvaluation<'tcx> {
     pub goal: CanonicalInput<'tcx>,
-    pub kind: Option<WipGoalEvaluationKind>,
+    pub kind: Option<WipCanonicalGoalEvaluationKind>,
     pub revisions: Vec<WipGoalEvaluationStep<'tcx>>,
     pub result: Option<QueryResult<'tcx>>,
 }
@@ -45,11 +56,13 @@ pub struct WipCanonicalGoalEvaluation<'tcx> {
 impl<'tcx> WipCanonicalGoalEvaluation<'tcx> {
     pub fn finalize(self) -> inspect::CanonicalGoalEvaluation<'tcx> {
         let kind = match self.kind {
-            Some(WipGoalEvaluationKind::Overflow) => inspect::GoalEvaluationKind::Overflow,
-            Some(WipGoalEvaluationKind::CacheHit(hit)) => {
-                inspect::GoalEvaluationKind::CacheHit(hit)
+            Some(WipCanonicalGoalEvaluationKind::Overflow) => {
+                inspect::CanonicalGoalEvaluationKind::Overflow
             }
-            None => inspect::GoalEvaluationKind::Uncached {
+            Some(WipCanonicalGoalEvaluationKind::CacheHit(hit)) => {
+                inspect::CanonicalGoalEvaluationKind::CacheHit(hit)
+            }
+            None => inspect::CanonicalGoalEvaluationKind::Uncached {
                 revisions: self
                     .revisions
                     .into_iter()
@@ -87,7 +100,7 @@ impl<'tcx> WipAddedGoalsEvaluation<'tcx> {
 pub struct WipGoalEvaluationStep<'tcx> {
     pub instantiated_goal: QueryInput<'tcx, ty::Predicate<'tcx>>,
 
-    pub evaluation: WipGoalCandidate<'tcx>,
+    pub evaluation: WipProbe<'tcx>,
 }
 
 impl<'tcx> WipGoalEvaluationStep<'tcx> {
@@ -102,26 +115,37 @@ impl<'tcx> WipGoalEvaluationStep<'tcx> {
 }
 
 #[derive(Eq, PartialEq, Debug)]
-pub struct WipGoalCandidate<'tcx> {
-    pub added_goals_evaluations: Vec<WipAddedGoalsEvaluation<'tcx>>,
-    pub candidates: Vec<WipGoalCandidate<'tcx>>,
+pub struct WipProbe<'tcx> {
+    pub steps: Vec<WipProbeStep<'tcx>>,
     pub kind: Option<ProbeKind<'tcx>>,
 }
 
-impl<'tcx> WipGoalCandidate<'tcx> {
-    pub fn finalize(self) -> inspect::GoalCandidate<'tcx> {
-        inspect::GoalCandidate {
-            added_goals_evaluations: self
-                .added_goals_evaluations
-                .into_iter()
-                .map(WipAddedGoalsEvaluation::finalize)
-                .collect(),
-            candidates: self.candidates.into_iter().map(WipGoalCandidate::finalize).collect(),
+impl<'tcx> WipProbe<'tcx> {
+    pub fn finalize(self) -> inspect::Probe<'tcx> {
+        inspect::Probe {
+            steps: self.steps.into_iter().map(WipProbeStep::finalize).collect(),
             kind: self.kind.unwrap(),
         }
     }
 }
 
+#[derive(Eq, PartialEq, Debug)]
+pub enum WipProbeStep<'tcx> {
+    AddGoal(Goal<'tcx, ty::Predicate<'tcx>>),
+    EvaluateGoals(WipAddedGoalsEvaluation<'tcx>),
+    NestedProbe(WipProbe<'tcx>),
+}
+
+impl<'tcx> WipProbeStep<'tcx> {
+    pub fn finalize(self) -> inspect::ProbeStep<'tcx> {
+        match self {
+            WipProbeStep::AddGoal(goal) => inspect::ProbeStep::AddGoal(goal),
+            WipProbeStep::EvaluateGoals(eval) => inspect::ProbeStep::EvaluateGoals(eval.finalize()),
+            WipProbeStep::NestedProbe(probe) => inspect::ProbeStep::NestedProbe(probe.finalize()),
+        }
+    }
+}
+
 #[derive(Debug)]
 pub enum DebugSolver<'tcx> {
     Root,
@@ -129,7 +153,7 @@ pub enum DebugSolver<'tcx> {
     CanonicalGoalEvaluation(WipCanonicalGoalEvaluation<'tcx>),
     AddedGoalsEvaluation(WipAddedGoalsEvaluation<'tcx>),
     GoalEvaluationStep(WipGoalEvaluationStep<'tcx>),
-    GoalCandidate(WipGoalCandidate<'tcx>),
+    Probe(WipProbe<'tcx>),
 }
 
 impl<'tcx> From<WipGoalEvaluation<'tcx>> for DebugSolver<'tcx> {
@@ -156,9 +180,9 @@ impl<'tcx> From<WipGoalEvaluationStep<'tcx>> for DebugSolver<'tcx> {
     }
 }
 
-impl<'tcx> From<WipGoalCandidate<'tcx>> for DebugSolver<'tcx> {
-    fn from(g: WipGoalCandidate<'tcx>) -> DebugSolver<'tcx> {
-        DebugSolver::GoalCandidate(g)
+impl<'tcx> From<WipProbe<'tcx>> for DebugSolver<'tcx> {
+    fn from(p: WipProbe<'tcx>) -> DebugSolver<'tcx> {
+        DebugSolver::Probe(p)
     }
 }
 
@@ -249,15 +273,20 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
         self.state.is_none()
     }
 
-    pub fn new_goal_evaluation(
+    pub(super) fn new_goal_evaluation(
         &mut self,
         goal: Goal<'tcx, ty::Predicate<'tcx>>,
-        is_normalizes_to_hack: IsNormalizesToHack,
+        kind: GoalEvaluationKind,
     ) -> ProofTreeBuilder<'tcx> {
         self.nested(|| WipGoalEvaluation {
             uncanonicalized_goal: goal,
+            kind: match kind {
+                GoalEvaluationKind::Root => WipGoalEvaluationKind::Root,
+                GoalEvaluationKind::Nested { is_normalizes_to_hack } => {
+                    WipGoalEvaluationKind::Nested { is_normalizes_to_hack }
+                }
+            },
             evaluation: None,
-            is_normalizes_to_hack,
             returned_goals: vec![],
         })
     }
@@ -286,7 +315,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
         }
     }
 
-    pub fn goal_evaluation_kind(&mut self, kind: WipGoalEvaluationKind) {
+    pub fn goal_evaluation_kind(&mut self, kind: WipCanonicalGoalEvaluationKind) {
         if let Some(this) = self.as_mut() {
             match this {
                 DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation) => {
@@ -329,11 +358,7 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
     ) -> ProofTreeBuilder<'tcx> {
         self.nested(|| WipGoalEvaluationStep {
             instantiated_goal,
-            evaluation: WipGoalCandidate {
-                added_goals_evaluations: vec![],
-                candidates: vec![],
-                kind: None,
-            },
+            evaluation: WipProbe { steps: vec![], kind: None },
         })
     }
     pub fn goal_evaluation_step(&mut self, goal_evaluation_step: ProofTreeBuilder<'tcx>) {
@@ -350,18 +375,14 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
         }
     }
 
-    pub fn new_goal_candidate(&mut self) -> ProofTreeBuilder<'tcx> {
-        self.nested(|| WipGoalCandidate {
-            added_goals_evaluations: vec![],
-            candidates: vec![],
-            kind: None,
-        })
+    pub fn new_probe(&mut self) -> ProofTreeBuilder<'tcx> {
+        self.nested(|| WipProbe { steps: vec![], kind: None })
     }
 
     pub fn probe_kind(&mut self, probe_kind: ProbeKind<'tcx>) {
         if let Some(this) = self.as_mut() {
             match this {
-                DebugSolver::GoalCandidate(this) => {
+                DebugSolver::Probe(this) => {
                     assert_eq!(this.kind.replace(probe_kind), None)
                 }
                 _ => unreachable!(),
@@ -369,17 +390,32 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
         }
     }
 
-    pub fn goal_candidate(&mut self, candidate: ProofTreeBuilder<'tcx>) {
+    pub fn add_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) {
         if let Some(this) = self.as_mut() {
-            match (this, candidate.state.unwrap().tree) {
+            match this {
+                DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
+                    evaluation: WipProbe { steps, .. },
+                    ..
+                })
+                | DebugSolver::Probe(WipProbe { steps, .. }) => {
+                    steps.push(WipProbeStep::AddGoal(goal))
+                }
+                _ => unreachable!(),
+            }
+        }
+    }
+
+    pub fn finish_probe(&mut self, probe: ProofTreeBuilder<'tcx>) {
+        if let Some(this) = self.as_mut() {
+            match (this, probe.state.unwrap().tree) {
                 (
-                    DebugSolver::GoalCandidate(WipGoalCandidate { candidates, .. })
+                    DebugSolver::Probe(WipProbe { steps, .. })
                     | DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
-                        evaluation: WipGoalCandidate { candidates, .. },
+                        evaluation: WipProbe { steps, .. },
                         ..
                     }),
-                    DebugSolver::GoalCandidate(candidate),
-                ) => candidates.push(candidate),
+                    DebugSolver::Probe(probe),
+                ) => steps.push(WipProbeStep::NestedProbe(probe)),
                 _ => unreachable!(),
             }
         }
@@ -416,14 +452,12 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
             match (this, added_goals_evaluation.state.unwrap().tree) {
                 (
                     DebugSolver::GoalEvaluationStep(WipGoalEvaluationStep {
-                        evaluation: WipGoalCandidate { added_goals_evaluations, .. },
+                        evaluation: WipProbe { steps, .. },
                         ..
                     })
-                    | DebugSolver::GoalCandidate(WipGoalCandidate {
-                        added_goals_evaluations, ..
-                    }),
+                    | DebugSolver::Probe(WipProbe { steps, .. }),
                     DebugSolver::AddedGoalsEvaluation(added_goals_evaluation),
-                ) => added_goals_evaluations.push(added_goals_evaluation),
+                ) => steps.push(WipProbeStep::EvaluateGoals(added_goals_evaluation)),
                 _ => unreachable!(),
             }
         }
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index c492408bc76..bd612ce4778 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -19,7 +19,8 @@ use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues};
 use rustc_infer::traits::query::NoSolution;
 use rustc_middle::infer::canonical::CanonicalVarInfos;
 use rustc_middle::traits::solve::{
-    CanonicalResponse, Certainty, ExternalConstraintsData, Goal, QueryResult, Response,
+    CanonicalResponse, Certainty, ExternalConstraintsData, Goal, IsNormalizesToHack, QueryResult,
+    Response,
 };
 use rustc_middle::ty::{self, Ty, TyCtxt, UniverseIndex};
 use rustc_middle::ty::{
@@ -59,6 +60,12 @@ enum SolverMode {
     Coherence,
 }
 
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+enum GoalEvaluationKind {
+    Root,
+    Nested { is_normalizes_to_hack: IsNormalizesToHack },
+}
+
 trait CanonicalResponseExt {
     fn has_no_inference_or_external_constraints(&self) -> bool;
 
@@ -228,6 +235,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
 
     #[instrument(level = "debug", skip(self))]
     fn add_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) {
+        self.inspect.add_goal(goal);
         self.nested_goals.goals.push(goal);
     }
 
diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
index c816b51f67a..16de518e8e0 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs
@@ -187,7 +187,7 @@ impl<'tcx> SearchGraph<'tcx> {
                 last.encountered_overflow = true;
             }
 
-            inspect.goal_evaluation_kind(inspect::WipGoalEvaluationKind::Overflow);
+            inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::Overflow);
             return Self::response_no_constraints(tcx, input, Certainty::OVERFLOW);
         };
 
@@ -203,7 +203,7 @@ impl<'tcx> SearchGraph<'tcx> {
                     available_depth,
                 )
             {
-                inspect.goal_evaluation_kind(inspect::WipGoalEvaluationKind::CacheHit(
+                inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::CacheHit(
                     CacheHit::Global,
                 ));
                 self.on_cache_hit(reached_depth, encountered_overflow);
@@ -240,7 +240,7 @@ impl<'tcx> SearchGraph<'tcx> {
             // Finally we can return either the provisional response for that goal if we have a
             // coinductive cycle or an ambiguous result if the cycle is inductive.
             Entry::Occupied(entry_index) => {
-                inspect.goal_evaluation_kind(inspect::WipGoalEvaluationKind::CacheHit(
+                inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::CacheHit(
                     CacheHit::Provisional,
                 ));
 
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
index fe71a9f07df..4086db2ab55 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
@@ -9,6 +9,7 @@ use rustc_hir::def_id::DefId;
 use rustc_middle::ty::GenericArgsRef;
 use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
 use rustc_parse_format::{ParseMode, Parser, Piece, Position};
+use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
 use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::{Span, DUMMY_SP};
 use std::iter;
@@ -336,6 +337,10 @@ pub enum AppendConstMessage {
     Custom(Symbol),
 }
 
+#[derive(LintDiagnostic)]
+#[diag(trait_selection_malformed_on_unimplemented_attr)]
+pub struct NoValueInOnUnimplementedLint;
+
 impl<'tcx> OnUnimplementedDirective {
     fn parse(
         tcx: TyCtxt<'tcx>,
@@ -343,7 +348,8 @@ impl<'tcx> OnUnimplementedDirective {
         items: &[NestedMetaItem],
         span: Span,
         is_root: bool,
-    ) -> Result<Self, ErrorGuaranteed> {
+        is_diagnostic_namespace_variant: bool,
+    ) -> Result<Option<Self>, ErrorGuaranteed> {
         let mut errored = None;
         let mut item_iter = items.iter();
 
@@ -391,7 +397,10 @@ impl<'tcx> OnUnimplementedDirective {
                     note = parse_value(note_)?;
                     continue;
                 }
-            } else if item.has_name(sym::parent_label) && parent_label.is_none() {
+            } else if item.has_name(sym::parent_label)
+                && parent_label.is_none()
+                && !is_diagnostic_namespace_variant
+            {
                 if let Some(parent_label_) = item.value_str() {
                     parent_label = parse_value(parent_label_)?;
                     continue;
@@ -401,15 +410,30 @@ impl<'tcx> OnUnimplementedDirective {
                 && message.is_none()
                 && label.is_none()
                 && note.is_none()
+                && !is_diagnostic_namespace_variant
+            // FIXME(diagnostic_namespace): disallow filters for now
             {
                 if let Some(items) = item.meta_item_list() {
-                    match Self::parse(tcx, item_def_id, &items, item.span(), false) {
-                        Ok(subcommand) => subcommands.push(subcommand),
+                    match Self::parse(
+                        tcx,
+                        item_def_id,
+                        &items,
+                        item.span(),
+                        false,
+                        is_diagnostic_namespace_variant,
+                    ) {
+                        Ok(Some(subcommand)) => subcommands.push(subcommand),
+                        Ok(None) => bug!(
+                            "This cannot happen for now as we only reach that if `is_diagnostic_namespace_variant` is false"
+                        ),
                         Err(reported) => errored = Some(reported),
                     };
                     continue;
                 }
-            } else if item.has_name(sym::append_const_msg) && append_const_msg.is_none() {
+            } else if item.has_name(sym::append_const_msg)
+                && append_const_msg.is_none()
+                && !is_diagnostic_namespace_variant
+            {
                 if let Some(msg) = item.value_str() {
                     append_const_msg = Some(AppendConstMessage::Custom(msg));
                     continue;
@@ -419,14 +443,23 @@ impl<'tcx> OnUnimplementedDirective {
                 }
             }
 
-            // nothing found
-            tcx.sess.emit_err(NoValueInOnUnimplemented { span: item.span() });
+            if is_diagnostic_namespace_variant {
+                tcx.emit_spanned_lint(
+                    UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                    tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()),
+                    vec![item.span()],
+                    NoValueInOnUnimplementedLint,
+                );
+            } else {
+                // nothing found
+                tcx.sess.emit_err(NoValueInOnUnimplemented { span: item.span() });
+            }
         }
 
         if let Some(reported) = errored {
-            Err(reported)
+            if is_diagnostic_namespace_variant { Ok(None) } else { Err(reported) }
         } else {
-            Ok(OnUnimplementedDirective {
+            Ok(Some(OnUnimplementedDirective {
                 condition,
                 subcommands,
                 message,
@@ -434,32 +467,58 @@ impl<'tcx> OnUnimplementedDirective {
                 note,
                 parent_label,
                 append_const_msg,
-            })
+            }))
         }
     }
 
     pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
-        let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) else {
+        let mut is_diagnostic_namespace_variant = false;
+        let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented).or_else(|| {
+            if tcx.features().diagnostic_namespace {
+                is_diagnostic_namespace_variant = true;
+                tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, sym::on_unimplemented]).next()
+            } else {
+                None
+            }
+        }) else {
             return Ok(None);
         };
 
         let result = if let Some(items) = attr.meta_item_list() {
-            Self::parse(tcx, item_def_id, &items, attr.span, true).map(Some)
+            Self::parse(tcx, item_def_id, &items, attr.span, true, is_diagnostic_namespace_variant)
         } else if let Some(value) = attr.value_str() {
-            Ok(Some(OnUnimplementedDirective {
-                condition: None,
-                message: None,
-                subcommands: vec![],
-                label: Some(OnUnimplementedFormatString::try_parse(
-                    tcx,
-                    item_def_id,
-                    value,
+            if !is_diagnostic_namespace_variant {
+                Ok(Some(OnUnimplementedDirective {
+                    condition: None,
+                    message: None,
+                    subcommands: vec![],
+                    label: Some(OnUnimplementedFormatString::try_parse(
+                        tcx,
+                        item_def_id,
+                        value,
+                        attr.span,
+                    )?),
+                    note: None,
+                    parent_label: None,
+                    append_const_msg: None,
+                }))
+            } else {
+                tcx.emit_spanned_lint(
+                    UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                    tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()),
                     attr.span,
-                )?),
-                note: None,
-                parent_label: None,
-                append_const_msg: None,
-            }))
+                    NoValueInOnUnimplementedLint,
+                );
+                Ok(None)
+            }
+        } else if is_diagnostic_namespace_variant {
+            tcx.emit_spanned_lint(
+                UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()),
+                attr.span,
+                NoValueInOnUnimplementedLint,
+            );
+            Ok(None)
         } else {
             let reported =
                 tcx.sess.delay_span_bug(DUMMY_SP, "of_item: neither meta_item_list nor value_str");
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
index 979498fb6e6..e415d70479e 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
@@ -13,7 +13,7 @@ use rustc_span::def_id::CRATE_DEF_ID;
 use rustc_span::source_map::DUMMY_SP;
 use smallvec::{smallvec, SmallVec};
 
-#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable, Lift)]
+#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)]
 pub struct ImpliedOutlivesBounds<'tcx> {
     pub ty: Ty<'tcx>,
 }
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs
index 59f4a22ac75..f2c1243f931 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs
@@ -6,7 +6,7 @@ use crate::traits::ObligationCtxt;
 use rustc_middle::traits::query::{DropckOutlivesResult, NoSolution};
 use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt};
 
-#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable, Lift)]
+#[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)]
 pub struct DropckOutlives<'tcx> {
     dropped_ty: Ty<'tcx>,
 }
diff --git a/config.example.toml b/config.example.toml
index 2e0558c3f1b..f3c2366d674 100644
--- a/config.example.toml
+++ b/config.example.toml
@@ -458,7 +458,6 @@ changelog-seen = 2
 # Sets the number of codegen units to build the standard library with,
 # regardless of what the codegen-unit setting for the rest of the compiler is.
 # NOTE: building with anything other than 1 is known to occasionally have bugs.
-# See https://github.com/rust-lang/rust/issues/83600.
 #codegen-units-std = codegen-units
 
 # Whether or not debug assertions are enabled for the compiler and standard library.
diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs
index 5965ec2affa..4ef8af9b034 100644
--- a/library/alloc/src/collections/vec_deque/mod.rs
+++ b/library/alloc/src/collections/vec_deque/mod.rs
@@ -1015,8 +1015,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
     /// Shortens the deque, keeping the first `len` elements and dropping
     /// the rest.
     ///
-    /// If `len` is greater than the deque's current length, this has no
-    /// effect.
+    /// If `len` is greater or equal to the deque's current length, this has
+    /// no effect.
     ///
     /// # Examples
     ///
diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs
index fb8d00e8d87..1e2c35bf735 100644
--- a/library/alloc/src/fmt.rs
+++ b/library/alloc/src/fmt.rs
@@ -177,8 +177,8 @@
 //! These are all flags altering the behavior of the formatter.
 //!
 //! * `+` - This is intended for numeric types and indicates that the sign
-//!         should always be printed. Positive signs are never printed by
-//!         default, and the negative sign is only printed by default for signed values.
+//!         should always be printed. By default only the negative sign of signed values
+//!         is printed, and the sign of positive or unsigned values is omitted.
 //!         This flag indicates that the correct sign (`+` or `-`) should always be printed.
 //! * `-` - Currently not used
 //! * `#` - This flag indicates that the "alternate" form of printing should
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index e43b6ac4039..f435f503fc1 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -144,7 +144,6 @@
 #![feature(ptr_metadata)]
 #![feature(ptr_sub_ptr)]
 #![feature(receiver_trait)]
-#![feature(saturating_int_impl)]
 #![feature(set_ptr_value)]
 #![feature(sized_type_properties)]
 #![feature(slice_from_ptr_range)]
diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs
index 3f19561e1ac..0f767df6063 100644
--- a/library/alloc/src/macros.rs
+++ b/library/alloc/src/macros.rs
@@ -88,15 +88,19 @@ macro_rules! vec {
 ///
 /// A common use for `format!` is concatenation and interpolation of strings.
 /// The same convention is used with [`print!`] and [`write!`] macros,
-/// depending on the intended destination of the string.
+/// depending on the intended destination of the string; all these macros internally use [`format_args!`].
 ///
 /// To convert a single value to a string, use the [`to_string`] method. This
 /// will use the [`Display`] formatting trait.
 ///
+/// To concatenate literals into a `&'static str`, use the [`concat!`] macro.
+///
 /// [`print!`]: ../std/macro.print.html
 /// [`write!`]: core::write
+/// [`format_args!`]: core::format_args
 /// [`to_string`]: crate::string::ToString
 /// [`Display`]: core::fmt::Display
+/// [`concat!`]: core::concat
 ///
 /// # Panics
 ///
@@ -107,11 +111,11 @@ macro_rules! vec {
 /// # Examples
 ///
 /// ```
-/// format!("test");
-/// format!("hello {}", "world!");
-/// format!("x = {}, y = {y}", 10, y = 30);
+/// format!("test");                             // => "test"
+/// format!("hello {}", "world!");               // => "hello world!"
+/// format!("x = {}, y = {val}", 10, val = 30);  // => "x = 10, y = 30"
 /// let (x, y) = (1, 2);
-/// format!("{x} + {y} = 3");
+/// format!("{x} + {y} = 3");                    // => "1 + 2 = 3"
 /// ```
 #[macro_export]
 #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs
index 4cfffdf18c6..02331db3341 100644
--- a/library/alloc/src/vec/mod.rs
+++ b/library/alloc/src/vec/mod.rs
@@ -1110,8 +1110,8 @@ impl<T, A: Allocator> Vec<T, A> {
     /// Shortens the vector, keeping the first `len` elements and dropping
     /// the rest.
     ///
-    /// If `len` is greater than the vector's current length, this has no
-    /// effect.
+    /// If `len` is greater or equal to the vector's current length, this has
+    /// no effect.
     ///
     /// The [`drain`] method can emulate `truncate`, but causes the excess
     /// elements to be returned instead of dropped.
diff --git a/library/core/src/ascii/ascii_char.rs b/library/core/src/ascii/ascii_char.rs
index 5378b210e67..cc872a5343d 100644
--- a/library/core/src/ascii/ascii_char.rs
+++ b/library/core/src/ascii/ascii_char.rs
@@ -3,7 +3,7 @@
 //! suggestions from rustc if you get anything slightly wrong in here, and overall
 //! helps with clarity as we're also referring to `char` intentionally in here.
 
-use crate::fmt;
+use crate::fmt::{self, Write};
 use crate::mem::transmute;
 
 /// One of the 128 Unicode characters from U+0000 through U+007F,
@@ -54,7 +54,7 @@ use crate::mem::transmute;
 /// [chart]: https://www.unicode.org/charts/PDF/U0000.pdf
 /// [NIST FIPS 1-2]: https://nvlpubs.nist.gov/nistpubs/Legacy/FIPS/fipspub1-2-1977.pdf
 /// [NamesList]: https://www.unicode.org/Public/15.0.0/ucd/NamesList.txt
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
 #[unstable(feature = "ascii_char", issue = "110998")]
 #[repr(u8)]
 pub enum AsciiChar {
@@ -563,3 +563,40 @@ impl fmt::Display for AsciiChar {
         <str as fmt::Display>::fmt(self.as_str(), f)
     }
 }
+
+#[unstable(feature = "ascii_char", issue = "110998")]
+impl fmt::Debug for AsciiChar {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        #[inline]
+        fn backslash(a: AsciiChar) -> ([AsciiChar; 4], u8) {
+            ([AsciiChar::ReverseSolidus, a, AsciiChar::Null, AsciiChar::Null], 2)
+        }
+
+        let (buf, len) = match self {
+            AsciiChar::Null => backslash(AsciiChar::Digit0),
+            AsciiChar::CharacterTabulation => backslash(AsciiChar::SmallT),
+            AsciiChar::CarriageReturn => backslash(AsciiChar::SmallR),
+            AsciiChar::LineFeed => backslash(AsciiChar::SmallN),
+            AsciiChar::ReverseSolidus => backslash(AsciiChar::ReverseSolidus),
+            AsciiChar::Apostrophe => backslash(AsciiChar::Apostrophe),
+            _ => {
+                let byte = self.to_u8();
+                if !byte.is_ascii_control() {
+                    ([*self, AsciiChar::Null, AsciiChar::Null, AsciiChar::Null], 1)
+                } else {
+                    const HEX_DIGITS: [AsciiChar; 16] = *b"0123456789abcdef".as_ascii().unwrap();
+
+                    let hi = HEX_DIGITS[usize::from(byte >> 4)];
+                    let lo = HEX_DIGITS[usize::from(byte & 0xf)];
+                    ([AsciiChar::ReverseSolidus, AsciiChar::SmallX, hi, lo], 4)
+                }
+            }
+        };
+
+        f.write_char('\'')?;
+        for byte in &buf[..len as usize] {
+            f.write_str(byte.as_str())?;
+        }
+        f.write_char('\'')
+    }
+}
diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs
index d2c9f980042..9227248041e 100644
--- a/library/core/src/fmt/builders.rs
+++ b/library/core/src/fmt/builders.rs
@@ -40,6 +40,14 @@ impl fmt::Write for PadAdapter<'_, '_> {
 
         Ok(())
     }
+
+    fn write_char(&mut self, c: char) -> fmt::Result {
+        if self.state.on_newline {
+            self.buf.write_str("    ")?;
+        }
+        self.state.on_newline = c == '\n';
+        self.buf.write_char(c)
+    }
 }
 
 /// A struct to help with [`fmt::Debug`](Debug) implementations.
diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs
index 9ce6093f1d1..8204b3855bd 100644
--- a/library/core/src/fmt/mod.rs
+++ b/library/core/src/fmt/mod.rs
@@ -188,8 +188,28 @@ pub trait Write {
     /// assert_eq!(&buf, "world");
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
-    fn write_fmt(mut self: &mut Self, args: Arguments<'_>) -> Result {
-        write(&mut self, args)
+    fn write_fmt(&mut self, args: Arguments<'_>) -> Result {
+        // We use a specialization for `Sized` types to avoid an indirection
+        // through `&mut self`
+        trait SpecWriteFmt {
+            fn spec_write_fmt(self, args: Arguments<'_>) -> Result;
+        }
+
+        impl<W: Write + ?Sized> SpecWriteFmt for &mut W {
+            #[inline]
+            default fn spec_write_fmt(mut self, args: Arguments<'_>) -> Result {
+                write(&mut self, args)
+            }
+        }
+
+        impl<W: Write> SpecWriteFmt for &mut W {
+            #[inline]
+            fn spec_write_fmt(self, args: Arguments<'_>) -> Result {
+                write(self, args)
+            }
+        }
+
+        self.spec_write_fmt(args)
     }
 }
 
diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs
index 75c104ce2fa..4bf3da07354 100644
--- a/library/core/src/hint.rs
+++ b/library/core/src/hint.rs
@@ -175,34 +175,27 @@ pub fn spin_loop() {
         unsafe { crate::arch::x86_64::_mm_pause() };
     }
 
-    // RISC-V platform spin loop hint implementation
+    #[cfg(target_arch = "riscv32")]
     {
-        // RISC-V RV32 and RV64 share the same PAUSE instruction, but they are located in different
-        // modules in `core::arch`.
-        // In this case, here we call `pause` function in each core arch module.
-        #[cfg(target_arch = "riscv32")]
-        {
-            crate::arch::riscv32::pause();
-        }
-        #[cfg(target_arch = "riscv64")]
-        {
-            crate::arch::riscv64::pause();
-        }
+        crate::arch::riscv32::pause();
     }
 
-    #[cfg(any(target_arch = "aarch64", all(target_arch = "arm", target_feature = "v6")))]
+    #[cfg(target_arch = "riscv64")]
     {
-        #[cfg(target_arch = "aarch64")]
-        {
-            // SAFETY: the `cfg` attr ensures that we only execute this on aarch64 targets.
-            unsafe { crate::arch::aarch64::__isb(crate::arch::aarch64::SY) };
-        }
-        #[cfg(target_arch = "arm")]
-        {
-            // SAFETY: the `cfg` attr ensures that we only execute this on arm targets
-            // with support for the v6 feature.
-            unsafe { crate::arch::arm::__yield() };
-        }
+        crate::arch::riscv64::pause();
+    }
+
+    #[cfg(target_arch = "aarch64")]
+    {
+        // SAFETY: the `cfg` attr ensures that we only execute this on aarch64 targets.
+        unsafe { crate::arch::aarch64::__isb(crate::arch::aarch64::SY) };
+    }
+
+    #[cfg(all(target_arch = "arm", target_feature = "v6"))]
+    {
+        // SAFETY: the `cfg` attr ensures that we only execute this on arm targets
+        // with support for the v6 feature.
+        unsafe { crate::arch::arm::__yield() };
     }
 }
 
diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs
index 391e03636ab..0e03d0c2d4e 100644
--- a/library/core/src/iter/range.rs
+++ b/library/core/src/iter/range.rs
@@ -1,6 +1,7 @@
 use crate::ascii::Char as AsciiChar;
 use crate::convert::TryFrom;
 use crate::mem;
+use crate::net::{Ipv4Addr, Ipv6Addr};
 use crate::num::NonZeroUsize;
 use crate::ops::{self, Try};
 
@@ -15,7 +16,7 @@ macro_rules! unsafe_impl_trusted_step {
         unsafe impl TrustedStep for $type {}
     )*};
 }
-unsafe_impl_trusted_step![AsciiChar char i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize];
+unsafe_impl_trusted_step![AsciiChar char i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize Ipv4Addr Ipv6Addr];
 
 /// Objects that have a notion of *successor* and *predecessor* operations.
 ///
@@ -527,6 +528,70 @@ impl Step for AsciiChar {
     }
 }
 
+#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
+impl Step for Ipv4Addr {
+    #[inline]
+    fn steps_between(&start: &Ipv4Addr, &end: &Ipv4Addr) -> Option<usize> {
+        u32::steps_between(&start.to_bits(), &end.to_bits())
+    }
+
+    #[inline]
+    fn forward_checked(start: Ipv4Addr, count: usize) -> Option<Ipv4Addr> {
+        u32::forward_checked(start.to_bits(), count).map(Ipv4Addr::from_bits)
+    }
+
+    #[inline]
+    fn backward_checked(start: Ipv4Addr, count: usize) -> Option<Ipv4Addr> {
+        u32::backward_checked(start.to_bits(), count).map(Ipv4Addr::from_bits)
+    }
+
+    #[inline]
+    unsafe fn forward_unchecked(start: Ipv4Addr, count: usize) -> Ipv4Addr {
+        // SAFETY: Since u32 and Ipv4Addr are losslessly convertible,
+        //   this is as safe as the u32 version.
+        Ipv4Addr::from_bits(unsafe { u32::forward_unchecked(start.to_bits(), count) })
+    }
+
+    #[inline]
+    unsafe fn backward_unchecked(start: Ipv4Addr, count: usize) -> Ipv4Addr {
+        // SAFETY: Since u32 and Ipv4Addr are losslessly convertible,
+        //   this is as safe as the u32 version.
+        Ipv4Addr::from_bits(unsafe { u32::backward_unchecked(start.to_bits(), count) })
+    }
+}
+
+#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
+impl Step for Ipv6Addr {
+    #[inline]
+    fn steps_between(&start: &Ipv6Addr, &end: &Ipv6Addr) -> Option<usize> {
+        u128::steps_between(&start.to_bits(), &end.to_bits())
+    }
+
+    #[inline]
+    fn forward_checked(start: Ipv6Addr, count: usize) -> Option<Ipv6Addr> {
+        u128::forward_checked(start.to_bits(), count).map(Ipv6Addr::from_bits)
+    }
+
+    #[inline]
+    fn backward_checked(start: Ipv6Addr, count: usize) -> Option<Ipv6Addr> {
+        u128::backward_checked(start.to_bits(), count).map(Ipv6Addr::from_bits)
+    }
+
+    #[inline]
+    unsafe fn forward_unchecked(start: Ipv6Addr, count: usize) -> Ipv6Addr {
+        // SAFETY: Since u128 and Ipv6Addr are losslessly convertible,
+        //   this is as safe as the u128 version.
+        Ipv6Addr::from_bits(unsafe { u128::forward_unchecked(start.to_bits(), count) })
+    }
+
+    #[inline]
+    unsafe fn backward_unchecked(start: Ipv6Addr, count: usize) -> Ipv6Addr {
+        // SAFETY: Since u128 and Ipv6Addr are losslessly convertible,
+        //   this is as safe as the u128 version.
+        Ipv6Addr::from_bits(unsafe { u128::backward_unchecked(start.to_bits(), count) })
+    }
+}
+
 macro_rules! range_exact_iter_impl {
     ($($t:ty)*) => ($(
         #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs
index 46628bcea00..646100fe27b 100644
--- a/library/core/src/macros/mod.rs
+++ b/library/core/src/macros/mod.rs
@@ -1044,7 +1044,7 @@ pub(crate) mod builtin {
     /// expression of type `&'static str` which represents all of the literals
     /// concatenated left-to-right.
     ///
-    /// Integer and floating point literals are stringified in order to be
+    /// Integer and floating point literals are [stringified](core::stringify) in order to be
     /// concatenated.
     ///
     /// # Examples
diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs
index 95dcaf5dd73..4232319fecb 100644
--- a/library/core/src/num/mod.rs
+++ b/library/core/src/num/mod.rs
@@ -44,11 +44,10 @@ mod uint_macros; // import uint_impl!
 mod error;
 mod int_log10;
 mod nonzero;
-#[unstable(feature = "saturating_int_impl", issue = "87920")]
 mod saturating;
 mod wrapping;
 
-#[unstable(feature = "saturating_int_impl", issue = "87920")]
+#[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
 pub use saturating::Saturating;
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use wrapping::Wrapping;
diff --git a/library/core/src/num/saturating.rs b/library/core/src/num/saturating.rs
index e9f794e7d62..5757e7498ad 100644
--- a/library/core/src/num/saturating.rs
+++ b/library/core/src/num/saturating.rs
@@ -4,7 +4,7 @@ use crate::fmt;
 use crate::ops::{Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign};
 use crate::ops::{BitXor, BitXorAssign, Div, DivAssign};
 use crate::ops::{Mul, MulAssign, Neg, Not, Rem, RemAssign};
-use crate::ops::{Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign};
+use crate::ops::{Sub, SubAssign};
 
 /// Provides intentionally-saturating arithmetic on `T`.
 ///
@@ -24,7 +24,6 @@ use crate::ops::{Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign};
 /// # Examples
 ///
 /// ```
-/// #![feature(saturating_int_impl)]
 /// use std::num::Saturating;
 ///
 /// let max = Saturating(u32::MAX);
@@ -32,181 +31,186 @@ use crate::ops::{Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign};
 ///
 /// assert_eq!(u32::MAX, (max + one).0);
 /// ```
-#[unstable(feature = "saturating_int_impl", issue = "87920")]
+#[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
 #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default, Hash)]
 #[repr(transparent)]
 #[rustc_diagnostic_item = "Saturating"]
-pub struct Saturating<T>(#[unstable(feature = "saturating_int_impl", issue = "87920")] pub T);
+pub struct Saturating<T>(
+    #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] pub T,
+);
 
-#[unstable(feature = "saturating_int_impl", issue = "87920")]
+#[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
 impl<T: fmt::Debug> fmt::Debug for Saturating<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.0.fmt(f)
     }
 }
 
-#[unstable(feature = "saturating_int_impl", issue = "87920")]
+#[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
 impl<T: fmt::Display> fmt::Display for Saturating<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.0.fmt(f)
     }
 }
 
-#[unstable(feature = "saturating_int_impl", issue = "87920")]
+#[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
 impl<T: fmt::Binary> fmt::Binary for Saturating<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.0.fmt(f)
     }
 }
 
-#[unstable(feature = "saturating_int_impl", issue = "87920")]
+#[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
 impl<T: fmt::Octal> fmt::Octal for Saturating<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.0.fmt(f)
     }
 }
 
-#[unstable(feature = "saturating_int_impl", issue = "87920")]
+#[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
 impl<T: fmt::LowerHex> fmt::LowerHex for Saturating<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.0.fmt(f)
     }
 }
 
-#[unstable(feature = "saturating_int_impl", issue = "87920")]
+#[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
 impl<T: fmt::UpperHex> fmt::UpperHex for Saturating<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.0.fmt(f)
     }
 }
-#[allow(unused_macros)]
-macro_rules! sh_impl_signed {
-    ($t:ident, $f:ident) => {
-        // FIXME what is the correct implementation here? see discussion https://github.com/rust-lang/rust/pull/87921#discussion_r695870065
-        //
-        // #[unstable(feature = "saturating_int_impl", issue = "87920")]
-        // impl Shl<$f> for Saturating<$t> {
-        //     type Output = Saturating<$t>;
-        //
-        //     #[inline]
-        //     fn shl(self, other: $f) -> Saturating<$t> {
-        //         if other < 0 {
-        //             Saturating(self.0.shr((-other & self::shift_max::$t as $f) as u32))
-        //         } else {
-        //             Saturating(self.0.shl((other & self::shift_max::$t as $f) as u32))
-        //         }
-        //     }
-        // }
-        // forward_ref_binop! { impl Shl, shl for Saturating<$t>, $f,
-        // #[unstable(feature = "saturating_int_impl", issue = "87920")] }
-        //
-        // #[unstable(feature = "saturating_int_impl", issue = "87920")]
-        // impl ShlAssign<$f> for Saturating<$t> {
-        //     #[inline]
-        //     fn shl_assign(&mut self, other: $f) {
-        //         *self = *self << other;
-        //     }
-        // }
-        // forward_ref_op_assign! { impl ShlAssign, shl_assign for Saturating<$t>, $f }
-
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
-        impl Shr<$f> for Saturating<$t> {
-            type Output = Saturating<$t>;
-
-            #[inline]
-            fn shr(self, other: $f) -> Saturating<$t> {
-                if other < 0 {
-                    Saturating(self.0.shl((-other & self::shift_max::$t as $f) as u32))
-                } else {
-                    Saturating(self.0.shr((other & self::shift_max::$t as $f) as u32))
-                }
-            }
-        }
-        forward_ref_binop! { impl Shr, shr for Saturating<$t>, $f,
-        #[unstable(feature = "saturating_int_impl", issue = "87920")] }
-
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
-        impl ShrAssign<$f> for Saturating<$t> {
-            #[inline]
-            fn shr_assign(&mut self, other: $f) {
-                *self = *self >> other;
-            }
-        }
-        forward_ref_op_assign! { impl ShrAssign, shr_assign for Saturating<$t>, $f }
-    };
-}
-
-macro_rules! sh_impl_unsigned {
-    ($t:ident, $f:ident) => {
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
-        impl Shl<$f> for Saturating<$t> {
-            type Output = Saturating<$t>;
-
-            #[inline]
-            fn shl(self, other: $f) -> Saturating<$t> {
-                Saturating(self.0.wrapping_shl(other as u32))
-            }
-        }
-        forward_ref_binop! { impl Shl, shl for Saturating<$t>, $f,
-        #[unstable(feature = "saturating_int_impl", issue = "87920")] }
-
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
-        impl ShlAssign<$f> for Saturating<$t> {
-            #[inline]
-            fn shl_assign(&mut self, other: $f) {
-                *self = *self << other;
-            }
-        }
-        forward_ref_op_assign! { impl ShlAssign, shl_assign for Saturating<$t>, $f }
-
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
-        impl Shr<$f> for Saturating<$t> {
-            type Output = Saturating<$t>;
-
-            #[inline]
-            fn shr(self, other: $f) -> Saturating<$t> {
-                Saturating(self.0.wrapping_shr(other as u32))
-            }
-        }
-        forward_ref_binop! { impl Shr, shr for Saturating<$t>, $f,
-        #[unstable(feature = "saturating_int_impl", issue = "87920")] }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
-        impl ShrAssign<$f> for Saturating<$t> {
-            #[inline]
-            fn shr_assign(&mut self, other: $f) {
-                *self = *self >> other;
-            }
-        }
-        forward_ref_op_assign! { impl ShrAssign, shr_assign for Saturating<$t>, $f }
-    };
-}
-
-// FIXME (#23545): uncomment the remaining impls
-macro_rules! sh_impl_all {
-    ($($t:ident)*) => ($(
-        //sh_impl_unsigned! { $t, u8 }
-        //sh_impl_unsigned! { $t, u16 }
-        //sh_impl_unsigned! { $t, u32 }
-        //sh_impl_unsigned! { $t, u64 }
-        //sh_impl_unsigned! { $t, u128 }
-        sh_impl_unsigned! { $t, usize }
-
-        //sh_impl_signed! { $t, i8 }
-        //sh_impl_signed! { $t, i16 }
-        //sh_impl_signed! { $t, i32 }
-        //sh_impl_signed! { $t, i64 }
-        //sh_impl_signed! { $t, i128 }
-        //sh_impl_signed! { $t, isize }
-    )*)
-}
-
-sh_impl_all! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize }
+// FIXME the correct implementation is not clear. Waiting for a real world use case at https://github.com/rust-lang/libs-team/issues/230
+//
+// #[allow(unused_macros)]
+// macro_rules! sh_impl_signed {
+//     ($t:ident, $f:ident) => {
+//         // FIXME what is the correct implementation here? see discussion https://github.com/rust-lang/rust/pull/87921#discussion_r695870065
+//         //
+//         // #[unstable(feature = "saturating_int_impl", issue = "87920")]
+//         // impl Shl<$f> for Saturating<$t> {
+//         //     type Output = Saturating<$t>;
+//         //
+//         //     #[inline]
+//         //     fn shl(self, other: $f) -> Saturating<$t> {
+//         //         if other < 0 {
+//         //             Saturating(self.0.shr((-other & self::shift_max::$t as $f) as u32))
+//         //         } else {
+//         //             Saturating(self.0.shl((other & self::shift_max::$t as $f) as u32))
+//         //         }
+//         //     }
+//         // }
+//         // forward_ref_binop! { impl Shl, shl for Saturating<$t>, $f,
+//         // #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+//         //
+//         // #[unstable(feature = "saturating_int_impl", issue = "87920")]
+//         // impl ShlAssign<$f> for Saturating<$t> {
+//         //     #[inline]
+//         //     fn shl_assign(&mut self, other: $f) {
+//         //         *self = *self << other;
+//         //     }
+//         // }
+//         // forward_ref_op_assign! { impl ShlAssign, shl_assign for Saturating<$t>, $f }
+//
+//         #[unstable(feature = "saturating_int_impl", issue = "87920")]
+//         impl Shr<$f> for Saturating<$t> {
+//             type Output = Saturating<$t>;
+//
+//             #[inline]
+//             fn shr(self, other: $f) -> Saturating<$t> {
+//                 if other < 0 {
+//                     Saturating(self.0.shl((-other & self::shift_max::$t as $f) as u32))
+//                 } else {
+//                     Saturating(self.0.shr((other & self::shift_max::$t as $f) as u32))
+//                 }
+//             }
+//         }
+//         forward_ref_binop! { impl Shr, shr for Saturating<$t>, $f,
+//         #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+//
+//         #[unstable(feature = "saturating_int_impl", issue = "87920")]
+//         impl ShrAssign<$f> for Saturating<$t> {
+//             #[inline]
+//             fn shr_assign(&mut self, other: $f) {
+//                 *self = *self >> other;
+//             }
+//         }
+//         forward_ref_op_assign! { impl ShrAssign, shr_assign for Saturating<$t>, $f }
+//     };
+// }
+//
+// macro_rules! sh_impl_unsigned {
+//     ($t:ident, $f:ident) => {
+//         #[unstable(feature = "saturating_int_impl", issue = "87920")]
+//         impl Shl<$f> for Saturating<$t> {
+//             type Output = Saturating<$t>;
+//
+//             #[inline]
+//             fn shl(self, other: $f) -> Saturating<$t> {
+//                 Saturating(self.0.wrapping_shl(other as u32))
+//             }
+//         }
+//         forward_ref_binop! { impl Shl, shl for Saturating<$t>, $f,
+//         #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+//
+//         #[unstable(feature = "saturating_int_impl", issue = "87920")]
+//         impl ShlAssign<$f> for Saturating<$t> {
+//             #[inline]
+//             fn shl_assign(&mut self, other: $f) {
+//                 *self = *self << other;
+//             }
+//         }
+//         forward_ref_op_assign! { impl ShlAssign, shl_assign for Saturating<$t>, $f }
+//
+//         #[unstable(feature = "saturating_int_impl", issue = "87920")]
+//         impl Shr<$f> for Saturating<$t> {
+//             type Output = Saturating<$t>;
+//
+//             #[inline]
+//             fn shr(self, other: $f) -> Saturating<$t> {
+//                 Saturating(self.0.wrapping_shr(other as u32))
+//             }
+//         }
+//         forward_ref_binop! { impl Shr, shr for Saturating<$t>, $f,
+//         #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+//
+//         #[unstable(feature = "saturating_int_impl", issue = "87920")]
+//         impl ShrAssign<$f> for Saturating<$t> {
+//             #[inline]
+//             fn shr_assign(&mut self, other: $f) {
+//                 *self = *self >> other;
+//             }
+//         }
+//         forward_ref_op_assign! { impl ShrAssign, shr_assign for Saturating<$t>, $f }
+//     };
+// }
+//
+// // FIXME (#23545): uncomment the remaining impls
+// macro_rules! sh_impl_all {
+//     ($($t:ident)*) => ($(
+//         //sh_impl_unsigned! { $t, u8 }
+//         //sh_impl_unsigned! { $t, u16 }
+//         //sh_impl_unsigned! { $t, u32 }
+//         //sh_impl_unsigned! { $t, u64 }
+//         //sh_impl_unsigned! { $t, u128 }
+//         sh_impl_unsigned! { $t, usize }
+//
+//         //sh_impl_signed! { $t, i8 }
+//         //sh_impl_signed! { $t, i16 }
+//         //sh_impl_signed! { $t, i32 }
+//         //sh_impl_signed! { $t, i64 }
+//         //sh_impl_signed! { $t, i128 }
+//         //sh_impl_signed! { $t, isize }
+//     )*)
+// }
+//
+// sh_impl_all! { u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize }
 
 // FIXME(30524): impl Op<T> for Saturating<T>, impl OpAssign<T> for Saturating<T>
 macro_rules! saturating_impl {
     ($($t:ty)*) => ($(
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl Add for Saturating<$t> {
             type Output = Saturating<$t>;
 
@@ -216,9 +220,9 @@ macro_rules! saturating_impl {
             }
         }
         forward_ref_binop! { impl Add, add for Saturating<$t>, Saturating<$t>,
-                #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+                #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl AddAssign for Saturating<$t> {
             #[inline]
             fn add_assign(&mut self, other: Saturating<$t>) {
@@ -227,7 +231,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl AddAssign, add_assign for Saturating<$t>, Saturating<$t> }
 
-        #[unstable(feature = "saturating_int_assign_impl", issue = "92354")]
+        #[stable(feature = "saturating_int_assign_impl", since = "CURRENT_RUSTC_VERSION")]
         impl AddAssign<$t> for Saturating<$t> {
             #[inline]
             fn add_assign(&mut self, other: $t) {
@@ -236,7 +240,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl AddAssign, add_assign for Saturating<$t>, $t }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl Sub for Saturating<$t> {
             type Output = Saturating<$t>;
 
@@ -246,9 +250,9 @@ macro_rules! saturating_impl {
             }
         }
         forward_ref_binop! { impl Sub, sub for Saturating<$t>, Saturating<$t>,
-                #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+                #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl SubAssign for Saturating<$t> {
             #[inline]
             fn sub_assign(&mut self, other: Saturating<$t>) {
@@ -257,7 +261,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl SubAssign, sub_assign for Saturating<$t>, Saturating<$t> }
 
-        #[unstable(feature = "saturating_int_assign_impl", issue = "92354")]
+        #[stable(feature = "saturating_int_assign_impl", since = "CURRENT_RUSTC_VERSION")]
         impl SubAssign<$t> for Saturating<$t> {
             #[inline]
             fn sub_assign(&mut self, other: $t) {
@@ -266,7 +270,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl SubAssign, sub_assign for Saturating<$t>, $t }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl Mul for Saturating<$t> {
             type Output = Saturating<$t>;
 
@@ -276,9 +280,9 @@ macro_rules! saturating_impl {
             }
         }
         forward_ref_binop! { impl Mul, mul for Saturating<$t>, Saturating<$t>,
-                #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+                #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl MulAssign for Saturating<$t> {
             #[inline]
             fn mul_assign(&mut self, other: Saturating<$t>) {
@@ -287,7 +291,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl MulAssign, mul_assign for Saturating<$t>, Saturating<$t> }
 
-        #[unstable(feature = "saturating_int_assign_impl", issue = "92354")]
+        #[stable(feature = "saturating_int_assign_impl", since = "CURRENT_RUSTC_VERSION")]
         impl MulAssign<$t> for Saturating<$t> {
             #[inline]
             fn mul_assign(&mut self, other: $t) {
@@ -301,7 +305,6 @@ macro_rules! saturating_impl {
         /// Basic usage:
         ///
         /// ```
-        /// #![feature(saturating_int_impl)]
         /// use std::num::Saturating;
         ///
         #[doc = concat!("assert_eq!(Saturating(2", stringify!($t), "), Saturating(5", stringify!($t), ") / Saturating(2));")]
@@ -310,12 +313,11 @@ macro_rules! saturating_impl {
         /// ```
         ///
         /// ```should_panic
-        /// #![feature(saturating_int_impl)]
         /// use std::num::Saturating;
         ///
         #[doc = concat!("let _ = Saturating(0", stringify!($t), ") / Saturating(0);")]
         /// ```
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl Div for Saturating<$t> {
             type Output = Saturating<$t>;
 
@@ -325,10 +327,10 @@ macro_rules! saturating_impl {
             }
         }
         forward_ref_binop! { impl Div, div for Saturating<$t>, Saturating<$t>,
-                #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+                #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] }
 
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl DivAssign for Saturating<$t> {
             #[inline]
             fn div_assign(&mut self, other: Saturating<$t>) {
@@ -337,7 +339,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl DivAssign, div_assign for Saturating<$t>, Saturating<$t> }
 
-        #[unstable(feature = "saturating_int_assign_impl", issue = "92354")]
+        #[stable(feature = "saturating_int_assign_impl", since = "CURRENT_RUSTC_VERSION")]
         impl DivAssign<$t> for Saturating<$t> {
             #[inline]
             fn div_assign(&mut self, other: $t) {
@@ -346,7 +348,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl DivAssign, div_assign for Saturating<$t>, $t }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl Rem for Saturating<$t> {
             type Output = Saturating<$t>;
 
@@ -356,9 +358,9 @@ macro_rules! saturating_impl {
             }
         }
         forward_ref_binop! { impl Rem, rem for Saturating<$t>, Saturating<$t>,
-                #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+                #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl RemAssign for Saturating<$t> {
             #[inline]
             fn rem_assign(&mut self, other: Saturating<$t>) {
@@ -367,7 +369,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl RemAssign, rem_assign for Saturating<$t>, Saturating<$t> }
 
-        #[unstable(feature = "saturating_int_assign_impl", issue = "92354")]
+        #[stable(feature = "saturating_int_assign_impl", since = "CURRENT_RUSTC_VERSION")]
         impl RemAssign<$t> for Saturating<$t> {
             #[inline]
             fn rem_assign(&mut self, other: $t) {
@@ -376,7 +378,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl RemAssign, rem_assign for Saturating<$t>, $t }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl Not for Saturating<$t> {
             type Output = Saturating<$t>;
 
@@ -386,9 +388,9 @@ macro_rules! saturating_impl {
             }
         }
         forward_ref_unop! { impl Not, not for Saturating<$t>,
-                #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+                #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl BitXor for Saturating<$t> {
             type Output = Saturating<$t>;
 
@@ -398,9 +400,9 @@ macro_rules! saturating_impl {
             }
         }
         forward_ref_binop! { impl BitXor, bitxor for Saturating<$t>, Saturating<$t>,
-                #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+                #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl BitXorAssign for Saturating<$t> {
             #[inline]
             fn bitxor_assign(&mut self, other: Saturating<$t>) {
@@ -409,7 +411,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl BitXorAssign, bitxor_assign for Saturating<$t>, Saturating<$t> }
 
-        #[unstable(feature = "saturating_int_assign_impl", issue = "92354")]
+        #[stable(feature = "saturating_int_assign_impl", since = "CURRENT_RUSTC_VERSION")]
         impl BitXorAssign<$t> for Saturating<$t> {
             #[inline]
             fn bitxor_assign(&mut self, other: $t) {
@@ -418,7 +420,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl BitXorAssign, bitxor_assign for Saturating<$t>, $t }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl BitOr for Saturating<$t> {
             type Output = Saturating<$t>;
 
@@ -428,9 +430,9 @@ macro_rules! saturating_impl {
             }
         }
         forward_ref_binop! { impl BitOr, bitor for Saturating<$t>, Saturating<$t>,
-                #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+                #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl BitOrAssign for Saturating<$t> {
             #[inline]
             fn bitor_assign(&mut self, other: Saturating<$t>) {
@@ -439,7 +441,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl BitOrAssign, bitor_assign for Saturating<$t>, Saturating<$t> }
 
-        #[unstable(feature = "saturating_int_assign_impl", issue = "92354")]
+        #[stable(feature = "saturating_int_assign_impl", since = "CURRENT_RUSTC_VERSION")]
         impl BitOrAssign<$t> for Saturating<$t> {
             #[inline]
             fn bitor_assign(&mut self, other: $t) {
@@ -448,7 +450,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl BitOrAssign, bitor_assign for Saturating<$t>, $t }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl BitAnd for Saturating<$t> {
             type Output = Saturating<$t>;
 
@@ -458,9 +460,9 @@ macro_rules! saturating_impl {
             }
         }
         forward_ref_binop! { impl BitAnd, bitand for Saturating<$t>, Saturating<$t>,
-                #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+                #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl BitAndAssign for Saturating<$t> {
             #[inline]
             fn bitand_assign(&mut self, other: Saturating<$t>) {
@@ -469,7 +471,7 @@ macro_rules! saturating_impl {
         }
         forward_ref_op_assign! { impl BitAndAssign, bitand_assign for Saturating<$t>, Saturating<$t> }
 
-        #[unstable(feature = "saturating_int_assign_impl", issue = "92354")]
+        #[stable(feature = "saturating_int_assign_impl", since = "CURRENT_RUSTC_VERSION")]
         impl BitAndAssign<$t> for Saturating<$t> {
             #[inline]
             fn bitand_assign(&mut self, other: $t) {
@@ -493,12 +495,11 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("assert_eq!(<Saturating<", stringify!($t), ">>::MIN, Saturating(", stringify!($t), "::MIN));")]
             /// ```
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const MIN: Self = Self(<$t>::MIN);
 
             /// Returns the largest value that can be represented by this integer type.
@@ -508,12 +509,11 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("assert_eq!(<Saturating<", stringify!($t), ">>::MAX, Saturating(", stringify!($t), "::MAX));")]
             /// ```
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const MAX: Self = Self(<$t>::MAX);
 
             /// Returns the size of this integer type in bits.
@@ -523,12 +523,11 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("assert_eq!(<Saturating<", stringify!($t), ">>::BITS, ", stringify!($t), "::BITS);")]
             /// ```
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const BITS: u32 = <$t>::BITS;
 
             /// Returns the number of ones in the binary representation of `self`.
@@ -538,7 +537,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("let n = Saturating(0b01001100", stringify!($t), ");")]
@@ -550,7 +548,8 @@ macro_rules! saturating_int_impl {
             #[doc(alias = "popcnt")]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const fn count_ones(self) -> u32 {
                 self.0.count_ones()
             }
@@ -562,7 +561,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("assert_eq!(Saturating(!0", stringify!($t), ").count_zeros(), 0);")]
@@ -570,7 +568,8 @@ macro_rules! saturating_int_impl {
             #[inline]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const fn count_zeros(self) -> u32 {
                 self.0.count_zeros()
             }
@@ -582,7 +581,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("let n = Saturating(0b0101000", stringify!($t), ");")]
@@ -592,7 +590,8 @@ macro_rules! saturating_int_impl {
             #[inline]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const fn trailing_zeros(self) -> u32 {
                 self.0.trailing_zeros()
             }
@@ -609,7 +608,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             /// let n: Saturating<i64> = Saturating(0x0123456789ABCDEF);
@@ -620,7 +618,8 @@ macro_rules! saturating_int_impl {
             #[inline]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const fn rotate_left(self, n: u32) -> Self {
                 Saturating(self.0.rotate_left(n))
             }
@@ -637,7 +636,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             /// let n: Saturating<i64> = Saturating(0x0123456789ABCDEF);
@@ -648,7 +646,8 @@ macro_rules! saturating_int_impl {
             #[inline]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const fn rotate_right(self, n: u32) -> Self {
                 Saturating(self.0.rotate_right(n))
             }
@@ -660,7 +659,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             /// let n: Saturating<i16> = Saturating(0b0000000_01010101);
@@ -674,7 +672,8 @@ macro_rules! saturating_int_impl {
             #[inline]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const fn swap_bytes(self) -> Self {
                 Saturating(self.0.swap_bytes())
             }
@@ -689,7 +688,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             /// let n = Saturating(0b0000000_01010101i16);
@@ -701,8 +699,8 @@ macro_rules! saturating_int_impl {
             /// assert_eq!(m, Saturating(-22016));
             /// ```
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
-            #[rustc_const_unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
             pub const fn reverse_bits(self) -> Self {
@@ -719,7 +717,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("let n = Saturating(0x1A", stringify!($t), ");")]
@@ -732,7 +729,8 @@ macro_rules! saturating_int_impl {
             /// ```
             #[inline]
             #[must_use]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const fn from_be(x: Self) -> Self {
                 Saturating(<$t>::from_be(x.0))
             }
@@ -747,7 +745,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("let n = Saturating(0x1A", stringify!($t), ");")]
@@ -760,7 +757,8 @@ macro_rules! saturating_int_impl {
             /// ```
             #[inline]
             #[must_use]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const fn from_le(x: Self) -> Self {
                 Saturating(<$t>::from_le(x.0))
             }
@@ -775,7 +773,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("let n = Saturating(0x1A", stringify!($t), ");")]
@@ -787,7 +784,8 @@ macro_rules! saturating_int_impl {
             /// }
             /// ```
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
             pub const fn to_be(self) -> Self {
@@ -804,7 +802,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("let n = Saturating(0x1A", stringify!($t), ");")]
@@ -816,7 +813,8 @@ macro_rules! saturating_int_impl {
             /// }
             /// ```
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
             pub const fn to_le(self) -> Self {
@@ -830,7 +828,6 @@ macro_rules! saturating_int_impl {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("assert_eq!(Saturating(3", stringify!($t), ").pow(4), Saturating(81));")]
@@ -839,17 +836,17 @@ macro_rules! saturating_int_impl {
             /// Results that are too large are saturated:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             /// assert_eq!(Saturating(3i8).pow(5), Saturating(127));
             /// assert_eq!(Saturating(3i8).pow(6), Saturating(127));
             /// ```
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
-            pub fn pow(self, exp: u32) -> Self {
+            pub const fn pow(self, exp: u32) -> Self {
                 Saturating(self.0.saturating_pow(exp))
             }
         }
@@ -868,7 +865,6 @@ macro_rules! saturating_int_impl_signed {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("let n = Saturating(", stringify!($t), "::MAX >> 2);")]
@@ -876,7 +872,8 @@ macro_rules! saturating_int_impl_signed {
             /// assert_eq!(n.leading_zeros(), 3);
             /// ```
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
             pub const fn leading_zeros(self) -> u32 {
@@ -891,7 +888,6 @@ macro_rules! saturating_int_impl_signed {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("assert_eq!(Saturating(100", stringify!($t), ").abs(), Saturating(100));")]
@@ -901,10 +897,11 @@ macro_rules! saturating_int_impl_signed {
             #[doc = concat!("assert_eq!(Saturating(", stringify!($t), "::MIN).abs(), Saturating(", stringify!($t), "::MAX));")]
             /// ```
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
-            pub fn abs(self) -> Saturating<$t> {
+            pub const fn abs(self) -> Saturating<$t> {
                 Saturating(self.0.saturating_abs())
             }
 
@@ -919,7 +916,6 @@ macro_rules! saturating_int_impl_signed {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("assert_eq!(Saturating(10", stringify!($t), ").signum(), Saturating(1));")]
@@ -927,10 +923,11 @@ macro_rules! saturating_int_impl_signed {
             #[doc = concat!("assert_eq!(Saturating(-10", stringify!($t), ").signum(), Saturating(-1));")]
             /// ```
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
-            pub fn signum(self) -> Saturating<$t> {
+            pub const fn signum(self) -> Saturating<$t> {
                 Saturating(self.0.signum())
             }
 
@@ -942,7 +939,6 @@ macro_rules! saturating_int_impl_signed {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("assert!(Saturating(10", stringify!($t), ").is_positive());")]
@@ -950,7 +946,8 @@ macro_rules! saturating_int_impl_signed {
             /// ```
             #[must_use]
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const fn is_positive(self) -> bool {
                 self.0.is_positive()
             }
@@ -963,7 +960,6 @@ macro_rules! saturating_int_impl_signed {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("assert!(Saturating(-10", stringify!($t), ").is_negative());")]
@@ -971,13 +967,14 @@ macro_rules! saturating_int_impl_signed {
             /// ```
             #[must_use]
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             pub const fn is_negative(self) -> bool {
                 self.0.is_negative()
             }
         }
 
-        #[unstable(feature = "saturating_int_impl", issue = "87920")]
+        #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
         impl Neg for Saturating<$t> {
             type Output = Self;
             #[inline]
@@ -986,7 +983,7 @@ macro_rules! saturating_int_impl_signed {
             }
         }
         forward_ref_unop! { impl Neg, neg for Saturating<$t>,
-                #[unstable(feature = "saturating_int_impl", issue = "87920")] }
+                #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")] }
     )*)
 }
 
@@ -1002,7 +999,6 @@ macro_rules! saturating_int_impl_unsigned {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("let n = Saturating(", stringify!($t), "::MAX >> 2);")]
@@ -1010,7 +1006,8 @@ macro_rules! saturating_int_impl_unsigned {
             /// assert_eq!(n.leading_zeros(), 2);
             /// ```
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
             #[must_use = "this returns the result of the operation, \
                           without modifying the original"]
             pub const fn leading_zeros(self) -> u32 {
@@ -1024,7 +1021,6 @@ macro_rules! saturating_int_impl_unsigned {
             /// Basic usage:
             ///
             /// ```
-            /// #![feature(saturating_int_impl)]
             /// use std::num::Saturating;
             ///
             #[doc = concat!("assert!(Saturating(16", stringify!($t), ").is_power_of_two());")]
@@ -1032,8 +1028,9 @@ macro_rules! saturating_int_impl_unsigned {
             /// ```
             #[must_use]
             #[inline]
-            #[unstable(feature = "saturating_int_impl", issue = "87920")]
-            pub fn is_power_of_two(self) -> bool {
+            #[rustc_const_stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            #[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
+            pub const fn is_power_of_two(self) -> bool {
                 self.0.is_power_of_two()
             }
 
diff --git a/library/core/src/primitive_docs.rs b/library/core/src/primitive_docs.rs
index 80289ca08c3..2cfa896e5b9 100644
--- a/library/core/src/primitive_docs.rs
+++ b/library/core/src/primitive_docs.rs
@@ -612,7 +612,7 @@ mod prim_pointer {}
 /// statically generated up to size 32.
 ///
 /// Arrays of sizes from 1 to 12 (inclusive) implement [`From<Tuple>`], where `Tuple`
-/// is a homogenous [prim@tuple] of appropriate length.
+/// is a homogeneous [prim@tuple] of appropriate length.
 ///
 /// Arrays coerce to [slices (`[T]`)][slice], so a slice method may be called on
 /// an array. Indeed, this provides most of the API for working with arrays.
@@ -676,7 +676,7 @@ mod prim_pointer {}
 /// move_away(roa);
 /// ```
 ///
-/// Arrays can be created from homogenous tuples of appropriate length:
+/// Arrays can be created from homogeneous tuples of appropriate length:
 ///
 /// ```
 /// let tuple: (u32, u32, u32) = (1, 2, 3);
@@ -1065,7 +1065,7 @@ mod prim_str {}
 /// assert_eq!(y, 5);
 /// ```
 ///
-/// Homogenous tuples can be created from arrays of appropriate length:
+/// Homogeneous tuples can be created from arrays of appropriate length:
 ///
 /// ```
 /// let array: [u32; 3] = [1, 2, 3];
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 1955ef815ff..22ccfc0dbe9 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -152,6 +152,31 @@
 //! contains further primitive shared memory types, including [`atomic`] and
 //! [`mpsc`], which contains the channel types for message passing.
 //!
+//! # Use before and after `main()`
+//!
+//! Many parts of the standard library are expected to work before and after `main()`;
+//! but this is not guaranteed or ensured by tests. It is recommended that you write your own tests
+//! and run them on each platform you wish to support.
+//! This means that use of `std` before/after main, especially of features that interact with the
+//! OS or global state, is exempted from stability and portability guarantees and instead only
+//! provided on a best-effort basis. Nevertheless bug reports are appreciated.
+//!
+//! On the other hand `core` and `alloc` are most likely to work in such environments with
+//! the caveat that any hookable behavior such as panics, oom handling or allocators will also
+//! depend on the compatibility of the hooks.
+//!
+//! Some features may also behave differently outside main, e.g. stdio could become unbuffered,
+//! some panics might turn into aborts, backtraces might not get symbolicated or similar.
+//!
+//! Non-exhaustive list of known limitations:
+//!
+//! - after-main use of thread-locals, which also affects additional features:
+//!   - [`thread::current()`]
+//!   - [`thread::scope()`]
+//!   - [`sync::mpsc`]
+//! - before-main stdio file descriptors are not guaranteed to be open on unix platforms
+//!
+//!
 //! [I/O]: io
 //! [`MIN`]: i32::MIN
 //! [`MAX`]: i32::MAX
@@ -187,7 +212,6 @@
 //! [rust-discord]: https://discord.gg/rust-lang
 //! [array]: prim@array
 //! [slice]: prim@slice
-
 // To run std tests without x.py without ending up with two copies of std, Miri needs to be
 // able to "empty" this crate. See <https://github.com/rust-lang/miri-test-libstd/issues/4>.
 // rustc itself never sets the feature, so this line has no effect there.
@@ -351,7 +375,6 @@
 #![feature(get_many_mut)]
 #![feature(lazy_cell)]
 #![feature(log_syntax)]
-#![feature(saturating_int_impl)]
 #![feature(stdsimd)]
 #![feature(test)]
 #![feature(trace_macros)]
diff --git a/library/std/src/num.rs b/library/std/src/num.rs
index 46064bd2837..9e021b23fec 100644
--- a/library/std/src/num.rs
+++ b/library/std/src/num.rs
@@ -12,7 +12,7 @@ mod tests;
 #[cfg(test)]
 mod benches;
 
-#[unstable(feature = "saturating_int_impl", issue = "87920")]
+#[stable(feature = "saturating_int_impl", since = "CURRENT_RUSTC_VERSION")]
 pub use core::num::Saturating;
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::num::Wrapping;
diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs
index b6dc1a062ed..0eb4e88cfad 100644
--- a/library/std/src/os/unix/fs.rs
+++ b/library/std/src/os/unix/fs.rs
@@ -155,7 +155,7 @@ pub trait FileExt {
     /// flag fail to respect the offset parameter, always appending to the end
     /// of the file instead.
     ///
-    /// It is possible to inadvertantly set this flag, like in the example below.
+    /// It is possible to inadvertently set this flag, like in the example below.
     /// Therefore, it is important to be vigilant while changing options to mitigate
     /// unexpected behaviour.
     ///
diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs
index 80289ca08c3..2cfa896e5b9 100644
--- a/library/std/src/primitive_docs.rs
+++ b/library/std/src/primitive_docs.rs
@@ -612,7 +612,7 @@ mod prim_pointer {}
 /// statically generated up to size 32.
 ///
 /// Arrays of sizes from 1 to 12 (inclusive) implement [`From<Tuple>`], where `Tuple`
-/// is a homogenous [prim@tuple] of appropriate length.
+/// is a homogeneous [prim@tuple] of appropriate length.
 ///
 /// Arrays coerce to [slices (`[T]`)][slice], so a slice method may be called on
 /// an array. Indeed, this provides most of the API for working with arrays.
@@ -676,7 +676,7 @@ mod prim_pointer {}
 /// move_away(roa);
 /// ```
 ///
-/// Arrays can be created from homogenous tuples of appropriate length:
+/// Arrays can be created from homogeneous tuples of appropriate length:
 ///
 /// ```
 /// let tuple: (u32, u32, u32) = (1, 2, 3);
@@ -1065,7 +1065,7 @@ mod prim_str {}
 /// assert_eq!(y, 5);
 /// ```
 ///
-/// Homogenous tuples can be created from arrays of appropriate length:
+/// Homogeneous tuples can be created from arrays of appropriate length:
 ///
 /// ```
 /// let array: [u32; 3] = [1, 2, 3];
diff --git a/library/std/src/sync/mpsc/mod.rs b/library/std/src/sync/mpsc/mod.rs
index f92bb1a4b1f..d353c7bd5de 100644
--- a/library/std/src/sync/mpsc/mod.rs
+++ b/library/std/src/sync/mpsc/mod.rs
@@ -626,11 +626,6 @@ impl<T> Clone for Sender<T> {
     }
 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> Drop for Sender<T> {
-    fn drop(&mut self) {}
-}
-
 #[stable(feature = "mpsc_debug", since = "1.8.0")]
 impl<T> fmt::Debug for Sender<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -755,11 +750,6 @@ impl<T> Clone for SyncSender<T> {
     }
 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> Drop for SyncSender<T> {
-    fn drop(&mut self) {}
-}
-
 #[stable(feature = "mpsc_debug", since = "1.8.0")]
 impl<T> fmt::Debug for SyncSender<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -1096,11 +1086,6 @@ impl<T> IntoIterator for Receiver<T> {
     }
 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> Drop for Receiver<T> {
-    fn drop(&mut self) {}
-}
-
 #[stable(feature = "mpsc_debug", since = "1.8.0")]
 impl<T> fmt::Debug for Receiver<T> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index e4581c2de78..7b26068c294 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -178,7 +178,7 @@ use crate::sys_common::thread;
 use crate::sys_common::thread_info;
 use crate::sys_common::thread_parking::Parker;
 use crate::sys_common::{AsInner, IntoInner};
-use crate::time::Duration;
+use crate::time::{Duration, Instant};
 
 #[stable(feature = "scoped_threads", since = "1.63.0")]
 mod scoped;
@@ -872,6 +872,86 @@ pub fn sleep(dur: Duration) {
     imp::Thread::sleep(dur)
 }
 
+/// Puts the current thread to sleep until the specified deadline has passed.
+///
+/// The thread may still be asleep after the deadline specified due to
+/// scheduling specifics or platform-dependent functionality. It will never
+/// wake before.
+///
+/// This function is blocking, and should not be used in `async` functions.
+///
+/// # Platform-specific behavior
+///
+/// This function uses [`sleep`] internally, see its platform-specific behaviour.
+///
+///
+/// # Examples
+///
+/// A simple game loop that limits the game to 60 frames per second.
+///
+/// ```no_run
+/// #![feature(thread_sleep_until)]
+/// # use std::time::{Duration, Instant};
+/// # use std::thread;
+/// #
+/// # fn update() {}
+/// # fn render() {}
+/// #
+/// let max_fps = 60.0;
+/// let frame_time = Duration::from_secs_f32(1.0/max_fps);
+/// let mut next_frame = Instant::now();
+/// loop {
+///     thread::sleep_until(next_frame);
+///     next_frame += frame_time;
+///     update();
+///     render();
+/// }
+/// ```
+///
+/// A slow api we must not call too fast and which takes a few
+/// tries before succeeding. By using `sleep_until` the time the
+/// api call takes does not influence when we retry or when we give up
+///
+/// ```no_run
+/// #![feature(thread_sleep_until)]
+/// # use std::time::{Duration, Instant};
+/// # use std::thread;
+/// #
+/// # enum Status {
+/// #     Ready(usize),
+/// #     Waiting,
+/// # }
+/// # fn slow_web_api_call() -> Status { Status::Ready(42) }
+/// #
+/// # const MAX_DURATION: Duration = Duration::from_secs(10);
+/// #
+/// # fn try_api_call() -> Result<usize, ()> {
+/// let deadline = Instant::now() + MAX_DURATION;
+/// let delay = Duration::from_millis(250);
+/// let mut next_attempt = Instant::now();
+/// loop {
+///     if Instant::now() > deadline {
+///         break Err(());
+///     }
+///     if let Status::Ready(data) = slow_web_api_call() {
+///         break Ok(data);
+///     }
+///
+///     next_attempt = deadline.min(next_attempt + delay);
+///     thread::sleep_until(next_attempt);
+/// }
+/// # }
+/// # let _data = try_api_call();
+/// ```
+#[unstable(feature = "thread_sleep_until", issue = "113752")]
+pub fn sleep_until(deadline: Instant) {
+    let now = Instant::now();
+
+    if let Some(delay) = deadline.checked_duration_since(now) {
+        sleep(delay);
+    }
+}
+
 /// Used to ensure that `park` and `park_timeout` do not unwind, as that can
 /// cause undefined behaviour if not handled correctly (see #102398 for context).
 struct PanicGuard;
diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md
index 253d504d7bd..548281ca506 100644
--- a/src/bootstrap/README.md
+++ b/src/bootstrap/README.md
@@ -1,8 +1,7 @@
 # rustbuild - Bootstrapping Rust
 
-This is an in-progress README which is targeted at helping to explain how Rust
-is bootstrapped and in general, some of the technical details of the build
-system.
+This README is aimed at helping to explain how Rust is bootstrapped and in general,
+some of the technical details of the build system.
 
 Note that this README only covers internal information, not how to use the tool.
 Please check [bootstrapping dev guide][bootstrapping-dev-guide] for further information.
diff --git a/src/bootstrap/bin/_helper.rs b/src/bootstrap/bin/_helper.rs
new file mode 100644
index 00000000000..09aa471dba4
--- /dev/null
+++ b/src/bootstrap/bin/_helper.rs
@@ -0,0 +1,24 @@
+/// Parses the value of the "RUSTC_VERBOSE" environment variable and returns it as a `usize`.
+/// If it was not defined, returns 0 by default.
+///
+/// Panics if "RUSTC_VERBOSE" is defined with the value that is not an unsigned integer.
+fn parse_rustc_verbose() -> usize {
+    use std::str::FromStr;
+
+    match std::env::var("RUSTC_VERBOSE") {
+        Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"),
+        Err(_) => 0,
+    }
+}
+
+/// Parses the value of the "RUSTC_STAGE" environment variable and returns it as a `String`.
+///
+/// If "RUSTC_STAGE" was not set, the program will be terminated with 101.
+fn parse_rustc_stage() -> String {
+    std::env::var("RUSTC_STAGE").unwrap_or_else(|_| {
+        // Don't panic here; it's reasonable to try and run these shims directly. Give a helpful error instead.
+        eprintln!("rustc shim: fatal: RUSTC_STAGE was not set");
+        eprintln!("rustc shim: note: use `x.py build -vvv` to see all environment variables set by bootstrap");
+        exit(101);
+    })
+}
diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs
index 10718aeb89f..20cd63b966b 100644
--- a/src/bootstrap/bin/rustc.rs
+++ b/src/bootstrap/bin/rustc.rs
@@ -16,27 +16,25 @@
 //! never get replaced.
 
 include!("../dylib_util.rs");
+include!("./_helper.rs");
 
 use std::env;
 use std::path::PathBuf;
 use std::process::{exit, Child, Command};
-use std::str::FromStr;
 use std::time::Instant;
 
 fn main() {
     let args = env::args_os().skip(1).collect::<Vec<_>>();
     let arg = |name| args.windows(2).find(|args| args[0] == name).and_then(|args| args[1].to_str());
 
+    let stage = parse_rustc_stage();
+    let verbose = parse_rustc_verbose();
+
     // Detect whether or not we're a build script depending on whether --target
     // is passed (a bit janky...)
     let target = arg("--target");
     let version = args.iter().find(|w| &**w == "-vV");
 
-    let verbose = match env::var("RUSTC_VERBOSE") {
-        Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"),
-        Err(_) => 0,
-    };
-
     // Use a different compiler for build scripts, since there may not yet be a
     // libstd for the real compiler to use. However, if Cargo is attempting to
     // determine the version of the compiler, the real compiler needs to be
@@ -47,12 +45,7 @@ fn main() {
     } else {
         ("RUSTC_REAL", "RUSTC_LIBDIR")
     };
-    let stage = env::var("RUSTC_STAGE").unwrap_or_else(|_| {
-        // Don't panic here; it's reasonable to try and run these shims directly. Give a helpful error instead.
-        eprintln!("rustc shim: fatal: RUSTC_STAGE was not set");
-        eprintln!("rustc shim: note: use `x.py build -vvv` to see all environment variables set by bootstrap");
-        exit(101);
-    });
+
     let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set");
     let on_fail = env::var_os("RUSTC_ON_FAIL").map(Command::new);
 
diff --git a/src/bootstrap/bin/rustdoc.rs b/src/bootstrap/bin/rustdoc.rs
index 4ecb3349816..6561c1c1933 100644
--- a/src/bootstrap/bin/rustdoc.rs
+++ b/src/bootstrap/bin/rustdoc.rs
@@ -9,14 +9,14 @@ use std::process::{exit, Command};
 
 include!("../dylib_util.rs");
 
+include!("./_helper.rs");
+
 fn main() {
     let args = env::args_os().skip(1).collect::<Vec<_>>();
-    let stage = env::var("RUSTC_STAGE").unwrap_or_else(|_| {
-        // Don't panic here; it's reasonable to try and run these shims directly. Give a helpful error instead.
-        eprintln!("rustc shim: fatal: RUSTC_STAGE was not set");
-        eprintln!("rustc shim: note: use `x.py build -vvv` to see all environment variables set by bootstrap");
-        exit(101);
-    });
+
+    let stage = parse_rustc_stage();
+    let verbose = parse_rustc_verbose();
+
     let rustdoc = env::var_os("RUSTDOC_REAL").expect("RUSTDOC_REAL was not set");
     let libdir = env::var_os("RUSTDOC_LIBDIR").expect("RUSTDOC_LIBDIR was not set");
     let sysroot = env::var_os("RUSTC_SYSROOT").expect("RUSTC_SYSROOT was not set");
@@ -25,13 +25,6 @@ fn main() {
     // is passed (a bit janky...)
     let target = args.windows(2).find(|w| &*w[0] == "--target").and_then(|w| w[1].to_str());
 
-    use std::str::FromStr;
-
-    let verbose = match env::var("RUSTC_VERBOSE") {
-        Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"),
-        Err(_) => 0,
-    };
-
     let mut dylib_path = dylib_path();
     dylib_path.insert(0, PathBuf::from(libdir.clone()));
 
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index e4c76226454..a9aa7524e8b 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -312,6 +312,14 @@ def default_build_triple(verbose):
         # non-standard string (e.g. gnuwin32 tools returns `windows32`). In
         # these cases, fall back to using sys.platform.
         return 'x86_64-pc-windows-msvc'
+    elif kernel == 'AIX':
+        # `uname -m` returns the machine ID rather than machine hardware on AIX,
+        # so we are unable to use cputype to form triple. AIX 7.2 and
+        # above supports 32-bit and 64-bit mode simultaneously and `uname -p`
+        # returns `powerpc`, however we only supports `powerpc64-ibm-aix` in
+        # rust on AIX. For above reasons, kerneltype_mapper and cputype_mapper
+        # are not used to infer AIX's triple.
+        return 'powerpc64-ibm-aix'
     else:
         err = "unknown OS type: {}".format(kernel)
         sys.exit(err)
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 91fa5ec6633..50f1007e1ff 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -525,7 +525,7 @@ impl<'a> ShouldRun<'a> {
                 .iter()
                 .map(|p| {
                     // assert only if `p` isn't submodule
-                    if !submodules_paths.iter().find(|sm_p| p.contains(*sm_p)).is_some() {
+                    if submodules_paths.iter().find(|sm_p| p.contains(*sm_p)).is_none() {
                         assert!(
                             self.builder.src.join(p).exists(),
                             "`should_run.paths` should correspond to real on-disk paths - use `alias` if there is no relevant path: {}",
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index 9c68e5a78d8..2686a8c1752 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -876,10 +876,8 @@ impl Step for Rustc {
                     cargo.rustflag("-Clto=off");
                 }
             }
-        } else {
-            if builder.config.rust_lto == RustcLto::Off {
-                cargo.rustflag("-Clto=off");
-            }
+        } else if builder.config.rust_lto == RustcLto::Off {
+            cargo.rustflag("-Clto=off");
         }
 
         for krate in &*self.crates {
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index e5fdac3ceda..176ef8e92db 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -322,33 +322,23 @@ pub struct RustfmtMetadata {
     pub version: String,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Default)]
 pub enum RustfmtState {
     SystemToolchain(PathBuf),
     Downloaded(PathBuf),
     Unavailable,
+    #[default]
     LazyEvaluated,
 }
 
-impl Default for RustfmtState {
-    fn default() -> Self {
-        RustfmtState::LazyEvaluated
-    }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq)]
+#[derive(Debug, Default, Clone, Copy, PartialEq)]
 pub enum LlvmLibunwind {
+    #[default]
     No,
     InTree,
     System,
 }
 
-impl Default for LlvmLibunwind {
-    fn default() -> Self {
-        Self::No
-    }
-}
-
 impl FromStr for LlvmLibunwind {
     type Err = String;
 
@@ -362,19 +352,14 @@ impl FromStr for LlvmLibunwind {
     }
 }
 
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub enum SplitDebuginfo {
     Packed,
     Unpacked,
+    #[default]
     Off,
 }
 
-impl Default for SplitDebuginfo {
-    fn default() -> Self {
-        SplitDebuginfo::Off
-    }
-}
-
 impl std::str::FromStr for SplitDebuginfo {
     type Err = ();
 
@@ -1529,7 +1514,7 @@ impl Config {
             let asserts = llvm_assertions.unwrap_or(false);
             config.llvm_from_ci = match llvm.download_ci_llvm {
                 Some(StringOrBool::String(s)) => {
-                    assert!(s == "if-available", "unknown option `{s}` for download-ci-llvm");
+                    assert_eq!(s, "if-available", "unknown option `{s}` for download-ci-llvm");
                     crate::llvm::is_ci_llvm_available(&config, asserts)
                 }
                 Some(StringOrBool::Bool(b)) => b,
diff --git a/src/bootstrap/config/tests.rs b/src/bootstrap/config/tests.rs
index b8f3be96062..aac76cdcbcf 100644
--- a/src/bootstrap/config/tests.rs
+++ b/src/bootstrap/config/tests.rs
@@ -136,7 +136,7 @@ build-config = {}
         "setting string value without quotes"
     );
     assert_eq!(config.gdb, Some("bar".into()), "setting string value with quotes");
-    assert_eq!(config.deny_warnings, false, "setting boolean value");
+    assert!(!config.deny_warnings, "setting boolean value");
     assert_eq!(
         config.tools,
         Some(["cargo".to_string()].into_iter().collect()),
@@ -181,13 +181,13 @@ fn profile_user_dist() {
 
 #[test]
 fn rust_optimize() {
-    assert_eq!(parse("").rust_optimize.is_release(), true);
-    assert_eq!(parse("rust.optimize = false").rust_optimize.is_release(), false);
-    assert_eq!(parse("rust.optimize = true").rust_optimize.is_release(), true);
-    assert_eq!(parse("rust.optimize = 0").rust_optimize.is_release(), false);
-    assert_eq!(parse("rust.optimize = 1").rust_optimize.is_release(), true);
+    assert!(parse("").rust_optimize.is_release());
+    assert!(!parse("rust.optimize = false").rust_optimize.is_release());
+    assert!(parse("rust.optimize = true").rust_optimize.is_release());
+    assert!(!parse("rust.optimize = 0").rust_optimize.is_release());
+    assert!(parse("rust.optimize = 1").rust_optimize.is_release());
+    assert!(parse("rust.optimize = \"s\"").rust_optimize.is_release());
     assert_eq!(parse("rust.optimize = 1").rust_optimize.get_opt_level(), Some("1".to_string()));
-    assert_eq!(parse("rust.optimize = \"s\"").rust_optimize.is_release(), true);
     assert_eq!(parse("rust.optimize = \"s\"").rust_optimize.get_opt_level(), Some("s".to_string()));
 }
 
diff --git a/src/bootstrap/download.rs b/src/bootstrap/download.rs
index 9489297d67d..8e9614ec89a 100644
--- a/src/bootstrap/download.rs
+++ b/src/bootstrap/download.rs
@@ -441,7 +441,7 @@ impl Config {
     }
 
     pub(crate) fn download_beta_toolchain(&self) {
-        self.verbose(&format!("downloading stage0 beta artifacts"));
+        self.verbose("downloading stage0 beta artifacts");
 
         let date = &self.stage0_metadata.compiler.date;
         let version = &self.stage0_metadata.compiler.version;
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 671d25484d0..1e001fae8ab 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -116,7 +116,7 @@ pub const VERSION: usize = 2;
 
 /// Extra --check-cfg to add when building
 /// (Mode restriction, config name, config values (if any))
-const EXTRA_CHECK_CFGS: &[(Option<Mode>, &'static str, Option<&[&'static str]>)] = &[
+const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[
     (None, "bootstrap", None),
     (Some(Mode::Rustc), "parallel_compiler", None),
     (Some(Mode::ToolRustc), "parallel_compiler", None),
@@ -1757,10 +1757,11 @@ to download LLVM rather than building it.
         //
         // In these cases we automatically enable Ninja if we find it in the
         // environment.
-        if !self.config.ninja_in_file && self.config.build.contains("msvc") {
-            if cmd_finder.maybe_have("ninja").is_some() {
-                return true;
-            }
+        if !self.config.ninja_in_file
+            && self.config.build.contains("msvc")
+            && cmd_finder.maybe_have("ninja").is_some()
+        {
+            return true;
         }
 
         self.config.ninja_in_file
diff --git a/src/bootstrap/llvm.rs b/src/bootstrap/llvm.rs
index aefed501513..07288a1863a 100644
--- a/src/bootstrap/llvm.rs
+++ b/src/bootstrap/llvm.rs
@@ -155,7 +155,7 @@ pub(crate) fn detect_llvm_sha(config: &Config, is_git: bool) -> String {
         "".to_owned()
     };
 
-    if &llvm_sha == "" {
+    if llvm_sha.is_empty() {
         eprintln!("error: could not find commit hash for downloading LLVM");
         eprintln!("help: maybe your repository history is too shallow?");
         eprintln!("help: consider disabling `download-ci-llvm`");
@@ -208,10 +208,10 @@ pub(crate) fn is_ci_llvm_available(config: &Config, asserts: bool) -> bool {
         ("x86_64-unknown-netbsd", false),
     ];
 
-    if !supported_platforms.contains(&(&*config.build.triple, asserts)) {
-        if asserts == true || !supported_platforms.contains(&(&*config.build.triple, true)) {
-            return false;
-        }
+    if !supported_platforms.contains(&(&*config.build.triple, asserts))
+        && (asserts || !supported_platforms.contains(&(&*config.build.triple, true)))
+    {
+        return false;
     }
 
     if is_ci_llvm_modified(config) {
@@ -497,11 +497,11 @@ impl Step for Llvm {
             let mut cmd = Command::new(&res.llvm_config);
             let version = output(cmd.arg("--version"));
             let major = version.split('.').next().unwrap();
-            let lib_name = match &llvm_version_suffix {
+
+            match &llvm_version_suffix {
                 Some(version_suffix) => format!("libLLVM-{major}{version_suffix}.{extension}"),
                 None => format!("libLLVM-{major}.{extension}"),
-            };
-            lib_name
+            }
         };
 
         // When building LLVM with LLVM_LINK_LLVM_DYLIB for macOS, an unversioned
@@ -756,13 +756,15 @@ fn configure_cmake(
 
     // For distribution we want the LLVM tools to be *statically* linked to libstdc++.
     // We also do this if the user explicitly requested static libstdc++.
-    if builder.config.llvm_static_stdcpp {
-        if !target.contains("msvc") && !target.contains("netbsd") && !target.contains("solaris") {
-            if target.contains("apple") || target.contains("windows") {
-                ldflags.push_all("-static-libstdc++");
-            } else {
-                ldflags.push_all("-Wl,-Bsymbolic -static-libstdc++");
-            }
+    if builder.config.llvm_static_stdcpp
+        && !target.contains("msvc")
+        && !target.contains("netbsd")
+        && !target.contains("solaris")
+    {
+        if target.contains("apple") || target.contains("windows") {
+            ldflags.push_all("-static-libstdc++");
+        } else {
+            ldflags.push_all("-Wl,-Bsymbolic -static-libstdc++");
         }
     }
 
diff --git a/src/bootstrap/sanity.rs b/src/bootstrap/sanity.rs
index 144e2acd09e..0febdf250d3 100644
--- a/src/bootstrap/sanity.rs
+++ b/src/bootstrap/sanity.rs
@@ -95,20 +95,19 @@ pub fn check(build: &mut Build) {
                     .unwrap_or(true)
             })
             .any(|build_llvm_ourselves| build_llvm_ourselves);
+
     let need_cmake = building_llvm || build.config.any_sanitizers_enabled();
-    if need_cmake {
-        if cmd_finder.maybe_have("cmake").is_none() {
-            eprintln!(
-                "
+    if need_cmake && cmd_finder.maybe_have("cmake").is_none() {
+        eprintln!(
+            "
 Couldn't find required command: cmake
 
 You should install cmake, or set `download-ci-llvm = true` in the
 `[llvm]` section of `config.toml` to download LLVM rather
 than building it.
 "
-            );
-            crate::exit!(1);
-        }
+        );
+        crate::exit!(1);
     }
 
     build.config.python = build
@@ -202,10 +201,10 @@ than building it.
             .entry(*target)
             .or_insert_with(|| Target::from_triple(&target.triple));
 
-        if target.contains("-none-") || target.contains("nvptx") {
-            if build.no_std(*target) == Some(false) {
-                panic!("All the *-none-* and nvptx* targets are no-std targets")
-            }
+        if (target.contains("-none-") || target.contains("nvptx"))
+            && build.no_std(*target) == Some(false)
+        {
+            panic!("All the *-none-* and nvptx* targets are no-std targets")
         }
 
         // Some environments don't want or need these tools, such as when testing Miri.
diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs
index 30730f50491..ef0234957b5 100644
--- a/src/bootstrap/setup.rs
+++ b/src/bootstrap/setup.rs
@@ -33,6 +33,7 @@ static SETTINGS_HASHES: &[&str] = &[
     "af1b5efe196aed007577899db9dae15d6dbc923d6fa42fa0934e68617ba9bbe0",
     "3468fea433c25fff60be6b71e8a215a732a7b1268b6a83bf10d024344e140541",
     "47d227f424bf889b0d899b9cc992d5695e1b78c406e183cd78eafefbe5488923",
+    "b526bd58d0262dd4dda2bff5bc5515b705fb668a46235ace3e057f807963a11a",
 ];
 static RUST_ANALYZER_SETTINGS: &str = include_str!("../etc/rust_analyzer_settings.json");
 
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index 96611ca873a..ba030f0f525 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -1139,16 +1139,14 @@ help: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to
             .map(|filename| builder.src.join("src/etc/completions").join(filename));
         if builder.config.cmd.bless() {
             builder.ensure(crate::run::GenerateCompletions);
-        } else {
-            if crate::flags::get_completion(shells::Bash, &bash).is_some()
-                || crate::flags::get_completion(shells::Fish, &fish).is_some()
-                || crate::flags::get_completion(shells::PowerShell, &powershell).is_some()
-            {
-                eprintln!(
-                    "x.py completions were changed; run `x.py run generate-completions` to update them"
-                );
-                crate::exit!(1);
-            }
+        } else if crate::flags::get_completion(shells::Bash, &bash).is_some()
+            || crate::flags::get_completion(shells::Fish, &fish).is_some()
+            || crate::flags::get_completion(shells::PowerShell, &powershell).is_some()
+        {
+            eprintln!(
+                "x.py completions were changed; run `x.py run generate-completions` to update them"
+            );
+            crate::exit!(1);
         }
     }
 
@@ -1378,7 +1376,7 @@ impl Step for MirOpt {
         let run = |target| {
             builder.ensure(Compiletest {
                 compiler: self.compiler,
-                target: target,
+                target,
                 mode: "mir-opt",
                 suite: "mir-opt",
                 path: "tests/mir-opt",
diff --git a/src/ci/docker/host-x86_64/arm-android/Dockerfile b/src/ci/docker/host-x86_64/arm-android/Dockerfile
index b6b4fdc67a9..db11700af28 100644
--- a/src/ci/docker/host-x86_64/arm-android/Dockerfile
+++ b/src/ci/docker/host-x86_64/arm-android/Dockerfile
@@ -1,4 +1,4 @@
-FROM ubuntu:22.10
+FROM ubuntu:23.04
 
 ARG DEBIAN_FRONTEND=noninteractive
 COPY scripts/android-base-apt-get.sh /scripts/
diff --git a/src/ci/docker/host-x86_64/dist-android/Dockerfile b/src/ci/docker/host-x86_64/dist-android/Dockerfile
index 9c6f648896b..b09b6edb01a 100644
--- a/src/ci/docker/host-x86_64/dist-android/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-android/Dockerfile
@@ -1,4 +1,4 @@
-FROM ubuntu:22.10
+FROM ubuntu:23.04
 
 COPY scripts/android-base-apt-get.sh /scripts/
 RUN sh /scripts/android-base-apt-get.sh
diff --git a/src/ci/docker/host-x86_64/dist-various-1/install-x86_64-redox.sh b/src/ci/docker/host-x86_64/dist-various-1/install-x86_64-redox.sh
index dad97922338..f86402b0180 100755
--- a/src/ci/docker/host-x86_64/dist-various-1/install-x86_64-redox.sh
+++ b/src/ci/docker/host-x86_64/dist-various-1/install-x86_64-redox.sh
@@ -2,5 +2,5 @@
 
 set -ex
 
-curl https://static.redox-os.org/toolchain/x86_64-unknown-redox/relibc-install.tar.gz | \
+curl https://ci-mirrors.rust-lang.org/rustc/2022-11-27-relibc-install.tar.gz | \
 tar --extract --gzip --directory /usr/local
diff --git a/src/etc/rust_analyzer_settings.json b/src/etc/rust_analyzer_settings.json
index 6e5e2c35005..32a04cfd5d1 100644
--- a/src/etc/rust_analyzer_settings.json
+++ b/src/etc/rust_analyzer_settings.json
@@ -16,10 +16,10 @@
         "compiler/rustc_codegen_gcc/Cargo.toml"
     ],
     "rust-analyzer.rustfmt.overrideCommand": [
-        "./build/host/rustfmt/bin/rustfmt",
+        "${workspaceFolder}/build/host/rustfmt/bin/rustfmt",
         "--edition=2021"
     ],
-    "rust-analyzer.procMacro.server": "./build/host/stage0/libexec/rust-analyzer-proc-macro-srv",
+    "rust-analyzer.procMacro.server": "${workspaceFolder}/build/host/stage0/libexec/rust-analyzer-proc-macro-srv",
     "rust-analyzer.procMacro.enable": true,
     "rust-analyzer.cargo.buildScripts.enable": true,
     "rust-analyzer.cargo.buildScripts.invocationLocation": "root",
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index ea87268877f..24597c3aca3 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -1243,6 +1243,7 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
                     def_id.to_def_id(),
                     span_of_fragments(&attrs.doc_strings).unwrap_or(sp),
                 )),
+                self.tcx.features().custom_code_classes_in_docs,
             );
         }
 
diff --git a/src/librustdoc/externalfiles.rs b/src/librustdoc/externalfiles.rs
index f0ebb8e5a39..b34b69b1f15 100644
--- a/src/librustdoc/externalfiles.rs
+++ b/src/librustdoc/externalfiles.rs
@@ -46,6 +46,8 @@ impl ExternalHtml {
                 edition,
                 playground,
                 heading_offset: HeadingOffset::H2,
+                // For external files, it'll be disabled until the feature is enabled by default.
+                custom_code_classes_in_docs: false,
             }
             .into_string()
         );
@@ -61,6 +63,8 @@ impl ExternalHtml {
                 edition,
                 playground,
                 heading_offset: HeadingOffset::H2,
+                // For external files, it'll be disabled until the feature is enabled by default.
+                custom_code_classes_in_docs: false,
             }
             .into_string()
         );
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 177fb1a9426..59958fbaef9 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -20,6 +20,7 @@
 //!     edition: Edition::Edition2015,
 //!     playground: &None,
 //!     heading_offset: HeadingOffset::H2,
+//!     custom_code_classes_in_docs: true,
 //! };
 //! let html = md.into_string();
 //! // ... something using html
@@ -95,6 +96,8 @@ pub struct Markdown<'a> {
     /// Offset at which we render headings.
     /// E.g. if `heading_offset: HeadingOffset::H2`, then `# something` renders an `<h2>`.
     pub heading_offset: HeadingOffset,
+    /// `true` if the `custom_code_classes_in_docs` feature is enabled.
+    pub custom_code_classes_in_docs: bool,
 }
 /// A struct like `Markdown` that renders the markdown with a table of contents.
 pub(crate) struct MarkdownWithToc<'a> {
@@ -103,6 +106,8 @@ pub(crate) struct MarkdownWithToc<'a> {
     pub(crate) error_codes: ErrorCodes,
     pub(crate) edition: Edition,
     pub(crate) playground: &'a Option<Playground>,
+    /// `true` if the `custom_code_classes_in_docs` feature is enabled.
+    pub(crate) custom_code_classes_in_docs: bool,
 }
 /// A tuple struct like `Markdown` that renders the markdown escaping HTML tags
 /// and includes no paragraph tags.
@@ -203,6 +208,7 @@ struct CodeBlocks<'p, 'a, I: Iterator<Item = Event<'a>>> {
     // Information about the playground if a URL has been specified, containing an
     // optional crate name and the URL.
     playground: &'p Option<Playground>,
+    custom_code_classes_in_docs: bool,
 }
 
 impl<'p, 'a, I: Iterator<Item = Event<'a>>> CodeBlocks<'p, 'a, I> {
@@ -211,8 +217,15 @@ impl<'p, 'a, I: Iterator<Item = Event<'a>>> CodeBlocks<'p, 'a, I> {
         error_codes: ErrorCodes,
         edition: Edition,
         playground: &'p Option<Playground>,
+        custom_code_classes_in_docs: bool,
     ) -> Self {
-        CodeBlocks { inner: iter, check_error_codes: error_codes, edition, playground }
+        CodeBlocks {
+            inner: iter,
+            check_error_codes: error_codes,
+            edition,
+            playground,
+            custom_code_classes_in_docs,
+        }
     }
 }
 
@@ -242,8 +255,12 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
 
         let parse_result = match kind {
             CodeBlockKind::Fenced(ref lang) => {
-                let parse_result =
-                    LangString::parse_without_check(lang, self.check_error_codes, false);
+                let parse_result = LangString::parse_without_check(
+                    lang,
+                    self.check_error_codes,
+                    false,
+                    self.custom_code_classes_in_docs,
+                );
                 if !parse_result.rust {
                     let added_classes = parse_result.added_classes;
                     let lang_string = if let Some(lang) = parse_result.unknown.first() {
@@ -725,8 +742,17 @@ pub(crate) fn find_testable_code<T: doctest::Tester>(
     error_codes: ErrorCodes,
     enable_per_target_ignores: bool,
     extra_info: Option<&ExtraInfo<'_>>,
+    custom_code_classes_in_docs: bool,
 ) {
-    find_codes(doc, tests, error_codes, enable_per_target_ignores, extra_info, false)
+    find_codes(
+        doc,
+        tests,
+        error_codes,
+        enable_per_target_ignores,
+        extra_info,
+        false,
+        custom_code_classes_in_docs,
+    )
 }
 
 pub(crate) fn find_codes<T: doctest::Tester>(
@@ -736,6 +762,7 @@ pub(crate) fn find_codes<T: doctest::Tester>(
     enable_per_target_ignores: bool,
     extra_info: Option<&ExtraInfo<'_>>,
     include_non_rust: bool,
+    custom_code_classes_in_docs: bool,
 ) {
     let mut parser = Parser::new(doc).into_offset_iter();
     let mut prev_offset = 0;
@@ -754,6 +781,7 @@ pub(crate) fn find_codes<T: doctest::Tester>(
                                 error_codes,
                                 enable_per_target_ignores,
                                 extra_info,
+                                custom_code_classes_in_docs,
                             )
                         }
                     }
@@ -1153,8 +1181,15 @@ impl LangString {
         string: &str,
         allow_error_code_check: ErrorCodes,
         enable_per_target_ignores: bool,
+        custom_code_classes_in_docs: bool,
     ) -> Self {
-        Self::parse(string, allow_error_code_check, enable_per_target_ignores, None)
+        Self::parse(
+            string,
+            allow_error_code_check,
+            enable_per_target_ignores,
+            None,
+            custom_code_classes_in_docs,
+        )
     }
 
     fn parse(
@@ -1162,6 +1197,7 @@ impl LangString {
         allow_error_code_check: ErrorCodes,
         enable_per_target_ignores: bool,
         extra: Option<&ExtraInfo<'_>>,
+        custom_code_classes_in_docs: bool,
     ) -> Self {
         let allow_error_code_check = allow_error_code_check.as_bool();
         let mut seen_rust_tags = false;
@@ -1197,7 +1233,11 @@ impl LangString {
                     seen_rust_tags = true;
                 }
                 LangStringToken::LangToken("custom") => {
-                    seen_custom_tag = true;
+                    if custom_code_classes_in_docs {
+                        seen_custom_tag = true;
+                    } else {
+                        seen_other_tags = true;
+                    }
                 }
                 LangStringToken::LangToken("test_harness") => {
                     data.test_harness = true;
@@ -1268,11 +1308,16 @@ impl LangString {
                     data.unknown.push(x.to_owned());
                 }
                 LangStringToken::KeyValueAttribute(key, value) => {
-                    if key == "class" {
-                        data.added_classes.push(value.to_owned());
-                    } else if let Some(extra) = extra {
-                        extra
-                            .error_invalid_codeblock_attr(format!("unsupported attribute `{key}`"));
+                    if custom_code_classes_in_docs {
+                        if key == "class" {
+                            data.added_classes.push(value.to_owned());
+                        } else if let Some(extra) = extra {
+                            extra.error_invalid_codeblock_attr(format!(
+                                "unsupported attribute `{key}`"
+                            ));
+                        }
+                    } else {
+                        seen_other_tags = true;
                     }
                 }
                 LangStringToken::ClassAttribute(class) => {
@@ -1302,6 +1347,7 @@ impl Markdown<'_> {
             edition,
             playground,
             heading_offset,
+            custom_code_classes_in_docs,
         } = self;
 
         // This is actually common enough to special-case
@@ -1324,7 +1370,7 @@ impl Markdown<'_> {
         let p = Footnotes::new(p);
         let p = LinkReplacer::new(p.map(|(ev, _)| ev), links);
         let p = TableWrapper::new(p);
-        let p = CodeBlocks::new(p, codes, edition, playground);
+        let p = CodeBlocks::new(p, codes, edition, playground, custom_code_classes_in_docs);
         html::push_html(&mut s, p);
 
         s
@@ -1333,7 +1379,14 @@ impl Markdown<'_> {
 
 impl MarkdownWithToc<'_> {
     pub(crate) fn into_string(self) -> String {
-        let MarkdownWithToc { content: md, ids, error_codes: codes, edition, playground } = self;
+        let MarkdownWithToc {
+            content: md,
+            ids,
+            error_codes: codes,
+            edition,
+            playground,
+            custom_code_classes_in_docs,
+        } = self;
 
         let p = Parser::new_ext(md, main_body_opts()).into_offset_iter();
 
@@ -1345,7 +1398,7 @@ impl MarkdownWithToc<'_> {
             let p = HeadingLinks::new(p, Some(&mut toc), ids, HeadingOffset::H1);
             let p = Footnotes::new(p);
             let p = TableWrapper::new(p.map(|(ev, _)| ev));
-            let p = CodeBlocks::new(p, codes, edition, playground);
+            let p = CodeBlocks::new(p, codes, edition, playground, custom_code_classes_in_docs);
             html::push_html(&mut s, p);
         }
 
@@ -1786,7 +1839,11 @@ pub(crate) struct RustCodeBlock {
 
 /// Returns a range of bytes for each code block in the markdown that is tagged as `rust` or
 /// untagged (and assumed to be rust).
-pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec<RustCodeBlock> {
+pub(crate) fn rust_code_blocks(
+    md: &str,
+    extra_info: &ExtraInfo<'_>,
+    custom_code_classes_in_docs: bool,
+) -> Vec<RustCodeBlock> {
     let mut code_blocks = vec![];
 
     if md.is_empty() {
@@ -1803,7 +1860,13 @@ pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec<Rust
                     let lang_string = if syntax.is_empty() {
                         Default::default()
                     } else {
-                        LangString::parse(&*syntax, ErrorCodes::Yes, false, Some(extra_info))
+                        LangString::parse(
+                            &*syntax,
+                            ErrorCodes::Yes,
+                            false,
+                            Some(extra_info),
+                            custom_code_classes_in_docs,
+                        )
                     };
                     if !lang_string.rust {
                         continue;
diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs
index 7d89cb0c4e6..32957ac57fa 100644
--- a/src/librustdoc/html/markdown/tests.rs
+++ b/src/librustdoc/html/markdown/tests.rs
@@ -49,7 +49,7 @@ fn test_unique_id() {
 fn test_lang_string_parse() {
     fn t(lg: LangString) {
         let s = &lg.original;
-        assert_eq!(LangString::parse(s, ErrorCodes::Yes, true, None), lg)
+        assert_eq!(LangString::parse(s, ErrorCodes::Yes, true, None, true), lg)
     }
 
     t(Default::default());
@@ -290,6 +290,7 @@ fn test_header() {
             edition: DEFAULT_EDITION,
             playground: &None,
             heading_offset: HeadingOffset::H2,
+            custom_code_classes_in_docs: true,
         }
         .into_string();
         assert_eq!(output, expect, "original: {}", input);
@@ -329,6 +330,7 @@ fn test_header_ids_multiple_blocks() {
             edition: DEFAULT_EDITION,
             playground: &None,
             heading_offset: HeadingOffset::H2,
+            custom_code_classes_in_docs: true,
         }
         .into_string();
         assert_eq!(output, expect, "original: {}", input);
@@ -433,7 +435,7 @@ fn test_find_testable_code_line() {
             }
         }
         let mut lines = Vec::<usize>::new();
-        find_testable_code(input, &mut lines, ErrorCodes::No, false, None);
+        find_testable_code(input, &mut lines, ErrorCodes::No, false, None, true);
         assert_eq!(lines, expect);
     }
 
@@ -458,6 +460,7 @@ fn test_ascii_with_prepending_hashtag() {
             edition: DEFAULT_EDITION,
             playground: &None,
             heading_offset: HeadingOffset::H2,
+            custom_code_classes_in_docs: true,
         }
         .into_string();
         assert_eq!(output, expect, "original: {}", input);
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 5adbecd6d04..f70f59d3be3 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -403,7 +403,8 @@ fn scrape_examples_help(shared: &SharedContext<'_>) -> String {
             error_codes: shared.codes,
             edition: shared.edition(),
             playground: &shared.playground,
-            heading_offset: HeadingOffset::H1
+            heading_offset: HeadingOffset::H1,
+            custom_code_classes_in_docs: false,
         }
         .into_string()
     )
@@ -437,6 +438,7 @@ fn render_markdown<'a, 'cx: 'a>(
     heading_offset: HeadingOffset,
 ) -> impl fmt::Display + 'a + Captures<'cx> {
     display_fn(move |f| {
+        let custom_code_classes_in_docs = cx.tcx().features().custom_code_classes_in_docs;
         write!(
             f,
             "<div class=\"docblock\">{}</div>",
@@ -448,6 +450,7 @@ fn render_markdown<'a, 'cx: 'a>(
                 edition: cx.shared.edition(),
                 playground: &cx.shared.playground,
                 heading_offset,
+                custom_code_classes_in_docs,
             }
             .into_string()
         )
@@ -1778,6 +1781,7 @@ fn render_impl(
                      </div>",
                 );
             }
+            let custom_code_classes_in_docs = cx.tcx().features().custom_code_classes_in_docs;
             write!(
                 w,
                 "<div class=\"docblock\">{}</div>",
@@ -1788,7 +1792,8 @@ fn render_impl(
                     error_codes: cx.shared.codes,
                     edition: cx.shared.edition(),
                     playground: &cx.shared.playground,
-                    heading_offset: HeadingOffset::H4
+                    heading_offset: HeadingOffset::H4,
+                    custom_code_classes_in_docs,
                 }
                 .into_string()
             );
diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs
index 526eea30478..b74a9392f27 100644
--- a/src/librustdoc/markdown.rs
+++ b/src/librustdoc/markdown.rs
@@ -80,6 +80,8 @@ pub(crate) fn render<P: AsRef<Path>>(
             error_codes,
             edition,
             playground: &playground,
+            // For markdown files, it'll be disabled until the feature is enabled by default.
+            custom_code_classes_in_docs: false,
         }
         .into_string()
     } else {
@@ -91,6 +93,8 @@ pub(crate) fn render<P: AsRef<Path>>(
             edition,
             playground: &playground,
             heading_offset: HeadingOffset::H1,
+            // For markdown files, it'll be disabled until the feature is enabled by default.
+            custom_code_classes_in_docs: false,
         }
         .into_string()
     };
@@ -154,7 +158,15 @@ pub(crate) fn test(options: Options) -> Result<(), String> {
     collector.set_position(DUMMY_SP);
     let codes = ErrorCodes::from(options.unstable_features.is_nightly_build());
 
-    find_testable_code(&input_str, &mut collector, codes, options.enable_per_target_ignores, None);
+    // For markdown files, custom code classes will be disabled until the feature is enabled by default.
+    find_testable_code(
+        &input_str,
+        &mut collector,
+        codes,
+        options.enable_per_target_ignores,
+        None,
+        false,
+    );
 
     crate::doctest::run_tests(options.test_args, options.nocapture, collector.tests);
     Ok(())
diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs
index 592dd0a145c..60def40588a 100644
--- a/src/librustdoc/passes/calculate_doc_coverage.rs
+++ b/src/librustdoc/passes/calculate_doc_coverage.rs
@@ -208,7 +208,14 @@ impl<'a, 'b> DocVisitor for CoverageCalculator<'a, 'b> {
                 let has_docs = !i.attrs.doc_strings.is_empty();
                 let mut tests = Tests { found_tests: 0 };
 
-                find_testable_code(&i.doc_value(), &mut tests, ErrorCodes::No, false, None);
+                find_testable_code(
+                    &i.doc_value(),
+                    &mut tests,
+                    ErrorCodes::No,
+                    false,
+                    None,
+                    self.ctx.tcx.features().custom_code_classes_in_docs,
+                );
 
                 let has_doc_example = tests.found_tests != 0;
                 let hir_id = DocContext::as_local_hir_id(self.ctx.tcx, i.item_id).unwrap();
diff --git a/src/librustdoc/passes/check_custom_code_classes.rs b/src/librustdoc/passes/check_custom_code_classes.rs
index 73e80372e4a..1a703a4e967 100644
--- a/src/librustdoc/passes/check_custom_code_classes.rs
+++ b/src/librustdoc/passes/check_custom_code_classes.rs
@@ -9,7 +9,9 @@ use crate::core::DocContext;
 use crate::fold::DocFolder;
 use crate::html::markdown::{find_codes, ErrorCodes, LangString};
 
-use rustc_session::parse::feature_err;
+use rustc_errors::StashKey;
+use rustc_feature::GateIssue;
+use rustc_session::parse::add_feature_diagnostics_for_issue;
 use rustc_span::symbol::sym;
 
 pub(crate) const CHECK_CUSTOM_CODE_CLASSES: Pass = Pass {
@@ -55,23 +57,32 @@ pub(crate) fn look_for_custom_classes<'tcx>(cx: &DocContext<'tcx>, item: &Item)
     let mut tests = TestsWithCustomClasses { custom_classes_found: vec![] };
 
     let dox = item.attrs.doc_value();
-    find_codes(&dox, &mut tests, ErrorCodes::No, false, None, true);
+    find_codes(&dox, &mut tests, ErrorCodes::No, false, None, true, true);
 
     if !tests.custom_classes_found.is_empty() && !cx.tcx.features().custom_code_classes_in_docs {
-        feature_err(
-            &cx.tcx.sess.parse_sess,
+        let span = item.attr_span(cx.tcx);
+        let sess = &cx.tcx.sess.parse_sess;
+        let mut err = sess
+            .span_diagnostic
+            .struct_span_warn(span, "custom classes in code blocks will change behaviour");
+        add_feature_diagnostics_for_issue(
+            &mut err,
+            sess,
             sym::custom_code_classes_in_docs,
-            item.attr_span(cx.tcx),
-            "custom classes in code blocks are unstable",
-        )
-        .note(
+            GateIssue::Language,
+            false,
+        );
+
+        err.note(
             // This will list the wrong items to make them more easily searchable.
             // To ensure the most correct hits, it adds back the 'class:' that was stripped.
             format!(
                 "found these custom classes: class={}",
                 tests.custom_classes_found.join(",class=")
             ),
-        )
-        .emit();
+        );
+
+        // A later feature_err call can steal and cancel this warning.
+        err.stash(span, StashKey::EarlySyntaxWarning);
     }
 }
diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs
index 96224d7c6e2..d1c4cc1f595 100644
--- a/src/librustdoc/passes/check_doc_test_visibility.rs
+++ b/src/librustdoc/passes/check_doc_test_visibility.rs
@@ -113,7 +113,14 @@ pub(crate) fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item
 
     let mut tests = Tests { found_tests: 0 };
 
-    find_testable_code(dox, &mut tests, ErrorCodes::No, false, None);
+    find_testable_code(
+        dox,
+        &mut tests,
+        ErrorCodes::No,
+        false,
+        None,
+        cx.tcx.features().custom_code_classes_in_docs,
+    );
 
     if tests.found_tests == 0 && cx.tcx.features().rustdoc_missing_doc_code_examples {
         if should_have_doc_example(cx, item) {
diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs
index 316b1a41c7d..ac8a75a4f18 100644
--- a/src/librustdoc/passes/lint/check_code_block_syntax.rs
+++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs
@@ -20,7 +20,9 @@ pub(crate) fn visit_item(cx: &DocContext<'_>, item: &clean::Item) {
     if let Some(dox) = &item.opt_doc_value() {
         let sp = item.attr_span(cx.tcx);
         let extra = crate::html::markdown::ExtraInfo::new(cx.tcx, item.item_id.expect_def_id(), sp);
-        for code_block in markdown::rust_code_blocks(dox, &extra) {
+        for code_block in
+            markdown::rust_code_blocks(dox, &extra, cx.tcx.features().custom_code_classes_in_docs)
+        {
             check_rust_syntax(cx, item, dox, code_block);
         }
     }
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject d5336f813df39d476e61fc46daabb1446350660
+Subproject b4ddf95ad9954118ac0dae835f2966394ad04c0
diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
index def30f5903d..b454c29aef4 100644
--- a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
+++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs
@@ -10,7 +10,7 @@
     arithmetic_overflow,
     unconditional_panic
 )]
-#![feature(const_mut_refs, inline_const, saturating_int_impl)]
+#![feature(const_mut_refs, inline_const)]
 #![warn(clippy::arithmetic_side_effects)]
 
 extern crate proc_macro_derive;
diff --git a/tests/mir-opt/issue_99325.main.built.after.32bit.mir b/tests/mir-opt/issue_99325.main.built.after.32bit.mir
index b6a673b6355..132b713356e 100644
--- a/tests/mir-opt/issue_99325.main.built.after.32bit.mir
+++ b/tests/mir-opt/issue_99325.main.built.after.32bit.mir
@@ -1,8 +1,8 @@
 // MIR for `main` after built
 
 | User Type Annotations
-| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [Const { ty: &ReStatic [u8; Const { ty: usize, kind: Leaf(0x00000004) }], kind: Branch([Leaf(0x41), Leaf(0x41), Leaf(0x41), Leaf(0x41)]) }], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:12:16: 12:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}
-| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [Const { ty: &ReStatic [u8; Const { ty: usize, kind: Leaf(0x00000004) }], kind: UnevaluatedConst { def: DefId(0:8 ~ issue_99325[22bb]::main::{constant#1}), args: [] } }], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:13:16: 13:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}
+| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:12:16: 12:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}
+| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[22bb]::main::{constant#1}), args: [] }: &ReStatic [u8; 4_usize]], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:13:16: 13:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}
 |
 fn main() -> () {
     let mut _0: ();
diff --git a/tests/mir-opt/issue_99325.main.built.after.64bit.mir b/tests/mir-opt/issue_99325.main.built.after.64bit.mir
index 9d112cfa20a..132b713356e 100644
--- a/tests/mir-opt/issue_99325.main.built.after.64bit.mir
+++ b/tests/mir-opt/issue_99325.main.built.after.64bit.mir
@@ -1,8 +1,8 @@
 // MIR for `main` after built
 
 | User Type Annotations
-| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [Const { ty: &ReStatic [u8; Const { ty: usize, kind: Leaf(0x0000000000000004) }], kind: Branch([Leaf(0x41), Leaf(0x41), Leaf(0x41), Leaf(0x41)]) }], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:12:16: 12:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}
-| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [Const { ty: &ReStatic [u8; Const { ty: usize, kind: Leaf(0x0000000000000004) }], kind: UnevaluatedConst { def: DefId(0:8 ~ issue_99325[22bb]::main::{constant#1}), args: [] } }], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:13:16: 13:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}
+| 0: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [&*b"AAAA"], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:12:16: 12:46, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}
+| 1: user_ty: Canonical { value: TypeOf(DefId(0:3 ~ issue_99325[22bb]::function_with_bytes), UserArgs { args: [UnevaluatedConst { def: DefId(0:8 ~ issue_99325[22bb]::main::{constant#1}), args: [] }: &ReStatic [u8; 4_usize]], user_self_ty: None }), max_universe: U0, variables: [] }, span: $DIR/issue_99325.rs:13:16: 13:68, inferred_ty: fn() -> &'static [u8] {function_with_bytes::<&*b"AAAA">}
 |
 fn main() -> () {
     let mut _0: ();
diff --git a/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir b/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir
index 56b0f816573..c581d0f8471 100644
--- a/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir
+++ b/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir
@@ -22,7 +22,7 @@
 |
 fn main() -> () {
     let mut _0: ();
-    let mut _1: [usize; Const { ty: usize, kind: Leaf(0x00000003) }];
+    let mut _1: [usize; ValTree(Leaf(0x00000003): usize)];
     let _3: usize;
     let mut _4: usize;
     let mut _5: bool;
diff --git a/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir b/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir
index 83b851eed74..48243e34d08 100644
--- a/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir
+++ b/tests/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir
@@ -22,7 +22,7 @@
 |
 fn main() -> () {
     let mut _0: ();
-    let mut _1: [usize; Const { ty: usize, kind: Leaf(0x0000000000000003) }];
+    let mut _1: [usize; ValTree(Leaf(0x0000000000000003): usize)];
     let _3: usize;
     let mut _4: usize;
     let mut _5: bool;
diff --git a/tests/run-make/metadata-dep-info/Makefile b/tests/run-make/metadata-dep-info/Makefile
new file mode 100644
index 00000000000..f9043f21433
--- /dev/null
+++ b/tests/run-make/metadata-dep-info/Makefile
@@ -0,0 +1,13 @@
+include ../tools.mk
+
+ifdef RUSTC_BLESS_TEST
+    RUSTC_TEST_OP = cp
+else
+    RUSTC_TEST_OP = $(DIFF)
+endif
+
+all:
+	$(RUSTC) --emit=metadata,dep-info --crate-type lib dash-separated.rs -C extra-filename=_something-extra
+	# Strip TMPDIR since it is a machine specific absolute path
+	sed "s%.*[/\\]%%" "$(TMPDIR)"/dash-separated_something-extra.d > "$(TMPDIR)"/dash-separated_something-extra.normalized.d
+	$(RUSTC_TEST_OP) "$(TMPDIR)"/dash-separated_something-extra.normalized.d dash-separated_something-extra.normalized.d
diff --git a/tests/run-make/metadata-dep-info/dash-separated.rs b/tests/run-make/metadata-dep-info/dash-separated.rs
new file mode 100644
index 00000000000..4202afd3d08
--- /dev/null
+++ b/tests/run-make/metadata-dep-info/dash-separated.rs
@@ -0,0 +1,4 @@
+//! It is important that this file has at least one `-` in the file name since
+//! we want to test that it becomes a `_` when appropriate.
+
+pub struct Foo;
diff --git a/tests/run-make/metadata-dep-info/dash-separated_something-extra.normalized.d b/tests/run-make/metadata-dep-info/dash-separated_something-extra.normalized.d
new file mode 100644
index 00000000000..497d76b4ea1
--- /dev/null
+++ b/tests/run-make/metadata-dep-info/dash-separated_something-extra.normalized.d
@@ -0,0 +1,5 @@
+libdash_separated_something-extra.rmeta: dash-separated.rs
+
+dash-separated_something-extra.d: dash-separated.rs
+
+dash-separated.rs:
diff --git a/tests/rustdoc-gui/search-result-color.goml b/tests/rustdoc-gui/search-result-color.goml
index f9f81c5ba04..44677dfbfef 100644
--- a/tests/rustdoc-gui/search-result-color.goml
+++ b/tests/rustdoc-gui/search-result-color.goml
@@ -151,7 +151,7 @@ assert-css: (
 )
 assert-css: (
     "//*[@class='result-name']//*[text()='test_docs::']/ancestor::a",
-    {"color": "#fff", "background-color": "rgb(60, 60, 60)"},
+    {"color": "#fff", "background-color": "#3c3c3c"},
 )
 
 // Dark theme
diff --git a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs
index 8aa13b2d5d1..3f0f8b5b9f9 100644
--- a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs
+++ b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.rs
@@ -1,5 +1,10 @@
+// check-pass
+
 /// ```{class=language-c}
 /// int main(void) { return 0; }
 /// ```
-//~^^^ ERROR 1:1: 3:8: custom classes in code blocks are unstable [E0658]
+//~^^^ WARNING custom classes in code blocks will change behaviour
+//~| NOTE found these custom classes: class=language-c
+//~| NOTE see issue #79483 <https://github.com/rust-lang/rust/issues/79483>
+//~| HELP add `#![feature(custom_code_classes_in_docs)]` to the crate attributes to enable
 pub struct Bar;
diff --git a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr
index c41ebfc8073..1a2360d9b30 100644
--- a/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr
+++ b/tests/rustdoc-ui/feature-gate-custom_code_classes_in_docs.stderr
@@ -1,5 +1,5 @@
-error[E0658]: custom classes in code blocks are unstable
-  --> $DIR/feature-gate-custom_code_classes_in_docs.rs:1:1
+warning: custom classes in code blocks will change behaviour
+  --> $DIR/feature-gate-custom_code_classes_in_docs.rs:3:1
    |
 LL | / /// ```{class=language-c}
 LL | | /// int main(void) { return 0; }
@@ -10,6 +10,5 @@ LL | | /// ```
    = help: add `#![feature(custom_code_classes_in_docs)]` to the crate attributes to enable
    = note: found these custom classes: class=language-c
 
-error: aborting due to previous error
+warning: 1 warning emitted
 
-For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/abi/debug.stderr b/tests/ui/abi/debug.stderr
index ceaf5136a6f..00fc7d1ece1 100644
--- a/tests/ui/abi/debug.stderr
+++ b/tests/ui/abi/debug.stderr
@@ -450,7 +450,7 @@ error: ABIs are not compatible
                                Align(1 bytes),
                            ),
                        },
-                       extra_attrs: None,
+                       meta_attrs: None,
                        on_stack: false,
                    },
                },
@@ -521,7 +521,7 @@ error: ABIs are not compatible
                                Align(4 bytes),
                            ),
                        },
-                       extra_attrs: None,
+                       meta_attrs: None,
                        on_stack: false,
                    },
                },
diff --git a/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.rs b/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.rs
index a686ed9c84e..b08e291621f 100644
--- a/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.rs
+++ b/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.rs
@@ -1,12 +1,12 @@
 #[diagnostic::non_existing_attribute]
 //~^ERROR `#[diagnostic]` attribute name space is experimental [E0658]
-//~|WARNING unknown diagnostic attribute [unknown_diagnostic_attributes]
+//~|WARNING unknown diagnostic attribute [unknown_or_malformed_diagnostic_attributes]
 pub trait Bar {
 }
 
 #[diagnostic::non_existing_attribute(with_option = "foo")]
 //~^ERROR `#[diagnostic]` attribute name space is experimental [E0658]
-//~|WARNING unknown diagnostic attribute [unknown_diagnostic_attributes]
+//~|WARNING unknown diagnostic attribute [unknown_or_malformed_diagnostic_attributes]
 struct Foo;
 
 fn main() {
diff --git a/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.stderr b/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.stderr
index 45c95cbb3c7..017d00e2c8e 100644
--- a/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.stderr
+++ b/tests/ui/diagnostic_namespace/feature-gate-diagnostic_namespace.stderr
@@ -4,7 +4,7 @@ error[E0658]: `#[diagnostic]` attribute name space is experimental
 LL | #[diagnostic::non_existing_attribute]
    |   ^^^^^^^^^^
    |
-   = note: see issue #94785 <https://github.com/rust-lang/rust/issues/94785> for more information
+   = note: see issue #111996 <https://github.com/rust-lang/rust/issues/111996> for more information
    = help: add `#![feature(diagnostic_namespace)]` to the crate attributes to enable
 
 error[E0658]: `#[diagnostic]` attribute name space is experimental
@@ -13,7 +13,7 @@ error[E0658]: `#[diagnostic]` attribute name space is experimental
 LL | #[diagnostic::non_existing_attribute(with_option = "foo")]
    |   ^^^^^^^^^^
    |
-   = note: see issue #94785 <https://github.com/rust-lang/rust/issues/94785> for more information
+   = note: see issue #111996 <https://github.com/rust-lang/rust/issues/111996> for more information
    = help: add `#![feature(diagnostic_namespace)]` to the crate attributes to enable
 
 warning: unknown diagnostic attribute
@@ -22,7 +22,7 @@ warning: unknown diagnostic attribute
 LL | #[diagnostic::non_existing_attribute]
    |               ^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `#[warn(unknown_diagnostic_attributes)]` on by default
+   = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
 
 warning: unknown diagnostic attribute
   --> $DIR/feature-gate-diagnostic_namespace.rs:7:15
diff --git a/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr b/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr
index 4f9b7ba2bcf..753077b365e 100644
--- a/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr
+++ b/tests/ui/diagnostic_namespace/non_existing_attributes_accepted.stderr
@@ -4,7 +4,7 @@ warning: unknown diagnostic attribute
 LL | #[diagnostic::non_existing_attribute]
    |               ^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `#[warn(unknown_diagnostic_attributes)]` on by default
+   = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
 
 warning: unknown diagnostic attribute
   --> $DIR/non_existing_attributes_accepted.rs:8:15
diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs
new file mode 100644
index 00000000000..00fb59d14d7
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs
@@ -0,0 +1,37 @@
+#![feature(diagnostic_namespace)]
+
+#[diagnostic::on_unimplemented(unsupported = "foo")]
+//~^WARN malformed `on_unimplemented` attribute
+//~|WARN malformed `on_unimplemented` attribute
+trait Foo {}
+
+#[diagnostic::on_unimplemented(message = "Baz")]
+//~^WARN `#[diagnostic::on_unimplemented]` can only be applied to trait definitions
+struct Bar {}
+
+#[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")]
+//~^WARN malformed `on_unimplemented` attribute
+//~|WARN malformed `on_unimplemented` attribute
+trait Baz {}
+
+#[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))]
+//~^WARN malformed `on_unimplemented` attribute
+//~|WARN malformed `on_unimplemented` attribute
+trait Boom {}
+
+#[diagnostic::on_unimplemented = "boom"]
+//~^WARN malformed `on_unimplemented` attribute
+trait Doom {}
+
+fn take_foo(_: impl Foo) {}
+fn take_baz(_: impl Baz) {}
+fn take_boom(_: impl Boom) {}
+
+fn main() {
+    take_foo(1_i32);
+    //~^ERROR the trait bound `i32: Foo` is not satisfied
+    take_baz(1_i32);
+    //~^ERROR Boom
+    take_boom(1_i32);
+    //~^ERROR Boom
+}
diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr
new file mode 100644
index 00000000000..bb1b29ef248
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr
@@ -0,0 +1,110 @@
+warning: `#[diagnostic::on_unimplemented]` can only be applied to trait definitions
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:8:1
+   |
+LL | #[diagnostic::on_unimplemented(message = "Baz")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
+
+warning: malformed `on_unimplemented` attribute
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:3:32
+   |
+LL | #[diagnostic::on_unimplemented(unsupported = "foo")]
+   |                                ^^^^^^^^^^^^^^^^^^^
+
+warning: malformed `on_unimplemented` attribute
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:12:50
+   |
+LL | #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")]
+   |                                                  ^^^^^^^^^^^^^^^^^^^
+
+warning: malformed `on_unimplemented` attribute
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:17:50
+   |
+LL | #[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))]
+   |                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: malformed `on_unimplemented` attribute
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:22:1
+   |
+LL | #[diagnostic::on_unimplemented = "boom"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: malformed `on_unimplemented` attribute
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:3:32
+   |
+LL | #[diagnostic::on_unimplemented(unsupported = "foo")]
+   |                                ^^^^^^^^^^^^^^^^^^^
+
+error[E0277]: the trait bound `i32: Foo` is not satisfied
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:14
+   |
+LL |     take_foo(1_i32);
+   |     -------- ^^^^^ the trait `Foo` is not implemented for `i32`
+   |     |
+   |     required by a bound introduced by this call
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:6:1
+   |
+LL | trait Foo {}
+   | ^^^^^^^^^
+note: required by a bound in `take_foo`
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:26:21
+   |
+LL | fn take_foo(_: impl Foo) {}
+   |                     ^^^ required by this bound in `take_foo`
+
+warning: malformed `on_unimplemented` attribute
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:12:50
+   |
+LL | #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")]
+   |                                                  ^^^^^^^^^^^^^^^^^^^
+
+error[E0277]: Boom
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:33:14
+   |
+LL |     take_baz(1_i32);
+   |     -------- ^^^^^ the trait `Baz` is not implemented for `i32`
+   |     |
+   |     required by a bound introduced by this call
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:15:1
+   |
+LL | trait Baz {}
+   | ^^^^^^^^^
+note: required by a bound in `take_baz`
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:27:21
+   |
+LL | fn take_baz(_: impl Baz) {}
+   |                     ^^^ required by this bound in `take_baz`
+
+warning: malformed `on_unimplemented` attribute
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:17:50
+   |
+LL | #[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))]
+   |                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0277]: Boom
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:35:15
+   |
+LL |     take_boom(1_i32);
+   |     --------- ^^^^^ the trait `Boom` is not implemented for `i32`
+   |     |
+   |     required by a bound introduced by this call
+   |
+help: this trait has no implementations, consider adding one
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:20:1
+   |
+LL | trait Boom {}
+   | ^^^^^^^^^^
+note: required by a bound in `take_boom`
+  --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:28:22
+   |
+LL | fn take_boom(_: impl Boom) {}
+   |                      ^^^^ required by this bound in `take_boom`
+
+error: aborting due to 3 previous errors; 8 warnings emitted
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.rs b/tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.rs
new file mode 100644
index 00000000000..609a840c118
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.rs
@@ -0,0 +1,7 @@
+#[diagnostic::on_unimplemented(message = "Foo")]
+//~^ERROR `#[diagnostic]` attribute name space is experimental [E0658]
+pub trait Bar {
+}
+
+fn main() {
+}
diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.stderr
new file mode 100644
index 00000000000..21f02e3a73b
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/on_unimplemented/feature-gate-diagnostic_on_unimplemented.stderr
@@ -0,0 +1,12 @@
+error[E0658]: `#[diagnostic]` attribute name space is experimental
+  --> $DIR/feature-gate-diagnostic_on_unimplemented.rs:1:3
+   |
+LL | #[diagnostic::on_unimplemented(message = "Foo")]
+   |   ^^^^^^^^^^
+   |
+   = note: see issue #111996 <https://github.com/rust-lang/rust/issues/111996> for more information
+   = help: add `#![feature(diagnostic_namespace)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.rs b/tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.rs
new file mode 100644
index 00000000000..797edbc9ec5
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.rs
@@ -0,0 +1,11 @@
+#![feature(diagnostic_namespace)]
+
+#[diagnostic::on_unimplemented(message = "Foo", label = "Bar", note = "Baz")]
+trait Foo {}
+
+fn takes_foo(_: impl Foo) {}
+
+fn main() {
+    takes_foo(());
+    //~^ERROR Foo
+}
diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.stderr
new file mode 100644
index 00000000000..549c7caa720
--- /dev/null
+++ b/tests/ui/diagnostic_namespace/on_unimplemented/on_unimplemented_simple.stderr
@@ -0,0 +1,24 @@
+error[E0277]: Foo
+  --> $DIR/on_unimplemented_simple.rs:9:15
+   |
+LL |     takes_foo(());
+   |     --------- ^^ Bar
+   |     |
+   |     required by a bound introduced by this call
+   |
+   = help: the trait `Foo` is not implemented for `()`
+   = note: Baz
+help: this trait has no implementations, consider adding one
+  --> $DIR/on_unimplemented_simple.rs:4:1
+   |
+LL | trait Foo {}
+   | ^^^^^^^^^
+note: required by a bound in `takes_foo`
+  --> $DIR/on_unimplemented_simple.rs:6:22
+   |
+LL | fn takes_foo(_: impl Foo) {}
+   |                      ^^^ required by this bound in `takes_foo`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/error-codes/E0040.stderr b/tests/ui/error-codes/E0040.stderr
index 9fcda1a9385..839be79d28d 100644
--- a/tests/ui/error-codes/E0040.stderr
+++ b/tests/ui/error-codes/E0040.stderr
@@ -2,10 +2,12 @@ error[E0040]: explicit use of destructor method
   --> $DIR/E0040.rs:16:7
    |
 LL |     x.drop();
-   |     --^^^^--
-   |     | |
-   |     | explicit destructor calls not allowed
-   |     help: consider using `drop` function: `drop(x)`
+   |       ^^^^ explicit destructor calls not allowed
+   |
+help: consider using `drop` function
+   |
+LL |     drop(x);
+   |     +++++ ~
 
 error: aborting due to previous error
 
diff --git a/tests/ui/explicit/explicit-call-to-dtor.stderr b/tests/ui/explicit/explicit-call-to-dtor.stderr
index f3c9bf6cccd..f2e0b73b6c5 100644
--- a/tests/ui/explicit/explicit-call-to-dtor.stderr
+++ b/tests/ui/explicit/explicit-call-to-dtor.stderr
@@ -2,10 +2,12 @@ error[E0040]: explicit use of destructor method
   --> $DIR/explicit-call-to-dtor.rs:15:7
    |
 LL |     x.drop();
-   |     --^^^^--
-   |     | |
-   |     | explicit destructor calls not allowed
-   |     help: consider using `drop` function: `drop(x)`
+   |       ^^^^ explicit destructor calls not allowed
+   |
+help: consider using `drop` function
+   |
+LL |     drop(x);
+   |     +++++ ~
 
 error: aborting due to previous error
 
diff --git a/tests/ui/explicit/explicit-call-to-supertrait-dtor.stderr b/tests/ui/explicit/explicit-call-to-supertrait-dtor.stderr
index c7067117349..5fa42fcf191 100644
--- a/tests/ui/explicit/explicit-call-to-supertrait-dtor.stderr
+++ b/tests/ui/explicit/explicit-call-to-supertrait-dtor.stderr
@@ -2,10 +2,12 @@ error[E0040]: explicit use of destructor method
   --> $DIR/explicit-call-to-supertrait-dtor.rs:22:14
    |
 LL |         self.drop();
-   |         -----^^^^--
-   |         |    |
-   |         |    explicit destructor calls not allowed
-   |         help: consider using `drop` function: `drop(self)`
+   |              ^^^^ explicit destructor calls not allowed
+   |
+help: consider using `drop` function
+   |
+LL |         drop(self);
+   |         +++++    ~
 
 error: aborting due to previous error
 
diff --git a/tests/ui/fmt/raw-idents.rs b/tests/ui/fmt/raw-idents.rs
new file mode 100644
index 00000000000..29a74c55a4a
--- /dev/null
+++ b/tests/ui/fmt/raw-idents.rs
@@ -0,0 +1,17 @@
+// Regression test for https://github.com/rust-lang/rust/issues/115466
+
+// The "identifier" in format strings is parsed as an IDENTIFIER_OR_KEYWORD, not an IDENTIFIER.
+// Test that there is an actionable diagnostic if a RAW_IDENTIFIER is used instead.
+
+fn main() {
+    let r#type = "foobar";
+    println!("It is {r#type}"); //~ ERROR: invalid format string: raw identifiers are not supported
+    println!(r##"It still is {r#type}"##); //~ ERROR: invalid format string: raw identifiers are not supported
+    println!(concat!("{r#", "type}")); //~ ERROR: invalid format string: raw identifiers are not supported
+    println!("{\x72\x23type:?}"); //~ ERROR: invalid format string: raw identifiers are not supported
+
+    // OK
+    println!("{type}");
+    println!("{let}", let = r#type);
+    println!("{let}", r#let = r#type);
+}
diff --git a/tests/ui/fmt/raw-idents.stderr b/tests/ui/fmt/raw-idents.stderr
new file mode 100644
index 00000000000..2ddc114d286
--- /dev/null
+++ b/tests/ui/fmt/raw-idents.stderr
@@ -0,0 +1,44 @@
+error: invalid format string: raw identifiers are not supported
+  --> $DIR/raw-idents.rs:8:22
+   |
+LL |     println!("It is {r#type}");
+   |                      --^^^^
+   |                      |
+   |                      raw identifier used here in format string
+   |                      help: remove the `r#`
+   |
+   = note: identifiers in format strings can be keywords and don't need to be prefixed with `r#`
+
+error: invalid format string: raw identifiers are not supported
+  --> $DIR/raw-idents.rs:9:31
+   |
+LL |     println!(r##"It still is {r#type}"##);
+   |                               --^^^^
+   |                               |
+   |                               raw identifier used here in format string
+   |                               help: remove the `r#`
+   |
+   = note: identifiers in format strings can be keywords and don't need to be prefixed with `r#`
+
+error: invalid format string: raw identifiers are not supported
+  --> $DIR/raw-idents.rs:10:14
+   |
+LL |     println!(concat!("{r#", "type}"));
+   |              ^^^^^^^^^^^^^^^^^^^^^^^ raw identifier used here in format string
+   |
+   = note: identifiers in format strings can be keywords and don't need to be prefixed with `r#`
+   = note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: invalid format string: raw identifiers are not supported
+  --> $DIR/raw-idents.rs:11:16
+   |
+LL |     println!("{\x72\x23type:?}");
+   |                --------^^^^
+   |                |
+   |                raw identifier used here in format string
+   |                help: remove the `r#`
+   |
+   = note: identifiers in format strings can be keywords and don't need to be prefixed with `r#`
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/range/range-1.stderr b/tests/ui/range/range-1.stderr
index ecfc56961ee..96c1ffb2f7e 100644
--- a/tests/ui/range/range-1.stderr
+++ b/tests/ui/range/range-1.stderr
@@ -19,7 +19,7 @@ LL |     for i in false..true {}
              i64
              i128
              usize
-           and 6 others
+           and 8 others
    = note: required for `std::ops::Range<bool>` to implement `Iterator`
    = note: required for `std::ops::Range<bool>` to implement `IntoIterator`
 
diff --git a/tests/ui/repr/repr-transparent.rs b/tests/ui/repr/repr-transparent.rs
index 8c9d1639c0a..87cf59ce9af 100644
--- a/tests/ui/repr/repr-transparent.rs
+++ b/tests/ui/repr/repr-transparent.rs
@@ -23,23 +23,26 @@ struct ContainsMultipleZst(PhantomData<*const i32>, NoFields);
 struct ContainsZstAndNonZst((), [i32; 2]);
 
 #[repr(transparent)]
-struct MultipleNonZst(u8, u8); //~ ERROR needs at most one non-zero-sized field
+struct MultipleNonZst(u8, u8); //~ ERROR needs at most one field with non-trivial size or alignment
 
 trait Mirror { type It: ?Sized; }
 impl<T: ?Sized> Mirror for T { type It = Self; }
 
 #[repr(transparent)]
 pub struct StructWithProjection(f32, <f32 as Mirror>::It);
-//~^ ERROR needs at most one non-zero-sized field
+//~^ ERROR needs at most one field with non-trivial size or alignment
 
 #[repr(transparent)]
-struct NontrivialAlignZst(u32, [u16; 0]); //~ ERROR alignment larger than 1
+struct NontrivialAlignZst(u32, [u16; 0]); //~ ERROR needs at most one field with non-trivial size or alignment
 
 #[repr(align(32))]
 struct ZstAlign32<T>(PhantomData<T>);
 
 #[repr(transparent)]
-struct GenericAlign<T>(ZstAlign32<T>, u32); //~ ERROR alignment larger than 1
+struct GenericAlign<T>(ZstAlign32<T>, u32); //~ ERROR needs at most one field with non-trivial size or alignment
+
+#[repr(transparent)]
+struct WrapsZstWithAlignment([i32; 0]);
 
 #[repr(transparent)] //~ ERROR unsupported representation for zero-variant enum
 enum Void {} //~ ERROR transparent enum needs exactly one variant, but has 0
@@ -58,7 +61,7 @@ enum UnitFieldEnum {
 enum TooManyFieldsEnum {
     Foo(u32, String),
 }
-//~^^^ ERROR transparent enum needs at most one non-zero-sized field, but has 2
+//~^^^ ERROR transparent enum needs at most one field with non-trivial size or alignment, but has 2
 
 #[repr(transparent)]
 enum MultipleVariants { //~ ERROR transparent enum needs exactly one variant, but has 2
@@ -67,13 +70,13 @@ enum MultipleVariants { //~ ERROR transparent enum needs exactly one variant, bu
 }
 
 #[repr(transparent)]
-enum NontrivialAlignZstEnum {
-    Foo(u32, [u16; 0]), //~ ERROR alignment larger than 1
+enum NontrivialAlignZstEnum { //~ ERROR needs at most one field with non-trivial size or alignment
+    Foo(u32, [u16; 0]),
 }
 
 #[repr(transparent)]
-enum GenericAlignEnum<T> {
-    Foo { bar: ZstAlign32<T>, baz: u32 } //~ ERROR alignment larger than 1
+enum GenericAlignEnum<T> { //~ ERROR needs at most one field with non-trivial size or alignment
+    Foo { bar: ZstAlign32<T>, baz: u32 }
 }
 
 #[repr(transparent)]
@@ -82,7 +85,7 @@ union UnitUnion {
 }
 
 #[repr(transparent)]
-union TooManyFields { //~ ERROR transparent union needs at most one non-zero-sized field, but has 2
+union TooManyFields { //~ ERROR transparent union needs at most one field with non-trivial size or alignment, but has 2
     u: u32,
     s: i32
 }
diff --git a/tests/ui/repr/repr-transparent.stderr b/tests/ui/repr/repr-transparent.stderr
index 028fc25db46..d0c78a8418a 100644
--- a/tests/ui/repr/repr-transparent.stderr
+++ b/tests/ui/repr/repr-transparent.stderr
@@ -1,35 +1,41 @@
-error[E0690]: transparent struct needs at most one non-zero-sized field, but has 2
+error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2
   --> $DIR/repr-transparent.rs:26:1
    |
 LL | struct MultipleNonZst(u8, u8);
-   | ^^^^^^^^^^^^^^^^^^^^^ --  -- this field is non-zero-sized
+   | ^^^^^^^^^^^^^^^^^^^^^ --  -- this field has non-zero size or requires alignment
    | |                     |
-   | |                     this field is non-zero-sized
-   | needs at most one non-zero-sized field, but has 2
+   | |                     this field has non-zero size or requires alignment
+   | needs at most one field with non-trivial size or alignment, but has 2
 
-error[E0690]: transparent struct needs at most one non-zero-sized field, but has 2
+error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2
   --> $DIR/repr-transparent.rs:32:1
    |
 LL | pub struct StructWithProjection(f32, <f32 as Mirror>::It);
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ---  ------------------- this field is non-zero-sized
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ---  ------------------- this field has non-zero size or requires alignment
    | |                               |
-   | |                               this field is non-zero-sized
-   | needs at most one non-zero-sized field, but has 2
+   | |                               this field has non-zero size or requires alignment
+   | needs at most one field with non-trivial size or alignment, but has 2
 
-error[E0691]: zero-sized field in transparent struct has alignment larger than 1
-  --> $DIR/repr-transparent.rs:36:32
+error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2
+  --> $DIR/repr-transparent.rs:36:1
    |
 LL | struct NontrivialAlignZst(u32, [u16; 0]);
-   |                                ^^^^^^^^ has alignment of 2, which is larger than 1
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^ ---  -------- this field has non-zero size or requires alignment
+   | |                         |
+   | |                         this field has non-zero size or requires alignment
+   | needs at most one field with non-trivial size or alignment, but has 2
 
-error[E0691]: zero-sized field in transparent struct has alignment larger than 1
-  --> $DIR/repr-transparent.rs:42:24
+error[E0690]: transparent struct needs at most one field with non-trivial size or alignment, but has 2
+  --> $DIR/repr-transparent.rs:42:1
    |
 LL | struct GenericAlign<T>(ZstAlign32<T>, u32);
-   |                        ^^^^^^^^^^^^^ has alignment of 32, which is larger than 1
+   | ^^^^^^^^^^^^^^^^^^^^^^ -------------  --- this field has non-zero size or requires alignment
+   | |                      |
+   | |                      this field has non-zero size or requires alignment
+   | needs at most one field with non-trivial size or alignment, but has 2
 
 error[E0084]: unsupported representation for zero-variant enum
-  --> $DIR/repr-transparent.rs:44:1
+  --> $DIR/repr-transparent.rs:47:1
    |
 LL | #[repr(transparent)]
    | ^^^^^^^^^^^^^^^^^^^^
@@ -37,23 +43,23 @@ LL | enum Void {}
    | --------- zero-variant enum
 
 error[E0731]: transparent enum needs exactly one variant, but has 0
-  --> $DIR/repr-transparent.rs:45:1
+  --> $DIR/repr-transparent.rs:48:1
    |
 LL | enum Void {}
    | ^^^^^^^^^ needs exactly one variant, but has 0
 
-error[E0690]: the variant of a transparent enum needs at most one non-zero-sized field, but has 2
-  --> $DIR/repr-transparent.rs:58:1
+error[E0690]: the variant of a transparent enum needs at most one field with non-trivial size or alignment, but has 2
+  --> $DIR/repr-transparent.rs:61:1
    |
 LL | enum TooManyFieldsEnum {
-   | ^^^^^^^^^^^^^^^^^^^^^^ needs at most one non-zero-sized field, but has 2
+   | ^^^^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2
 LL |     Foo(u32, String),
-   |         ---  ------ this field is non-zero-sized
+   |         ---  ------ this field has non-zero size or requires alignment
    |         |
-   |         this field is non-zero-sized
+   |         this field has non-zero size or requires alignment
 
 error[E0731]: transparent enum needs exactly one variant, but has 2
-  --> $DIR/repr-transparent.rs:64:1
+  --> $DIR/repr-transparent.rs:67:1
    |
 LL | enum MultipleVariants {
    | ^^^^^^^^^^^^^^^^^^^^^ needs exactly one variant, but has 2
@@ -62,29 +68,37 @@ LL |     Foo(String),
 LL |     Bar,
    |     --- too many variants in `MultipleVariants`
 
-error[E0691]: zero-sized field in transparent enum has alignment larger than 1
-  --> $DIR/repr-transparent.rs:71:14
+error[E0690]: the variant of a transparent enum needs at most one field with non-trivial size or alignment, but has 2
+  --> $DIR/repr-transparent.rs:73:1
    |
+LL | enum NontrivialAlignZstEnum {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2
 LL |     Foo(u32, [u16; 0]),
-   |              ^^^^^^^^ has alignment of 2, which is larger than 1
+   |         ---  -------- this field has non-zero size or requires alignment
+   |         |
+   |         this field has non-zero size or requires alignment
 
-error[E0691]: zero-sized field in transparent enum has alignment larger than 1
-  --> $DIR/repr-transparent.rs:76:11
+error[E0690]: the variant of a transparent enum needs at most one field with non-trivial size or alignment, but has 2
+  --> $DIR/repr-transparent.rs:78:1
    |
+LL | enum GenericAlignEnum<T> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2
 LL |     Foo { bar: ZstAlign32<T>, baz: u32 }
-   |           ^^^^^^^^^^^^^^^^^^ has alignment of 32, which is larger than 1
+   |           ------------------  -------- this field has non-zero size or requires alignment
+   |           |
+   |           this field has non-zero size or requires alignment
 
-error[E0690]: transparent union needs at most one non-zero-sized field, but has 2
-  --> $DIR/repr-transparent.rs:85:1
+error[E0690]: transparent union needs at most one field with non-trivial size or alignment, but has 2
+  --> $DIR/repr-transparent.rs:88:1
    |
 LL | union TooManyFields {
-   | ^^^^^^^^^^^^^^^^^^^ needs at most one non-zero-sized field, but has 2
+   | ^^^^^^^^^^^^^^^^^^^ needs at most one field with non-trivial size or alignment, but has 2
 LL |     u: u32,
-   |     ------ this field is non-zero-sized
+   |     ------ this field has non-zero size or requires alignment
 LL |     s: i32
-   |     ------ this field is non-zero-sized
+   |     ------ this field has non-zero size or requires alignment
 
 error: aborting due to 11 previous errors
 
-Some errors have detailed explanations: E0084, E0690, E0691, E0731.
+Some errors have detailed explanations: E0084, E0690, E0731.
 For more information about an error, try `rustc --explain E0084`.
diff --git a/tests/ui/span/send-is-not-static-std-sync.rs b/tests/ui/span/send-is-not-static-std-sync.rs
index f8ab5243c22..9c1ee287154 100644
--- a/tests/ui/span/send-is-not-static-std-sync.rs
+++ b/tests/ui/span/send-is-not-static-std-sync.rs
@@ -46,7 +46,7 @@ fn channel() {
         tx.send(&z).unwrap();
     }
     //~^^ ERROR `z` does not live long enough
-    // (channels lack #[may_dangle], thus their dtors are implicit uses of `z`)
+    tx.use_ref(); // (channel drop glue does not use `z` => needs explicit use)
 }
 
 fn main() {}
diff --git a/tests/ui/span/send-is-not-static-std-sync.stderr b/tests/ui/span/send-is-not-static-std-sync.stderr
index eaba415adaa..46534b39168 100644
--- a/tests/ui/span/send-is-not-static-std-sync.stderr
+++ b/tests/ui/span/send-is-not-static-std-sync.stderr
@@ -75,11 +75,9 @@ LL |         tx.send(&z).unwrap();
    |                 ^^ borrowed value does not live long enough
 LL |     }
    |     - `z` dropped here while still borrowed
-...
-LL | }
-   | - borrow might be used here, when `tx` is dropped and runs the `Drop` code for type `Sender`
-   |
-   = note: values in a scope are dropped in the opposite order they are defined
+LL |
+LL |     tx.use_ref(); // (channel drop glue does not use `z` => needs explicit use)
+   |     -- borrow later used here
 
 error: aborting due to 6 previous errors