about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-06-13 17:44:15 +0000
committerbors <bors@rust-lang.org>2025-06-13 17:44:15 +0000
commit8da623945f83933dd38644d5745532ee032e855b (patch)
tree5dbfcb038e6ed4e15f50c434a32e48eff1760e80
parent0d6ab209c525e276cbe7544cbd39a3c3619b6b18 (diff)
parent71490fff31bf02d177b548e44ab11ee5bd90ac8f (diff)
downloadrust-8da623945f83933dd38644d5745532ee032e855b.tar.gz
rust-8da623945f83933dd38644d5745532ee032e855b.zip
Auto merge of #142443 - matthiaskrgr:rollup-l1l6d0v, r=matthiaskrgr
Rollup of 9 pull requests

Successful merges:

 - rust-lang/rust#128425 (Make `missing_fragment_specifier` an unconditional error)
 - rust-lang/rust#135927 (retpoline and retpoline-external-thunk flags (target modifiers) to enable retpoline-related target features)
 - rust-lang/rust#140770 (add `extern "custom"` functions)
 - rust-lang/rust#142176 (tests: Split dont-shuffle-bswaps along opt-levels and arches)
 - rust-lang/rust#142248 (Add supported asm types for LoongArch32)
 - rust-lang/rust#142267 (assert more in release in `rustc_ast_lowering`)
 - rust-lang/rust#142274 (Update the stdarch submodule)
 - rust-lang/rust#142276 (Update dependencies in `library/Cargo.lock`)
 - rust-lang/rust#142308 (Upgrade `object`, `addr2line`, and `unwinding` in the standard library)

Failed merges:

 - rust-lang/rust#140920 (Extract some shared code from codegen backend target feature handling)

r? `@ghost`
`@rustbot` modify labels: rollup

try-job: aarch64-apple
try-job: x86_64-msvc-1
try-job: x86_64-gnu
try-job: dist-i586-gnu-i586-i686-musl
try-job: test-various
-rw-r--r--compiler/rustc_abi/src/canon_abi.rs4
-rw-r--r--compiler/rustc_abi/src/extern_abi.rs6
-rw-r--r--compiler/rustc_ast/src/ast.rs32
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs10
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs26
-rw-r--r--compiler/rustc_ast_lowering/src/stability.rs3
-rw-r--r--compiler/rustc_ast_passes/messages.ftl17
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs89
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs64
-rw-r--r--compiler/rustc_codegen_cranelift/src/abi/mod.rs5
-rw-r--r--compiler/rustc_codegen_gcc/messages.ftl7
-rw-r--r--compiler/rustc_codegen_gcc/src/abi.rs6
-rw-r--r--compiler/rustc_codegen_gcc/src/errors.rs15
-rw-r--r--compiler/rustc_codegen_gcc/src/gcc_util.rs27
-rw-r--r--compiler/rustc_codegen_llvm/messages.ftl9
-rw-r--r--compiler/rustc_codegen_llvm/src/abi.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/errors.rs17
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs25
-rw-r--r--compiler/rustc_codegen_ssa/src/target_features.rs3
-rw-r--r--compiler/rustc_expand/messages.ftl2
-rw-r--r--compiler/rustc_expand/src/mbe/macro_check.rs25
-rw-r--r--compiler/rustc_feature/src/unstable.rs2
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl4
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs14
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs14
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl4
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs18
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs6
-rw-r--r--compiler/rustc_lint/messages.ftl2
-rw-r--r--compiler/rustc_lint/src/early/diagnostics.rs3
-rw-r--r--compiler/rustc_lint/src/lib.rs5
-rw-r--r--compiler/rustc_lint/src/lints.rs4
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs46
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs1
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs1
-rw-r--r--compiler/rustc_session/messages.ftl8
-rw-r--r--compiler/rustc_session/src/config.rs9
-rw-r--r--compiler/rustc_session/src/errors.rs17
-rw-r--r--compiler/rustc_session/src/features.rs59
-rw-r--r--compiler/rustc_session/src/lib.rs1
-rw-r--r--compiler/rustc_session/src/options.rs13
-rw-r--r--compiler/rustc_smir/src/rustc_internal/internal.rs1
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/abi.rs1
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/ty.rs1
-rw-r--r--compiler/rustc_smir/src/stable_mir/abi.rs2
-rw-r--r--compiler/rustc_smir/src/stable_mir/ty.rs1
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_target/src/asm/loongarch.rs10
-rw-r--r--compiler/rustc_target/src/spec/abi_map.rs2
-rw-r--r--compiler/rustc_target/src/target_features.rs39
-rw-r--r--library/Cargo.lock69
-rw-r--r--library/std/Cargo.toml6
m---------library/stdarch0
-rw-r--r--library/unwind/Cargo.toml2
-rw-r--r--src/doc/unstable-book/src/language-features/asm-experimental-arch.md5
-rw-r--r--src/librustdoc/json/mod.rs5
-rw-r--r--tests/codegen/autovec/dont-shuffle-bswaps-opt2.rs31
-rw-r--r--tests/codegen/autovec/dont-shuffle-bswaps-opt3.rs (renamed from tests/codegen/dont-shuffle-bswaps.rs)30
-rw-r--r--tests/codegen/retpoline.rs29
-rw-r--r--tests/rustdoc/macro/macro-generated-macro.macro_morestuff_pre.html4
-rw-r--r--tests/rustdoc/macro/macro-generated-macro.rs2
-rw-r--r--tests/ui/abi/bad-custom.rs121
-rw-r--r--tests/ui/abi/bad-custom.stderr299
-rw-r--r--tests/ui/abi/custom.rs88
-rw-r--r--tests/ui/check-cfg/target_feature.stderr3
-rw-r--r--tests/ui/feature-gates/feature-gate-abi-custom.rs51
-rw-r--r--tests/ui/feature-gates/feature-gate-abi-custom.stderr117
-rw-r--r--tests/ui/lint/expansion-time.rs4
-rw-r--r--tests/ui/lint/expansion-time.stderr33
-rw-r--r--tests/ui/lint/future-incompatible-lint-group.rs19
-rw-r--r--tests/ui/lint/future-incompatible-lint-group.stderr44
-rw-r--r--tests/ui/macros/issue-39404.rs6
-rw-r--r--tests/ui/macros/issue-39404.stderr26
-rw-r--r--tests/ui/macros/macro-match-nonterminal.rs2
-rw-r--r--tests/ui/macros/macro-match-nonterminal.stderr39
-rw-r--r--tests/ui/macros/macro-missing-fragment-deduplication.rs6
-rw-r--r--tests/ui/macros/macro-missing-fragment-deduplication.stderr26
-rw-r--r--tests/ui/macros/macro-missing-fragment.e2015.stderr85
-rw-r--r--tests/ui/macros/macro-missing-fragment.rs24
-rw-r--r--tests/ui/macros/macro-missing-fragment.stderr (renamed from tests/ui/macros/macro-missing-fragment.e2024.stderr)14
-rw-r--r--tests/ui/parser/macro/issue-33569.rs1
-rw-r--r--tests/ui/parser/macro/issue-33569.stderr24
-rw-r--r--tests/ui/print-calling-conventions.stdout1
-rw-r--r--tests/ui/target-feature/retpoline-target-feature-flag.by_feature.stderr7
-rw-r--r--tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr7
-rw-r--r--tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr7
-rw-r--r--tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr7
-rw-r--r--tests/ui/target-feature/retpoline-target-feature-flag.rs23
91 files changed, 1443 insertions, 528 deletions
diff --git a/compiler/rustc_abi/src/canon_abi.rs b/compiler/rustc_abi/src/canon_abi.rs
index 03eeb645489..7c020be6761 100644
--- a/compiler/rustc_abi/src/canon_abi.rs
+++ b/compiler/rustc_abi/src/canon_abi.rs
@@ -28,6 +28,9 @@ pub enum CanonAbi {
     Rust,
     RustCold,
 
+    /// An ABI that rustc does not know how to call or define.
+    Custom,
+
     /// ABIs relevant to 32-bit Arm targets
     Arm(ArmCall),
     /// ABI relevant to GPUs: the entry point for a GPU kernel
@@ -57,6 +60,7 @@ impl fmt::Display for CanonAbi {
             CanonAbi::C => ExternAbi::C { unwind: false },
             CanonAbi::Rust => ExternAbi::Rust,
             CanonAbi::RustCold => ExternAbi::RustCold,
+            CanonAbi::Custom => ExternAbi::Custom,
             CanonAbi::Arm(arm_call) => match arm_call {
                 ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false },
                 ArmCall::CCmseNonSecureCall => ExternAbi::CCmseNonSecureCall,
diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs
index 0bc1c8a0930..7457ae1f033 100644
--- a/compiler/rustc_abi/src/extern_abi.rs
+++ b/compiler/rustc_abi/src/extern_abi.rs
@@ -40,6 +40,11 @@ pub enum ExternAbi {
     /// Even normally-compatible Rust types can become ABI-incompatible with this ABI!
     Unadjusted,
 
+    /// An ABI that rustc does not know how to call or define. Functions with this ABI can
+    /// only be created using `#[naked]` functions or `extern "custom"` blocks, and can only
+    /// be called from inline assembly.
+    Custom,
+
     /// UEFI ABI, usually an alias of C, but sometimes an arch-specific alias
     /// and only valid on platforms that have a UEFI standard
     EfiApi,
@@ -141,6 +146,7 @@ abi_impls! {
             AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt",
             Cdecl { unwind: false } =><= "cdecl",
             Cdecl { unwind: true } =><= "cdecl-unwind",
+            Custom =><= "custom",
             EfiApi =><= "efiapi",
             Fastcall { unwind: false } =><= "fastcall",
             Fastcall { unwind: true } =><= "fastcall-unwind",
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index bdd81e71e25..6b51cbd7fbe 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -3542,6 +3542,38 @@ impl FnHeader {
             || matches!(constness, Const::Yes(_))
             || !matches!(ext, Extern::None)
     }
+
+    /// Return a span encompassing the header, or none if all options are default.
+    pub fn span(&self) -> Option<Span> {
+        fn append(a: &mut Option<Span>, b: Span) {
+            *a = match a {
+                None => Some(b),
+                Some(x) => Some(x.to(b)),
+            }
+        }
+
+        let mut full_span = None;
+
+        match self.safety {
+            Safety::Unsafe(span) | Safety::Safe(span) => append(&mut full_span, span),
+            Safety::Default => {}
+        };
+
+        if let Some(coroutine_kind) = self.coroutine_kind {
+            append(&mut full_span, coroutine_kind.span());
+        }
+
+        if let Const::Yes(span) = self.constness {
+            append(&mut full_span, span);
+        }
+
+        match self.ext {
+            Extern::Implicit(span) | Extern::Explicit(_, span) => append(&mut full_span, span),
+            Extern::None => {}
+        }
+
+        full_span
+    }
 }
 
 impl Default for FnHeader {
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index d1a2ddbdb34..e2d291a536d 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -63,7 +63,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
 
         for (def_id, info) in lctx.children {
             let owner = self.owners.ensure_contains_elem(def_id, || hir::MaybeOwner::Phantom);
-            debug_assert!(
+            assert!(
                 matches!(owner, hir::MaybeOwner::Phantom),
                 "duplicate copy of {def_id:?} in lctx.children"
             );
@@ -78,7 +78,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
             match node {
                 AstOwner::NonOwner => {}
                 AstOwner::Crate(c) => {
-                    debug_assert_eq!(self.resolver.node_id_to_def_id[&CRATE_NODE_ID], CRATE_DEF_ID);
+                    assert_eq!(self.resolver.node_id_to_def_id[&CRATE_NODE_ID], CRATE_DEF_ID);
                     self.with_lctx(CRATE_NODE_ID, |lctx| {
                         let module = lctx.lower_mod(&c.items, &c.spans);
                         // FIXME(jdonszelman): is dummy span ever a problem here?
@@ -1160,7 +1160,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
     ) -> hir::BodyId {
         let body = hir::Body { params, value: self.arena.alloc(value) };
         let id = body.id();
-        debug_assert_eq!(id.hir_id.owner, self.current_hir_id_owner);
+        assert_eq!(id.hir_id.owner, self.current_hir_id_owner);
         self.bodies.push((id.hir_id.local_id, self.arena.alloc(body)));
         id
     }
@@ -1673,8 +1673,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
         itctx: ImplTraitContext,
         f: impl FnOnce(&mut Self) -> T,
     ) -> (&'hir hir::Generics<'hir>, T) {
-        debug_assert!(self.impl_trait_defs.is_empty());
-        debug_assert!(self.impl_trait_bounds.is_empty());
+        assert!(self.impl_trait_defs.is_empty());
+        assert!(self.impl_trait_bounds.is_empty());
 
         // Error if `?Trait` bounds in where clauses don't refer directly to type parameters.
         // Note: we used to clone these bounds directly onto the type parameter (and avoid lowering
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 249d1a99d72..4f0368bcb01 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -523,7 +523,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         span: Span,
     ) -> LocalDefId {
         let parent = self.current_hir_id_owner.def_id;
-        debug_assert_ne!(node_id, ast::DUMMY_NODE_ID);
+        assert_ne!(node_id, ast::DUMMY_NODE_ID);
         assert!(
             self.opt_local_def_id(node_id).is_none(),
             "adding a def'n for node-id {:?} and def kind {:?} but a previous def'n exists: {:?}",
@@ -607,10 +607,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         }
 
         let item = f(self);
-        debug_assert_eq!(owner_id, item.def_id());
+        assert_eq!(owner_id, item.def_id());
         // `f` should have consumed all the elements in these vectors when constructing `item`.
-        debug_assert!(self.impl_trait_defs.is_empty());
-        debug_assert!(self.impl_trait_bounds.is_empty());
+        assert!(self.impl_trait_defs.is_empty());
+        assert!(self.impl_trait_bounds.is_empty());
         let info = self.make_owner_info(item);
 
         self.attrs = current_attrs;
@@ -918,7 +918,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         } else {
             let lowered_attrs = self.lower_attrs_vec(attrs, self.lower_span(target_span), id);
 
-            debug_assert_eq!(id.owner, self.current_hir_id_owner);
+            assert_eq!(id.owner, self.current_hir_id_owner);
             let ret = self.arena.alloc_from_iter(lowered_attrs);
 
             // this is possible if an item contained syntactical attribute,
@@ -956,10 +956,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     }
 
     fn alias_attrs(&mut self, id: HirId, target_id: HirId) {
-        debug_assert_eq!(id.owner, self.current_hir_id_owner);
-        debug_assert_eq!(target_id.owner, self.current_hir_id_owner);
+        assert_eq!(id.owner, self.current_hir_id_owner);
+        assert_eq!(target_id.owner, self.current_hir_id_owner);
         if let Some(&a) = self.attrs.get(&target_id.local_id) {
-            debug_assert!(!a.is_empty());
+            assert!(!a.is_empty());
             self.attrs.insert(id.local_id, a);
         }
     }
@@ -1438,7 +1438,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 let id = if let Some(LifetimeRes::ElidedAnchor { start, end }) =
                     self.resolver.get_lifetime_res(t.id)
                 {
-                    debug_assert_eq!(start.plus(1), end);
+                    assert_eq!(start.plus(1), end);
                     start
                 } else {
                     self.next_node_id()
@@ -1846,16 +1846,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         let res = match res {
             LifetimeRes::Param { param, .. } => hir::LifetimeKind::Param(param),
             LifetimeRes::Fresh { param, .. } => {
-                debug_assert_eq!(ident.name, kw::UnderscoreLifetime);
+                assert_eq!(ident.name, kw::UnderscoreLifetime);
                 let param = self.local_def_id(param);
                 hir::LifetimeKind::Param(param)
             }
             LifetimeRes::Infer => {
-                debug_assert_eq!(ident.name, kw::UnderscoreLifetime);
+                assert_eq!(ident.name, kw::UnderscoreLifetime);
                 hir::LifetimeKind::Infer
             }
             LifetimeRes::Static { .. } => {
-                debug_assert!(matches!(ident.name, kw::StaticLifetime | kw::UnderscoreLifetime));
+                assert!(matches!(ident.name, kw::StaticLifetime | kw::UnderscoreLifetime));
                 hir::LifetimeKind::Static
             }
             LifetimeRes::Error => hir::LifetimeKind::Error,
@@ -2285,7 +2285,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     ) -> hir::Stmt<'hir> {
         let hir_id = self.next_id();
         if let Some(a) = attrs {
-            debug_assert!(!a.is_empty());
+            assert!(!a.is_empty());
             self.attrs.insert(hir_id.local_id, a);
         }
         let local = hir::LetStmt {
diff --git a/compiler/rustc_ast_lowering/src/stability.rs b/compiler/rustc_ast_lowering/src/stability.rs
index eb052ba1c6d..b8fa2dd3dd6 100644
--- a/compiler/rustc_ast_lowering/src/stability.rs
+++ b/compiler/rustc_ast_lowering/src/stability.rs
@@ -134,5 +134,8 @@ pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> {
             feature: sym::cmse_nonsecure_entry,
             explain: GateReason::Experimental,
         }),
+        ExternAbi::Custom => {
+            Err(UnstableAbi { abi, feature: sym::abi_custom, explain: GateReason::Experimental })
+        }
     }
 }
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index 80754a8f65a..9a267501230 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -1,3 +1,20 @@
+ast_passes_abi_custom_coroutine =
+    functions with the `"custom"` ABI cannot be `{$coroutine_kind_str}`
+    .suggestion = remove the `{$coroutine_kind_str}` keyword from this definiton
+
+ast_passes_abi_custom_invalid_signature =
+    invalid signature for `extern "custom"` function
+    .note = functions with the `"custom"` ABI cannot have any parameters or return type
+    .suggestion = remove the parameters and return type
+
+ast_passes_abi_custom_safe_foreign_function =
+    foreign functions with the `"custom"` ABI cannot be safe
+    .suggestion = remove the `safe` keyword from this definition
+
+ast_passes_abi_custom_safe_function =
+    functions with the `"custom"` ABI must be unsafe
+    .suggestion = add the `unsafe` keyword to this definition
+
 ast_passes_assoc_const_without_body =
     associated constant in `impl` without body
     .suggestion = provide a definition for the constant
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index d6fe04d2994..018887d0e8e 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -18,6 +18,7 @@
 
 use std::mem;
 use std::ops::{Deref, DerefMut};
+use std::str::FromStr;
 
 use itertools::{Either, Itertools};
 use rustc_abi::ExternAbi;
@@ -81,6 +82,7 @@ struct AstValidator<'a> {
 
     /// Used to ban explicit safety on foreign items when the extern block is not marked as unsafe.
     extern_mod_safety: Option<Safety>,
+    extern_mod_abi: Option<ExternAbi>,
 
     lint_node_id: NodeId,
 
@@ -121,10 +123,17 @@ impl<'a> AstValidator<'a> {
         self.outer_trait_or_trait_impl = old;
     }
 
-    fn with_in_extern_mod(&mut self, extern_mod_safety: Safety, f: impl FnOnce(&mut Self)) {
-        let old = mem::replace(&mut self.extern_mod_safety, Some(extern_mod_safety));
+    fn with_in_extern_mod(
+        &mut self,
+        extern_mod_safety: Safety,
+        abi: Option<ExternAbi>,
+        f: impl FnOnce(&mut Self),
+    ) {
+        let old_safety = mem::replace(&mut self.extern_mod_safety, Some(extern_mod_safety));
+        let old_abi = mem::replace(&mut self.extern_mod_abi, abi);
         f(self);
-        self.extern_mod_safety = old;
+        self.extern_mod_safety = old_safety;
+        self.extern_mod_abi = old_abi;
     }
 
     fn with_tilde_const(
@@ -370,6 +379,65 @@ impl<'a> AstValidator<'a> {
         }
     }
 
+    /// An `extern "custom"` function must be unsafe, and must not have any parameters or return
+    /// type.
+    fn check_custom_abi(&self, ctxt: FnCtxt, ident: &Ident, sig: &FnSig) {
+        let dcx = self.dcx();
+
+        // An `extern "custom"` function must be unsafe.
+        match sig.header.safety {
+            Safety::Unsafe(_) => { /* all good */ }
+            Safety::Safe(safe_span) => {
+                let safe_span =
+                    self.sess.psess.source_map().span_until_non_whitespace(safe_span.to(sig.span));
+                dcx.emit_err(errors::AbiCustomSafeForeignFunction { span: sig.span, safe_span });
+            }
+            Safety::Default => match ctxt {
+                FnCtxt::Foreign => { /* all good */ }
+                FnCtxt::Free | FnCtxt::Assoc(_) => {
+                    self.dcx().emit_err(errors::AbiCustomSafeFunction {
+                        span: sig.span,
+                        unsafe_span: sig.span.shrink_to_lo(),
+                    });
+                }
+            },
+        }
+
+        // An `extern "custom"` function cannot be `async` and/or `gen`.
+        if let Some(coroutine_kind) = sig.header.coroutine_kind {
+            let coroutine_kind_span = self
+                .sess
+                .psess
+                .source_map()
+                .span_until_non_whitespace(coroutine_kind.span().to(sig.span));
+
+            self.dcx().emit_err(errors::AbiCustomCoroutine {
+                span: sig.span,
+                coroutine_kind_span,
+                coroutine_kind_str: coroutine_kind.as_str(),
+            });
+        }
+
+        // An `extern "custom"` function must not have any parameters or return type.
+        let mut spans: Vec<_> = sig.decl.inputs.iter().map(|p| p.span).collect();
+        if let FnRetTy::Ty(ref ret_ty) = sig.decl.output {
+            spans.push(ret_ty.span);
+        }
+
+        if !spans.is_empty() {
+            let header_span = sig.header.span().unwrap_or(sig.span.shrink_to_lo());
+            let suggestion_span = header_span.shrink_to_hi().to(sig.decl.output.span());
+            let padding = if header_span.is_empty() { "" } else { " " };
+
+            self.dcx().emit_err(errors::AbiCustomInvalidSignature {
+                spans,
+                symbol: ident.name,
+                suggestion_span,
+                padding,
+            });
+        }
+    }
+
     /// This ensures that items can only be `unsafe` (or unmarked) outside of extern
     /// blocks.
     ///
@@ -1005,7 +1073,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 if abi.is_none() {
                     self.handle_missing_abi(*extern_span, item.id);
                 }
-                self.with_in_extern_mod(*safety, |this| {
+
+                let extern_abi = abi.and_then(|abi| ExternAbi::from_str(abi.symbol.as_str()).ok());
+                self.with_in_extern_mod(*safety, extern_abi, |this| {
                     visit::walk_item(this, item);
                 });
                 self.extern_mod_span = old_item;
@@ -1145,6 +1215,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                 self.check_foreign_fn_bodyless(*ident, body.as_deref());
                 self.check_foreign_fn_headerless(sig.header);
                 self.check_foreign_item_ascii_only(*ident);
+                if self.extern_mod_abi == Some(ExternAbi::Custom) {
+                    self.check_custom_abi(FnCtxt::Foreign, ident, sig);
+                }
             }
             ForeignItemKind::TyAlias(box TyAlias {
                 defaultness,
@@ -1352,6 +1425,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             self.check_item_safety(span, safety);
         }
 
+        if let FnKind::Fn(ctxt, _, fun) = fk
+            && let Extern::Explicit(str_lit, _) = fun.sig.header.ext
+            && let Ok(ExternAbi::Custom) = ExternAbi::from_str(str_lit.symbol.as_str())
+        {
+            self.check_custom_abi(ctxt, &fun.ident, &fun.sig);
+        }
+
         self.check_c_variadic_type(fk);
 
         // Functions cannot both be `const async` or `const gen`
@@ -1703,6 +1783,7 @@ pub fn check_crate(
         outer_impl_trait_span: None,
         disallow_tilde_const: Some(TildeConstReason::Item),
         extern_mod_safety: None,
+        extern_mod_abi: None,
         lint_node_id: CRATE_NODE_ID,
         is_sdylib_interface,
         lint_buffer: lints,
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 6f9737e0831..c437e62f4d3 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -824,3 +824,67 @@ pub(crate) struct MissingAbi {
     #[suggestion(code = "extern \"<abi>\"", applicability = "has-placeholders")]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_abi_custom_safe_foreign_function)]
+pub(crate) struct AbiCustomSafeForeignFunction {
+    #[primary_span]
+    pub span: Span,
+
+    #[suggestion(
+        ast_passes_suggestion,
+        applicability = "maybe-incorrect",
+        code = "",
+        style = "verbose"
+    )]
+    pub safe_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_abi_custom_safe_function)]
+pub(crate) struct AbiCustomSafeFunction {
+    #[primary_span]
+    pub span: Span,
+
+    #[suggestion(
+        ast_passes_suggestion,
+        applicability = "maybe-incorrect",
+        code = "unsafe ",
+        style = "verbose"
+    )]
+    pub unsafe_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_abi_custom_coroutine)]
+pub(crate) struct AbiCustomCoroutine {
+    #[primary_span]
+    pub span: Span,
+
+    #[suggestion(
+        ast_passes_suggestion,
+        applicability = "maybe-incorrect",
+        code = "",
+        style = "verbose"
+    )]
+    pub coroutine_kind_span: Span,
+    pub coroutine_kind_str: &'static str,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_abi_custom_invalid_signature)]
+#[note]
+pub(crate) struct AbiCustomInvalidSignature {
+    #[primary_span]
+    pub spans: Vec<Span>,
+
+    #[suggestion(
+        ast_passes_suggestion,
+        applicability = "maybe-incorrect",
+        code = "{padding}fn {symbol}()",
+        style = "verbose"
+    )]
+    pub suggestion_span: Span,
+    pub symbol: Symbol,
+    pub padding: &'static str,
+}
diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
index fe5b220117f..4c6fd907815 100644
--- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
@@ -51,6 +51,11 @@ pub(crate) fn conv_to_call_conv(
         CanonAbi::Rust | CanonAbi::C => default_call_conv,
         CanonAbi::RustCold => CallConv::Cold,
 
+        // Functions with this calling convention can only be called from assembly, but it is
+        // possible to declare an `extern "custom"` block, so the backend still needs a calling
+        // convention for declaring foreign functions.
+        CanonAbi::Custom => default_call_conv,
+
         CanonAbi::X86(x86_call) => match x86_call {
             X86Call::SysV64 => CallConv::SystemV,
             X86Call::Win64 => CallConv::WindowsFastcall,
diff --git a/compiler/rustc_codegen_gcc/messages.ftl b/compiler/rustc_codegen_gcc/messages.ftl
index 546bfc87b68..18a8a5a1e04 100644
--- a/compiler/rustc_codegen_gcc/messages.ftl
+++ b/compiler/rustc_codegen_gcc/messages.ftl
@@ -2,9 +2,6 @@ codegen_gcc_unknown_ctarget_feature_prefix =
     unknown feature specified for `-Ctarget-feature`: `{$feature}`
     .note = features must begin with a `+` to enable or `-` to disable it
 
-codegen_gcc_forbidden_ctarget_feature =
-    target feature `{$feature}` cannot be toggled with `-Ctarget-feature`: {$reason}
-
 codegen_gcc_unwinding_inline_asm =
     GCC backend does not support unwinding from inline asm
 
@@ -26,10 +23,6 @@ codegen_gcc_unknown_ctarget_feature =
     .possible_feature = you might have meant: `{$rust_feature}`
     .consider_filing_feature_request = consider filing a feature request
 
-codegen_gcc_unstable_ctarget_feature =
-    unstable feature specified for `-Ctarget-feature`: `{$feature}`
-    .note = this feature is not stably supported; its behavior can change in the future
-
 codegen_gcc_missing_features =
     add the missing features in a `target_feature` attribute
 
diff --git a/compiler/rustc_codegen_gcc/src/abi.rs b/compiler/rustc_codegen_gcc/src/abi.rs
index 3d0c258f576..08f3d281904 100644
--- a/compiler/rustc_codegen_gcc/src/abi.rs
+++ b/compiler/rustc_codegen_gcc/src/abi.rs
@@ -239,12 +239,16 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> {
 pub fn conv_to_fn_attribute<'gcc>(conv: CanonAbi, arch: &str) -> Option<FnAttribute<'gcc>> {
     let attribute = match conv {
         CanonAbi::C | CanonAbi::Rust => return None,
+        CanonAbi::RustCold => FnAttribute::Cold,
+        // Functions with this calling convention can only be called from assembly, but it is
+        // possible to declare an `extern "custom"` block, so the backend still needs a calling
+        // convention for declaring foreign functions.
+        CanonAbi::Custom => return None,
         CanonAbi::Arm(arm_call) => match arm_call {
             ArmCall::CCmseNonSecureCall => FnAttribute::ArmCmseNonsecureCall,
             ArmCall::CCmseNonSecureEntry => FnAttribute::ArmCmseNonsecureEntry,
             ArmCall::Aapcs => FnAttribute::ArmPcs("aapcs"),
         },
-        CanonAbi::RustCold => FnAttribute::Cold,
         CanonAbi::GpuKernel => {
             if arch == "amdgpu" {
                 FnAttribute::GcnAmdGpuHsaKernel
diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs
index ccd9abe3804..7786be9ae5d 100644
--- a/compiler/rustc_codegen_gcc/src/errors.rs
+++ b/compiler/rustc_codegen_gcc/src/errors.rs
@@ -17,21 +17,6 @@ pub(crate) struct UnknownCTargetFeature<'a> {
     pub rust_feature: PossibleFeature<'a>,
 }
 
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_unstable_ctarget_feature)]
-#[note]
-pub(crate) struct UnstableCTargetFeature<'a> {
-    pub feature: &'a str,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_gcc_forbidden_ctarget_feature)]
-pub(crate) struct ForbiddenCTargetFeature<'a> {
-    pub feature: &'a str,
-    pub enabled: &'a str,
-    pub reason: &'a str,
-}
-
 #[derive(Subdiagnostic)]
 pub(crate) enum PossibleFeature<'a> {
     #[help(codegen_gcc_possible_feature)]
diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs
index 2b053abdd19..d90e66aea31 100644
--- a/compiler/rustc_codegen_gcc/src/gcc_util.rs
+++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs
@@ -5,13 +5,17 @@ use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::unord::UnordSet;
 use rustc_session::Session;
+use rustc_session::features::{StabilityExt, retpoline_features_by_flags};
 use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES;
 use smallvec::{SmallVec, smallvec};
 
-use crate::errors::{
-    ForbiddenCTargetFeature, PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix,
-    UnstableCTargetFeature,
-};
+use crate::errors::{PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix};
+
+fn gcc_features_by_flags(sess: &Session) -> Vec<&str> {
+    let mut features: Vec<&str> = Vec::new();
+    retpoline_features_by_flags(sess, &mut features);
+    features
+}
 
 /// The list of GCC features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
 /// `--target` and similar).
@@ -45,7 +49,7 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
 
     // Compute implied features
     let mut all_rust_features = vec![];
-    for feature in sess.opts.cg.target_feature.split(',') {
+    for feature in sess.opts.cg.target_feature.split(',').chain(gcc_features_by_flags(sess)) {
         if let Some(feature) = feature.strip_prefix('+') {
             all_rust_features.extend(
                 UnordSet::from(sess.target.implied_target_features(feature))
@@ -94,18 +98,7 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
                     sess.dcx().emit_warn(unknown_feature);
                 }
                 Some(&(_, stability, _)) => {
-                    if let Err(reason) = stability.toggle_allowed() {
-                        sess.dcx().emit_warn(ForbiddenCTargetFeature {
-                            feature,
-                            enabled: if enable { "enabled" } else { "disabled" },
-                            reason,
-                        });
-                    } else if stability.requires_nightly().is_some() {
-                        // An unstable feature. Warn about using it. (It makes little sense
-                        // to hard-error here since we just warn about fully unknown
-                        // features above).
-                        sess.dcx().emit_warn(UnstableCTargetFeature { feature });
-                    }
+                    stability.verify_feature_enabled_by_flag(sess, enable, feature);
                 }
             }
 
diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl
index bda121c67fb..3faeb9b3b22 100644
--- a/compiler/rustc_codegen_llvm/messages.ftl
+++ b/compiler/rustc_codegen_llvm/messages.ftl
@@ -10,11 +10,6 @@ codegen_llvm_dynamic_linking_with_lto =
 
 codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture
 
-codegen_llvm_forbidden_ctarget_feature =
-    target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason}
-    .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-codegen_llvm_forbidden_ctarget_feature_issue = for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
-
 codegen_llvm_from_llvm_diag = {$message}
 
 codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message}
@@ -76,10 +71,6 @@ codegen_llvm_unknown_ctarget_feature_prefix =
 
 codegen_llvm_unknown_debuginfo_compression = unknown debuginfo compression algorithm {$algorithm} - will fall back to uncompressed debuginfo
 
-codegen_llvm_unstable_ctarget_feature =
-    unstable feature specified for `-Ctarget-feature`: `{$feature}`
-    .note = this feature is not stably supported; its behavior can change in the future
-
 codegen_llvm_write_bytecode = failed to write bytecode to {$path}: {$err}
 
 codegen_llvm_write_ir = failed to write LLVM IR to {$path}
diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs
index 119cd634f98..aba63d75f1d 100644
--- a/compiler/rustc_codegen_llvm/src/abi.rs
+++ b/compiler/rustc_codegen_llvm/src/abi.rs
@@ -649,6 +649,10 @@ impl llvm::CallConv {
         match conv {
             CanonAbi::C | CanonAbi::Rust => llvm::CCallConv,
             CanonAbi::RustCold => llvm::PreserveMost,
+            // Functions with this calling convention can only be called from assembly, but it is
+            // possible to declare an `extern "custom"` block, so the backend still needs a calling
+            // convention for declaring foreign functions.
+            CanonAbi::Custom => llvm::CCallConv,
             CanonAbi::GpuKernel => {
                 if arch == "amdgpu" {
                     llvm::AmdgpuKernel
diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs
index eaafc680712..8bc74fbec7e 100644
--- a/compiler/rustc_codegen_llvm/src/errors.rs
+++ b/compiler/rustc_codegen_llvm/src/errors.rs
@@ -24,23 +24,6 @@ pub(crate) struct UnknownCTargetFeature<'a> {
     pub rust_feature: PossibleFeature<'a>,
 }
 
-#[derive(Diagnostic)]
-#[diag(codegen_llvm_unstable_ctarget_feature)]
-#[note]
-pub(crate) struct UnstableCTargetFeature<'a> {
-    pub feature: &'a str,
-}
-
-#[derive(Diagnostic)]
-#[diag(codegen_llvm_forbidden_ctarget_feature)]
-#[note]
-#[note(codegen_llvm_forbidden_ctarget_feature_issue)]
-pub(crate) struct ForbiddenCTargetFeature<'a> {
-    pub feature: &'a str,
-    pub enabled: &'a str,
-    pub reason: &'a str,
-}
-
 #[derive(Subdiagnostic)]
 pub(crate) enum PossibleFeature<'a> {
     #[help(codegen_llvm_possible_feature)]
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index 9718c95f38a..0e77bc43df8 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -16,6 +16,7 @@ use rustc_fs_util::path_to_c_string;
 use rustc_middle::bug;
 use rustc_session::Session;
 use rustc_session::config::{PrintKind, PrintRequest};
+use rustc_session::features::{StabilityExt, retpoline_features_by_flags};
 use rustc_span::Symbol;
 use rustc_target::spec::{MergeFunctions, PanicStrategy, SmallDataThresholdSupport};
 use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES};
@@ -23,8 +24,7 @@ use smallvec::{SmallVec, smallvec};
 
 use crate::back::write::create_informational_target_machine;
 use crate::errors::{
-    FixedX18InvalidArch, ForbiddenCTargetFeature, PossibleFeature, UnknownCTargetFeature,
-    UnknownCTargetFeaturePrefix, UnstableCTargetFeature,
+    FixedX18InvalidArch, PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix,
 };
 use crate::llvm;
 
@@ -707,6 +707,12 @@ pub(crate) fn target_cpu(sess: &Session) -> &str {
     handle_native(cpu_name)
 }
 
+fn llvm_features_by_flags(sess: &Session) -> Vec<&str> {
+    let mut features: Vec<&str> = Vec::new();
+    retpoline_features_by_flags(sess, &mut features);
+    features
+}
+
 /// The list of LLVM features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
 /// `--target` and similar).
 pub(crate) fn global_llvm_features(
@@ -787,7 +793,7 @@ pub(crate) fn global_llvm_features(
 
         // Compute implied features
         let mut all_rust_features = vec![];
-        for feature in sess.opts.cg.target_feature.split(',') {
+        for feature in sess.opts.cg.target_feature.split(',').chain(llvm_features_by_flags(sess)) {
             if let Some(feature) = feature.strip_prefix('+') {
                 all_rust_features.extend(
                     UnordSet::from(sess.target.implied_target_features(feature))
@@ -840,18 +846,7 @@ pub(crate) fn global_llvm_features(
                         sess.dcx().emit_warn(unknown_feature);
                     }
                     Some((_, stability, _)) => {
-                        if let Err(reason) = stability.toggle_allowed() {
-                            sess.dcx().emit_warn(ForbiddenCTargetFeature {
-                                feature,
-                                enabled: if enable { "enabled" } else { "disabled" },
-                                reason,
-                            });
-                        } else if stability.requires_nightly().is_some() {
-                            // An unstable feature. Warn about using it. It makes little sense
-                            // to hard-error here since we just warn about fully unknown
-                            // features above.
-                            sess.dcx().emit_warn(UnstableCTargetFeature { feature });
-                        }
+                        stability.verify_feature_enabled_by_flag(sess, enable, feature);
                     }
                 }
 
diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs
index 6bb3150c1c5..640d197c219 100644
--- a/compiler/rustc_codegen_ssa/src/target_features.rs
+++ b/compiler/rustc_codegen_ssa/src/target_features.rs
@@ -8,6 +8,7 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
 use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
+use rustc_session::features::StabilityExt;
 use rustc_session::lint::builtin::AARCH64_SOFTFLOAT_NEON;
 use rustc_session::parse::feature_err;
 use rustc_span::{Span, Symbol, sym};
@@ -66,7 +67,7 @@ pub(crate) fn from_target_feature_attr(
 
             // Only allow target features whose feature gates have been enabled
             // and which are permitted to be toggled.
-            if let Err(reason) = stability.toggle_allowed() {
+            if let Err(reason) = stability.is_toggle_permitted(tcx.sess) {
                 tcx.dcx().emit_err(errors::ForbiddenTargetFeatureAttr {
                     span: item.span(),
                     feature,
diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl
index 08b7a362083..8b7c47dad99 100644
--- a/compiler/rustc_expand/messages.ftl
+++ b/compiler/rustc_expand/messages.ftl
@@ -113,7 +113,7 @@ expand_meta_var_expr_unrecognized_var =
     variable `{$key}` is not recognized in meta-variable expression
 
 expand_missing_fragment_specifier = missing fragment specifier
-    .note = fragment specifiers must be specified in the 2024 edition
+    .note = fragment specifiers must be provided
     .suggestion_add_fragspec = try adding a specifier here
     .valid = {$valid}
 
diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs
index 1a2db233b7a..3cd803c3e84 100644
--- a/compiler/rustc_expand/src/mbe/macro_check.rs
+++ b/compiler/rustc_expand/src/mbe/macro_check.rs
@@ -112,9 +112,8 @@ use rustc_ast::{DUMMY_NODE_ID, NodeId};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::MultiSpan;
 use rustc_lint_defs::BuiltinLintDiag;
-use rustc_session::lint::builtin::{META_VARIABLE_MISUSE, MISSING_FRAGMENT_SPECIFIER};
+use rustc_session::lint::builtin::META_VARIABLE_MISUSE;
 use rustc_session::parse::ParseSess;
-use rustc_span::edition::Edition;
 use rustc_span::{ErrorGuaranteed, MacroRulesNormalizedIdent, Span, kw};
 use smallvec::SmallVec;
 
@@ -266,23 +265,11 @@ fn check_binders(
         // Similarly, this can only happen when checking a toplevel macro.
         TokenTree::MetaVarDecl(span, name, kind) => {
             if kind.is_none() && node_id != DUMMY_NODE_ID {
-                // FIXME: Report this as a hard error eventually and remove equivalent errors from
-                // `parse_tt_inner` and `nameize`. Until then the error may be reported twice, once
-                // as a hard error and then once as a buffered lint.
-                if span.edition() >= Edition::Edition2024 {
-                    psess.dcx().emit_err(errors::MissingFragmentSpecifier {
-                        span,
-                        add_span: span.shrink_to_hi(),
-                        valid: VALID_FRAGMENT_NAMES_MSG,
-                    });
-                } else {
-                    psess.buffer_lint(
-                        MISSING_FRAGMENT_SPECIFIER,
-                        span,
-                        node_id,
-                        BuiltinLintDiag::MissingFragmentSpecifier,
-                    );
-                }
+                psess.dcx().emit_err(errors::MissingFragmentSpecifier {
+                    span,
+                    add_span: span.shrink_to_hi(),
+                    valid: VALID_FRAGMENT_NAMES_MSG,
+                });
             }
             if !macros.is_empty() {
                 psess.dcx().span_bug(span, "unexpected MetaVarDecl in nested lhs");
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 594021d78d2..cfdca8c48b0 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -353,6 +353,8 @@ declare_features! (
     (unstable, abi_avr_interrupt, "1.45.0", Some(69664)),
     /// Allows `extern "C-cmse-nonsecure-call" fn()`.
     (unstable, abi_c_cmse_nonsecure_call, "1.51.0", Some(81391)),
+    /// Allows `extern "custom" fn()`.
+    (unstable, abi_custom, "CURRENT_RUSTC_VERSION", Some(140829)),
     /// Allows `extern "gpu-kernel" fn()`.
     (unstable, abi_gpu_kernel, "1.86.0", Some(135467)),
     /// Allows `extern "msp430-interrupt" fn()`.
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 4fcd9f8a646..3e98bd213d3 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -1,3 +1,7 @@
+hir_analysis_abi_custom_clothed_function =
+    items with the `"custom"` ABI can only be declared externally or defined via naked functions
+    .suggestion = convert this to an `#[unsafe(naked)]` function
+
 hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_ident}` in bounds of `{$qself}`
     .label = ambiguous associated {$assoc_kind} `{$assoc_ident}`
 
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 60ca0155bdd..32fec0604c0 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -21,7 +21,7 @@ use rustc_middle::ty::error::TypeErrorToStringExt;
 use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
 use rustc_middle::ty::util::Discr;
 use rustc_middle::ty::{
-    AdtDef, BottomUpFolder, GenericArgKind, RegionKind, TypeFoldable, TypeSuperVisitable,
+    AdtDef, BottomUpFolder, FnSig, GenericArgKind, RegionKind, TypeFoldable, TypeSuperVisitable,
     TypeVisitable, TypeVisitableExt, fold_regions,
 };
 use rustc_session::lint::builtin::UNINHABITED_STATIC;
@@ -100,6 +100,18 @@ pub fn check_abi_fn_ptr(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Ex
     }
 }
 
+pub fn check_custom_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, fn_sig: FnSig<'_>, fn_sig_span: Span) {
+    if fn_sig.abi == ExternAbi::Custom {
+        // Function definitions that use `extern "custom"` must be naked functions.
+        if !tcx.has_attr(def_id, sym::naked) {
+            tcx.dcx().emit_err(crate::errors::AbiCustomClothedFunction {
+                span: fn_sig_span,
+                naked_span: tcx.def_span(def_id).shrink_to_lo(),
+            });
+        }
+    }
+}
+
 fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     let def = tcx.adt_def(def_id);
     let span = tcx.def_span(def_id);
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index fad8abf5fae..c5c7e6b2aa7 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -72,7 +72,7 @@ pub mod wfcheck;
 
 use std::num::NonZero;
 
-pub use check::{check_abi, check_abi_fn_ptr};
+pub use check::{check_abi, check_abi_fn_ptr, check_custom_abi};
 use rustc_abi::{ExternAbi, VariantIdx};
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_errors::{Diag, ErrorGuaranteed, pluralize, struct_span_code_err};
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index a27d1ed6c53..809cb311c1f 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -1698,3 +1698,17 @@ pub(crate) struct SelfInTypeAlias {
     #[label]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_abi_custom_clothed_function)]
+pub(crate) struct AbiCustomClothedFunction {
+    #[primary_span]
+    pub span: Span,
+    #[suggestion(
+        hir_analysis_suggestion,
+        applicability = "maybe-incorrect",
+        code = "#[unsafe(naked)]\n",
+        style = "short"
+    )]
+    pub naked_span: Span,
+}
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl
index 3bdd1b48666..ac7ff65528d 100644
--- a/compiler/rustc_hir_typeck/messages.ftl
+++ b/compiler/rustc_hir_typeck/messages.ftl
@@ -1,3 +1,7 @@
+hir_typeck_abi_custom_call =
+    functions with the `"custom"` ABI cannot be called
+    .note = an `extern "custom"` function can only be called from within inline assembly
+
 hir_typeck_add_missing_parentheses_in_range = you must surround the range in parentheses to call its `{$func_name}` function
 
 hir_typeck_add_return_type_add = try adding a return type
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index d173fe7c2c2..e915b4fc626 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -1,5 +1,6 @@
 use std::iter;
 
+use rustc_abi::ExternAbi;
 use rustc_ast::util::parser::ExprPrecedence;
 use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey};
 use rustc_hir::def::{self, CtorKind, Namespace, Res};
@@ -83,6 +84,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         while result.is_none() && autoderef.next().is_some() {
             result = self.try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &autoderef);
         }
+        self.check_call_custom_abi(autoderef.final_ty(false), call_expr.span);
         self.register_predicates(autoderef.into_obligations());
 
         let output = match result {
@@ -135,6 +137,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         output
     }
 
+    /// Functions of type `extern "custom" fn(/* ... */)` cannot be called using `ExprKind::Call`.
+    ///
+    /// These functions have a calling convention that is unknown to rust, hence it cannot generate
+    /// code for the call. The only way to execute such a function is via inline assembly.
+    fn check_call_custom_abi(&self, callee_ty: Ty<'tcx>, span: Span) {
+        let abi = match callee_ty.kind() {
+            ty::FnDef(def_id, _) => self.tcx.fn_sig(def_id).skip_binder().skip_binder().abi,
+            ty::FnPtr(_, header) => header.abi,
+            _ => return,
+        };
+
+        if let ExternAbi::Custom = abi {
+            self.tcx.dcx().emit_err(errors::AbiCustomCall { span });
+        }
+    }
+
     #[instrument(level = "debug", skip(self, call_expr, callee_expr, arg_exprs, autoderef), ret)]
     fn try_overloaded_call_step(
         &self,
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 774815015d5..abb8cdc1cdf 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -1163,3 +1163,10 @@ pub(crate) struct NakedFunctionsMustNakedAsm {
     #[label]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_typeck_abi_custom_call)]
+pub(crate) struct AbiCustomCall {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index dfc7935d02b..87682d52dbf 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -5,7 +5,7 @@
 //!
 //! See [`rustc_hir_analysis::check`] for more context on type checking in general.
 
-use rustc_abi::{FIRST_VARIANT, FieldIdx};
+use rustc_abi::{ExternAbi, FIRST_VARIANT, FieldIdx};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_data_structures::unord::UnordMap;
@@ -1627,6 +1627,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     Some(method.def_id),
                 );
 
+                // Functions of type `extern "custom" fn(/* ... */)` cannot be called using
+                // `ExprKind::MethodCall`. These functions have a calling convention that is
+                // unknown to rust, hence it cannot generate code for the call. The only way
+                // to execute such a function is via inline assembly.
+                if let ExternAbi::Custom = method.sig.abi {
+                    self.tcx.dcx().emit_err(crate::errors::AbiCustomCall { span: expr.span });
+                }
+
                 method.sig.output()
             }
             Err(error) => {
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index a45a7715340..043a687914b 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -48,7 +48,7 @@ use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_e
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{HirId, HirIdMap, Node};
-use rustc_hir_analysis::check::check_abi;
+use rustc_hir_analysis::check::{check_abi, check_custom_abi};
 use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
 use rustc_infer::traits::{ObligationCauseCode, ObligationInspector, WellFormedLoc};
 use rustc_middle::query::Providers;
@@ -138,7 +138,7 @@ fn typeck_with_inspect<'tcx>(
         // for visit the asm expr of the body.
         let ty = fcx.check_expr(body.value);
         fcx.write_ty(id, ty);
-    } else if let Some(hir::FnSig { header, decl, .. }) = node.fn_sig() {
+    } else if let Some(hir::FnSig { header, decl, span: fn_sig_span }) = node.fn_sig() {
         let fn_sig = if decl.output.is_suggestable_infer_ty().is_some() {
             // In the case that we're recovering `fn() -> W<_>` or some other return
             // type that has an infer in it, lower the type directly so that it'll
@@ -150,6 +150,8 @@ fn typeck_with_inspect<'tcx>(
         };
 
         check_abi(tcx, id, span, fn_sig.abi());
+        check_custom_abi(tcx, def_id, fn_sig.skip_binder(), *fn_sig_span);
+
         loops::check(tcx, def_id, body);
 
         // Compute the function signature from point of view of inside the fn.
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 142069c338a..46ae4146528 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -530,8 +530,6 @@ lint_mismatched_lifetime_syntaxes_suggestion_implicit =
 lint_mismatched_lifetime_syntaxes_suggestion_mixed =
     one option is to remove the lifetime for references and use the anonymous lifetime for paths
 
-lint_missing_fragment_specifier = missing fragment specifier
-
 lint_missing_unsafe_on_extern = extern blocks should be unsafe
     .suggestion = needs `unsafe` before the extern keyword
 
diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs
index 60c477dd6c7..3b0a36186b6 100644
--- a/compiler/rustc_lint/src/early/diagnostics.rs
+++ b/compiler/rustc_lint/src/early/diagnostics.rs
@@ -432,9 +432,6 @@ pub fn decorate_builtin_lint(
         BuiltinLintDiag::CfgAttrNoAttributes => {
             lints::CfgAttrNoAttributes.decorate_lint(diag);
         }
-        BuiltinLintDiag::MissingFragmentSpecifier => {
-            lints::MissingFragmentSpecifier.decorate_lint(diag);
-        }
         BuiltinLintDiag::MetaVariableStillRepeating(name) => {
             lints::MetaVariableStillRepeating { name }.decorate_lint(diag);
         }
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index df1e6f8eb1f..e84cdb581b5 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -618,6 +618,11 @@ fn register_builtins(store: &mut LintStore) {
         "converted into hard error, \
          see <https://github.com/rust-lang/rust/issues/116558> for more information",
     );
+    store.register_removed(
+        "missing_fragment_specifier",
+        "converted into hard error, \
+         see <https://github.com/rust-lang/rust/issues/40107> for more information",
+    );
 }
 
 fn register_internals(store: &mut LintStore) {
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index f91ff9dc916..3d17dfbc451 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -2591,10 +2591,6 @@ pub(crate) struct DuplicateMacroAttribute;
 pub(crate) struct CfgAttrNoAttributes;
 
 #[derive(LintDiagnostic)]
-#[diag(lint_missing_fragment_specifier)]
-pub(crate) struct MissingFragmentSpecifier;
-
-#[derive(LintDiagnostic)]
 #[diag(lint_metavariable_still_repeating)]
 pub(crate) struct MetaVariableStillRepeating {
     pub name: MacroRulesNormalizedIdent,
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 2b15564dcfa..295dd82fead 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -65,7 +65,6 @@ declare_lint_pass! {
         MACRO_USE_EXTERN_CRATE,
         META_VARIABLE_MISUSE,
         MISSING_ABI,
-        MISSING_FRAGMENT_SPECIFIER,
         MISSING_UNSAFE_ON_EXTERN,
         MUST_NOT_SUSPEND,
         NAMED_ARGUMENTS_USED_POSITIONALLY,
@@ -1418,51 +1417,6 @@ declare_lint! {
 }
 
 declare_lint! {
-    /// The `missing_fragment_specifier` lint is issued when an unused pattern in a
-    /// `macro_rules!` macro definition has a meta-variable (e.g. `$e`) that is not
-    /// followed by a fragment specifier (e.g. `:expr`).
-    ///
-    /// This warning can always be fixed by removing the unused pattern in the
-    /// `macro_rules!` macro definition.
-    ///
-    /// ### Example
-    ///
-    /// ```rust,compile_fail,edition2021
-    /// macro_rules! foo {
-    ///    () => {};
-    ///    ($name) => { };
-    /// }
-    ///
-    /// fn main() {
-    ///    foo!();
-    /// }
-    /// ```
-    ///
-    /// {{produces}}
-    ///
-    /// ### Explanation
-    ///
-    /// To fix this, remove the unused pattern from the `macro_rules!` macro definition:
-    ///
-    /// ```rust
-    /// macro_rules! foo {
-    ///     () => {};
-    /// }
-    /// fn main() {
-    ///     foo!();
-    /// }
-    /// ```
-    pub MISSING_FRAGMENT_SPECIFIER,
-    Deny,
-    "detects missing fragment specifiers in unused `macro_rules!` patterns",
-    @future_incompatible = FutureIncompatibleInfo {
-        reason: FutureIncompatibilityReason::FutureReleaseError,
-        reference: "issue #40107 <https://github.com/rust-lang/rust/issues/40107>",
-        report_in_deps: true,
-    };
-}
-
-declare_lint! {
     /// The `late_bound_lifetime_arguments` lint detects generic lifetime
     /// arguments in path segments with late bound lifetime parameters.
     ///
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 1d9b7a7fcb9..cd402c9234f 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -778,7 +778,6 @@ pub enum BuiltinLintDiag {
     UnnameableTestItems,
     DuplicateMacroAttribute,
     CfgAttrNoAttributes,
-    MissingFragmentSpecifier,
     MetaVariableStillRepeating(MacroRulesNormalizedIdent),
     MetaVariableWrongOperator,
     DuplicateMatcherBinding,
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index c2ae6b06192..9bce2845680 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -1266,6 +1266,7 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: ExternAbi)
         | RiscvInterruptS
         | CCmseNonSecureCall
         | CCmseNonSecureEntry
+        | Custom
         | Unadjusted => false,
         Rust | RustCall | RustCold => tcx.sess.panic_strategy() == PanicStrategy::Unwind,
     }
diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl
index 528c52eace7..61953614c77 100644
--- a/compiler/rustc_session/messages.ftl
+++ b/compiler/rustc_session/messages.ftl
@@ -40,6 +40,11 @@ session_file_is_not_writeable = output file {$file} is not writeable -- check it
 
 session_file_write_fail = failed to write `{$path}` due to error `{$err}`
 
+session_forbidden_ctarget_feature =
+    target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason}
+    .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+session_forbidden_ctarget_feature_issue = for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
+
 session_function_return_requires_x86_or_x86_64 = `-Zfunction-return` (except `keep`) is only supported on x86 and x86_64
 
 session_function_return_thunk_extern_requires_non_large_code_model = `-Zfunction-return=thunk-extern` is only supported on non-large code models
@@ -132,6 +137,9 @@ session_target_stack_protector_not_supported = `-Z stack-protector={$stack_prote
 session_unleashed_feature_help_named = skipping check for `{$gate}` feature
 session_unleashed_feature_help_unnamed = skipping check that does not even have a feature gate
 
+session_unstable_ctarget_feature =
+    unstable feature specified for `-Ctarget-feature`: `{$feature}`
+    .note = this feature is not stably supported; its behavior can change in the future
 session_unstable_virtual_function_elimination = `-Zvirtual-function-elimination` requires `-Clto`
 
 session_unsupported_crate_type_for_target =
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 60e1b465ba9..8984634e5ec 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -2649,6 +2649,15 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
 
     let prints = collect_print_requests(early_dcx, &mut cg, &unstable_opts, matches);
 
+    // -Zretpoline-external-thunk also requires -Zretpoline
+    if unstable_opts.retpoline_external_thunk {
+        unstable_opts.retpoline = true;
+        target_modifiers.insert(
+            OptionsTargetModifiers::UnstableOptions(UnstableOptionsTargetModifiers::retpoline),
+            "true".to_string(),
+        );
+    }
+
     let cg = cg;
 
     let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m));
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index bf95014843d..9c591dcf619 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -501,3 +501,20 @@ pub(crate) struct SoftFloatIgnored;
 #[note]
 #[note(session_soft_float_deprecated_issue)]
 pub(crate) struct SoftFloatDeprecated;
+
+#[derive(Diagnostic)]
+#[diag(session_forbidden_ctarget_feature)]
+#[note]
+#[note(session_forbidden_ctarget_feature_issue)]
+pub(crate) struct ForbiddenCTargetFeature<'a> {
+    pub feature: &'a str,
+    pub enabled: &'a str,
+    pub reason: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(session_unstable_ctarget_feature)]
+#[note]
+pub(crate) struct UnstableCTargetFeature<'a> {
+    pub feature: &'a str,
+}
diff --git a/compiler/rustc_session/src/features.rs b/compiler/rustc_session/src/features.rs
new file mode 100644
index 00000000000..70a088a236f
--- /dev/null
+++ b/compiler/rustc_session/src/features.rs
@@ -0,0 +1,59 @@
+use rustc_target::target_features::Stability;
+
+use crate::Session;
+use crate::errors::{ForbiddenCTargetFeature, UnstableCTargetFeature};
+
+pub trait StabilityExt {
+    /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`.
+    /// Otherwise, some features also may only be enabled by flag (target modifier).
+    /// (It might still be nightly-only even if this returns `true`, so make sure to also check
+    /// `requires_nightly`.)
+    fn is_toggle_permitted(&self, sess: &Session) -> Result<(), &'static str>;
+
+    /// Check that feature is correctly enabled/disabled by command line flag (emits warnings)
+    fn verify_feature_enabled_by_flag(&self, sess: &Session, enable: bool, feature: &str);
+}
+
+impl StabilityExt for Stability {
+    fn is_toggle_permitted(&self, sess: &Session) -> Result<(), &'static str> {
+        match self {
+            Stability::Forbidden { reason } => Err(reason),
+            Stability::TargetModifierOnly { reason, flag } => {
+                if !sess.opts.target_feature_flag_enabled(*flag) { Err(reason) } else { Ok(()) }
+            }
+            _ => Ok(()),
+        }
+    }
+    fn verify_feature_enabled_by_flag(&self, sess: &Session, enable: bool, feature: &str) {
+        if let Err(reason) = self.is_toggle_permitted(sess) {
+            sess.dcx().emit_warn(ForbiddenCTargetFeature {
+                feature,
+                enabled: if enable { "enabled" } else { "disabled" },
+                reason,
+            });
+        } else if self.requires_nightly().is_some() {
+            // An unstable feature. Warn about using it. It makes little sense
+            // to hard-error here since we just warn about fully unknown
+            // features above.
+            sess.dcx().emit_warn(UnstableCTargetFeature { feature });
+        }
+    }
+}
+
+pub fn retpoline_features_by_flags(sess: &Session, features: &mut Vec<&str>) {
+    // -Zretpoline without -Zretpoline-external-thunk enables
+    // retpoline-indirect-branches and retpoline-indirect-calls target features
+    let unstable_opts = &sess.opts.unstable_opts;
+    if unstable_opts.retpoline && !unstable_opts.retpoline_external_thunk {
+        features.push("+retpoline-indirect-branches");
+        features.push("+retpoline-indirect-calls");
+    }
+    // -Zretpoline-external-thunk (maybe, with -Zretpoline too) enables
+    // retpoline-external-thunk, retpoline-indirect-branches and
+    // retpoline-indirect-calls target features
+    if unstable_opts.retpoline_external_thunk {
+        features.push("+retpoline-external-thunk");
+        features.push("+retpoline-indirect-branches");
+        features.push("+retpoline-indirect-calls");
+    }
+}
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
index 5e5872ee068..4added19e56 100644
--- a/compiler/rustc_session/src/lib.rs
+++ b/compiler/rustc_session/src/lib.rs
@@ -29,6 +29,7 @@ pub use session::*;
 pub mod output;
 
 pub use getopts;
+pub mod features;
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index a26d3bc4044..6218521d4f0 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -290,6 +290,14 @@ macro_rules! top_level_options {
                 mods.sort_by(|a, b| a.opt.cmp(&b.opt));
                 mods
             }
+
+            pub fn target_feature_flag_enabled(&self, flag: &str) -> bool {
+                match flag {
+                    "retpoline" => self.unstable_opts.retpoline,
+                    "retpoline-external-thunk" => self.unstable_opts.retpoline_external_thunk,
+                    _ => false,
+                }
+            }
         }
     );
 }
@@ -2448,6 +2456,11 @@ options! {
     remark_dir: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
         "directory into which to write optimization remarks (if not specified, they will be \
 written to standard error output)"),
+    retpoline: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER],
+        "enables retpoline-indirect-branches and retpoline-indirect-calls target features (default: no)"),
+    retpoline_external_thunk: bool = (false, parse_bool, [TRACKED TARGET_MODIFIER],
+        "enables retpoline-external-thunk, retpoline-indirect-branches and retpoline-indirect-calls \
+        target features (default: no)"),
     sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED],
         "use a sanitizer"),
     sanitizer_cfi_canonical_jump_tables: Option<bool> = (Some(true), parse_opt_bool, [TRACKED],
diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs
index 6e13b87c41d..a4c6f186222 100644
--- a/compiler/rustc_smir/src/rustc_internal/internal.rs
+++ b/compiler/rustc_smir/src/rustc_internal/internal.rs
@@ -496,6 +496,7 @@ impl RustcInternal for Abi {
             Abi::RustCold => rustc_abi::ExternAbi::RustCold,
             Abi::RiscvInterruptM => rustc_abi::ExternAbi::RiscvInterruptM,
             Abi::RiscvInterruptS => rustc_abi::ExternAbi::RiscvInterruptS,
+            Abi::Custom => rustc_abi::ExternAbi::Custom,
         }
     }
 }
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs
index 46f1ca61cec..35d5b7fb89a 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/abi.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/abi.rs
@@ -101,6 +101,7 @@ impl<'tcx> Stable<'tcx> for CanonAbi {
             CanonAbi::C => CallConvention::C,
             CanonAbi::Rust => CallConvention::Rust,
             CanonAbi::RustCold => CallConvention::Cold,
+            CanonAbi::Custom => CallConvention::Custom,
             CanonAbi::Arm(arm_call) => match arm_call {
                 ArmCall::Aapcs => CallConvention::ArmAapcs,
                 ArmCall::CCmseNonSecureCall => CallConvention::CCmseNonSecureCall,
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
index b0c9dba78a6..6a26f5f7997 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
@@ -879,6 +879,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::ExternAbi {
             ExternAbi::RustCold => Abi::RustCold,
             ExternAbi::RiscvInterruptM => Abi::RiscvInterruptM,
             ExternAbi::RiscvInterruptS => Abi::RiscvInterruptS,
+            ExternAbi::Custom => Abi::Custom,
         }
     }
 }
diff --git a/compiler/rustc_smir/src/stable_mir/abi.rs b/compiler/rustc_smir/src/stable_mir/abi.rs
index 347c6ed16a2..d8a2b97662c 100644
--- a/compiler/rustc_smir/src/stable_mir/abi.rs
+++ b/compiler/rustc_smir/src/stable_mir/abi.rs
@@ -430,6 +430,8 @@ pub enum CallConvention {
     PreserveMost,
     PreserveAll,
 
+    Custom,
+
     // Target-specific calling conventions.
     ArmAapcs,
     CCmseNonSecureCall,
diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs
index 45936857d33..2934af31cd5 100644
--- a/compiler/rustc_smir/src/stable_mir/ty.rs
+++ b/compiler/rustc_smir/src/stable_mir/ty.rs
@@ -1111,6 +1111,7 @@ pub enum Abi {
     RustCold,
     RiscvInterruptM,
     RiscvInterruptS,
+    Custom,
 }
 
 /// A binder represents a possibly generic type and its bound vars.
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 3429d28e1d5..cb9ccf4cc3f 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -407,6 +407,7 @@ symbols! {
         abi_amdgpu_kernel,
         abi_avr_interrupt,
         abi_c_cmse_nonsecure_call,
+        abi_custom,
         abi_efiapi,
         abi_gpu_kernel,
         abi_msp430_interrupt,
diff --git a/compiler/rustc_target/src/asm/loongarch.rs b/compiler/rustc_target/src/asm/loongarch.rs
index b4ea6fc592a..8783d3953b1 100644
--- a/compiler/rustc_target/src/asm/loongarch.rs
+++ b/compiler/rustc_target/src/asm/loongarch.rs
@@ -34,11 +34,13 @@ impl LoongArchInlineAsmRegClass {
 
     pub fn supported_types(
         self,
-        _arch: InlineAsmArch,
+        arch: InlineAsmArch,
     ) -> &'static [(InlineAsmType, Option<Symbol>)] {
-        match self {
-            Self::reg => types! { _: I8, I16, I32, I64, F32, F64; },
-            Self::freg => types! { f: F32; d: F64; },
+        match (self, arch) {
+            (Self::reg, InlineAsmArch::LoongArch64) => types! { _: I8, I16, I32, I64, F32, F64; },
+            (Self::reg, InlineAsmArch::LoongArch32) => types! { _: I8, I16, I32, F32; },
+            (Self::freg, _) => types! { f: F32; d: F64; },
+            _ => unreachable!("unsupported register class"),
         }
     }
 }
diff --git a/compiler/rustc_target/src/spec/abi_map.rs b/compiler/rustc_target/src/spec/abi_map.rs
index c4978a8e52a..4659bbdb890 100644
--- a/compiler/rustc_target/src/spec/abi_map.rs
+++ b/compiler/rustc_target/src/spec/abi_map.rs
@@ -71,6 +71,8 @@ impl AbiMap {
             (ExternAbi::RustCold, _) if self.os == OsKind::Windows => CanonAbi::Rust,
             (ExternAbi::RustCold, _) => CanonAbi::RustCold,
 
+            (ExternAbi::Custom, _) => CanonAbi::Custom,
+
             (ExternAbi::System { .. }, Arch::X86) if os == OsKind::Windows && !has_c_varargs => {
                 CanonAbi::X86(X86Call::Stdcall)
             }
diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs
index 5d1182fce48..a1eac1fba25 100644
--- a/compiler/rustc_target/src/target_features.rs
+++ b/compiler/rustc_target/src/target_features.rs
@@ -34,6 +34,9 @@ pub enum Stability {
     /// particular for features are actually ABI configuration flags (not all targets are as nice as
     /// RISC-V and have an explicit way to set the ABI separate from target features).
     Forbidden { reason: &'static str },
+    /// This feature can not be set via `-Ctarget-feature` or `#[target_feature]`, it can only be set
+    /// by target modifier flag. Target modifier flags are tracked to be consistent in linked modules.
+    TargetModifierOnly { reason: &'static str, flag: &'static str },
 }
 use Stability::*;
 
@@ -49,6 +52,7 @@ impl<CTX> HashStable<CTX> for Stability {
             Stability::Forbidden { reason } => {
                 reason.hash_stable(hcx, hasher);
             }
+            Stability::TargetModifierOnly { .. } => {}
         }
     }
 }
@@ -74,16 +78,7 @@ impl Stability {
             Stability::Unstable(nightly_feature) => Some(nightly_feature),
             Stability::Stable { .. } => None,
             Stability::Forbidden { .. } => panic!("forbidden features should not reach this far"),
-        }
-    }
-
-    /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`.
-    /// (It might still be nightly-only even if this returns `true`, so make sure to also check
-    /// `requires_nightly`.)
-    pub fn toggle_allowed(&self) -> Result<(), &'static str> {
-        match self {
-            Stability::Forbidden { reason } => Err(reason),
-            _ => Ok(()),
+            Stability::TargetModifierOnly { .. } => None,
         }
     }
 }
@@ -453,6 +448,30 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     ("prfchw", Unstable(sym::prfchw_target_feature), &[]),
     ("rdrand", Stable, &[]),
     ("rdseed", Stable, &[]),
+    (
+        "retpoline-external-thunk",
+        Stability::TargetModifierOnly {
+            reason: "use `retpoline-external-thunk` target modifier flag instead",
+            flag: "retpoline-external-thunk",
+        },
+        &[],
+    ),
+    (
+        "retpoline-indirect-branches",
+        Stability::TargetModifierOnly {
+            reason: "use `retpoline` target modifier flag instead",
+            flag: "retpoline",
+        },
+        &[],
+    ),
+    (
+        "retpoline-indirect-calls",
+        Stability::TargetModifierOnly {
+            reason: "use `retpoline` target modifier flag instead",
+            flag: "retpoline",
+        },
+        &[],
+    ),
     ("rtm", Unstable(sym::rtm_target_feature), &[]),
     ("sha", Stable, &["sse2"]),
     ("sha512", Stable, &["avx2"]),
diff --git a/library/Cargo.lock b/library/Cargo.lock
index 862d0938d37..1bd97e7b527 100644
--- a/library/Cargo.lock
+++ b/library/Cargo.lock
@@ -4,11 +4,10 @@ version = 4
 
 [[package]]
 name = "addr2line"
-version = "0.24.2"
+version = "0.25.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
+checksum = "9acbfca36652500c911ddb767ed433e3ed99b032b5d935be73c6923662db1d43"
 dependencies = [
- "compiler_builtins",
  "gimli",
  "rustc-std-workspace-alloc",
  "rustc-std-workspace-core",
@@ -16,11 +15,10 @@ dependencies = [
 
 [[package]]
 name = "adler2"
-version = "2.0.0"
+version = "2.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
 dependencies = [
- "compiler_builtins",
  "rustc-std-workspace-core",
 ]
 
@@ -51,11 +49,10 @@ dependencies = [
 
 [[package]]
 name = "cfg-if"
-version = "1.0.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
 dependencies = [
- "compiler_builtins",
  "rustc-std-workspace-core",
 ]
 
@@ -81,12 +78,11 @@ dependencies = [
 
 [[package]]
 name = "dlmalloc"
-version = "0.2.8"
+version = "0.2.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8cff88b751e7a276c4ab0e222c3f355190adc6dde9ce39c851db39da34990df7"
+checksum = "d01597dde41c0b9da50d5f8c219023d63d8f27f39a27095070fd191fddc83891"
 dependencies = [
  "cfg-if",
- "compiler_builtins",
  "libc",
  "rustc-std-workspace-core",
  "windows-sys",
@@ -104,9 +100,9 @@ dependencies = [
 
 [[package]]
 name = "getopts"
-version = "0.2.21"
+version = "0.2.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
+checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1"
 dependencies = [
  "rustc-std-workspace-core",
  "rustc-std-workspace-std",
@@ -115,11 +111,10 @@ dependencies = [
 
 [[package]]
 name = "gimli"
-version = "0.31.1"
+version = "0.32.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
+checksum = "93563d740bc9ef04104f9ed6f86f1e3275c2cdafb95664e26584b9ca807a8ffe"
 dependencies = [
- "compiler_builtins",
  "rustc-std-workspace-alloc",
  "rustc-std-workspace-core",
 ]
@@ -136,11 +131,10 @@ dependencies = [
 
 [[package]]
 name = "hermit-abi"
-version = "0.5.1"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08"
+checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
 dependencies = [
- "compiler_builtins",
  "rustc-std-workspace-alloc",
  "rustc-std-workspace-core",
 ]
@@ -156,33 +150,30 @@ dependencies = [
 
 [[package]]
 name = "memchr"
-version = "2.7.4"
+version = "2.7.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
 dependencies = [
- "compiler_builtins",
  "rustc-std-workspace-core",
 ]
 
 [[package]]
 name = "miniz_oxide"
-version = "0.8.8"
+version = "0.8.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
 dependencies = [
  "adler2",
- "compiler_builtins",
  "rustc-std-workspace-alloc",
  "rustc-std-workspace-core",
 ]
 
 [[package]]
 name = "object"
-version = "0.36.7"
+version = "0.37.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
+checksum = "03fd943161069e1768b4b3d050890ba48730e590f57e56d4aa04e7e090e61b4a"
 dependencies = [
- "compiler_builtins",
  "memchr",
  "rustc-std-workspace-alloc",
  "rustc-std-workspace-core",
@@ -273,11 +264,10 @@ dependencies = [
 
 [[package]]
 name = "rustc-demangle"
-version = "0.1.24"
+version = "0.1.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
+checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
 dependencies = [
- "compiler_builtins",
  "rustc-std-workspace-core",
 ]
 
@@ -352,7 +342,6 @@ name = "std_detect"
 version = "0.1.5"
 dependencies = [
  "cfg-if",
- "compiler_builtins",
  "libc",
  "rustc-std-workspace-alloc",
  "rustc-std-workspace-core",
@@ -380,11 +369,10 @@ dependencies = [
 
 [[package]]
 name = "unicode-width"
-version = "0.1.14"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
+checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
 dependencies = [
- "compiler_builtins",
  "rustc-std-workspace-core",
  "rustc-std-workspace-std",
 ]
@@ -402,9 +390,9 @@ dependencies = [
 
 [[package]]
 name = "unwinding"
-version = "0.2.6"
+version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8393f2782b6060a807337ff353780c1ca15206f9ba2424df18cb6e733bd7b345"
+checksum = "7d80f6c2bfede213d9a90b4a14f3eb99b84e33c52df6c1a15de0a100f5a88751"
 dependencies = [
  "compiler_builtins",
  "gimli",
@@ -413,11 +401,10 @@ dependencies = [
 
 [[package]]
 name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
+version = "0.11.1+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
 dependencies = [
- "compiler_builtins",
  "rustc-std-workspace-alloc",
  "rustc-std-workspace-core",
 ]
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index 53d78dcc488..ae7107938f3 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -32,7 +32,7 @@ rustc-demangle = { version = "0.1.24", features = ['rustc-dep-of-std'] }
 
 [target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies]
 miniz_oxide = { version = "0.8.0", optional = true, default-features = false }
-addr2line = { version = "0.24.0", optional = true, default-features = false }
+addr2line = { version = "0.25.0", optional = true, default-features = false }
 
 [target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies]
 libc = { version = "0.2.172", default-features = false, features = [
@@ -40,7 +40,7 @@ libc = { version = "0.2.172", default-features = false, features = [
 ], public = true }
 
 [target.'cfg(all(not(target_os = "aix"), not(all(windows, target_env = "msvc", not(target_vendor = "uwp")))))'.dependencies]
-object = { version = "0.36.0", default-features = false, optional = true, features = [
+object = { version = "0.37.1", default-features = false, optional = true, features = [
     'read_core',
     'elf',
     'macho',
@@ -50,7 +50,7 @@ object = { version = "0.36.0", default-features = false, optional = true, featur
 ] }
 
 [target.'cfg(target_os = "aix")'.dependencies]
-object = { version = "0.36.0", default-features = false, optional = true, features = [
+object = { version = "0.37.1", default-features = false, optional = true, features = [
     'read_core',
     'xcoff',
     'unaligned',
diff --git a/library/stdarch b/library/stdarch
-Subproject 5c1c436524c0bbc8db83577f42f8bea9006a7b7
+Subproject 1b4d15df12079504942d0a3f1030b2039b8a776
diff --git a/library/unwind/Cargo.toml b/library/unwind/Cargo.toml
index ad373420a96..f8da09f7193 100644
--- a/library/unwind/Cargo.toml
+++ b/library/unwind/Cargo.toml
@@ -22,7 +22,7 @@ cfg-if = "1.0"
 libc = { version = "0.2.140", features = ['rustc-dep-of-std'], default-features = false }
 
 [target.'cfg(target_os = "xous")'.dependencies]
-unwinding = { version = "0.2.6", features = ['rustc-dep-of-std', 'unwinder', 'fde-custom'], default-features = false }
+unwinding = { version = "0.2.7", features = ['rustc-dep-of-std', 'unwinder', 'fde-custom'], default-features = false }
 
 [features]
 
diff --git a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md
index d9566c9f55c..121f9493435 100644
--- a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md
+++ b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md
@@ -19,6 +19,7 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
 - M68k
 - CSKY
 - SPARC
+- LoongArch32
 
 ## Register classes
 
@@ -53,6 +54,8 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
 | CSKY         | `freg`         | `f[0-31]`                          | `f`                  |
 | SPARC        | `reg`          | `r[2-29]`                          | `r`                  |
 | SPARC        | `yreg`         | `y`                                | Only clobbers        |
+| LoongArch32  | `reg`          | `$r1`, `$r[4-20]`, `$r[23,30]`     | `r`                  |
+| LoongArch32  | `freg`         | `$f[0-31]`                         | `f`                  |
 
 > **Notes**:
 > - NVPTX doesn't have a fixed register set, so named registers are not supported.
@@ -91,6 +94,8 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
 | CSKY         | `freg`                          | None           | `f32`,                                  |
 | SPARC        | `reg`                           | None           | `i8`, `i16`, `i32`, `i64` (SPARC64 only) |
 | SPARC        | `yreg`                          | N/A            | Only clobbers                           |
+| LoongArch32  | `reg`                           | None           | `i8`, `i16`, `i32`, `f32`               |
+| LoongArch32  | `freg`                          | None           | `f32`, `f64`                            |
 
 ## Register aliases
 
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index 131a12ce228..0ceeea2b9b1 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -18,6 +18,7 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
+use rustc_session::features::StabilityExt;
 use rustc_span::def_id::LOCAL_CRATE;
 use rustdoc_json_types as types;
 // It's important to use the FxHashMap from rustdoc_json_types here, instead of
@@ -148,7 +149,7 @@ fn target(sess: &rustc_session::Session) -> types::Target {
             .copied()
             .filter(|(_, stability, _)| {
                 // Describe only target features which the user can toggle
-                stability.toggle_allowed().is_ok()
+                stability.is_toggle_permitted(sess).is_ok()
             })
             .map(|(name, stability, implied_features)| {
                 types::TargetFeature {
@@ -164,7 +165,7 @@ fn target(sess: &rustc_session::Session) -> types::Target {
                             // Imply only target features which the user can toggle
                             feature_stability
                                 .get(name)
-                                .map(|stability| stability.toggle_allowed().is_ok())
+                                .map(|stability| stability.is_toggle_permitted(sess).is_ok())
                                 .unwrap_or(false)
                         })
                         .map(String::from)
diff --git a/tests/codegen/autovec/dont-shuffle-bswaps-opt2.rs b/tests/codegen/autovec/dont-shuffle-bswaps-opt2.rs
new file mode 100644
index 00000000000..c354228acc5
--- /dev/null
+++ b/tests/codegen/autovec/dont-shuffle-bswaps-opt2.rs
@@ -0,0 +1,31 @@
+//@ compile-flags: -Copt-level=2
+
+#![crate_type = "lib"]
+#![no_std]
+
+// This test is paired with the arch-specific -opt3.rs test.
+
+// The code is from https://github.com/rust-lang/rust/issues/122805.
+// Ensure we do not generate the shufflevector instruction
+// to avoid complicating the code.
+
+// CHECK-LABEL: define{{.*}}void @convert(
+// CHECK-NOT: shufflevector
+#[no_mangle]
+pub fn convert(value: [u16; 8]) -> [u8; 16] {
+    #[cfg(target_endian = "little")]
+    let bswap = u16::to_be;
+    #[cfg(target_endian = "big")]
+    let bswap = u16::to_le;
+    let addr16 = [
+        bswap(value[0]),
+        bswap(value[1]),
+        bswap(value[2]),
+        bswap(value[3]),
+        bswap(value[4]),
+        bswap(value[5]),
+        bswap(value[6]),
+        bswap(value[7]),
+    ];
+    unsafe { core::mem::transmute::<_, [u8; 16]>(addr16) }
+}
diff --git a/tests/codegen/dont-shuffle-bswaps.rs b/tests/codegen/autovec/dont-shuffle-bswaps-opt3.rs
index c1dab2bc295..203d12005de 100644
--- a/tests/codegen/dont-shuffle-bswaps.rs
+++ b/tests/codegen/autovec/dont-shuffle-bswaps-opt3.rs
@@ -1,29 +1,27 @@
-//@ revisions: OPT2 OPT3 OPT3_S390X
-//@[OPT2] compile-flags: -Copt-level=2
-//@[OPT3] compile-flags: -C opt-level=3
-// some targets don't do the opt we are looking for
-//@[OPT3] only-64bit
-//@[OPT3] ignore-s390x
-//@[OPT3_S390X] compile-flags: -C opt-level=3 -C target-cpu=z13
-//@[OPT3_S390X] only-s390x
+//@ revisions: AARCH64 X86_64 Z13
+//@ compile-flags: -Copt-level=3
+//@[AARCH64] only-aarch64
+//@[X86_64] only-x86_64
+//@[Z13] only-s390x
+//@[Z13] compile-flags: -Ctarget-cpu=z13
 
 #![crate_type = "lib"]
 #![no_std]
 
+// This test is paired with the arch-neutral -opt2.rs test
+
 // The code is from https://github.com/rust-lang/rust/issues/122805.
 // Ensure we do not generate the shufflevector instruction
 // to avoid complicating the code.
+
 // CHECK-LABEL: define{{.*}}void @convert(
 // CHECK-NOT: shufflevector
+
 // On higher opt levels, this should just be a bswap:
-// OPT3: load <8 x i16>
-// OPT3-NEXT: call <8 x i16> @llvm.bswap
-// OPT3-NEXT: store <8 x i16>
-// OPT3-NEXT: ret void
-// OPT3_S390X: load <8 x i16>
-// OPT3_S390X-NEXT: call <8 x i16> @llvm.bswap
-// OPT3_S390X-NEXT: store <8 x i16>
-// OPT3_S390X-NEXT: ret void
+// CHECK: load <8 x i16>
+// CHECK-NEXT: call <8 x i16> @llvm.bswap
+// CHECK-NEXT: store <8 x i16>
+// CHECK-NEXT: ret void
 #[no_mangle]
 pub fn convert(value: [u16; 8]) -> [u8; 16] {
     #[cfg(target_endian = "little")]
diff --git a/tests/codegen/retpoline.rs b/tests/codegen/retpoline.rs
new file mode 100644
index 00000000000..30fdd9c2db2
--- /dev/null
+++ b/tests/codegen/retpoline.rs
@@ -0,0 +1,29 @@
+// ignore-tidy-linelength
+// Test that the
+// `retpoline-external-thunk`, `retpoline-indirect-branches`, `retpoline-indirect-calls`
+// target features are (not) emitted when the `retpoline/retpoline-external-thunk` flag is (not) set.
+
+//@ revisions: disabled enabled_retpoline enabled_retpoline_external_thunk
+//@ needs-llvm-components: x86
+//@ compile-flags: --target x86_64-unknown-linux-gnu
+//@ [enabled_retpoline] compile-flags: -Zretpoline
+//@ [enabled_retpoline_external_thunk] compile-flags: -Zretpoline-external-thunk
+
+#![crate_type = "lib"]
+#![feature(no_core, lang_items)]
+#![no_core]
+
+#[lang = "sized"]
+trait Sized {}
+
+#[no_mangle]
+pub fn foo() {
+    // CHECK: @foo() unnamed_addr #0
+
+    // disabled-NOT: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+retpoline-external-thunk{{.*}} }
+    // disabled-NOT: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+retpoline-indirect-branches{{.*}} }
+    // disabled-NOT: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+retpoline-indirect-calls{{.*}} }
+
+    // enabled_retpoline: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+retpoline-indirect-branches,+retpoline-indirect-calls{{.*}} }
+    // enabled_retpoline_external_thunk: attributes #0 = { {{.*}}"target-features"="{{[^"]*}}+retpoline-external-thunk,+retpoline-indirect-branches,+retpoline-indirect-calls{{.*}} }
+}
diff --git a/tests/rustdoc/macro/macro-generated-macro.macro_morestuff_pre.html b/tests/rustdoc/macro/macro-generated-macro.macro_morestuff_pre.html
index 28f15522a82..4dcc8111c6f 100644
--- a/tests/rustdoc/macro/macro-generated-macro.macro_morestuff_pre.html
+++ b/tests/rustdoc/macro/macro-generated-macro.macro_morestuff_pre.html
@@ -1,7 +1,7 @@
 macro_rules! morestuff {
     (
-        <= "space between most kinds of tokens" : 1 $x + @ :: >>= 'static
-        "no space inside paren or bracket" : (2 a) [2 a] $(2 $a:tt)*
+        <= "space between most kinds of tokens" : 1 $x:ident + @ :: >>=
+        'static "no space inside paren or bracket" : (2 a) [2 a] $(2 $a:tt)*
         "space inside curly brace" : { 2 a }
         "no space inside empty delimiters" : () [] {}
         "no space before comma or semicolon" : a, (a), { a }, a; [T; 0];
diff --git a/tests/rustdoc/macro/macro-generated-macro.rs b/tests/rustdoc/macro/macro-generated-macro.rs
index e77d0cf89e7..dfb152bafb6 100644
--- a/tests/rustdoc/macro/macro-generated-macro.rs
+++ b/tests/rustdoc/macro/macro-generated-macro.rs
@@ -25,7 +25,7 @@ make_macro!(linebreak 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 
 //@ snapshot macro_morestuff_pre macro_generated_macro/macro.morestuff.html //pre/text()
 make_macro!(morestuff
-    "space between most kinds of tokens": 1 $x + @ :: >>= 'static
+    "space between most kinds of tokens": 1 $x:ident + @ :: >>= 'static
     "no space inside paren or bracket": (2 a) [2 a] $(2 $a:tt)*
     "space inside curly brace": { 2 a }
     "no space inside empty delimiters": () [] {}
diff --git a/tests/ui/abi/bad-custom.rs b/tests/ui/abi/bad-custom.rs
new file mode 100644
index 00000000000..e792f0955b9
--- /dev/null
+++ b/tests/ui/abi/bad-custom.rs
@@ -0,0 +1,121 @@
+//@ edition: 2021
+//@ check-fail
+//@ needs-asm-support
+#![feature(abi_custom)]
+
+#[unsafe(naked)]
+extern "custom" fn must_be_unsafe(a: i64) -> i64 {
+    //~^ ERROR functions with the `"custom"` ABI must be unsafe
+    //~| ERROR invalid signature for `extern "custom"` function
+    std::arch::naked_asm!("")
+}
+
+#[unsafe(naked)]
+unsafe extern "custom" fn no_parameters(a: i64) {
+    //~^ ERROR invalid signature for `extern "custom"` function
+    std::arch::naked_asm!("")
+}
+
+#[unsafe(naked)]
+unsafe extern "custom" fn no_return_type() -> i64 {
+    //~^ ERROR invalid signature for `extern "custom"` function
+    std::arch::naked_asm!("")
+}
+
+unsafe extern "custom" fn double(a: i64) -> i64 {
+    //~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
+    //~| ERROR invalid signature for `extern "custom"` function
+    unimplemented!()
+}
+
+struct Thing(i64);
+
+impl Thing {
+    unsafe extern "custom" fn is_even(self) -> bool {
+        //~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
+        //~| ERROR invalid signature for `extern "custom"` function
+        unimplemented!()
+    }
+}
+
+trait BitwiseNot {
+    unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
+        //~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
+        //~| ERROR invalid signature for `extern "custom"` function
+        unimplemented!()
+    }
+}
+
+impl BitwiseNot for Thing {}
+
+trait Negate {
+    extern "custom" fn negate(a: i64) -> i64;
+    //~^ ERROR functions with the `"custom"` ABI must be unsafe
+    //~| ERROR invalid signature for `extern "custom"` function
+}
+
+impl Negate for Thing {
+    extern "custom" fn negate(a: i64) -> i64 {
+        //~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
+        //~| ERROR functions with the `"custom"` ABI must be unsafe
+        //~| ERROR invalid signature for `extern "custom"` function
+        -a
+    }
+}
+
+unsafe extern "custom" {
+    fn increment(a: i64) -> i64;
+    //~^ ERROR invalid signature for `extern "custom"` function
+
+    safe fn extern_cannot_be_safe();
+    //~^ ERROR foreign functions with the `"custom"` ABI cannot be safe
+}
+
+fn caller(f: unsafe extern "custom" fn(i64) -> i64, mut x: i64) -> i64 {
+    unsafe { f(x) }
+    //~^ ERROR functions with the `"custom"` ABI cannot be called
+}
+
+fn caller_by_ref(f: &unsafe extern "custom" fn(i64) -> i64, mut x: i64) -> i64 {
+    unsafe { f(x) }
+    //~^ ERROR functions with the `"custom"` ABI cannot be called
+}
+
+type Custom = unsafe extern "custom" fn(i64) -> i64;
+
+fn caller_alias(f: Custom, mut x: i64) -> i64 {
+    unsafe { f(x) }
+    //~^ ERROR functions with the `"custom"` ABI cannot be called
+}
+
+#[unsafe(naked)]
+const unsafe extern "custom" fn no_const_fn() {
+    std::arch::naked_asm!("")
+    //~^ ERROR inline assembly is not allowed in constant functions
+}
+
+async unsafe extern "custom" fn no_async_fn() {
+    //~^ ERROR items with the `"custom"` ABI can only be declared externally or defined via naked functions
+    //~| ERROR functions with the `"custom"` ABI cannot be `async`
+}
+
+fn no_promotion_to_fn_trait(f: unsafe extern "custom" fn()) -> impl Fn()  {
+    //~^ ERROR expected a `Fn()` closure, found `unsafe extern "custom" fn()`
+    f
+}
+
+pub fn main() {
+    unsafe {
+        assert_eq!(double(21), 42);
+        //~^ ERROR functions with the `"custom"` ABI cannot be called
+
+        assert_eq!(unsafe { increment(41) }, 42);
+        //~^ ERROR functions with the `"custom"` ABI cannot be called
+
+        assert!(Thing(41).is_even());
+        //~^ ERROR functions with the `"custom"` ABI cannot be called
+
+        assert_eq!(Thing::bitwise_not(42), !42);
+        //~^ ERROR functions with the `"custom"` ABI cannot be called
+    }
+}
diff --git a/tests/ui/abi/bad-custom.stderr b/tests/ui/abi/bad-custom.stderr
new file mode 100644
index 00000000000..ec0f11af898
--- /dev/null
+++ b/tests/ui/abi/bad-custom.stderr
@@ -0,0 +1,299 @@
+error: functions with the `"custom"` ABI must be unsafe
+  --> $DIR/bad-custom.rs:7:1
+   |
+LL | extern "custom" fn must_be_unsafe(a: i64) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add the `unsafe` keyword to this definition
+   |
+LL | unsafe extern "custom" fn must_be_unsafe(a: i64) -> i64 {
+   | ++++++
+
+error: invalid signature for `extern "custom"` function
+  --> $DIR/bad-custom.rs:7:35
+   |
+LL | extern "custom" fn must_be_unsafe(a: i64) -> i64 {
+   |                                   ^^^^^^     ^^^
+   |
+   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+help: remove the parameters and return type
+   |
+LL - extern "custom" fn must_be_unsafe(a: i64) -> i64 {
+LL + extern "custom" fn must_be_unsafe() {
+   |
+
+error: invalid signature for `extern "custom"` function
+  --> $DIR/bad-custom.rs:14:41
+   |
+LL | unsafe extern "custom" fn no_parameters(a: i64) {
+   |                                         ^^^^^^
+   |
+   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+help: remove the parameters and return type
+   |
+LL - unsafe extern "custom" fn no_parameters(a: i64) {
+LL + unsafe extern "custom" fn no_parameters() {
+   |
+
+error: invalid signature for `extern "custom"` function
+  --> $DIR/bad-custom.rs:20:47
+   |
+LL | unsafe extern "custom" fn no_return_type() -> i64 {
+   |                                               ^^^
+   |
+   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+help: remove the parameters and return type
+   |
+LL - unsafe extern "custom" fn no_return_type() -> i64 {
+LL + unsafe extern "custom" fn no_return_type() {
+   |
+
+error: invalid signature for `extern "custom"` function
+  --> $DIR/bad-custom.rs:25:34
+   |
+LL | unsafe extern "custom" fn double(a: i64) -> i64 {
+   |                                  ^^^^^^     ^^^
+   |
+   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+help: remove the parameters and return type
+   |
+LL - unsafe extern "custom" fn double(a: i64) -> i64 {
+LL + unsafe extern "custom" fn double() {
+   |
+
+error: invalid signature for `extern "custom"` function
+  --> $DIR/bad-custom.rs:34:39
+   |
+LL |     unsafe extern "custom" fn is_even(self) -> bool {
+   |                                       ^^^^     ^^^^
+   |
+   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+help: remove the parameters and return type
+   |
+LL -     unsafe extern "custom" fn is_even(self) -> bool {
+LL +     unsafe extern "custom" fn is_even() {
+   |
+
+error: invalid signature for `extern "custom"` function
+  --> $DIR/bad-custom.rs:42:43
+   |
+LL |     unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
+   |                                           ^^^^^^     ^^^
+   |
+   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+help: remove the parameters and return type
+   |
+LL -     unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
+LL +     unsafe extern "custom" fn bitwise_not() {
+   |
+
+error: functions with the `"custom"` ABI must be unsafe
+  --> $DIR/bad-custom.rs:52:5
+   |
+LL |     extern "custom" fn negate(a: i64) -> i64;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add the `unsafe` keyword to this definition
+   |
+LL |     unsafe extern "custom" fn negate(a: i64) -> i64;
+   |     ++++++
+
+error: invalid signature for `extern "custom"` function
+  --> $DIR/bad-custom.rs:52:31
+   |
+LL |     extern "custom" fn negate(a: i64) -> i64;
+   |                               ^^^^^^     ^^^
+   |
+   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+help: remove the parameters and return type
+   |
+LL -     extern "custom" fn negate(a: i64) -> i64;
+LL +     extern "custom" fn negate();
+   |
+
+error: functions with the `"custom"` ABI must be unsafe
+  --> $DIR/bad-custom.rs:58:5
+   |
+LL |     extern "custom" fn negate(a: i64) -> i64 {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add the `unsafe` keyword to this definition
+   |
+LL |     unsafe extern "custom" fn negate(a: i64) -> i64 {
+   |     ++++++
+
+error: invalid signature for `extern "custom"` function
+  --> $DIR/bad-custom.rs:58:31
+   |
+LL |     extern "custom" fn negate(a: i64) -> i64 {
+   |                               ^^^^^^     ^^^
+   |
+   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+help: remove the parameters and return type
+   |
+LL -     extern "custom" fn negate(a: i64) -> i64 {
+LL +     extern "custom" fn negate() {
+   |
+
+error: invalid signature for `extern "custom"` function
+  --> $DIR/bad-custom.rs:67:18
+   |
+LL |     fn increment(a: i64) -> i64;
+   |                  ^^^^^^     ^^^
+   |
+   = note: functions with the `"custom"` ABI cannot have any parameters or return type
+help: remove the parameters and return type
+   |
+LL -     fn increment(a: i64) -> i64;
+LL +     fn increment();
+   |
+
+error: foreign functions with the `"custom"` ABI cannot be safe
+  --> $DIR/bad-custom.rs:70:5
+   |
+LL |     safe fn extern_cannot_be_safe();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `safe` keyword from this definition
+   |
+LL -     safe fn extern_cannot_be_safe();
+LL +     fn extern_cannot_be_safe();
+   |
+
+error: functions with the `"custom"` ABI cannot be `async`
+  --> $DIR/bad-custom.rs:97:1
+   |
+LL | async unsafe extern "custom" fn no_async_fn() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the `async` keyword from this definiton
+   |
+LL - async unsafe extern "custom" fn no_async_fn() {
+LL + unsafe extern "custom" fn no_async_fn() {
+   |
+
+error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
+  --> $DIR/bad-custom.rs:97:1
+   |
+LL | async unsafe extern "custom" fn no_async_fn() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: convert this to an `#[unsafe(naked)]` function
+   |
+LL + #[unsafe(naked)]
+LL | async unsafe extern "custom" fn no_async_fn() {
+   |
+
+error[E0277]: expected a `Fn()` closure, found `unsafe extern "custom" fn()`
+  --> $DIR/bad-custom.rs:102:64
+   |
+LL | fn no_promotion_to_fn_trait(f: unsafe extern "custom" fn()) -> impl Fn()  {
+   |                                                                ^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }`
+LL |
+LL |     f
+   |     - return type was inferred to be `unsafe extern "custom" fn()` here
+   |
+   = help: the trait `Fn()` is not implemented for `unsafe extern "custom" fn()`
+   = note: unsafe function cannot be called generically without an unsafe block
+   = note: wrap the `unsafe extern "custom" fn()` in a closure with no arguments: `|| { /* code */ }`
+
+error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
+  --> $DIR/bad-custom.rs:25:1
+   |
+LL | unsafe extern "custom" fn double(a: i64) -> i64 {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: convert this to an `#[unsafe(naked)]` function
+   |
+LL + #[unsafe(naked)]
+LL | unsafe extern "custom" fn double(a: i64) -> i64 {
+   |
+
+error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
+  --> $DIR/bad-custom.rs:34:5
+   |
+LL |     unsafe extern "custom" fn is_even(self) -> bool {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: convert this to an `#[unsafe(naked)]` function
+   |
+LL +     #[unsafe(naked)]
+LL |     unsafe extern "custom" fn is_even(self) -> bool {
+   |
+
+error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
+  --> $DIR/bad-custom.rs:42:5
+   |
+LL |     unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: convert this to an `#[unsafe(naked)]` function
+   |
+LL +     #[unsafe(naked)]
+LL |     unsafe extern "custom" fn bitwise_not(a: i64) -> i64 {
+   |
+
+error: items with the `"custom"` ABI can only be declared externally or defined via naked functions
+  --> $DIR/bad-custom.rs:58:5
+   |
+LL |     extern "custom" fn negate(a: i64) -> i64 {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: convert this to an `#[unsafe(naked)]` function
+   |
+LL +     #[unsafe(naked)]
+LL |     extern "custom" fn negate(a: i64) -> i64 {
+   |
+
+error: functions with the `"custom"` ABI cannot be called
+  --> $DIR/bad-custom.rs:75:14
+   |
+LL |     unsafe { f(x) }
+   |              ^^^^
+
+error: functions with the `"custom"` ABI cannot be called
+  --> $DIR/bad-custom.rs:80:14
+   |
+LL |     unsafe { f(x) }
+   |              ^^^^
+
+error: functions with the `"custom"` ABI cannot be called
+  --> $DIR/bad-custom.rs:87:14
+   |
+LL |     unsafe { f(x) }
+   |              ^^^^
+
+error: functions with the `"custom"` ABI cannot be called
+  --> $DIR/bad-custom.rs:109:20
+   |
+LL |         assert_eq!(double(21), 42);
+   |                    ^^^^^^^^^^
+
+error: functions with the `"custom"` ABI cannot be called
+  --> $DIR/bad-custom.rs:112:29
+   |
+LL |         assert_eq!(unsafe { increment(41) }, 42);
+   |                             ^^^^^^^^^^^^^
+
+error: functions with the `"custom"` ABI cannot be called
+  --> $DIR/bad-custom.rs:115:17
+   |
+LL |         assert!(Thing(41).is_even());
+   |                 ^^^^^^^^^^^^^^^^^^^
+
+error: functions with the `"custom"` ABI cannot be called
+  --> $DIR/bad-custom.rs:118:20
+   |
+LL |         assert_eq!(Thing::bitwise_not(42), !42);
+   |                    ^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0015]: inline assembly is not allowed in constant functions
+  --> $DIR/bad-custom.rs:93:5
+   |
+LL |     std::arch::naked_asm!("")
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 28 previous errors
+
+Some errors have detailed explanations: E0015, E0277.
+For more information about an error, try `rustc --explain E0015`.
diff --git a/tests/ui/abi/custom.rs b/tests/ui/abi/custom.rs
new file mode 100644
index 00000000000..0f6ff77f580
--- /dev/null
+++ b/tests/ui/abi/custom.rs
@@ -0,0 +1,88 @@
+// Test that `extern "custom"` functions can be called from assembly, and defined using a naked
+// function, and `global_asm!` with an `extern "custom"` block.
+//
+//@ run-pass
+//@ only-x86_64
+#![feature(abi_custom)]
+
+use std::arch::{asm, global_asm, naked_asm};
+
+#[unsafe(naked)]
+unsafe extern "custom" fn double() {
+    naked_asm!("add rax, rax", "ret");
+}
+
+global_asm!(
+    // work around macOS prefixing symbols with _
+    "    .globl  {0}",
+    "{0}:",
+    "    add     rax, 1",
+    "    ret",
+    sym increment,
+);
+
+unsafe extern "custom" {
+    fn increment();
+}
+
+#[repr(transparent)]
+struct Thing(u64);
+
+impl Thing {
+    #[unsafe(naked)]
+    unsafe extern "custom" fn is_even() {
+        naked_asm!("test al, 1", "sete al", "ret");
+    }
+}
+
+trait BitwiseNot {
+    #[unsafe(naked)]
+    unsafe extern "custom" fn bitwise_not() {
+        naked_asm!("not rax", "ret");
+    }
+}
+
+impl BitwiseNot for Thing {}
+
+#[unsafe(naked)]
+unsafe extern "C" fn const_generic<const N: u64>() {
+    naked_asm!(
+        "mov rax, {}",
+        "ret",
+        const N,
+    );
+}
+
+pub fn main() {
+    let mut x: u64 = 21;
+    unsafe { asm!("call {}", sym double, inout("rax") x) };
+    assert_eq!(x, 42);
+
+    let mut x: u64 = 41;
+    unsafe { asm!("call {}", sym increment, inout("rax") x) };
+    assert_eq!(x, 42);
+
+    let mut x: u8;
+    unsafe { asm!("call {}", sym Thing::is_even, inout("al") 42u8 => x) };
+    assert!(x != 0);
+
+    let mut x: u64 = 42;
+    unsafe { asm!("call {}", sym Thing::bitwise_not, inout("rax") x) };
+    assert_eq!(x, !42);
+
+    // Create and call in `asm!` an `extern "custom"` function pointer.
+    fn caller(f: unsafe extern "custom" fn(), mut x: u64) -> u64 {
+        unsafe { asm!("call {}", in(reg) f, inout("rax") x) };
+        x
+    }
+
+    assert_eq!(caller(double, 2), 4);
+
+    let x: u64;
+    unsafe { asm!("call {}", sym const_generic::<42>, out("rax") x) };
+    assert_eq!(x, 42);
+
+    let x: u64;
+    unsafe { asm!("call {}", sym const_generic::<84>, out("rax") x) };
+    assert_eq!(x, 84);
+}
diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr
index ec81ba2e3d8..f29a41d6a8e 100644
--- a/tests/ui/check-cfg/target_feature.stderr
+++ b/tests/ui/check-cfg/target_feature.stderr
@@ -212,6 +212,9 @@ LL |     cfg!(target_feature = "_UNEXPECTED_VALUE");
 `relax`
 `relaxed-simd`
 `reserve-x18`
+`retpoline-external-thunk`
+`retpoline-indirect-branches`
+`retpoline-indirect-calls`
 `rtm`
 `sb`
 `scq`
diff --git a/tests/ui/feature-gates/feature-gate-abi-custom.rs b/tests/ui/feature-gates/feature-gate-abi-custom.rs
new file mode 100644
index 00000000000..3ddce974dd7
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-abi-custom.rs
@@ -0,0 +1,51 @@
+//@ add-core-stubs
+//@ needs-asm-support
+#![no_core]
+#![feature(no_core, lang_items)]
+#![crate_type = "rlib"]
+
+extern crate minicore;
+use minicore::*;
+
+#[unsafe(naked)]
+unsafe extern "custom" fn f7() {
+    //~^ ERROR "custom" ABI is experimental
+    naked_asm!("")
+}
+trait Tr {
+    extern "custom" fn m7();
+    //~^ ERROR "custom" ABI is experimental
+    //~| ERROR functions with the `"custom"` ABI must be unsafe
+    #[unsafe(naked)]
+    extern "custom" fn dm7() {
+        //~^ ERROR "custom" ABI is experimental
+        //~| ERROR functions with the `"custom"` ABI must be unsafe
+        naked_asm!("")
+    }
+}
+
+struct S;
+
+// Methods in trait impl
+impl Tr for S {
+    #[unsafe(naked)]
+    extern "custom" fn m7() {
+        //~^ ERROR "custom" ABI is experimental
+        //~| ERROR functions with the `"custom"` ABI must be unsafe
+        naked_asm!("")
+    }
+}
+
+// Methods in inherent impl
+impl S {
+    #[unsafe(naked)]
+    extern "custom" fn im7() {
+        //~^ ERROR "custom" ABI is experimental
+        //~| ERROR functions with the `"custom"` ABI must be unsafe
+        naked_asm!("")
+    }
+}
+
+type A7 = extern "custom" fn(); //~ ERROR "custom" ABI is experimental
+
+extern "custom" {} //~ ERROR "custom" ABI is experimental
diff --git a/tests/ui/feature-gates/feature-gate-abi-custom.stderr b/tests/ui/feature-gates/feature-gate-abi-custom.stderr
new file mode 100644
index 00000000000..e6dce0126d6
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-abi-custom.stderr
@@ -0,0 +1,117 @@
+error: functions with the `"custom"` ABI must be unsafe
+  --> $DIR/feature-gate-abi-custom.rs:16:5
+   |
+LL |     extern "custom" fn m7();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add the `unsafe` keyword to this definition
+   |
+LL |     unsafe extern "custom" fn m7();
+   |     ++++++
+
+error: functions with the `"custom"` ABI must be unsafe
+  --> $DIR/feature-gate-abi-custom.rs:20:5
+   |
+LL |     extern "custom" fn dm7() {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add the `unsafe` keyword to this definition
+   |
+LL |     unsafe extern "custom" fn dm7() {
+   |     ++++++
+
+error: functions with the `"custom"` ABI must be unsafe
+  --> $DIR/feature-gate-abi-custom.rs:32:5
+   |
+LL |     extern "custom" fn m7() {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add the `unsafe` keyword to this definition
+   |
+LL |     unsafe extern "custom" fn m7() {
+   |     ++++++
+
+error: functions with the `"custom"` ABI must be unsafe
+  --> $DIR/feature-gate-abi-custom.rs:42:5
+   |
+LL |     extern "custom" fn im7() {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add the `unsafe` keyword to this definition
+   |
+LL |     unsafe extern "custom" fn im7() {
+   |     ++++++
+
+error[E0658]: the extern "custom" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi-custom.rs:11:15
+   |
+LL | unsafe extern "custom" fn f7() {
+   |               ^^^^^^^^
+   |
+   = note: see issue #140829 <https://github.com/rust-lang/rust/issues/140829> for more information
+   = help: add `#![feature(abi_custom)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "custom" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi-custom.rs:16:12
+   |
+LL |     extern "custom" fn m7();
+   |            ^^^^^^^^
+   |
+   = note: see issue #140829 <https://github.com/rust-lang/rust/issues/140829> for more information
+   = help: add `#![feature(abi_custom)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "custom" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi-custom.rs:20:12
+   |
+LL |     extern "custom" fn dm7() {
+   |            ^^^^^^^^
+   |
+   = note: see issue #140829 <https://github.com/rust-lang/rust/issues/140829> for more information
+   = help: add `#![feature(abi_custom)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "custom" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi-custom.rs:32:12
+   |
+LL |     extern "custom" fn m7() {
+   |            ^^^^^^^^
+   |
+   = note: see issue #140829 <https://github.com/rust-lang/rust/issues/140829> for more information
+   = help: add `#![feature(abi_custom)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "custom" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi-custom.rs:42:12
+   |
+LL |     extern "custom" fn im7() {
+   |            ^^^^^^^^
+   |
+   = note: see issue #140829 <https://github.com/rust-lang/rust/issues/140829> for more information
+   = help: add `#![feature(abi_custom)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "custom" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi-custom.rs:49:18
+   |
+LL | type A7 = extern "custom" fn();
+   |                  ^^^^^^^^
+   |
+   = note: see issue #140829 <https://github.com/rust-lang/rust/issues/140829> for more information
+   = help: add `#![feature(abi_custom)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: the extern "custom" ABI is experimental and subject to change
+  --> $DIR/feature-gate-abi-custom.rs:51:8
+   |
+LL | extern "custom" {}
+   |        ^^^^^^^^
+   |
+   = note: see issue #140829 <https://github.com/rust-lang/rust/issues/140829> for more information
+   = help: add `#![feature(abi_custom)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 11 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/lint/expansion-time.rs b/tests/ui/lint/expansion-time.rs
index 5ffb0c7881e..2c59bf00065 100644
--- a/tests/ui/lint/expansion-time.rs
+++ b/tests/ui/lint/expansion-time.rs
@@ -5,10 +5,6 @@ macro_rules! foo {
     ( $($i:ident)* ) => { $($i)+ }; //~ WARN meta-variable repeats with different Kleene operator
 }
 
-#[warn(missing_fragment_specifier)]
-macro_rules! m { ($i) => {} } //~ WARN missing fragment specifier
-                              //~| WARN this was previously accepted
-
 #[deprecated = "reason"]
 macro_rules! deprecated {
     () => {}
diff --git a/tests/ui/lint/expansion-time.stderr b/tests/ui/lint/expansion-time.stderr
index f24d1b68a8d..b1154d1a54c 100644
--- a/tests/ui/lint/expansion-time.stderr
+++ b/tests/ui/lint/expansion-time.stderr
@@ -12,20 +12,6 @@ note: the lint level is defined here
 LL | #[warn(meta_variable_misuse)]
    |        ^^^^^^^^^^^^^^^^^^^^
 
-warning: missing fragment specifier
-  --> $DIR/expansion-time.rs:9:19
-   |
-LL | macro_rules! m { ($i) => {} }
-   |                   ^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-note: the lint level is defined here
-  --> $DIR/expansion-time.rs:8:8
-   |
-LL | #[warn(missing_fragment_specifier)]
-   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^
-
 warning: include macro expected single expression in source
   --> $DIR/expansion-time-include.rs:4:1
    |
@@ -33,25 +19,10 @@ LL | 2
    | ^
    |
 note: the lint level is defined here
-  --> $DIR/expansion-time.rs:22:8
+  --> $DIR/expansion-time.rs:18:8
    |
 LL | #[warn(incomplete_include)]
    |        ^^^^^^^^^^^^^^^^^^
 
-warning: 3 warnings emitted
-
-Future incompatibility report: Future breakage diagnostic:
-warning: missing fragment specifier
-  --> $DIR/expansion-time.rs:9:19
-   |
-LL | macro_rules! m { ($i) => {} }
-   |                   ^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-note: the lint level is defined here
-  --> $DIR/expansion-time.rs:8:8
-   |
-LL | #[warn(missing_fragment_specifier)]
-   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^
+warning: 2 warnings emitted
 
diff --git a/tests/ui/lint/future-incompatible-lint-group.rs b/tests/ui/lint/future-incompatible-lint-group.rs
index d1adcf21cdc..22a7ccb463b 100644
--- a/tests/ui/lint/future-incompatible-lint-group.rs
+++ b/tests/ui/lint/future-incompatible-lint-group.rs
@@ -4,14 +4,23 @@
 // lints for changes that are not tied to an edition
 #![deny(future_incompatible)]
 
-// Error since this is a `future_incompatible` lint
-macro_rules! m {
-    ($i) => {};
-    //~^ ERROR missing fragment specifier
+enum E { V }
+
+trait Tr1 {
+    type V;
+    fn foo() -> Self::V;
+}
+
+impl Tr1 for E {
+    type V = u8;
+
+    // Error since this is a `future_incompatible` lint
+    fn foo() -> Self::V { 0 }
+    //~^ ERROR ambiguous associated item
     //~| WARN this was previously accepted
 }
 
-trait Tr {
+trait Tr2 {
     // Warn only since this is not a `future_incompatible` lint
     fn f(u8) {}
     //~^ WARN anonymous parameters are deprecated
diff --git a/tests/ui/lint/future-incompatible-lint-group.stderr b/tests/ui/lint/future-incompatible-lint-group.stderr
index 264911b46d4..87b9ebec08b 100644
--- a/tests/ui/lint/future-incompatible-lint-group.stderr
+++ b/tests/ui/lint/future-incompatible-lint-group.stderr
@@ -1,20 +1,5 @@
-error: missing fragment specifier
-  --> $DIR/future-incompatible-lint-group.rs:9:6
-   |
-LL |     ($i) => {};
-   |      ^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-note: the lint level is defined here
-  --> $DIR/future-incompatible-lint-group.rs:5:9
-   |
-LL | #![deny(future_incompatible)]
-   |         ^^^^^^^^^^^^^^^^^^^
-   = note: `#[deny(missing_fragment_specifier)]` implied by `#[deny(future_incompatible)]`
-
 warning: anonymous parameters are deprecated and will be removed in the next edition
-  --> $DIR/future-incompatible-lint-group.rs:16:10
+  --> $DIR/future-incompatible-lint-group.rs:25:10
    |
 LL |     fn f(u8) {}
    |          ^^ help: try naming the parameter or explicitly ignoring it: `_: u8`
@@ -23,21 +8,30 @@ LL |     fn f(u8) {}
    = note: for more information, see issue #41686 <https://github.com/rust-lang/rust/issues/41686>
    = note: `#[warn(anonymous_parameters)]` on by default
 
-error: aborting due to 1 previous error; 1 warning emitted
-
-Future incompatibility report: Future breakage diagnostic:
-error: missing fragment specifier
-  --> $DIR/future-incompatible-lint-group.rs:9:6
+error: ambiguous associated item
+  --> $DIR/future-incompatible-lint-group.rs:18:17
    |
-LL |     ($i) => {};
-   |      ^^
+LL |     fn foo() -> Self::V { 0 }
+   |                 ^^^^^^^ help: use fully-qualified syntax: `<E as Tr1>::V`
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
+   = note: for more information, see issue #57644 <https://github.com/rust-lang/rust/issues/57644>
+note: `V` could refer to the variant defined here
+  --> $DIR/future-incompatible-lint-group.rs:7:10
+   |
+LL | enum E { V }
+   |          ^
+note: `V` could also refer to the associated type defined here
+  --> $DIR/future-incompatible-lint-group.rs:10:5
+   |
+LL |     type V;
+   |     ^^^^^^
 note: the lint level is defined here
   --> $DIR/future-incompatible-lint-group.rs:5:9
    |
 LL | #![deny(future_incompatible)]
    |         ^^^^^^^^^^^^^^^^^^^
-   = note: `#[deny(missing_fragment_specifier)]` implied by `#[deny(future_incompatible)]`
+   = note: `#[deny(ambiguous_associated_items)]` implied by `#[deny(future_incompatible)]`
+
+error: aborting due to 1 previous error; 1 warning emitted
 
diff --git a/tests/ui/macros/issue-39404.rs b/tests/ui/macros/issue-39404.rs
index 2229f2c3900..ceeb6231bc8 100644
--- a/tests/ui/macros/issue-39404.rs
+++ b/tests/ui/macros/issue-39404.rs
@@ -1,7 +1,7 @@
 #![allow(unused)]
 
-macro_rules! m { ($i) => {} }
-//~^ ERROR missing fragment specifier
-//~| WARN previously accepted
+macro_rules! m {
+    ($i) => {}; //~ ERROR missing fragment specifier
+}
 
 fn main() {}
diff --git a/tests/ui/macros/issue-39404.stderr b/tests/ui/macros/issue-39404.stderr
index 176c8e9f073..62d0bc1018c 100644
--- a/tests/ui/macros/issue-39404.stderr
+++ b/tests/ui/macros/issue-39404.stderr
@@ -1,23 +1,15 @@
 error: missing fragment specifier
-  --> $DIR/issue-39404.rs:3:19
+  --> $DIR/issue-39404.rs:4:6
    |
-LL | macro_rules! m { ($i) => {} }
-   |                   ^^
+LL |     ($i) => {};
+   |      ^^
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-   = note: `#[deny(missing_fragment_specifier)]` on by default
+   = note: fragment specifiers must be provided
+   = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility
+help: try adding a specifier here
+   |
+LL |     ($i:spec) => {};
+   |        +++++
 
 error: aborting due to 1 previous error
 
-Future incompatibility report: Future breakage diagnostic:
-error: missing fragment specifier
-  --> $DIR/issue-39404.rs:3:19
-   |
-LL | macro_rules! m { ($i) => {} }
-   |                   ^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-   = note: `#[deny(missing_fragment_specifier)]` on by default
-
diff --git a/tests/ui/macros/macro-match-nonterminal.rs b/tests/ui/macros/macro-match-nonterminal.rs
index 5d9eb55fee0..fa2af945a1f 100644
--- a/tests/ui/macros/macro-match-nonterminal.rs
+++ b/tests/ui/macros/macro-match-nonterminal.rs
@@ -3,8 +3,6 @@ macro_rules! test {
         //~^ ERROR missing fragment
         //~| ERROR missing fragment
         //~| ERROR missing fragment
-        //~| WARN this was previously accepted
-        //~| WARN this was previously accepted
         ()
     };
 }
diff --git a/tests/ui/macros/macro-match-nonterminal.stderr b/tests/ui/macros/macro-match-nonterminal.stderr
index f221f92c3cd..8196d795c4c 100644
--- a/tests/ui/macros/macro-match-nonterminal.stderr
+++ b/tests/ui/macros/macro-match-nonterminal.stderr
@@ -3,16 +3,13 @@ error: missing fragment specifier
    |
 LL |     ($a, $b) => {
    |      ^^
-
-error: missing fragment specifier
-  --> $DIR/macro-match-nonterminal.rs:2:6
    |
-LL |     ($a, $b) => {
-   |      ^^
+   = note: fragment specifiers must be provided
+   = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility
+help: try adding a specifier here
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-   = note: `#[deny(missing_fragment_specifier)]` on by default
+LL |     ($a:spec, $b) => {
+   |        +++++
 
 error: missing fragment specifier
   --> $DIR/macro-match-nonterminal.rs:2:10
@@ -20,30 +17,18 @@ error: missing fragment specifier
 LL |     ($a, $b) => {
    |          ^^
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-
-error: aborting due to 3 previous errors
+   = note: fragment specifiers must be provided
+   = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility
+help: try adding a specifier here
+   |
+LL |     ($a, $b:spec) => {
+   |            +++++
 
-Future incompatibility report: Future breakage diagnostic:
 error: missing fragment specifier
   --> $DIR/macro-match-nonterminal.rs:2:6
    |
 LL |     ($a, $b) => {
    |      ^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-   = note: `#[deny(missing_fragment_specifier)]` on by default
 
-Future breakage diagnostic:
-error: missing fragment specifier
-  --> $DIR/macro-match-nonterminal.rs:2:10
-   |
-LL |     ($a, $b) => {
-   |          ^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-   = note: `#[deny(missing_fragment_specifier)]` on by default
+error: aborting due to 3 previous errors
 
diff --git a/tests/ui/macros/macro-missing-fragment-deduplication.rs b/tests/ui/macros/macro-missing-fragment-deduplication.rs
index b77c51e055b..481f08fa111 100644
--- a/tests/ui/macros/macro-missing-fragment-deduplication.rs
+++ b/tests/ui/macros/macro-missing-fragment-deduplication.rs
@@ -1,10 +1,8 @@
 //@ compile-flags: -Zdeduplicate-diagnostics=yes
 
 macro_rules! m {
-    ($name) => {}
-    //~^ ERROR missing fragment
-    //~| ERROR missing fragment
-    //~| WARN this was previously accepted
+    ($name) => {}; //~ ERROR missing fragment
+                   //~| ERROR missing fragment
 }
 
 fn main() {
diff --git a/tests/ui/macros/macro-missing-fragment-deduplication.stderr b/tests/ui/macros/macro-missing-fragment-deduplication.stderr
index c46712f70fd..820f7eb3cf7 100644
--- a/tests/ui/macros/macro-missing-fragment-deduplication.stderr
+++ b/tests/ui/macros/macro-missing-fragment-deduplication.stderr
@@ -1,29 +1,21 @@
 error: missing fragment specifier
   --> $DIR/macro-missing-fragment-deduplication.rs:4:6
    |
-LL |     ($name) => {}
+LL |     ($name) => {};
    |      ^^^^^
-
-error: missing fragment specifier
-  --> $DIR/macro-missing-fragment-deduplication.rs:4:6
    |
-LL |     ($name) => {}
-   |      ^^^^^
+   = note: fragment specifiers must be provided
+   = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility
+help: try adding a specifier here
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-   = note: `#[deny(missing_fragment_specifier)]` on by default
+LL |     ($name:spec) => {};
+   |           +++++
 
-error: aborting due to 2 previous errors
-
-Future incompatibility report: Future breakage diagnostic:
 error: missing fragment specifier
   --> $DIR/macro-missing-fragment-deduplication.rs:4:6
    |
-LL |     ($name) => {}
+LL |     ($name) => {};
    |      ^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-   = note: `#[deny(missing_fragment_specifier)]` on by default
+
+error: aborting due to 2 previous errors
 
diff --git a/tests/ui/macros/macro-missing-fragment.e2015.stderr b/tests/ui/macros/macro-missing-fragment.e2015.stderr
deleted file mode 100644
index 3d32f203d4a..00000000000
--- a/tests/ui/macros/macro-missing-fragment.e2015.stderr
+++ /dev/null
@@ -1,85 +0,0 @@
-error: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:8:20
-   |
-LL |     ( $( any_token $field_rust_type )* ) => {};
-   |                    ^^^^^^^^^^^^^^^^
-
-warning: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:8:20
-   |
-LL |     ( $( any_token $field_rust_type )* ) => {};
-   |                    ^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-note: the lint level is defined here
-  --> $DIR/macro-missing-fragment.rs:5:9
-   |
-LL | #![warn(missing_fragment_specifier)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-warning: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:18:7
-   |
-LL |     ( $name ) => {};
-   |       ^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-
-warning: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:25:7
-   |
-LL |     ( $name ) => {};
-   |       ^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-
-error: aborting due to 1 previous error; 3 warnings emitted
-
-Future incompatibility report: Future breakage diagnostic:
-warning: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:8:20
-   |
-LL |     ( $( any_token $field_rust_type )* ) => {};
-   |                    ^^^^^^^^^^^^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-note: the lint level is defined here
-  --> $DIR/macro-missing-fragment.rs:5:9
-   |
-LL | #![warn(missing_fragment_specifier)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Future breakage diagnostic:
-warning: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:18:7
-   |
-LL |     ( $name ) => {};
-   |       ^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-note: the lint level is defined here
-  --> $DIR/macro-missing-fragment.rs:5:9
-   |
-LL | #![warn(missing_fragment_specifier)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Future breakage diagnostic:
-warning: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:25:7
-   |
-LL |     ( $name ) => {};
-   |       ^^^^^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-note: the lint level is defined here
-  --> $DIR/macro-missing-fragment.rs:5:9
-   |
-LL | #![warn(missing_fragment_specifier)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
-
diff --git a/tests/ui/macros/macro-missing-fragment.rs b/tests/ui/macros/macro-missing-fragment.rs
index 42387e8dbbf..533aa147bcb 100644
--- a/tests/ui/macros/macro-missing-fragment.rs
+++ b/tests/ui/macros/macro-missing-fragment.rs
@@ -1,31 +1,17 @@
-//@ revisions: e2015 e2024
-//@[e2015] edition:2015
-//@[e2024] edition:2024
-
-#![warn(missing_fragment_specifier)]
+//! Ensure that macros produce an error if fragment specifiers are missing.
 
 macro_rules! used_arm {
-    ( $( any_token $field_rust_type )* ) => {};
-    //[e2015]~^ ERROR missing fragment
-    //[e2015]~| WARN missing fragment
-    //[e2015]~| WARN this was previously accepted
-    //[e2024]~^^^^ ERROR missing fragment
-    //[e2024]~| ERROR missing fragment
+    ( $( any_token $field_rust_type )* ) => {}; //~ ERROR missing fragment
+                                                //~| ERROR missing fragment
 }
 
 macro_rules! used_macro_unused_arm {
     () => {};
-    ( $name ) => {};
-    //[e2015]~^ WARN missing fragment
-    //[e2015]~| WARN this was previously accepted
-    //[e2024]~^^^ ERROR missing fragment
+    ( $name ) => {}; //~ ERROR missing fragment
 }
 
 macro_rules! unused_macro {
-    ( $name ) => {};
-    //[e2015]~^ WARN missing fragment
-    //[e2015]~| WARN this was previously accepted
-    //[e2024]~^^^ ERROR missing fragment
+    ( $name ) => {}; //~ ERROR missing fragment
 }
 
 fn main() {
diff --git a/tests/ui/macros/macro-missing-fragment.e2024.stderr b/tests/ui/macros/macro-missing-fragment.stderr
index a9195063a5b..4a99d7d949c 100644
--- a/tests/ui/macros/macro-missing-fragment.e2024.stderr
+++ b/tests/ui/macros/macro-missing-fragment.stderr
@@ -1,10 +1,10 @@
 error: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:8:20
+  --> $DIR/macro-missing-fragment.rs:4:20
    |
 LL |     ( $( any_token $field_rust_type )* ) => {};
    |                    ^^^^^^^^^^^^^^^^
    |
-   = note: fragment specifiers must be specified in the 2024 edition
+   = note: fragment specifiers must be provided
    = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility
 help: try adding a specifier here
    |
@@ -12,12 +12,12 @@ LL |     ( $( any_token $field_rust_type:spec )* ) => {};
    |                                    +++++
 
 error: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:18:7
+  --> $DIR/macro-missing-fragment.rs:10:7
    |
 LL |     ( $name ) => {};
    |       ^^^^^
    |
-   = note: fragment specifiers must be specified in the 2024 edition
+   = note: fragment specifiers must be provided
    = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility
 help: try adding a specifier here
    |
@@ -25,12 +25,12 @@ LL |     ( $name:spec ) => {};
    |            +++++
 
 error: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:25:7
+  --> $DIR/macro-missing-fragment.rs:14:7
    |
 LL |     ( $name ) => {};
    |       ^^^^^
    |
-   = note: fragment specifiers must be specified in the 2024 edition
+   = note: fragment specifiers must be provided
    = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility
 help: try adding a specifier here
    |
@@ -38,7 +38,7 @@ LL |     ( $name:spec ) => {};
    |            +++++
 
 error: missing fragment specifier
-  --> $DIR/macro-missing-fragment.rs:8:20
+  --> $DIR/macro-missing-fragment.rs:4:20
    |
 LL |     ( $( any_token $field_rust_type )* ) => {};
    |                    ^^^^^^^^^^^^^^^^
diff --git a/tests/ui/parser/macro/issue-33569.rs b/tests/ui/parser/macro/issue-33569.rs
index 069d181e962..e0a5352ab06 100644
--- a/tests/ui/parser/macro/issue-33569.rs
+++ b/tests/ui/parser/macro/issue-33569.rs
@@ -2,7 +2,6 @@ macro_rules! foo {
     { $+ } => { //~ ERROR expected identifier, found `+`
                 //~^ ERROR missing fragment specifier
                 //~| ERROR missing fragment specifier
-                //~| WARN this was previously accepted
         $(x)(y) //~ ERROR expected one of: `*`, `+`, or `?`
     }
 }
diff --git a/tests/ui/parser/macro/issue-33569.stderr b/tests/ui/parser/macro/issue-33569.stderr
index d1b6abfeeeb..0d53c04c1c9 100644
--- a/tests/ui/parser/macro/issue-33569.stderr
+++ b/tests/ui/parser/macro/issue-33569.stderr
@@ -5,7 +5,7 @@ LL |     { $+ } => {
    |        ^
 
 error: expected one of: `*`, `+`, or `?`
-  --> $DIR/issue-33569.rs:6:13
+  --> $DIR/issue-33569.rs:5:13
    |
 LL |         $(x)(y)
    |             ^^^
@@ -15,27 +15,19 @@ error: missing fragment specifier
    |
 LL |     { $+ } => {
    |        ^
-
-error: missing fragment specifier
-  --> $DIR/issue-33569.rs:2:8
    |
-LL |     { $+ } => {
-   |        ^
+   = note: fragment specifiers must be provided
+   = help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, `literal`, `path`, `meta`, `tt`, `item` and `vis`, along with `expr_2021` and `pat_param` for edition compatibility
+help: try adding a specifier here
    |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-   = note: `#[deny(missing_fragment_specifier)]` on by default
+LL |     { $+:spec } => {
+   |         +++++
 
-error: aborting due to 4 previous errors
-
-Future incompatibility report: Future breakage diagnostic:
 error: missing fragment specifier
   --> $DIR/issue-33569.rs:2:8
    |
 LL |     { $+ } => {
    |        ^
-   |
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-   = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>
-   = note: `#[deny(missing_fragment_specifier)]` on by default
+
+error: aborting due to 4 previous errors
 
diff --git a/tests/ui/print-calling-conventions.stdout b/tests/ui/print-calling-conventions.stdout
index feee8cc3aa9..7b5ae495660 100644
--- a/tests/ui/print-calling-conventions.stdout
+++ b/tests/ui/print-calling-conventions.stdout
@@ -9,6 +9,7 @@ avr-interrupt
 avr-non-blocking-interrupt
 cdecl
 cdecl-unwind
+custom
 efiapi
 fastcall
 fastcall-unwind
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature.stderr b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature.stderr
new file mode 100644
index 00000000000..e2b6078b7a8
--- /dev/null
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature.stderr
@@ -0,0 +1,7 @@
+warning: target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`: use `x86-retpoline` target modifier flag instead
+   |
+   = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr
new file mode 100644
index 00000000000..2a0f5f01aef
--- /dev/null
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature1.stderr
@@ -0,0 +1,7 @@
+warning: target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`: use `retpoline-external-thunk` target modifier flag instead
+   |
+   = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr
new file mode 100644
index 00000000000..f7b6cb16447
--- /dev/null
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature2.stderr
@@ -0,0 +1,7 @@
+warning: target feature `retpoline-indirect-branches` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead
+   |
+   = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr
new file mode 100644
index 00000000000..4f2cd1d1a52
--- /dev/null
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.by_feature3.stderr
@@ -0,0 +1,7 @@
+warning: target feature `retpoline-indirect-calls` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead
+   |
+   = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+   = note: for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
+
+warning: 1 warning emitted
+
diff --git a/tests/ui/target-feature/retpoline-target-feature-flag.rs b/tests/ui/target-feature/retpoline-target-feature-flag.rs
new file mode 100644
index 00000000000..3e614a4236c
--- /dev/null
+++ b/tests/ui/target-feature/retpoline-target-feature-flag.rs
@@ -0,0 +1,23 @@
+//@ revisions: by_flag by_feature1 by_feature2 by_feature3
+//@ compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=lib
+//@ needs-llvm-components: x86
+//@ [by_flag]compile-flags: -Zretpoline
+
+//@ [by_feature1]compile-flags: -Ctarget-feature=+retpoline-external-thunk
+//@ [by_feature2]compile-flags: -Ctarget-feature=+retpoline-indirect-branches
+//@ [by_feature3]compile-flags: -Ctarget-feature=+retpoline-indirect-calls
+//@ [by_flag]build-pass
+// For now this is just a warning.
+//@ [by_feature1]build-pass
+//@ [by_feature2]build-pass
+//@ [by_feature3]build-pass
+#![feature(no_core, lang_items)]
+#![no_std]
+#![no_core]
+
+#[lang = "sized"]
+pub trait Sized {}
+
+//[by_feature1]~? WARN target feature `retpoline-external-thunk` cannot be enabled with `-Ctarget-feature`: use `retpoline-external-thunk` target modifier flag instead
+//[by_feature2]~? WARN target feature `retpoline-indirect-branches` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead
+//[by_feature3]~? WARN target feature `retpoline-indirect-calls` cannot be enabled with `-Ctarget-feature`: use `retpoline` target modifier flag instead