about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/context.rs6
-rw-r--r--compiler/rustc_const_eval/src/util/type_name.rs24
-rw-r--r--compiler/rustc_expand/src/base.rs48
-rw-r--r--compiler/rustc_expand/src/expand.rs27
-rw-r--r--compiler/rustc_expand/src/mbe/diagnostics.rs12
-rw-r--r--compiler/rustc_expand/src/mbe/macro_check.rs18
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs26
-rw-r--r--compiler/rustc_hir/Cargo.toml1
-rw-r--r--compiler/rustc_hir/src/def.rs59
-rw-r--r--compiler/rustc_hir/src/hir.rs7
-rw-r--r--compiler/rustc_lint/src/context.rs16
-rw-r--r--compiler/rustc_lint/src/non_local_def.rs4
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs2
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs2
-rw-r--r--compiler/rustc_metadata/src/rmeta/table.rs26
-rw-r--r--compiler/rustc_middle/src/mir/pretty.rs4
-rw-r--r--compiler/rustc_middle/src/ty/print/mod.rs82
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs109
-rw-r--r--compiler/rustc_resolve/messages.ftl3
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs3
-rw-r--r--compiler/rustc_resolve/src/def_collector.rs4
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs50
-rw-r--r--compiler/rustc_resolve/src/errors.rs6
-rw-r--r--compiler/rustc_resolve/src/ident.rs16
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs9
-rw-r--r--compiler/rustc_resolve/src/lib.rs10
-rw-r--r--compiler/rustc_resolve/src/macros.rs57
-rw-r--r--compiler/rustc_symbol_mangling/src/legacy.rs33
-rw-r--r--compiler/rustc_symbol_mangling/src/v0.rs28
-rw-r--r--compiler/rustc_target/src/spec/mod.rs1
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs43
-rw-r--r--compiler/rustc_target/src/spec/targets/avr_none.rs2
-rw-r--r--compiler/rustc_target/src/target_features.rs4
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs43
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs2
-rw-r--r--compiler/rustc_type_ir/src/search_graph/mod.rs141
-rw-r--r--compiler/rustc_type_ir/src/search_graph/stack.rs22
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs124
-rw-r--r--src/bootstrap/src/core/build_steps/dist.rs14
-rw-r--r--src/bootstrap/src/core/build_steps/gcc.rs27
-rw-r--r--src/bootstrap/src/core/sanity.rs1
-rwxr-xr-xsrc/ci/docker/scripts/freebsd-toolchain.sh4
-rw-r--r--src/doc/rustc/src/SUMMARY.md1
-rw-r--r--src/doc/rustc/src/platform-support.md1
-rw-r--r--src/doc/rustc/src/platform-support/aarch64_be-unknown-none-softfloat.md74
-rw-r--r--src/doc/rustdoc/src/write-documentation/documentation-tests.md14
-rw-r--r--src/librustdoc/clean/inline.rs53
-rw-r--r--src/librustdoc/clean/mod.rs14
-rw-r--r--src/librustdoc/formats/item_type.rs9
-rw-r--r--src/librustdoc/html/static/js/search.js4
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs28
-rw-r--r--src/librustdoc/visit_ast.rs7
m---------src/llvm-project0
-rw-r--r--src/tools/clippy/clippy_lints/src/item_name_repetitions.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs3
-rw-r--r--tests/assembly-llvm/asm/aarch64-outline-atomics.rs4
-rw-r--r--tests/assembly-llvm/targets/targets-elf.rs3
-rw-r--r--tests/ui/check-cfg/target_feature.stderr1
-rw-r--r--tests/ui/macros/macro-rules-attr-error.rs39
-rw-r--r--tests/ui/macros/macro-rules-attr-error.stderr55
-rw-r--r--tests/ui/proc-macro/macro-namespace-reserved-2.stderr8
63 files changed, 944 insertions, 504 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 4eb246995b1..8a878faecbc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3889,6 +3889,7 @@ dependencies = [
 name = "rustc_hir"
 version = "0.0.0"
 dependencies = [
+ "bitflags",
  "odht",
  "rustc_abi",
  "rustc_arena",
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 235573c96e4..2cd2c41928e 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -436,14 +436,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let body = Box::new(self.lower_delim_args(body));
                 let def_id = self.local_def_id(id);
                 let def_kind = self.tcx.def_kind(def_id);
-                let DefKind::Macro(macro_kind) = def_kind else {
+                let DefKind::Macro(macro_kinds) = def_kind else {
                     unreachable!(
                         "expected DefKind::Macro for macro item, found {}",
                         def_kind.descr(def_id.to_def_id())
                     );
                 };
                 let macro_def = self.arena.alloc(ast::MacroDef { body, macro_rules: *macro_rules });
-                hir::ItemKind::Macro(ident, macro_def, macro_kind)
+                hir::ItemKind::Macro(ident, macro_def, macro_kinds)
             }
             ItemKind::Delegation(box delegation) => {
                 let delegation_results = self.lower_delegation(delegation, id, false);
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index ee77774c688..27ae729a531 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -213,6 +213,12 @@ pub(crate) unsafe fn create_module<'ll>(
             target_data_layout = target_data_layout.replace("p8:128:128:128:48", "p8:128:128")
         }
     }
+    if llvm_version < (22, 0, 0) {
+        if sess.target.arch == "avr" {
+            // LLVM 22.0 updated the default layout on avr: https://github.com/llvm/llvm-project/pull/153010
+            target_data_layout = target_data_layout.replace("n8:16", "n8")
+        }
+    }
 
     // Ensure the data-layout values hardcoded remain the defaults.
     {
diff --git a/compiler/rustc_const_eval/src/util/type_name.rs b/compiler/rustc_const_eval/src/util/type_name.rs
index 2dc746754f8..9d6674873b1 100644
--- a/compiler/rustc_const_eval/src/util/type_name.rs
+++ b/compiler/rustc_const_eval/src/util/type_name.rs
@@ -7,12 +7,12 @@ use rustc_middle::bug;
 use rustc_middle::ty::print::{PrettyPrinter, PrintError, Printer};
 use rustc_middle::ty::{self, GenericArg, GenericArgKind, Ty, TyCtxt};
 
-struct AbsolutePathPrinter<'tcx> {
+struct TypeNamePrinter<'tcx> {
     tcx: TyCtxt<'tcx>,
     path: String,
 }
 
-impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
+impl<'tcx> Printer<'tcx> for TypeNamePrinter<'tcx> {
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
@@ -75,26 +75,26 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
         self.pretty_print_dyn_existential(predicates)
     }
 
-    fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
+    fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
         self.path.push_str(self.tcx.crate_name(cnum).as_str());
         Ok(())
     }
 
-    fn path_qualified(
+    fn print_path_with_qualified(
         &mut self,
         self_ty: Ty<'tcx>,
         trait_ref: Option<ty::TraitRef<'tcx>>,
     ) -> Result<(), PrintError> {
-        self.pretty_path_qualified(self_ty, trait_ref)
+        self.pretty_print_path_with_qualified(self_ty, trait_ref)
     }
 
-    fn path_append_impl(
+    fn print_path_with_impl(
         &mut self,
         print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
         self_ty: Ty<'tcx>,
         trait_ref: Option<ty::TraitRef<'tcx>>,
     ) -> Result<(), PrintError> {
-        self.pretty_path_append_impl(
+        self.pretty_print_path_with_impl(
             |cx| {
                 print_prefix(cx)?;
 
@@ -107,7 +107,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
         )
     }
 
-    fn path_append(
+    fn print_path_with_simple(
         &mut self,
         print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
         disambiguated_data: &DisambiguatedDefPathData,
@@ -119,7 +119,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
         Ok(())
     }
 
-    fn path_generic_args(
+    fn print_path_with_generic_args(
         &mut self,
         print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
         args: &[GenericArg<'tcx>],
@@ -135,7 +135,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
     }
 }
 
-impl<'tcx> PrettyPrinter<'tcx> for AbsolutePathPrinter<'tcx> {
+impl<'tcx> PrettyPrinter<'tcx> for TypeNamePrinter<'tcx> {
     fn should_print_region(&self, _region: ty::Region<'_>) -> bool {
         false
     }
@@ -159,7 +159,7 @@ impl<'tcx> PrettyPrinter<'tcx> for AbsolutePathPrinter<'tcx> {
     }
 }
 
-impl Write for AbsolutePathPrinter<'_> {
+impl Write for TypeNamePrinter<'_> {
     fn write_str(&mut self, s: &str) -> std::fmt::Result {
         self.path.push_str(s);
         Ok(())
@@ -167,7 +167,7 @@ impl Write for AbsolutePathPrinter<'_> {
 }
 
 pub fn type_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> String {
-    let mut p = AbsolutePathPrinter { tcx, path: String::new() };
+    let mut p = TypeNamePrinter { tcx, path: String::new() };
     p.print_type(ty).unwrap();
     p.path
 }
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 7da3bf27eb5..f2c15071532 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -17,6 +17,7 @@ use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult};
 use rustc_feature::Features;
 use rustc_hir as hir;
 use rustc_hir::attrs::{AttributeKind, CfgEntry, Deprecation};
+use rustc_hir::def::MacroKinds;
 use rustc_hir::{Stability, find_attr};
 use rustc_lint_defs::{BufferedEarlyLint, RegisteredTools};
 use rustc_parse::MACRO_ARGUMENTS;
@@ -718,6 +719,9 @@ impl MacResult for DummyResult {
 /// A syntax extension kind.
 #[derive(Clone)]
 pub enum SyntaxExtensionKind {
+    /// A `macro_rules!` macro that can work as any `MacroKind`
+    MacroRules(Arc<crate::MacroRulesMacroExpander>),
+
     /// A token-based function-like macro.
     Bang(
         /// An expander with signature TokenStream -> TokenStream.
@@ -772,9 +776,39 @@ pub enum SyntaxExtensionKind {
     ),
 
     /// A glob delegation.
+    ///
+    /// This is for delegated function implementations, and has nothing to do with glob imports.
     GlobDelegation(Arc<dyn GlobDelegationExpander + sync::DynSync + sync::DynSend>),
 }
 
+impl SyntaxExtensionKind {
+    /// Returns `Some(expander)` for a macro usable as a `LegacyBang`; otherwise returns `None`
+    ///
+    /// This includes a `MacroRules` with function-like rules.
+    pub fn as_legacy_bang(&self) -> Option<&(dyn TTMacroExpander + sync::DynSync + sync::DynSend)> {
+        match self {
+            SyntaxExtensionKind::LegacyBang(exp) => Some(exp.as_ref()),
+            SyntaxExtensionKind::MacroRules(exp) if exp.kinds().contains(MacroKinds::BANG) => {
+                Some(exp.as_ref())
+            }
+            _ => None,
+        }
+    }
+
+    /// Returns `Some(expander)` for a macro usable as an `Attr`; otherwise returns `None`
+    ///
+    /// This includes a `MacroRules` with `attr` rules.
+    pub fn as_attr(&self) -> Option<&(dyn AttrProcMacro + sync::DynSync + sync::DynSend)> {
+        match self {
+            SyntaxExtensionKind::Attr(exp) => Some(exp.as_ref()),
+            SyntaxExtensionKind::MacroRules(exp) if exp.kinds().contains(MacroKinds::ATTR) => {
+                Some(exp.as_ref())
+            }
+            _ => None,
+        }
+    }
+}
+
 /// A struct representing a macro definition in "lowered" form ready for expansion.
 pub struct SyntaxExtension {
     /// A syntax extension kind.
@@ -804,18 +838,19 @@ pub struct SyntaxExtension {
 }
 
 impl SyntaxExtension {
-    /// Returns which kind of macro calls this syntax extension.
-    pub fn macro_kind(&self) -> MacroKind {
+    /// Returns which kinds of macro call this syntax extension.
+    pub fn macro_kinds(&self) -> MacroKinds {
         match self.kind {
             SyntaxExtensionKind::Bang(..)
             | SyntaxExtensionKind::LegacyBang(..)
-            | SyntaxExtensionKind::GlobDelegation(..) => MacroKind::Bang,
+            | SyntaxExtensionKind::GlobDelegation(..) => MacroKinds::BANG,
             SyntaxExtensionKind::Attr(..)
             | SyntaxExtensionKind::LegacyAttr(..)
-            | SyntaxExtensionKind::NonMacroAttr => MacroKind::Attr,
+            | SyntaxExtensionKind::NonMacroAttr => MacroKinds::ATTR,
             SyntaxExtensionKind::Derive(..) | SyntaxExtensionKind::LegacyDerive(..) => {
-                MacroKind::Derive
+                MacroKinds::DERIVE
             }
+            SyntaxExtensionKind::MacroRules(ref m) => m.kinds(),
         }
     }
 
@@ -1024,11 +1059,12 @@ impl SyntaxExtension {
         parent: LocalExpnId,
         call_site: Span,
         descr: Symbol,
+        kind: MacroKind,
         macro_def_id: Option<DefId>,
         parent_module: Option<DefId>,
     ) -> ExpnData {
         ExpnData::new(
-            ExpnKind::Macro(self.macro_kind(), descr),
+            ExpnKind::Macro(kind, descr),
             parent.to_expn_id(),
             call_site,
             self.span,
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 00533285fb4..670f5c91bb9 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -736,8 +736,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
 
         let (fragment_kind, span) = (invoc.fragment_kind, invoc.span());
         ExpandResult::Ready(match invoc.kind {
-            InvocationKind::Bang { mac, span } => match ext {
-                SyntaxExtensionKind::Bang(expander) => {
+            InvocationKind::Bang { mac, span } => {
+                if let SyntaxExtensionKind::Bang(expander) = ext {
                     match expander.expand(self.cx, span, mac.args.tokens.clone()) {
                         Ok(tok_result) => {
                             let fragment =
@@ -755,8 +755,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                         }
                         Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)),
                     }
-                }
-                SyntaxExtensionKind::LegacyBang(expander) => {
+                } else if let Some(expander) = ext.as_legacy_bang() {
                     let tok_result = match expander.expand(self.cx, span, mac.args.tokens.clone()) {
                         ExpandResult::Ready(tok_result) => tok_result,
                         ExpandResult::Retry(_) => {
@@ -776,11 +775,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                         let guar = self.error_wrong_fragment_kind(fragment_kind, &mac, span);
                         fragment_kind.dummy(span, guar)
                     }
+                } else {
+                    unreachable!();
                 }
-                _ => unreachable!(),
-            },
-            InvocationKind::Attr { attr, pos, mut item, derives } => match ext {
-                SyntaxExtensionKind::Attr(expander) => {
+            }
+            InvocationKind::Attr { attr, pos, mut item, derives } => {
+                if let Some(expander) = ext.as_attr() {
                     self.gate_proc_macro_input(&item);
                     self.gate_proc_macro_attr_item(span, &item);
                     let tokens = match &item {
@@ -835,8 +835,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                         }
                         Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)),
                     }
-                }
-                SyntaxExtensionKind::LegacyAttr(expander) => {
+                } else if let SyntaxExtensionKind::LegacyAttr(expander) = ext {
                     match validate_attr::parse_meta(&self.cx.sess.psess, &attr) {
                         Ok(meta) => {
                             let item_clone = macro_stats.then(|| item.clone());
@@ -878,15 +877,15 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                             fragment_kind.expect_from_annotatables(iter::once(item))
                         }
                     }
-                }
-                SyntaxExtensionKind::NonMacroAttr => {
+                } else if let SyntaxExtensionKind::NonMacroAttr = ext {
                     // `-Zmacro-stats` ignores these because they don't do any real expansion.
                     self.cx.expanded_inert_attrs.mark(&attr);
                     item.visit_attrs(|attrs| attrs.insert(pos, attr));
                     fragment_kind.expect_from_annotatables(iter::once(item))
+                } else {
+                    unreachable!();
                 }
-                _ => unreachable!(),
-            },
+            }
             InvocationKind::Derive { path, item, is_const } => match ext {
                 SyntaxExtensionKind::Derive(expander)
                 | SyntaxExtensionKind::LegacyDerive(expander) => {
diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs
index 5b9d56ee2bc..80433b7be91 100644
--- a/compiler/rustc_expand/src/mbe/diagnostics.rs
+++ b/compiler/rustc_expand/src/mbe/diagnostics.rs
@@ -58,18 +58,6 @@ pub(super) fn failed_to_match_macro(
 
     let Some(BestFailure { token, msg: label, remaining_matcher, .. }) = tracker.best_failure
     else {
-        // FIXME: we should report this at macro resolution time, as we do for
-        // `resolve_macro_cannot_use_as_attr`. We can do that once we track multiple macro kinds for a
-        // Def.
-        if attr_args.is_none() && !rules.iter().any(|rule| matches!(rule, MacroRule::Func { .. })) {
-            let msg = format!("macro has no rules for function-like invocation `{name}!`");
-            let mut err = psess.dcx().struct_span_err(sp, msg);
-            if !def_head_span.is_dummy() {
-                let msg = "this macro has no rules for function-like invocation";
-                err.span_label(def_head_span, msg);
-            }
-            return (sp, err.emit());
-        }
         return (sp, psess.dcx().span_delayed_bug(sp, "failed to match a macro"));
     };
 
diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs
index 25987a50366..faeae1f494e 100644
--- a/compiler/rustc_expand/src/mbe/macro_check.rs
+++ b/compiler/rustc_expand/src/mbe/macro_check.rs
@@ -357,10 +357,10 @@ enum NestedMacroState {
     /// The token `macro_rules` was processed.
     MacroRules,
     /// The tokens `macro_rules!` were processed.
-    MacroRulesNot,
+    MacroRulesBang,
     /// The tokens `macro_rules!` followed by a name were processed. The name may be either directly
     /// an identifier or a meta-variable (that hopefully would be instantiated by an identifier).
-    MacroRulesNotName,
+    MacroRulesBangName,
     /// The keyword `macro` was processed.
     Macro,
     /// The keyword `macro` followed by a name was processed.
@@ -408,24 +408,24 @@ fn check_nested_occurrences(
                 NestedMacroState::MacroRules,
                 &TokenTree::Token(Token { kind: TokenKind::Bang, .. }),
             ) => {
-                state = NestedMacroState::MacroRulesNot;
+                state = NestedMacroState::MacroRulesBang;
             }
             (
-                NestedMacroState::MacroRulesNot,
+                NestedMacroState::MacroRulesBang,
                 &TokenTree::Token(Token { kind: TokenKind::Ident(..), .. }),
             ) => {
-                state = NestedMacroState::MacroRulesNotName;
+                state = NestedMacroState::MacroRulesBangName;
             }
-            (NestedMacroState::MacroRulesNot, &TokenTree::MetaVar(..)) => {
-                state = NestedMacroState::MacroRulesNotName;
+            (NestedMacroState::MacroRulesBang, &TokenTree::MetaVar(..)) => {
+                state = NestedMacroState::MacroRulesBangName;
                 // We check that the meta-variable is correctly used.
                 check_occurrences(psess, node_id, tt, macros, binders, ops, guar);
             }
-            (NestedMacroState::MacroRulesNotName, TokenTree::Delimited(.., del))
+            (NestedMacroState::MacroRulesBangName, TokenTree::Delimited(.., del))
             | (NestedMacroState::MacroName, TokenTree::Delimited(.., del))
                 if del.delim == Delimiter::Brace =>
             {
-                let macro_rules = state == NestedMacroState::MacroRulesNotName;
+                let macro_rules = state == NestedMacroState::MacroRulesBangName;
                 state = NestedMacroState::Empty;
                 let rest =
                     check_nested_macro(psess, node_id, macro_rules, &del.tts, &nested_macros, guar);
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index 08b0efb74a0..334f57f9d62 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -15,6 +15,7 @@ use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan};
 use rustc_feature::Features;
 use rustc_hir as hir;
 use rustc_hir::attrs::AttributeKind;
+use rustc_hir::def::MacroKinds;
 use rustc_hir::find_attr;
 use rustc_lint_defs::BuiltinLintDiag;
 use rustc_lint_defs::builtin::{
@@ -144,6 +145,7 @@ pub struct MacroRulesMacroExpander {
     name: Ident,
     span: Span,
     transparency: Transparency,
+    kinds: MacroKinds,
     rules: Vec<MacroRule>,
 }
 
@@ -158,6 +160,10 @@ impl MacroRulesMacroExpander {
         };
         if has_compile_error_macro(rhs) { None } else { Some((&self.name, span)) }
     }
+
+    pub fn kinds(&self) -> MacroKinds {
+        self.kinds
+    }
 }
 
 impl TTMacroExpander for MacroRulesMacroExpander {
@@ -540,13 +546,13 @@ pub fn compile_declarative_macro(
     span: Span,
     node_id: NodeId,
     edition: Edition,
-) -> (SyntaxExtension, Option<Arc<SyntaxExtension>>, usize) {
+) -> (SyntaxExtension, usize) {
     let mk_syn_ext = |kind| {
         let is_local = is_defined_in_current_crate(node_id);
         SyntaxExtension::new(sess, kind, span, Vec::new(), edition, ident.name, attrs, is_local)
     };
-    let mk_bang_ext = |expander| mk_syn_ext(SyntaxExtensionKind::LegacyBang(expander));
-    let dummy_syn_ext = |guar| (mk_bang_ext(Arc::new(DummyExpander(guar))), None, 0);
+    let dummy_syn_ext =
+        |guar| (mk_syn_ext(SyntaxExtensionKind::LegacyBang(Arc::new(DummyExpander(guar)))), 0);
 
     let macro_rules = macro_def.macro_rules;
     let exp_sep = if macro_rules { exp!(Semi) } else { exp!(Comma) };
@@ -559,12 +565,12 @@ pub fn compile_declarative_macro(
     let mut guar = None;
     let mut check_emission = |ret: Result<(), ErrorGuaranteed>| guar = guar.or(ret.err());
 
-    let mut has_attr_rules = false;
+    let mut kinds = MacroKinds::empty();
     let mut rules = Vec::new();
 
     while p.token != token::Eof {
         let args = if p.eat_keyword_noexpect(sym::attr) {
-            has_attr_rules = true;
+            kinds |= MacroKinds::ATTR;
             if !features.macro_attr() {
                 feature_err(sess, sym::macro_attr, span, "`macro_rules!` attributes are unstable")
                     .emit();
@@ -581,6 +587,7 @@ pub fn compile_declarative_macro(
             }
             Some(args)
         } else {
+            kinds |= MacroKinds::BANG;
             None
         };
         let lhs_tt = p.parse_token_tree();
@@ -627,6 +634,7 @@ pub fn compile_declarative_macro(
         let guar = sess.dcx().span_err(span, "macros must contain at least one rule");
         return dummy_syn_ext(guar);
     }
+    assert!(!kinds.is_empty());
 
     let transparency = find_attr!(attrs, AttributeKind::MacroTransparency(x) => *x)
         .unwrap_or(Transparency::fallback(macro_rules));
@@ -640,12 +648,8 @@ pub fn compile_declarative_macro(
     // Return the number of rules for unused rule linting, if this is a local macro.
     let nrules = if is_defined_in_current_crate(node_id) { rules.len() } else { 0 };
 
-    let exp = Arc::new(MacroRulesMacroExpander { name: ident, span, node_id, transparency, rules });
-    let opt_attr_ext = has_attr_rules.then(|| {
-        let exp = Arc::clone(&exp);
-        Arc::new(mk_syn_ext(SyntaxExtensionKind::Attr(exp)))
-    });
-    (mk_bang_ext(exp), opt_attr_ext, nrules)
+    let exp = MacroRulesMacroExpander { name: ident, kinds, span, node_id, transparency, rules };
+    (mk_syn_ext(SyntaxExtensionKind::MacroRules(Arc::new(exp))), nrules)
 }
 
 fn check_no_eof(sess: &Session, p: &Parser<'_>, msg: &'static str) -> Option<ErrorGuaranteed> {
diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml
index 539d2e6f0b1..71496b7ec32 100644
--- a/compiler/rustc_hir/Cargo.toml
+++ b/compiler/rustc_hir/Cargo.toml
@@ -5,6 +5,7 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
+bitflags = "2.9.1"
 odht = { version = "0.3.1", features = ["nightly"] }
 rustc_abi = { path = "../rustc_abi" }
 rustc_arena = { path = "../rustc_arena" }
diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs
index 339d4e2eab7..79319e24266 100644
--- a/compiler/rustc_hir/src/def.rs
+++ b/compiler/rustc_hir/src/def.rs
@@ -31,6 +31,53 @@ pub enum CtorKind {
     Const,
 }
 
+/// A set of macro kinds, for macros that can have more than one kind
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable, Hash, Debug)]
+#[derive(HashStable_Generic)]
+pub struct MacroKinds(u8);
+bitflags::bitflags! {
+    impl MacroKinds: u8 {
+        const BANG = 1 << 0;
+        const ATTR = 1 << 1;
+        const DERIVE = 1 << 2;
+    }
+}
+
+impl From<MacroKind> for MacroKinds {
+    fn from(kind: MacroKind) -> Self {
+        match kind {
+            MacroKind::Bang => Self::BANG,
+            MacroKind::Attr => Self::ATTR,
+            MacroKind::Derive => Self::DERIVE,
+        }
+    }
+}
+
+impl MacroKinds {
+    /// Convert the MacroKinds to a static string.
+    ///
+    /// This hardcodes all the possibilities, in order to return a static string.
+    pub fn descr(self) -> &'static str {
+        match self {
+            // FIXME: change this to "function-like macro" and fix all tests
+            Self::BANG => "macro",
+            Self::ATTR => "attribute macro",
+            Self::DERIVE => "derive macro",
+            _ if self == (Self::ATTR | Self::BANG) => "attribute/function macro",
+            _ if self == (Self::DERIVE | Self::BANG) => "derive/function macro",
+            _ if self == (Self::ATTR | Self::DERIVE) => "attribute/derive macro",
+            _ if self.is_all() => "attribute/derive/function macro",
+            _ if self.is_empty() => "useless macro",
+            _ => unreachable!(),
+        }
+    }
+
+    /// Return an indefinite article (a/an) for use with `descr()`
+    pub fn article(self) -> &'static str {
+        if self.contains(Self::ATTR) { "an" } else { "a" }
+    }
+}
+
 /// An attribute that is not a macro; e.g., `#[inline]` or `#[rustfmt::skip]`.
 #[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug, HashStable_Generic)]
 pub enum NonMacroAttrKind {
@@ -101,7 +148,7 @@ pub enum DefKind {
     AssocConst,
 
     // Macro namespace
-    Macro(MacroKind),
+    Macro(MacroKinds),
 
     // Not namespaced (or they are, but we don't treat them so)
     ExternCrate,
@@ -177,7 +224,7 @@ impl DefKind {
             DefKind::AssocConst => "associated constant",
             DefKind::TyParam => "type parameter",
             DefKind::ConstParam => "const parameter",
-            DefKind::Macro(macro_kind) => macro_kind.descr(),
+            DefKind::Macro(kinds) => kinds.descr(),
             DefKind::LifetimeParam => "lifetime parameter",
             DefKind::Use => "import",
             DefKind::ForeignMod => "foreign module",
@@ -208,7 +255,7 @@ impl DefKind {
             | DefKind::Use
             | DefKind::InlineConst
             | DefKind::ExternCrate => "an",
-            DefKind::Macro(macro_kind) => macro_kind.article(),
+            DefKind::Macro(kinds) => kinds.article(),
             _ => "a",
         }
     }
@@ -845,10 +892,10 @@ impl<Id> Res<Id> {
         )
     }
 
-    pub fn macro_kind(self) -> Option<MacroKind> {
+    pub fn macro_kinds(self) -> Option<MacroKinds> {
         match self {
-            Res::Def(DefKind::Macro(kind), _) => Some(kind),
-            Res::NonMacroAttr(..) => Some(MacroKind::Attr),
+            Res::Def(DefKind::Macro(kinds), _) => Some(kinds),
+            Res::NonMacroAttr(..) => Some(MacroKinds::ATTR),
             _ => None,
         }
     }
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index b27c223527e..e8feb721984 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -20,7 +20,6 @@ use rustc_data_structures::tagged_ptr::TaggedRef;
 use rustc_index::IndexVec;
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 use rustc_span::def_id::LocalDefId;
-use rustc_span::hygiene::MacroKind;
 use rustc_span::source_map::Spanned;
 use rustc_span::{BytePos, DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
 use rustc_target::asm::InlineAsmRegOrRegClass;
@@ -30,7 +29,7 @@ use tracing::debug;
 
 use crate::LangItem;
 use crate::attrs::AttributeKind;
-use crate::def::{CtorKind, DefKind, PerNS, Res};
+use crate::def::{CtorKind, DefKind, MacroKinds, PerNS, Res};
 use crate::def_id::{DefId, LocalDefIdMap};
 pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId};
 use crate::intravisit::{FnKind, VisitorExt};
@@ -4157,7 +4156,7 @@ impl<'hir> Item<'hir> {
         expect_fn, (Ident, &FnSig<'hir>, &'hir Generics<'hir>, BodyId),
             ItemKind::Fn { ident, sig, generics, body, .. }, (*ident, sig, generics, *body);
 
-        expect_macro, (Ident, &ast::MacroDef, MacroKind),
+        expect_macro, (Ident, &ast::MacroDef, MacroKinds),
             ItemKind::Macro(ident, def, mk), (*ident, def, *mk);
 
         expect_mod, (Ident, &'hir Mod<'hir>), ItemKind::Mod(ident, m), (*ident, m);
@@ -4336,7 +4335,7 @@ pub enum ItemKind<'hir> {
         has_body: bool,
     },
     /// A MBE macro definition (`macro_rules!` or `macro`).
-    Macro(Ident, &'hir ast::MacroDef, MacroKind),
+    Macro(Ident, &'hir ast::MacroDef, MacroKinds),
     /// A module.
     Mod(Ident, &'hir Mod<'hir>),
     /// An external module, e.g. `extern { .. }`.
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 11181d10af5..d9163d94710 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -745,12 +745,12 @@ impl<'tcx> LateContext<'tcx> {
     /// }
     /// ```
     pub fn get_def_path(&self, def_id: DefId) -> Vec<Symbol> {
-        struct AbsolutePathPrinter<'tcx> {
+        struct LintPathPrinter<'tcx> {
             tcx: TyCtxt<'tcx>,
             path: Vec<Symbol>,
         }
 
-        impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
+        impl<'tcx> Printer<'tcx> for LintPathPrinter<'tcx> {
             fn tcx(&self) -> TyCtxt<'tcx> {
                 self.tcx
             }
@@ -774,12 +774,12 @@ impl<'tcx> LateContext<'tcx> {
                 unreachable!(); // because `path_generic_args` ignores the `GenericArgs`
             }
 
-            fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
+            fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
                 self.path = vec![self.tcx.crate_name(cnum)];
                 Ok(())
             }
 
-            fn path_qualified(
+            fn print_path_with_qualified(
                 &mut self,
                 self_ty: Ty<'tcx>,
                 trait_ref: Option<ty::TraitRef<'tcx>>,
@@ -800,7 +800,7 @@ impl<'tcx> LateContext<'tcx> {
                 })
             }
 
-            fn path_append_impl(
+            fn print_path_with_impl(
                 &mut self,
                 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
                 self_ty: Ty<'tcx>,
@@ -825,7 +825,7 @@ impl<'tcx> LateContext<'tcx> {
                 Ok(())
             }
 
-            fn path_append(
+            fn print_path_with_simple(
                 &mut self,
                 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
                 disambiguated_data: &DisambiguatedDefPathData,
@@ -844,7 +844,7 @@ impl<'tcx> LateContext<'tcx> {
                 Ok(())
             }
 
-            fn path_generic_args(
+            fn print_path_with_generic_args(
                 &mut self,
                 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
                 _args: &[GenericArg<'tcx>],
@@ -853,7 +853,7 @@ impl<'tcx> LateContext<'tcx> {
             }
         }
 
-        let mut p = AbsolutePathPrinter { tcx: self.tcx, path: vec![] };
+        let mut p = LintPathPrinter { tcx: self.tcx, path: vec![] };
         p.print_def_path(def_id, &[]).unwrap();
         p.path
     }
diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs
index 2dd3425e66c..dca22b986ff 100644
--- a/compiler/rustc_lint/src/non_local_def.rs
+++ b/compiler/rustc_lint/src/non_local_def.rs
@@ -5,7 +5,7 @@ use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::{declare_lint, impl_lint_pass};
 use rustc_span::def_id::{DefId, LOCAL_CRATE};
-use rustc_span::{ExpnKind, MacroKind, Span, kw, sym};
+use rustc_span::{ExpnKind, Span, kw, sym};
 
 use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag};
 use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
@@ -240,7 +240,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
                     },
                 )
             }
-            ItemKind::Macro(_, _macro, MacroKind::Bang)
+            ItemKind::Macro(_, _macro, _kinds)
                 if cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export) =>
             {
                 cx.emit_span_lint(
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index d42c8b947a4..a7e7e9985f4 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1981,7 +1981,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 def_key.disambiguated_data.data = DefPathData::MacroNs(name);
 
                 let def_id = id.to_def_id();
-                self.tables.def_kind.set_some(def_id.index, DefKind::Macro(macro_kind));
+                self.tables.def_kind.set_some(def_id.index, DefKind::Macro(macro_kind.into()));
                 self.tables.proc_macro.set_some(def_id.index, macro_kind);
                 self.encode_attrs(id);
                 record!(self.tables.def_keys[def_id] <- def_key);
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 99174e4ad2f..1f7d142d330 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -11,7 +11,7 @@ use rustc_abi::{FieldIdx, ReprOptions, VariantIdx};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::svh::Svh;
 use rustc_hir::attrs::StrippedCfgItem;
-use rustc_hir::def::{CtorKind, DefKind, DocLinkResMap};
+use rustc_hir::def::{CtorKind, DefKind, DocLinkResMap, MacroKinds};
 use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIndex, DefPathHash, StableCrateId};
 use rustc_hir::definitions::DefKey;
 use rustc_hir::lang_items::LangItem;
diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs
index 0671aa20399..2cb07a28a8a 100644
--- a/compiler/rustc_metadata/src/rmeta/table.rs
+++ b/compiler/rustc_metadata/src/rmeta/table.rs
@@ -81,7 +81,7 @@ impl FixedSizeEncoding for u64 {
 }
 
 macro_rules! fixed_size_enum {
-    ($ty:ty { $(($($pat:tt)*))* }) => {
+    ($ty:ty { $(($($pat:tt)*))* } $( unreachable { $(($($upat:tt)*))+ } )?) => {
         impl FixedSizeEncoding for Option<$ty> {
             type ByteArray = [u8;1];
 
@@ -103,12 +103,24 @@ macro_rules! fixed_size_enum {
                 b[0] = match self {
                     None => unreachable!(),
                     $(Some($($pat)*) => 1 + ${index()},)*
+                    $(Some($($($upat)*)|+) => unreachable!(),)?
                 }
             }
         }
     }
 }
 
+// Workaround; need const traits to construct bitflags in a const
+macro_rules! const_macro_kinds {
+    ($($name:ident),+$(,)?) => (MacroKinds::from_bits_truncate($(MacroKinds::$name.bits())|+))
+}
+const MACRO_KINDS_ATTR_BANG: MacroKinds = const_macro_kinds!(ATTR, BANG);
+const MACRO_KINDS_DERIVE_BANG: MacroKinds = const_macro_kinds!(DERIVE, BANG);
+const MACRO_KINDS_DERIVE_ATTR: MacroKinds = const_macro_kinds!(DERIVE, ATTR);
+const MACRO_KINDS_DERIVE_ATTR_BANG: MacroKinds = const_macro_kinds!(DERIVE, ATTR, BANG);
+// Ensure that we get a compilation error if MacroKinds gets extended without updating metadata.
+const _: () = assert!(MACRO_KINDS_DERIVE_ATTR_BANG.is_all());
+
 fixed_size_enum! {
     DefKind {
         ( Mod                                      )
@@ -151,10 +163,16 @@ fixed_size_enum! {
         ( Ctor(CtorOf::Struct, CtorKind::Const)    )
         ( Ctor(CtorOf::Variant, CtorKind::Fn)      )
         ( Ctor(CtorOf::Variant, CtorKind::Const)   )
-        ( Macro(MacroKind::Bang)                   )
-        ( Macro(MacroKind::Attr)                   )
-        ( Macro(MacroKind::Derive)                 )
+        ( Macro(MacroKinds::BANG)                  )
+        ( Macro(MacroKinds::ATTR)                  )
+        ( Macro(MacroKinds::DERIVE)                )
+        ( Macro(MACRO_KINDS_ATTR_BANG)             )
+        ( Macro(MACRO_KINDS_DERIVE_ATTR)           )
+        ( Macro(MACRO_KINDS_DERIVE_BANG)           )
+        ( Macro(MACRO_KINDS_DERIVE_ATTR_BANG)      )
         ( SyntheticCoroutineBody                   )
+    } unreachable {
+        ( Macro(_)                                 )
     }
 }
 
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 84abcf550d2..d4d925d2057 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -1932,7 +1932,7 @@ fn pretty_print_const_value_tcx<'tcx>(
                         let args = tcx.lift(args).unwrap();
                         let mut p = FmtPrinter::new(tcx, Namespace::ValueNS);
                         p.print_alloc_ids = true;
-                        p.print_value_path(variant_def.def_id, args)?;
+                        p.pretty_print_value_path(variant_def.def_id, args)?;
                         fmt.write_str(&p.into_buffer())?;
 
                         match variant_def.ctor_kind() {
@@ -1974,7 +1974,7 @@ fn pretty_print_const_value_tcx<'tcx>(
         (ConstValue::ZeroSized, ty::FnDef(d, s)) => {
             let mut p = FmtPrinter::new(tcx, Namespace::ValueNS);
             p.print_alloc_ids = true;
-            p.print_value_path(*d, s)?;
+            p.pretty_print_value_path(*d, s)?;
             fmt.write_str(&p.into_buffer())?;
             return Ok(());
         }
diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs
index efa017074db..e6feafea122 100644
--- a/compiler/rustc_middle/src/ty/print/mod.rs
+++ b/compiler/rustc_middle/src/ty/print/mod.rs
@@ -19,18 +19,16 @@ pub trait Print<'tcx, P> {
     fn print(&self, p: &mut P) -> Result<(), PrintError>;
 }
 
-/// Interface for outputting user-facing "type-system entities"
-/// (paths, types, lifetimes, constants, etc.) as a side-effect
-/// (e.g. formatting, like `PrettyPrinter` implementors do) or by
-/// constructing some alternative representation (e.g. an AST),
-/// which the associated types allow passing through the methods.
-///
-/// For pretty-printing/formatting in particular, see `PrettyPrinter`.
-//
-// FIXME(eddyb) find a better name; this is more general than "printing".
+/// A trait that "prints" user-facing type system entities: paths, types, lifetimes, constants,
+/// etc. "Printing" here means building up a representation of the entity's path, usually as a
+/// `String` (e.g. "std::io::Read") or a `Vec<Symbol>` (e.g. `[sym::std, sym::io, sym::Read]`). The
+/// representation is built up by appending one or more pieces. The specific details included in
+/// the built-up representation depend on the purpose of the printer. The more advanced printers
+/// also rely on the `PrettyPrinter` sub-trait.
 pub trait Printer<'tcx>: Sized {
     fn tcx<'a>(&'a self) -> TyCtxt<'tcx>;
 
+    /// Appends a representation of an entity with a normal path, e.g. "std::io::Read".
     fn print_def_path(
         &mut self,
         def_id: DefId,
@@ -39,6 +37,7 @@ pub trait Printer<'tcx>: Sized {
         self.default_print_def_path(def_id, args)
     }
 
+    /// Like `print_def_path`, but for `DefPathData::Impl`.
     fn print_impl_path(
         &mut self,
         impl_def_id: DefId,
@@ -64,48 +63,67 @@ pub trait Printer<'tcx>: Sized {
         self.default_print_impl_path(impl_def_id, self_ty, impl_trait_ref)
     }
 
+    /// Appends a representation of a region.
     fn print_region(&mut self, region: ty::Region<'tcx>) -> Result<(), PrintError>;
 
+    /// Appends a representation of a type.
     fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError>;
 
+    /// Appends a representation of a list of `PolyExistentialPredicate`s.
     fn print_dyn_existential(
         &mut self,
         predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
     ) -> Result<(), PrintError>;
 
+    /// Appends a representation of a const.
     fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError>;
 
-    fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError>;
+    /// Appends a representation of a crate name, e.g. `std`, or even ``.
+    fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError>;
 
-    fn path_qualified(
+    /// Appends a representation of a (full or partial) simple path, in two parts. `print_prefix`,
+    /// when called, appends the representation of the leading segments. The rest of the method
+    /// appends the representation of the final segment, the details of which are in
+    /// `disambiguated_data`.
+    ///
+    /// E.g. `std::io` + `Read` -> `std::io::Read`.
+    fn print_path_with_simple(
         &mut self,
-        self_ty: Ty<'tcx>,
-        trait_ref: Option<ty::TraitRef<'tcx>>,
+        print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
+        disambiguated_data: &DisambiguatedDefPathData,
     ) -> Result<(), PrintError>;
 
-    fn path_append_impl(
+    /// Similar to `print_path_with_simple`, but the final segment is an `impl` segment.
+    ///
+    /// E.g. `slice` + `<impl [T]>` -> `slice::<impl [T]>`, which may then be further appended to,
+    /// giving a longer path representation such as `slice::<impl [T]>::to_vec_in::ConvertVec`.
+    fn print_path_with_impl(
         &mut self,
         print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
         self_ty: Ty<'tcx>,
         trait_ref: Option<ty::TraitRef<'tcx>>,
     ) -> Result<(), PrintError>;
 
-    fn path_append(
+    /// Appends a representation of a path ending in generic args, in two parts. `print_prefix`,
+    /// when called, appends the leading segments. The rest of the method appends the
+    /// representation of the generic args. (Some printers choose to skip appending the generic
+    /// args.)
+    ///
+    /// E.g. `ImplementsTraitForUsize` + `<usize>` -> `ImplementsTraitForUsize<usize>`.
+    fn print_path_with_generic_args(
         &mut self,
         print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
-        disambiguated_data: &DisambiguatedDefPathData,
+        args: &[GenericArg<'tcx>],
     ) -> Result<(), PrintError>;
 
-    fn path_generic_args(
+    /// Appends a representation of a qualified path segment, e.g. `<OsString as From<&T>>`.
+    /// If `trait_ref` is `None`, it may fall back to simpler forms, e.g. `<Vec<T>>` or just `Foo`.
+    fn print_path_with_qualified(
         &mut self,
-        print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
-        args: &[GenericArg<'tcx>],
+        self_ty: Ty<'tcx>,
+        trait_ref: Option<ty::TraitRef<'tcx>>,
     ) -> Result<(), PrintError>;
 
-    fn should_truncate(&mut self) -> bool {
-        false
-    }
-
     // Defaults (should not be overridden):
 
     #[instrument(skip(self), level = "debug")]
@@ -120,7 +138,7 @@ pub trait Printer<'tcx>: Sized {
         match key.disambiguated_data.data {
             DefPathData::CrateRoot => {
                 assert!(key.parent.is_none());
-                self.path_crate(def_id.krate)
+                self.print_crate_name(def_id.krate)
             }
 
             DefPathData::Impl => self.print_impl_path(def_id, args),
@@ -144,7 +162,7 @@ pub trait Printer<'tcx>: Sized {
                             )) = self.tcx().coroutine_kind(def_id)
                                 && args.len() > parent_args.len()
                             {
-                                return self.path_generic_args(
+                                return self.print_path_with_generic_args(
                                     |p| p.print_def_path(def_id, parent_args),
                                     &args[..parent_args.len() + 1][..1],
                                 );
@@ -166,7 +184,7 @@ pub trait Printer<'tcx>: Sized {
                         _ => {
                             if !generics.is_own_empty() && args.len() >= generics.count() {
                                 let args = generics.own_args_no_defaults(self.tcx(), args);
-                                return self.path_generic_args(
+                                return self.print_path_with_generic_args(
                                     |p| p.print_def_path(def_id, parent_args),
                                     args,
                                 );
@@ -182,7 +200,7 @@ pub trait Printer<'tcx>: Sized {
                         && self.tcx().generics_of(parent_def_id).parent_count == 0;
                 }
 
-                self.path_append(
+                self.print_path_with_simple(
                     |p: &mut Self| {
                         if trait_qualify_parent {
                             let trait_ref = ty::TraitRef::new(
@@ -190,7 +208,7 @@ pub trait Printer<'tcx>: Sized {
                                 parent_def_id,
                                 parent_args.iter().copied(),
                             );
-                            p.path_qualified(trait_ref.self_ty(), Some(trait_ref))
+                            p.print_path_with_qualified(trait_ref.self_ty(), Some(trait_ref))
                         } else {
                             p.print_def_path(parent_def_id, parent_args)
                         }
@@ -233,11 +251,15 @@ pub trait Printer<'tcx>: Sized {
             // If the impl is not co-located with either self-type or
             // trait-type, then fallback to a format that identifies
             // the module more clearly.
-            self.path_append_impl(|p| p.print_def_path(parent_def_id, &[]), self_ty, impl_trait_ref)
+            self.print_path_with_impl(
+                |p| p.print_def_path(parent_def_id, &[]),
+                self_ty,
+                impl_trait_ref,
+            )
         } else {
             // Otherwise, try to give a good form that would be valid language
             // syntax. Preferably using associated item notation.
-            self.path_qualified(self_ty, impl_trait_ref)
+            self.print_path_with_qualified(self_ty, impl_trait_ref)
         }
     }
 }
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 67244e767cb..0a976f3a0ac 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -245,7 +245,7 @@ impl<'tcx> RegionHighlightMode<'tcx> {
 /// Trait for printers that pretty-print using `fmt::Write` to the printer.
 pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
     /// Like `print_def_path` but for value paths.
-    fn print_value_path(
+    fn pretty_print_value_path(
         &mut self,
         def_id: DefId,
         args: &'tcx [GenericArg<'tcx>],
@@ -253,7 +253,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
         self.print_def_path(def_id, args)
     }
 
-    fn print_in_binder<T>(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError>
+    fn pretty_print_in_binder<T>(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError>
     where
         T: Print<'tcx, Self> + TypeFoldable<TyCtxt<'tcx>>,
     {
@@ -333,6 +333,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
         f: impl FnOnce(&mut Self) -> Result<(), PrintError>,
     ) -> Result<(), PrintError>;
 
+    fn should_truncate(&mut self) -> bool {
+        false
+    }
+
     /// Returns `true` if the region should be printed in
     /// optional positions, e.g., `&'a T` or `dyn Tr + 'b`.
     /// This is typically the case for all non-`'_` regions.
@@ -470,7 +474,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
         // path to the crate followed by the path to the item within the crate.
         if let Some(cnum) = def_id.as_crate_root() {
             if cnum == LOCAL_CRATE {
-                self.path_crate(cnum)?;
+                self.print_crate_name(cnum)?;
                 return Ok(true);
             }
 
@@ -494,7 +498,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                         // or avoid ending up with `ExternCrateSource::Extern`,
                         // for the injected `std`/`core`.
                         if span.is_dummy() {
-                            self.path_crate(cnum)?;
+                            self.print_crate_name(cnum)?;
                             return Ok(true);
                         }
 
@@ -508,13 +512,13 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                         return Ok(true);
                     }
                     (ExternCrateSource::Path, LOCAL_CRATE) => {
-                        self.path_crate(cnum)?;
+                        self.print_crate_name(cnum)?;
                         return Ok(true);
                     }
                     _ => {}
                 },
                 None => {
-                    self.path_crate(cnum)?;
+                    self.print_crate_name(cnum)?;
                     return Ok(true);
                 }
             }
@@ -624,7 +628,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
             return Ok(false);
         }
         callers.push(visible_parent);
-        // HACK(eddyb) this bypasses `path_append`'s prefix printing to avoid
+        // HACK(eddyb) this bypasses `print_path_with_simple`'s prefix printing to avoid
         // knowing ahead of time whether the entire path will succeed or not.
         // To support printers that do not implement `PrettyPrinter`, a `Vec` or
         // linked list on the stack would need to be built, before any printing.
@@ -633,11 +637,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
             true => {}
         }
         callers.pop();
-        self.path_append(|_| Ok(()), &DisambiguatedDefPathData { data, disambiguator: 0 })?;
+        self.print_path_with_simple(
+            |_| Ok(()),
+            &DisambiguatedDefPathData { data, disambiguator: 0 },
+        )?;
         Ok(true)
     }
 
-    fn pretty_path_qualified(
+    fn pretty_print_path_with_qualified(
         &mut self,
         self_ty: Ty<'tcx>,
         trait_ref: Option<ty::TraitRef<'tcx>>,
@@ -672,7 +679,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
         })
     }
 
-    fn pretty_path_append_impl(
+    fn pretty_print_path_with_impl(
         &mut self,
         print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
         self_ty: Ty<'tcx>,
@@ -739,7 +746,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                     }
                     sig.print(self)?;
                     write!(self, " {{")?;
-                    self.print_value_path(def_id, args)?;
+                    self.pretty_print_value_path(def_id, args)?;
                     write!(self, "}}")?;
                 }
             }
@@ -1308,10 +1315,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
         alias_ty: ty::AliasTerm<'tcx>,
     ) -> Result<(), PrintError> {
         let def_key = self.tcx().def_key(alias_ty.def_id);
-        self.path_generic_args(
+        self.print_path_with_generic_args(
             |p| {
-                p.path_append(
-                    |p| p.path_qualified(alias_ty.self_ty(), None),
+                p.print_path_with_simple(
+                    |p| p.print_path_with_qualified(alias_ty.self_ty(), None),
                     &def_key.disambiguated_data,
                 )
             },
@@ -1386,7 +1393,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                     if let ty::Tuple(tys) = principal.args.type_at(0).kind() {
                         let mut projections = predicates.projection_bounds();
                         if let (Some(proj), None) = (projections.next(), projections.next()) {
-                            p.pretty_fn_sig(
+                            p.pretty_print_fn_sig(
                                 tys,
                                 false,
                                 proj.skip_binder().term.as_type().expect("Return type was a const"),
@@ -1396,7 +1403,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                     }
                 }
 
-                // HACK(eddyb) this duplicates `FmtPrinter`'s `path_generic_args`,
+                // HACK(eddyb) this duplicates `FmtPrinter`'s `print_path_with_generic_args`,
                 // in order to place the projections inside the `<...>`.
                 if !resugared {
                     let principal_with_self =
@@ -1488,7 +1495,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
         Ok(())
     }
 
-    fn pretty_fn_sig(
+    fn pretty_print_fn_sig(
         &mut self,
         inputs: &[Ty<'tcx>],
         c_variadic: bool,
@@ -1525,7 +1532,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
             ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => {
                 match self.tcx().def_kind(def) {
                     DefKind::Const | DefKind::AssocConst => {
-                        self.print_value_path(def, args)?;
+                        self.pretty_print_value_path(def, args)?;
                     }
                     DefKind::AnonConst => {
                         if def.is_local()
@@ -1534,13 +1541,13 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                         {
                             write!(self, "{snip}")?;
                         } else {
-                            // Do not call `print_value_path` as if a parent of this anon const is
-                            // an impl it will attempt to print out the impl trait ref i.e. `<T as
-                            // Trait>::{constant#0}`. This would cause printing to enter an
-                            // infinite recursion if the anon const is in the self type i.e.
-                            // `impl<T: Default> Default for [T; 32 - 1 - 1 - 1] {` where we would
-                            // try to print
-                            // `<[T; /* print constant#0 again */] as // Default>::{constant#0}`.
+                            // Do not call `pretty_print_value_path` as if a parent of this anon
+                            // const is an impl it will attempt to print out the impl trait ref
+                            // i.e. `<T as Trait>::{constant#0}`. This would cause printing to
+                            // enter an infinite recursion if the anon const is in the self type
+                            // i.e. `impl<T: Default> Default for [T; 32 - 1 - 1 - 1] {` where we
+                            // would try to print `<[T; /* print constant#0 again */] as //
+                            // Default>::{constant#0}`.
                             write!(
                                 self,
                                 "{}::{}",
@@ -1742,7 +1749,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                     self.tcx().try_get_global_alloc(prov.alloc_id())
                 {
                     self.typed_value(
-                        |this| this.print_value_path(instance.def_id(), instance.args),
+                        |this| this.pretty_print_value_path(instance.def_id(), instance.args),
                         |this| this.print_type(ty),
                         " as ",
                     )?;
@@ -1936,7 +1943,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                         let variant_idx =
                             contents.variant.expect("destructed const of adt without variant idx");
                         let variant_def = &def.variant(variant_idx);
-                        self.print_value_path(variant_def.def_id, args)?;
+                        self.pretty_print_value_path(variant_def.def_id, args)?;
                         match variant_def.ctor_kind() {
                             Some(CtorKind::Const) => {}
                             Some(CtorKind::Fn) => {
@@ -1972,7 +1979,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
             }
             (_, ty::FnDef(def_id, args)) => {
                 // Never allowed today, but we still encounter them in invalid const args.
-                self.print_value_path(def_id, args)?;
+                self.pretty_print_value_path(def_id, args)?;
                 return Ok(());
             }
             // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading
@@ -1993,7 +2000,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
         Ok(())
     }
 
-    fn pretty_closure_as_impl(
+    fn pretty_print_closure_as_impl(
         &mut self,
         closure: ty::ClosureArgs<TyCtxt<'tcx>>,
     ) -> Result<(), PrintError> {
@@ -2131,8 +2138,6 @@ impl<'a, 'tcx> FmtPrinter<'a, 'tcx> {
     }
 }
 
-// HACK(eddyb) get rid of `def_path_str` and/or pass `Namespace` explicitly always
-// (but also some things just print a `DefId` generally so maybe we need this?)
 fn guess_def_namespace(tcx: TyCtxt<'_>, def_id: DefId) -> Namespace {
     match tcx.def_key(def_id).disambiguated_data.data {
         DefPathData::TypeNs(..) | DefPathData::CrateRoot | DefPathData::OpaqueTy => {
@@ -2157,6 +2162,7 @@ impl<'t> TyCtxt<'t> {
         self.def_path_str_with_args(def_id, &[])
     }
 
+    /// For this one we determine the appropriate namespace for the `def_id`.
     pub fn def_path_str_with_args(
         self,
         def_id: impl IntoQueryParam<DefId>,
@@ -2169,16 +2175,17 @@ impl<'t> TyCtxt<'t> {
         FmtPrinter::print_string(self, ns, |p| p.print_def_path(def_id, args)).unwrap()
     }
 
+    /// For this one we always use value namespace.
     pub fn value_path_str_with_args(
         self,
         def_id: impl IntoQueryParam<DefId>,
         args: &'t [GenericArg<'t>],
     ) -> String {
         let def_id = def_id.into_query_param();
-        let ns = guess_def_namespace(self, def_id);
+        let ns = Namespace::ValueNS;
         debug!("value_path_str: def_id={:?}, ns={:?}", def_id, ns);
 
-        FmtPrinter::print_string(self, ns, |p| p.print_value_path(def_id, args)).unwrap()
+        FmtPrinter::print_string(self, ns, |p| p.print_def_path(def_id, args)).unwrap()
     }
 }
 
@@ -2230,7 +2237,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
 
                 self.print_def_path(parent_def_id, &[])?;
 
-                // HACK(eddyb) copy of `path_append` to avoid
+                // HACK(eddyb) copy of `print_path_with_simple` to avoid
                 // constructing a `DisambiguatedDefPathData`.
                 if !self.empty_path {
                     write!(self, "::")?;
@@ -2295,10 +2302,6 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
         }
     }
 
-    fn should_truncate(&mut self) -> bool {
-        !self.type_length_limit.value_within_limit(self.printed_type_count)
-    }
-
     fn print_dyn_existential(
         &mut self,
         predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
@@ -2310,7 +2313,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
         self.pretty_print_const(ct, false)
     }
 
-    fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
+    fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
         self.empty_path = true;
         if cnum == LOCAL_CRATE {
             if self.tcx.sess.at_least_rust_2018() {
@@ -2327,23 +2330,23 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
         Ok(())
     }
 
-    fn path_qualified(
+    fn print_path_with_qualified(
         &mut self,
         self_ty: Ty<'tcx>,
         trait_ref: Option<ty::TraitRef<'tcx>>,
     ) -> Result<(), PrintError> {
-        self.pretty_path_qualified(self_ty, trait_ref)?;
+        self.pretty_print_path_with_qualified(self_ty, trait_ref)?;
         self.empty_path = false;
         Ok(())
     }
 
-    fn path_append_impl(
+    fn print_path_with_impl(
         &mut self,
         print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
         self_ty: Ty<'tcx>,
         trait_ref: Option<ty::TraitRef<'tcx>>,
     ) -> Result<(), PrintError> {
-        self.pretty_path_append_impl(
+        self.pretty_print_path_with_impl(
             |p| {
                 print_prefix(p)?;
                 if !p.empty_path {
@@ -2359,7 +2362,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
         Ok(())
     }
 
-    fn path_append(
+    fn print_path_with_simple(
         &mut self,
         print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
         disambiguated_data: &DisambiguatedDefPathData,
@@ -2390,7 +2393,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
         Ok(())
     }
 
-    fn path_generic_args(
+    fn print_path_with_generic_args(
         &mut self,
         print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
         args: &[GenericArg<'tcx>],
@@ -2421,7 +2424,7 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
         self.0.const_infer_name_resolver.as_ref().and_then(|func| func(id))
     }
 
-    fn print_value_path(
+    fn pretty_print_value_path(
         &mut self,
         def_id: DefId,
         args: &'tcx [GenericArg<'tcx>],
@@ -2433,7 +2436,7 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
         Ok(())
     }
 
-    fn print_in_binder<T>(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError>
+    fn pretty_print_in_binder<T>(&mut self, value: &ty::Binder<'tcx, T>) -> Result<(), PrintError>
     where
         T: Print<'tcx, Self> + TypeFoldable<TyCtxt<'tcx>>,
     {
@@ -2487,6 +2490,10 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
         Ok(())
     }
 
+    fn should_truncate(&mut self) -> bool {
+        !self.type_length_limit.value_within_limit(self.printed_type_count)
+    }
+
     fn should_print_region(&self, region: ty::Region<'tcx>) -> bool {
         let highlight = self.region_highlight_mode;
         if highlight.region_highlighted(region).is_some() {
@@ -2892,7 +2899,7 @@ where
     T: Print<'tcx, P> + TypeFoldable<TyCtxt<'tcx>>,
 {
     fn print(&self, p: &mut P) -> Result<(), PrintError> {
-        p.print_in_binder(self)
+        p.pretty_print_in_binder(self)
     }
 }
 
@@ -3090,7 +3097,7 @@ define_print! {
         }
 
         write!(p, "fn")?;
-        p.pretty_fn_sig(self.inputs(), self.c_variadic, self.output())?;
+        p.pretty_print_fn_sig(self.inputs(), self.c_variadic, self.output())?;
     }
 
     ty::TraitRef<'tcx> {
@@ -3225,7 +3232,7 @@ define_print! {
         // The args don't contain the self ty (as it has been erased) but the corresp.
         // generics do as the trait always has a self ty param. We need to offset.
         let args = &self.args[p.tcx().generics_of(self.def_id).parent_count - 1..];
-        p.path_generic_args(|p| write!(p, "{name}"), args)?;
+        p.print_path_with_generic_args(|p| write!(p, "{name}"), args)?;
         write!(p, " = ")?;
         self.term.print(p)?;
     }
@@ -3314,7 +3321,7 @@ define_print_and_forward_display! {
     }
 
     PrintClosureAsImpl<'tcx> {
-        p.pretty_closure_as_impl(self.closure)?;
+        p.pretty_print_closure_as_impl(self.closure)?;
     }
 
     ty::ParamTy {
diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl
index ceef558c0cf..d5ff8a4b609 100644
--- a/compiler/rustc_resolve/messages.ftl
+++ b/compiler/rustc_resolve/messages.ftl
@@ -242,6 +242,9 @@ resolve_lowercase_self =
     attempt to use a non-constant value in a constant
     .suggestion = try using `Self`
 
+resolve_macro_cannot_use_as_fn_like =
+    `{$ident}` exists, but has no rules for function-like invocation
+
 resolve_macro_cannot_use_as_attr =
     `{$ident}` exists, but has no `attr` rules
 
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 819a5950f17..580fa4f5b2c 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -1227,7 +1227,8 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
             ItemKind::Fn(box ast::Fn { ident: fn_ident, .. }) => {
                 match self.proc_macro_stub(item, *fn_ident) {
                     Some((macro_kind, ident, span)) => {
-                        let res = Res::Def(DefKind::Macro(macro_kind), def_id.to_def_id());
+                        let macro_kinds = macro_kind.into();
+                        let res = Res::Def(DefKind::Macro(macro_kinds), def_id.to_def_id());
                         let macro_data = MacroData::new(self.r.dummy_ext(macro_kind));
                         self.r.new_local_macro(def_id, macro_data);
                         self.r.proc_macro_stubs.insert(def_id);
diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs
index 7d51fef28d3..1e4513eb787 100644
--- a/compiler/rustc_resolve/src/def_collector.rs
+++ b/compiler/rustc_resolve/src/def_collector.rs
@@ -149,9 +149,9 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
 
                 let macro_data =
                     self.resolver.compile_macro(def, *ident, &attrs, i.span, i.id, edition);
-                let macro_kind = macro_data.ext.macro_kind();
+                let macro_kinds = macro_data.ext.macro_kinds();
                 opt_macro_data = Some(macro_data);
-                DefKind::Macro(macro_kind)
+                DefKind::Macro(macro_kinds)
             }
             ItemKind::GlobalAsm(..) => DefKind::GlobalAsm,
             ItemKind::Use(use_tree) => {
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index e420e68264b..8526b233ba2 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -13,7 +13,7 @@ use rustc_errors::{
 use rustc_feature::BUILTIN_ATTRIBUTES;
 use rustc_hir::attrs::{AttributeKind, CfgEntry, StrippedCfgItem};
 use rustc_hir::def::Namespace::{self, *};
-use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind, PerNS};
+use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, MacroKinds, NonMacroAttrKind, PerNS};
 use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
 use rustc_hir::{PrimTy, Stability, StabilityLevel, find_attr};
 use rustc_middle::bug;
@@ -1493,11 +1493,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                     let Some(binding) = resolution.borrow().best_binding() else {
                         continue;
                     };
-                    let Res::Def(DefKind::Macro(MacroKind::Derive | MacroKind::Attr), def_id) =
-                        binding.res()
-                    else {
+                    let Res::Def(DefKind::Macro(kinds), def_id) = binding.res() else {
                         continue;
                     };
+                    if !kinds.intersects(MacroKinds::ATTR | MacroKinds::DERIVE) {
+                        continue;
+                    }
                     // By doing this all *imported* macros get added to the `macro_map` even if they
                     // are *unused*, which makes the later suggestions find them and work.
                     let _ = this.get_macro_by_def_id(def_id);
@@ -1506,7 +1507,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             },
         );
 
-        let is_expected = &|res: Res| res.macro_kind() == Some(macro_kind);
+        let is_expected =
+            &|res: Res| res.macro_kinds().is_some_and(|k| k.contains(macro_kind.into()));
         let suggestion = self.early_lookup_typo_candidate(
             ScopeSet::Macro(macro_kind),
             parent_scope,
@@ -1555,11 +1557,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         if let Some((def_id, unused_ident)) = unused_macro {
             let scope = self.local_macro_def_scopes[&def_id];
             let parent_nearest = parent_scope.module.nearest_parent_mod();
-            if Some(parent_nearest) == scope.opt_def_id() {
+            let unused_macro_kinds = self.local_macro_map[def_id].ext.macro_kinds();
+            if !unused_macro_kinds.contains(macro_kind.into()) {
                 match macro_kind {
                     MacroKind::Bang => {
-                        err.subdiagnostic(MacroDefinedLater { span: unused_ident.span });
-                        err.subdiagnostic(MacroSuggMovePosition { span: ident.span, ident });
+                        err.subdiagnostic(MacroRulesNot::Func { span: unused_ident.span, ident });
                     }
                     MacroKind::Attr => {
                         err.subdiagnostic(MacroRulesNot::Attr { span: unused_ident.span, ident });
@@ -1568,14 +1570,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                         err.subdiagnostic(MacroRulesNot::Derive { span: unused_ident.span, ident });
                     }
                 }
-
                 return;
             }
-        }
-
-        if self.macro_names.contains(&ident.normalize_to_macros_2_0()) {
-            err.subdiagnostic(AddedMacroUse);
-            return;
+            if Some(parent_nearest) == scope.opt_def_id() {
+                err.subdiagnostic(MacroDefinedLater { span: unused_ident.span });
+                err.subdiagnostic(MacroSuggMovePosition { span: ident.span, ident });
+                return;
+            }
         }
 
         if ident.name == kw::Default
@@ -1603,13 +1604,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             };
 
             let desc = match binding.res() {
-                Res::Def(DefKind::Macro(MacroKind::Bang), _) => "a function-like macro".to_string(),
-                Res::Def(DefKind::Macro(MacroKind::Attr), _) | Res::NonMacroAttr(..) => {
+                Res::Def(DefKind::Macro(MacroKinds::BANG), _) => {
+                    "a function-like macro".to_string()
+                }
+                Res::Def(DefKind::Macro(MacroKinds::ATTR), _) | Res::NonMacroAttr(..) => {
                     format!("an attribute: `#[{ident}]`")
                 }
-                Res::Def(DefKind::Macro(MacroKind::Derive), _) => {
+                Res::Def(DefKind::Macro(MacroKinds::DERIVE), _) => {
                     format!("a derive macro: `#[derive({ident})]`")
                 }
+                Res::Def(DefKind::Macro(kinds), _) => {
+                    format!("{} {}", kinds.article(), kinds.descr())
+                }
                 Res::ToolMod => {
                     // Don't confuse the user with tool modules.
                     continue;
@@ -1646,6 +1652,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             err.subdiagnostic(note);
             return;
         }
+
+        if self.macro_names.contains(&ident.normalize_to_macros_2_0()) {
+            err.subdiagnostic(AddedMacroUse);
+            return;
+        }
     }
 
     /// Given an attribute macro that failed to be resolved, look for `derive` macros that could
@@ -2757,9 +2768,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
 
         let binding_key = BindingKey::new(ident, MacroNS);
         let binding = self.resolution(crate_module, binding_key)?.binding()?;
-        let Res::Def(DefKind::Macro(MacroKind::Bang), _) = binding.res() else {
+        let Res::Def(DefKind::Macro(kinds), _) = binding.res() else {
             return None;
         };
+        if !kinds.contains(MacroKinds::BANG) {
+            return None;
+        }
         let module_name = crate_module.kind.name().unwrap_or(kw::Crate);
         let import_snippet = match import.kind {
             ImportKind::Single { source, target, .. } if source != target => {
diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs
index 2747ba135ed..a1d62ba7a68 100644
--- a/compiler/rustc_resolve/src/errors.rs
+++ b/compiler/rustc_resolve/src/errors.rs
@@ -672,6 +672,12 @@ pub(crate) struct MacroSuggMovePosition {
 
 #[derive(Subdiagnostic)]
 pub(crate) enum MacroRulesNot {
+    #[label(resolve_macro_cannot_use_as_fn_like)]
+    Func {
+        #[primary_span]
+        span: Span,
+        ident: Ident,
+    },
     #[label(resolve_macro_cannot_use_as_attr)]
     Attr {
         #[primary_span]
diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs
index 1864540655c..7ab932d9f2a 100644
--- a/compiler/rustc_resolve/src/ident.rs
+++ b/compiler/rustc_resolve/src/ident.rs
@@ -2,7 +2,7 @@ use Determinacy::*;
 use Namespace::*;
 use rustc_ast::{self as ast, NodeId};
 use rustc_errors::ErrorGuaranteed;
-use rustc_hir::def::{DefKind, Namespace, NonMacroAttrKind, PartialRes, PerNS};
+use rustc_hir::def::{DefKind, MacroKinds, Namespace, NonMacroAttrKind, PartialRes, PerNS};
 use rustc_middle::bug;
 use rustc_session::lint::BuiltinLintDiag;
 use rustc_session::lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK;
@@ -265,7 +265,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         {
             let ext = &self.get_macro_by_def_id(def_id).ext;
             if ext.builtin_name.is_none()
-                && ext.macro_kind() == MacroKind::Derive
+                && ext.macro_kinds() == MacroKinds::DERIVE
                 && parent.expansion.outer_expn_is_descendant_of(*ctxt)
             {
                 return Some((parent, derive_fallback_lint_id));
@@ -649,17 +649,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
 
                 match result {
                     Ok((binding, flags)) => {
-                        let binding_macro_kind = binding.macro_kind();
-                        // If we're looking for an attribute, that might be supported by a
-                        // `macro_rules!` macro.
-                        // FIXME: Replace this with tracking multiple macro kinds for one Def.
-                        if !(sub_namespace_match(binding_macro_kind, macro_kind)
-                            || (binding_macro_kind == Some(MacroKind::Bang)
-                                && macro_kind == Some(MacroKind::Attr)
-                                && this
-                                    .get_macro(binding.res())
-                                    .is_some_and(|macro_data| macro_data.attr_ext.is_some())))
-                        {
+                        if !sub_namespace_match(binding.macro_kinds(), macro_kind) {
                             return None;
                         }
 
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index aca251da71d..c8cab5a0fe9 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -19,14 +19,13 @@ use rustc_errors::{
 };
 use rustc_hir as hir;
 use rustc_hir::def::Namespace::{self, *};
-use rustc_hir::def::{self, CtorKind, CtorOf, DefKind};
+use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, MacroKinds};
 use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
 use rustc_hir::{MissingLifetimeKind, PrimTy};
 use rustc_middle::ty;
 use rustc_session::{Session, lint};
 use rustc_span::edit_distance::{edit_distance, find_best_match_for_name};
 use rustc_span::edition::Edition;
-use rustc_span::hygiene::MacroKind;
 use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
 use thin_vec::ThinVec;
 use tracing::debug;
@@ -1850,12 +1849,12 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
 
         match (res, source) {
             (
-                Res::Def(DefKind::Macro(MacroKind::Bang), def_id),
+                Res::Def(DefKind::Macro(kinds), def_id),
                 PathSource::Expr(Some(Expr {
                     kind: ExprKind::Index(..) | ExprKind::Call(..), ..
                 }))
                 | PathSource::Struct(_),
-            ) => {
+            ) if kinds.contains(MacroKinds::BANG) => {
                 // Don't suggest macro if it's unstable.
                 let suggestable = def_id.is_local()
                     || self.r.tcx.lookup_stability(def_id).is_none_or(|s| s.is_stable());
@@ -1880,7 +1879,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                     err.note("if you want the `try` keyword, you need Rust 2018 or later");
                 }
             }
-            (Res::Def(DefKind::Macro(MacroKind::Bang), _), _) => {
+            (Res::Def(DefKind::Macro(kinds), _), _) if kinds.contains(MacroKinds::BANG) => {
                 err.span_label(span, fallback_label.to_string());
             }
             (Res::Def(DefKind::TyAlias, def_id), PathSource::Trait(_)) => {
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 15dd7069602..cc30939f5e9 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -54,7 +54,8 @@ use rustc_feature::BUILTIN_ATTRIBUTES;
 use rustc_hir::attrs::StrippedCfgItem;
 use rustc_hir::def::Namespace::{self, *};
 use rustc_hir::def::{
-    self, CtorOf, DefKind, DocLinkResMap, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS,
+    self, CtorOf, DefKind, DocLinkResMap, LifetimeRes, MacroKinds, NonMacroAttrKind, PartialRes,
+    PerNS,
 };
 use rustc_hir::def_id::{CRATE_DEF_ID, CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalDefIdMap};
 use rustc_hir::definitions::DisambiguatorState;
@@ -982,8 +983,8 @@ impl<'ra> NameBindingData<'ra> {
         matches!(self.res(), Res::Def(DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy, _))
     }
 
-    fn macro_kind(&self) -> Option<MacroKind> {
-        self.res().macro_kind()
+    fn macro_kinds(&self) -> Option<MacroKinds> {
+        self.res().macro_kinds()
     }
 
     // Suppose that we resolved macro invocation with `invoc_parent_expansion` to binding `binding`
@@ -1045,14 +1046,13 @@ struct DeriveData {
 
 struct MacroData {
     ext: Arc<SyntaxExtension>,
-    attr_ext: Option<Arc<SyntaxExtension>>,
     nrules: usize,
     macro_rules: bool,
 }
 
 impl MacroData {
     fn new(ext: Arc<SyntaxExtension>) -> MacroData {
-        MacroData { ext, attr_ext: None, nrules: 0, macro_rules: false }
+        MacroData { ext, nrules: 0, macro_rules: false }
     }
 }
 
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 9173d0d3ea5..9f25635f1fd 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -1,7 +1,6 @@
 //! A bunch of methods and structures more or less related to resolving macros and
 //! interface provided by `Resolver` to macro expander.
 
-use std::any::Any;
 use std::cell::Cell;
 use std::mem;
 use std::sync::Arc;
@@ -13,13 +12,13 @@ use rustc_expand::base::{
     Annotatable, DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension,
     SyntaxExtensionKind,
 };
+use rustc_expand::compile_declarative_macro;
 use rustc_expand::expand::{
     AstFragment, AstFragmentKind, Invocation, InvocationKind, SupportsMacroExpansion,
 };
-use rustc_expand::{MacroRulesMacroExpander, compile_declarative_macro};
 use rustc_hir::StabilityLevel;
 use rustc_hir::attrs::{CfgEntry, StrippedCfgItem};
-use rustc_hir::def::{self, DefKind, Namespace, NonMacroAttrKind};
+use rustc_hir::def::{self, DefKind, MacroKinds, Namespace, NonMacroAttrKind};
 use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
 use rustc_middle::middle::stability;
 use rustc_middle::ty::{RegisteredTools, TyCtxt};
@@ -86,22 +85,19 @@ pub(crate) type MacroRulesScopeRef<'ra> = &'ra Cell<MacroRulesScope<'ra>>;
 /// one for attribute-like macros (attributes, derives).
 /// We ignore resolutions from one sub-namespace when searching names in scope for another.
 pub(crate) fn sub_namespace_match(
-    candidate: Option<MacroKind>,
+    candidate: Option<MacroKinds>,
     requirement: Option<MacroKind>,
 ) -> bool {
-    #[derive(PartialEq)]
-    enum SubNS {
-        Bang,
-        AttrLike,
-    }
-    let sub_ns = |kind| match kind {
-        MacroKind::Bang => SubNS::Bang,
-        MacroKind::Attr | MacroKind::Derive => SubNS::AttrLike,
-    };
-    let candidate = candidate.map(sub_ns);
-    let requirement = requirement.map(sub_ns);
     // "No specific sub-namespace" means "matches anything" for both requirements and candidates.
-    candidate.is_none() || requirement.is_none() || candidate == requirement
+    let (Some(candidate), Some(requirement)) = (candidate, requirement) else {
+        return true;
+    };
+    match requirement {
+        MacroKind::Bang => candidate.contains(MacroKinds::BANG),
+        MacroKind::Attr | MacroKind::Derive => {
+            candidate.intersects(MacroKinds::ATTR | MacroKinds::DERIVE)
+        }
+    }
 }
 
 // We don't want to format a path using pretty-printing,
@@ -323,6 +319,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
                 parent_scope.expansion,
                 span,
                 fast_print_path(path),
+                kind,
                 def_id,
                 def_id.map(|def_id| self.macro_def_scope(def_id).nearest_parent_mod()),
             ),
@@ -356,11 +353,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
             }
             let def_id = self.local_def_id(node_id);
             let m = &self.local_macro_map[&def_id];
-            let SyntaxExtensionKind::LegacyBang(ref ext) = m.ext.kind else {
-                continue;
-            };
-            let ext: &dyn Any = ext.as_ref();
-            let Some(m) = ext.downcast_ref::<MacroRulesMacroExpander>() else {
+            let SyntaxExtensionKind::MacroRules(ref m) = m.ext.kind else {
                 continue;
             };
             for arm_i in unused_arms.iter() {
@@ -633,7 +626,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
 
         self.check_stability_and_deprecation(&ext, path, node_id);
 
-        let unexpected_res = if ext.macro_kind() != kind {
+        let unexpected_res = if !ext.macro_kinds().contains(kind.into()) {
             Some((kind.article(), kind.descr_expected()))
         } else if matches!(res, Res::Def(..)) {
             match supports_macro_expansion {
@@ -665,7 +658,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             // Suggest moving the macro out of the derive() if the macro isn't Derive
             if !path.span.from_expansion()
                 && kind == MacroKind::Derive
-                && ext.macro_kind() != MacroKind::Derive
+                && !ext.macro_kinds().contains(MacroKinds::DERIVE)
+                && ext.macro_kinds().contains(MacroKinds::ATTR)
             {
                 err.remove_surrounding_derive = Some(RemoveSurroundingDerive { span: path.span });
                 err.add_as_non_derive = Some(AddAsNonDerive { macro_path: &path_str });
@@ -842,10 +836,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                 }
                 _ => None,
             },
-            None => self.get_macro(res).map(|macro_data| match kind {
-                Some(MacroKind::Attr) if let Some(ref ext) = macro_data.attr_ext => Arc::clone(ext),
-                _ => Arc::clone(&macro_data.ext),
-            }),
+            None => self.get_macro(res).map(|macro_data| Arc::clone(&macro_data.ext)),
         };
         Ok((ext, res))
     }
@@ -1114,7 +1105,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             && let Some(binding) = binding
             // This is a `macro_rules` itself, not some import.
             && let NameBindingKind::Res(res) = binding.kind
-            && let Res::Def(DefKind::Macro(MacroKind::Bang), def_id) = res
+            && let Res::Def(DefKind::Macro(kinds), def_id) = res
+            && kinds.contains(MacroKinds::BANG)
             // And the `macro_rules` is defined inside the attribute's module,
             // so it cannot be in scope unless imported.
             && self.tcx.is_descendant_of(def_id, mod_def_id.to_def_id())
@@ -1161,8 +1153,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         // Reserve some names that are not quite covered by the general check
         // performed on `Resolver::builtin_attrs`.
         if ident.name == sym::cfg || ident.name == sym::cfg_attr {
-            let macro_kind = self.get_macro(res).map(|macro_data| macro_data.ext.macro_kind());
-            if macro_kind.is_some() && sub_namespace_match(macro_kind, Some(MacroKind::Attr)) {
+            let macro_kinds = self.get_macro(res).map(|macro_data| macro_data.ext.macro_kinds());
+            if macro_kinds.is_some() && sub_namespace_match(macro_kinds, Some(MacroKind::Attr)) {
                 self.dcx()
                     .emit_err(errors::NameReservedInAttributeNamespace { span: ident.span, ident });
             }
@@ -1181,7 +1173,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         node_id: NodeId,
         edition: Edition,
     ) -> MacroData {
-        let (mut ext, mut attr_ext, mut nrules) = compile_declarative_macro(
+        let (mut ext, mut nrules) = compile_declarative_macro(
             self.tcx.sess,
             self.tcx.features(),
             macro_def,
@@ -1198,14 +1190,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                 // The macro is a built-in, replace its expander function
                 // while still taking everything else from the source code.
                 ext.kind = builtin_ext_kind.clone();
-                attr_ext = None;
                 nrules = 0;
             } else {
                 self.dcx().emit_err(errors::CannotFindBuiltinMacroWithName { span, ident });
             }
         }
 
-        MacroData { ext: Arc::new(ext), attr_ext, nrules, macro_rules: macro_def.macro_rules }
+        MacroData { ext: Arc::new(ext), nrules, macro_rules: macro_def.macro_rules }
     }
 
     fn path_accessible(
diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs
index a7f64085bd9..025fa299826 100644
--- a/compiler/rustc_symbol_mangling/src/legacy.rs
+++ b/compiler/rustc_symbol_mangling/src/legacy.rs
@@ -58,7 +58,7 @@ pub(super) fn mangle<'tcx>(
 
     let hash = get_symbol_hash(tcx, instance, instance_ty, instantiating_crate);
 
-    let mut p = SymbolPrinter { tcx, path: SymbolPath::new(), keep_within_component: false };
+    let mut p = LegacySymbolMangler { tcx, path: SymbolPath::new(), keep_within_component: false };
     p.print_def_path(
         def_id,
         if let ty::InstanceKind::DropGlue(_, _)
@@ -213,13 +213,13 @@ impl SymbolPath {
     }
 }
 
-struct SymbolPrinter<'tcx> {
+struct LegacySymbolMangler<'tcx> {
     tcx: TyCtxt<'tcx>,
     path: SymbolPath,
 
     // When `true`, `finalize_pending_component` isn't used.
-    // This is needed when recursing into `path_qualified`,
-    // or `path_generic_args`, as any nested paths are
+    // This is needed when recursing into `print_path_with_qualified`,
+    // or `print_path_with_generic_args`, as any nested paths are
     // logically within one component.
     keep_within_component: bool,
 }
@@ -228,7 +228,7 @@ struct SymbolPrinter<'tcx> {
 // `PrettyPrinter` aka pretty printing of e.g. types in paths,
 // symbol names should have their own printing machinery.
 
-impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> {
+impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> {
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
@@ -305,16 +305,17 @@ impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> {
         Ok(())
     }
 
-    fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
+    fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
         self.write_str(self.tcx.crate_name(cnum).as_str())?;
         Ok(())
     }
-    fn path_qualified(
+
+    fn print_path_with_qualified(
         &mut self,
         self_ty: Ty<'tcx>,
         trait_ref: Option<ty::TraitRef<'tcx>>,
     ) -> Result<(), PrintError> {
-        // Similar to `pretty_path_qualified`, but for the other
+        // Similar to `pretty_print_path_with_qualified`, but for the other
         // types that are printed as paths (see `print_type` above).
         match self_ty.kind() {
             ty::FnDef(..)
@@ -327,17 +328,17 @@ impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> {
                 self.print_type(self_ty)
             }
 
-            _ => self.pretty_path_qualified(self_ty, trait_ref),
+            _ => self.pretty_print_path_with_qualified(self_ty, trait_ref),
         }
     }
 
-    fn path_append_impl(
+    fn print_path_with_impl(
         &mut self,
         print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
         self_ty: Ty<'tcx>,
         trait_ref: Option<ty::TraitRef<'tcx>>,
     ) -> Result<(), PrintError> {
-        self.pretty_path_append_impl(
+        self.pretty_print_path_with_impl(
             |cx| {
                 print_prefix(cx)?;
 
@@ -354,7 +355,8 @@ impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> {
             trait_ref,
         )
     }
-    fn path_append(
+
+    fn print_path_with_simple(
         &mut self,
         print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
         disambiguated_data: &DisambiguatedDefPathData,
@@ -377,7 +379,8 @@ impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> {
 
         Ok(())
     }
-    fn path_generic_args(
+
+    fn print_path_with_generic_args(
         &mut self,
         print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
         args: &[GenericArg<'tcx>],
@@ -455,7 +458,7 @@ impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> {
     }
 }
 
-impl<'tcx> PrettyPrinter<'tcx> for SymbolPrinter<'tcx> {
+impl<'tcx> PrettyPrinter<'tcx> for LegacySymbolMangler<'tcx> {
     fn should_print_region(&self, _region: ty::Region<'_>) -> bool {
         false
     }
@@ -491,7 +494,7 @@ impl<'tcx> PrettyPrinter<'tcx> for SymbolPrinter<'tcx> {
     }
 }
 
-impl fmt::Write for SymbolPrinter<'_> {
+impl fmt::Write for LegacySymbolMangler<'_> {
     fn write_str(&mut self, s: &str) -> fmt::Result {
         // Name sanitation. LLVM will happily accept identifiers with weird names, but
         // gas doesn't!
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index c2458ae814b..0cbd48ba08c 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -33,7 +33,7 @@ pub(super) fn mangle<'tcx>(
     let args = tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), instance.args);
 
     let prefix = "_R";
-    let mut p: SymbolMangler<'_> = SymbolMangler {
+    let mut p: V0SymbolMangler<'_> = V0SymbolMangler {
         tcx,
         start_offset: prefix.len(),
         is_exportable,
@@ -88,7 +88,7 @@ pub fn mangle_internal_symbol<'tcx>(tcx: TyCtxt<'tcx>, item_name: &str) -> Strin
     }
 
     let prefix = "_R";
-    let mut p: SymbolMangler<'_> = SymbolMangler {
+    let mut p: V0SymbolMangler<'_> = V0SymbolMangler {
         tcx,
         start_offset: prefix.len(),
         is_exportable: false,
@@ -131,7 +131,7 @@ pub(super) fn mangle_typeid_for_trait_ref<'tcx>(
     trait_ref: ty::ExistentialTraitRef<'tcx>,
 ) -> String {
     // FIXME(flip1995): See comment in `mangle_typeid_for_fnabi`.
-    let mut p = SymbolMangler {
+    let mut p = V0SymbolMangler {
         tcx,
         start_offset: 0,
         is_exportable: false,
@@ -159,7 +159,7 @@ struct BinderLevel {
     lifetime_depths: Range<u32>,
 }
 
-struct SymbolMangler<'tcx> {
+struct V0SymbolMangler<'tcx> {
     tcx: TyCtxt<'tcx>,
     binders: Vec<BinderLevel>,
     out: String,
@@ -173,7 +173,7 @@ struct SymbolMangler<'tcx> {
     consts: FxHashMap<ty::Const<'tcx>, usize>,
 }
 
-impl<'tcx> SymbolMangler<'tcx> {
+impl<'tcx> V0SymbolMangler<'tcx> {
     fn push(&mut self, s: &str) {
         self.out.push_str(s);
     }
@@ -272,7 +272,7 @@ impl<'tcx> SymbolMangler<'tcx> {
     }
 }
 
-impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
+impl<'tcx> Printer<'tcx> for V0SymbolMangler<'tcx> {
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
@@ -365,7 +365,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
         // Encode impl generic params if the generic parameters contain non-region parameters
         // and this isn't an inherent impl.
         if impl_trait_ref.is_some() && args.iter().any(|a| a.has_non_region_param()) {
-            self.path_generic_args(
+            self.print_path_with_generic_args(
                 |this| {
                     this.path_append_ns(
                         |p| p.print_def_path(parent_def_id, &[]),
@@ -786,7 +786,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
                             None => {
                                 self.push("S");
                                 for (field_def, field) in iter::zip(&variant_def.fields, fields) {
-                                    // HACK(eddyb) this mimics `path_append`,
+                                    // HACK(eddyb) this mimics `print_path_with_simple`,
                                     // instead of simply using `field_def.ident`,
                                     // just to be able to handle disambiguators.
                                     let disambiguated_field =
@@ -819,7 +819,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
         Ok(())
     }
 
-    fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
+    fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
         self.push("C");
         if !self.is_exportable {
             let stable_crate_id = self.tcx.def_path_hash(cnum.as_def_id()).stable_crate_id();
@@ -830,7 +830,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
         Ok(())
     }
 
-    fn path_qualified(
+    fn print_path_with_qualified(
         &mut self,
         self_ty: Ty<'tcx>,
         trait_ref: Option<ty::TraitRef<'tcx>>,
@@ -843,7 +843,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
         self.print_def_path(trait_ref.def_id, trait_ref.args)
     }
 
-    fn path_append_impl(
+    fn print_path_with_impl(
         &mut self,
         _: impl FnOnce(&mut Self) -> Result<(), PrintError>,
         _: Ty<'tcx>,
@@ -853,7 +853,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
         unreachable!()
     }
 
-    fn path_append(
+    fn print_path_with_simple(
         &mut self,
         print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
         disambiguated_data: &DisambiguatedDefPathData,
@@ -873,7 +873,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
             DefPathData::SyntheticCoroutineBody => 's',
             DefPathData::NestedStatic => 'n',
 
-            // These should never show up as `path_append` arguments.
+            // These should never show up as `print_path_with_simple` arguments.
             DefPathData::CrateRoot
             | DefPathData::Use
             | DefPathData::GlobalAsm
@@ -896,7 +896,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
         )
     }
 
-    fn path_generic_args(
+    fn print_path_with_generic_args(
         &mut self,
         print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
         args: &[GenericArg<'tcx>],
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index b9fbff8db05..ee408c76006 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -2147,6 +2147,7 @@ supported_targets! {
 
     ("aarch64-unknown-none", aarch64_unknown_none),
     ("aarch64-unknown-none-softfloat", aarch64_unknown_none_softfloat),
+    ("aarch64_be-unknown-none-softfloat", aarch64_be_unknown_none_softfloat),
     ("aarch64-unknown-nuttx", aarch64_unknown_nuttx),
 
     ("x86_64-fortanix-unknown-sgx", x86_64_fortanix_unknown_sgx),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs
new file mode 100644
index 00000000000..7f918e85080
--- /dev/null
+++ b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_none_softfloat.rs
@@ -0,0 +1,43 @@
+// Generic big-endian AArch64 target for bare-metal code - Floating point disabled
+//
+// Can be used in conjunction with the `target-feature` and
+// `target-cpu` compiler flags to opt-in more hardware-specific
+// features.
+//
+// For example, `-C target-cpu=cortex-a53`.
+use rustc_abi::Endian;
+
+use crate::spec::{
+    Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, Target,
+    TargetMetadata, TargetOptions,
+};
+
+pub(crate) fn target() -> Target {
+    let opts = TargetOptions {
+        abi: "softfloat".into(),
+        linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
+        linker: Some("rust-lld".into()),
+        features: "+v8a,+strict-align,-neon,-fp-armv8".into(),
+        relocation_model: RelocModel::Static,
+        disable_redzone: true,
+        max_atomic_width: Some(128),
+        supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS,
+        stack_probes: StackProbeType::Inline,
+        panic_strategy: PanicStrategy::Abort,
+        endian: Endian::Big,
+        ..Default::default()
+    };
+    Target {
+        llvm_target: "aarch64_be-unknown-none".into(),
+        metadata: TargetMetadata {
+            description: Some("Bare ARM64 (big-endian), softfloat".into()),
+            tier: Some(3),
+            host_tools: Some(false),
+            std: Some(false),
+        },
+        pointer_width: 64,
+        data_layout: "E-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        arch: "aarch64".into(),
+        options: opts,
+    }
+}
diff --git a/compiler/rustc_target/src/spec/targets/avr_none.rs b/compiler/rustc_target/src/spec/targets/avr_none.rs
index 07ed2a37803..ad056d02326 100644
--- a/compiler/rustc_target/src/spec/targets/avr_none.rs
+++ b/compiler/rustc_target/src/spec/targets/avr_none.rs
@@ -9,7 +9,7 @@ pub(crate) fn target() -> Target {
             host_tools: None,
             std: None,
         },
-        data_layout: "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8".into(),
+        data_layout: "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8:16-a:8".into(),
         llvm_target: "avr-unknown-unknown".into(),
         pointer_width: 16,
         options: TargetOptions {
diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs
index 297d9ed84c5..4b450804f3b 100644
--- a/compiler/rustc_target/src/target_features.rs
+++ b/compiler/rustc_target/src/target_features.rs
@@ -248,6 +248,10 @@ static AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
     ("mte", Stable, &[]),
     // FEAT_AdvSimd & FEAT_FP
     ("neon", Stable, &[]),
+    // Backend option to turn atomic operations into an intrinsic call when `lse` is not known to be
+    // available, so the intrinsic can do runtime LSE feature detection rather than unconditionally
+    // using slower non-LSE operations. Unstable since it doesn't need to user-togglable.
+    ("outline-atomics", Unstable(sym::aarch64_unstable_target_feature), &[]),
     // FEAT_PAUTH (address authentication)
     ("paca", Stable, &[]),
     // FEAT_PAUTH (generic authentication)
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
index 8551780bcd5..20e425bfad1 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
@@ -224,12 +224,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         use ty::GenericArg;
         use ty::print::Printer;
 
-        struct AbsolutePathPrinter<'tcx> {
+        struct ConflictingPathPrinter<'tcx> {
             tcx: TyCtxt<'tcx>,
             segments: Vec<Symbol>,
         }
 
-        impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
+        impl<'tcx> Printer<'tcx> for ConflictingPathPrinter<'tcx> {
             fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
                 self.tcx
             }
@@ -253,12 +253,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 unreachable!(); // because `path_generic_args` ignores the `GenericArgs`
             }
 
-            fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
+            fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
                 self.segments = vec![self.tcx.crate_name(cnum)];
                 Ok(())
             }
 
-            fn path_qualified(
+            fn print_path_with_qualified(
                 &mut self,
                 _self_ty: Ty<'tcx>,
                 _trait_ref: Option<ty::TraitRef<'tcx>>,
@@ -266,7 +266,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 Err(fmt::Error)
             }
 
-            fn path_append_impl(
+            fn print_path_with_impl(
                 &mut self,
                 _print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
                 _self_ty: Ty<'tcx>,
@@ -275,7 +275,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 Err(fmt::Error)
             }
 
-            fn path_append(
+            fn print_path_with_simple(
                 &mut self,
                 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
                 disambiguated_data: &DisambiguatedDefPathData,
@@ -285,7 +285,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 Ok(())
             }
 
-            fn path_generic_args(
+            fn print_path_with_generic_args(
                 &mut self,
                 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
                 _args: &[GenericArg<'tcx>],
@@ -300,28 +300,27 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             // let _ = [{struct Foo; Foo}, {struct Foo; Foo}];
             if did1.krate != did2.krate {
                 let abs_path = |def_id| {
-                    let mut p = AbsolutePathPrinter { tcx: self.tcx, segments: vec![] };
+                    let mut p = ConflictingPathPrinter { tcx: self.tcx, segments: vec![] };
                     p.print_def_path(def_id, &[]).map(|_| p.segments)
                 };
 
-                // We compare strings because DefPath can be different
-                // for imported and non-imported crates
+                // We compare strings because DefPath can be different for imported and
+                // non-imported crates.
                 let expected_str = self.tcx.def_path_str(did1);
                 let found_str = self.tcx.def_path_str(did2);
                 let Ok(expected_abs) = abs_path(did1) else { return false };
                 let Ok(found_abs) = abs_path(did2) else { return false };
-                let same_path = || -> Result<_, PrintError> {
-                    Ok(expected_str == found_str || expected_abs == found_abs)
-                };
-                // We want to use as unique a type path as possible. If both types are "locally
-                // known" by the same name, we use the "absolute path" which uses the original
-                // crate name instead.
-                let (expected, found) = if expected_str == found_str {
-                    (join_path_syms(&expected_abs), join_path_syms(&found_abs))
-                } else {
-                    (expected_str.clone(), found_str.clone())
-                };
-                if same_path().unwrap_or(false) {
+                let same_path = expected_str == found_str || expected_abs == found_abs;
+                if same_path {
+                    // We want to use as unique a type path as possible. If both types are "locally
+                    // known" by the same name, we use the "absolute path" which uses the original
+                    // crate name instead.
+                    let (expected, found) = if expected_str == found_str {
+                        (join_path_syms(&expected_abs), join_path_syms(&found_abs))
+                    } else {
+                        (expected_str.clone(), found_str.clone())
+                    };
+
                     // We've displayed "expected `a::b`, found `a::b`". We add context to
                     // differentiate the different cases where that might happen.
                     let expected_crate_name = self.tcx.crate_name(did1.krate);
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs
index 8f0f6d0bf26..f31a85ec07a 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs
@@ -946,7 +946,7 @@ fn foo(&self) -> Self::T { String::new() }
 
     pub fn format_generic_args(&self, args: &[ty::GenericArg<'tcx>]) -> String {
         FmtPrinter::print_string(self.tcx, hir::def::Namespace::TypeNS, |p| {
-            p.path_generic_args(|_| Ok(()), args)
+            p.print_path_with_generic_args(|_| Ok(()), args)
         })
         .expect("could not write to `String`.")
     }
diff --git a/compiler/rustc_type_ir/src/search_graph/mod.rs b/compiler/rustc_type_ir/src/search_graph/mod.rs
index daacb4a3e44..24094eeea8d 100644
--- a/compiler/rustc_type_ir/src/search_graph/mod.rs
+++ b/compiler/rustc_type_ir/src/search_graph/mod.rs
@@ -12,10 +12,11 @@
 //! The global cache has to be completely unobservable, while the per-cycle cache may impact
 //! behavior as long as the resulting behavior is still correct.
 use std::cmp::Ordering;
-use std::collections::BTreeMap;
 use std::collections::hash_map::Entry;
+use std::collections::{BTreeMap, btree_map};
 use std::fmt::Debug;
 use std::hash::Hash;
+use std::iter;
 use std::marker::PhantomData;
 
 use derive_where::derive_where;
@@ -230,13 +231,19 @@ impl AvailableDepth {
     }
 }
 
+#[derive(Clone, Copy, Debug)]
+struct CycleHead {
+    paths_to_head: PathsToNested,
+    usage_kind: UsageKind,
+}
+
 /// All cycle heads a given goal depends on, ordered by their stack depth.
 ///
 /// We also track all paths from this goal to that head. This is necessary
 /// when rebasing provisional cache results.
 #[derive(Clone, Debug, Default)]
 struct CycleHeads {
-    heads: BTreeMap<StackDepth, PathsToNested>,
+    heads: BTreeMap<StackDepth, CycleHead>,
 }
 
 impl CycleHeads {
@@ -256,32 +263,32 @@ impl CycleHeads {
         self.heads.first_key_value().map(|(k, _)| *k)
     }
 
-    fn remove_highest_cycle_head(&mut self) -> PathsToNested {
+    fn remove_highest_cycle_head(&mut self) -> CycleHead {
         let last = self.heads.pop_last();
         last.unwrap().1
     }
 
-    fn insert(&mut self, head: StackDepth, path_from_entry: impl Into<PathsToNested> + Copy) {
-        *self.heads.entry(head).or_insert(path_from_entry.into()) |= path_from_entry.into();
+    fn insert(
+        &mut self,
+        head_index: StackDepth,
+        path_from_entry: impl Into<PathsToNested> + Copy,
+        usage_kind: UsageKind,
+    ) {
+        match self.heads.entry(head_index) {
+            btree_map::Entry::Vacant(entry) => {
+                entry.insert(CycleHead { paths_to_head: path_from_entry.into(), usage_kind });
+            }
+            btree_map::Entry::Occupied(entry) => {
+                let head = entry.into_mut();
+                head.paths_to_head |= path_from_entry.into();
+                head.usage_kind = head.usage_kind.merge(usage_kind);
+            }
+        }
     }
 
-    fn iter(&self) -> impl Iterator<Item = (StackDepth, PathsToNested)> + '_ {
+    fn iter(&self) -> impl Iterator<Item = (StackDepth, CycleHead)> + '_ {
         self.heads.iter().map(|(k, v)| (*k, *v))
     }
-
-    /// Update the cycle heads of a goal at depth `this` given the cycle heads
-    /// of a nested goal. This merges the heads after filtering the parent goal
-    /// itself.
-    fn extend_from_child(&mut self, this: StackDepth, step_kind: PathKind, child: &CycleHeads) {
-        for (&head, &path_from_entry) in child.heads.iter() {
-            match head.cmp(&this) {
-                Ordering::Less => {}
-                Ordering::Equal => continue,
-                Ordering::Greater => unreachable!(),
-            }
-            self.insert(head, path_from_entry.extend_with(step_kind));
-        }
-    }
 }
 
 bitflags::bitflags! {
@@ -487,9 +494,6 @@ impl<X: Cx> EvaluationResult<X> {
 
 pub struct SearchGraph<D: Delegate<Cx = X>, X: Cx = <D as Delegate>::Cx> {
     root_depth: AvailableDepth,
-    /// The stack of goals currently being computed.
-    ///
-    /// An element is *deeper* in the stack if its index is *lower*.
     stack: Stack<X>,
     /// The provisional cache contains entries for already computed goals which
     /// still depend on goals higher-up in the stack. We don't move them to the
@@ -511,6 +515,7 @@ pub struct SearchGraph<D: Delegate<Cx = X>, X: Cx = <D as Delegate>::Cx> {
 /// cache entry.
 enum UpdateParentGoalCtxt<'a, X: Cx> {
     Ordinary(&'a NestedGoals<X>),
+    CycleOnStack(X::Input),
     ProvisionalCacheHit,
 }
 
@@ -532,21 +537,42 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
         stack: &mut Stack<X>,
         step_kind_from_parent: PathKind,
         required_depth_for_nested: usize,
-        heads: &CycleHeads,
+        heads: impl Iterator<Item = (StackDepth, CycleHead)>,
         encountered_overflow: bool,
         context: UpdateParentGoalCtxt<'_, X>,
     ) {
-        if let Some(parent_index) = stack.last_index() {
-            let parent = &mut stack[parent_index];
+        if let Some((parent_index, parent)) = stack.last_mut_with_index() {
             parent.required_depth = parent.required_depth.max(required_depth_for_nested + 1);
             parent.encountered_overflow |= encountered_overflow;
 
-            parent.heads.extend_from_child(parent_index, step_kind_from_parent, heads);
+            for (head_index, head) in heads {
+                match head_index.cmp(&parent_index) {
+                    Ordering::Less => parent.heads.insert(
+                        head_index,
+                        head.paths_to_head.extend_with(step_kind_from_parent),
+                        head.usage_kind,
+                    ),
+                    Ordering::Equal => {
+                        let usage_kind = parent
+                            .has_been_used
+                            .map_or(head.usage_kind, |prev| prev.merge(head.usage_kind));
+                        parent.has_been_used = Some(usage_kind);
+                    }
+                    Ordering::Greater => unreachable!(),
+                }
+            }
             let parent_depends_on_cycle = match context {
                 UpdateParentGoalCtxt::Ordinary(nested_goals) => {
                     parent.nested_goals.extend_from_child(step_kind_from_parent, nested_goals);
                     !nested_goals.is_empty()
                 }
+                UpdateParentGoalCtxt::CycleOnStack(head) => {
+                    // We lookup provisional cache entries before detecting cycles.
+                    // We therefore can't use a global cache entry if it contains a cycle
+                    // whose head is in the provisional cache.
+                    parent.nested_goals.insert(head, step_kind_from_parent.into());
+                    true
+                }
                 UpdateParentGoalCtxt::ProvisionalCacheHit => true,
             };
             // Once we've got goals which encountered overflow or a cycle,
@@ -674,7 +700,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
             &mut self.stack,
             step_kind_from_parent,
             evaluation_result.required_depth,
-            &evaluation_result.heads,
+            evaluation_result.heads.iter(),
             evaluation_result.encountered_overflow,
             UpdateParentGoalCtxt::Ordinary(&evaluation_result.nested_goals),
         );
@@ -772,7 +798,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
         stack_entry: &StackEntry<X>,
         mut mutate_result: impl FnMut(X::Input, X::Result) -> X::Result,
     ) {
-        let popped_head = self.stack.next_index();
+        let popped_head_index = self.stack.next_index();
         #[allow(rustc::potential_query_instability)]
         self.provisional_cache.retain(|&input, entries| {
             entries.retain_mut(|entry| {
@@ -782,7 +808,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
                     path_from_head,
                     result,
                 } = entry;
-                let ep = if heads.highest_cycle_head() == popped_head {
+                let popped_head = if heads.highest_cycle_head() == popped_head_index {
                     heads.remove_highest_cycle_head()
                 } else {
                     return true;
@@ -795,9 +821,14 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
                 //
                 // After rebasing the cycles `hph` will go through `e`. We need to make
                 // sure that forall possible paths `hep`, `heph` is equal to `hph.`
-                for (h, ph) in stack_entry.heads.iter() {
-                    let hp =
-                        Self::cycle_path_kind(&self.stack, stack_entry.step_kind_from_parent, h);
+                let ep = popped_head.paths_to_head;
+                for (head_index, head) in stack_entry.heads.iter() {
+                    let ph = head.paths_to_head;
+                    let hp = Self::cycle_path_kind(
+                        &self.stack,
+                        stack_entry.step_kind_from_parent,
+                        head_index,
+                    );
 
                     // We first validate that all cycles while computing `p` would stay
                     // the same if we were to recompute it as a nested goal of `e`.
@@ -817,7 +848,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
                     // the heads of `e` to make sure that rebasing `e` again also considers
                     // them.
                     let eph = ep.extend_with_paths(ph);
-                    heads.insert(h, eph);
+                    heads.insert(head_index, eph, head.usage_kind);
                 }
 
                 let Some(head) = heads.opt_highest_cycle_head() else {
@@ -877,11 +908,10 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
                     &mut self.stack,
                     step_kind_from_parent,
                     0,
-                    heads,
+                    heads.iter(),
                     encountered_overflow,
                     UpdateParentGoalCtxt::ProvisionalCacheHit,
                 );
-                debug_assert!(self.stack[head].has_been_used.is_some());
                 debug!(?head, ?path_from_head, "provisional cache hit");
                 return Some(result);
             }
@@ -993,12 +1023,12 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
 
             // We don't move cycle participants to the global cache, so the
             // cycle heads are always empty.
-            let heads = Default::default();
+            let heads = iter::empty();
             Self::update_parent_goal(
                 &mut self.stack,
                 step_kind_from_parent,
                 required_depth,
-                &heads,
+                heads,
                 encountered_overflow,
                 UpdateParentGoalCtxt::Ordinary(nested_goals),
             );
@@ -1014,34 +1044,31 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
         input: X::Input,
         step_kind_from_parent: PathKind,
     ) -> Option<X::Result> {
-        let head = self.stack.find(input)?;
+        let head_index = self.stack.find(input)?;
         // We have a nested goal which directly relies on a goal deeper in the stack.
         //
         // We start by tagging all cycle participants, as that's necessary for caching.
         //
         // Finally we can return either the provisional response or the initial response
         // in case we're in the first fixpoint iteration for this goal.
-        let path_kind = Self::cycle_path_kind(&self.stack, step_kind_from_parent, head);
-        debug!(?path_kind, "encountered cycle with depth {head:?}");
-        let usage_kind = UsageKind::Single(path_kind);
-        self.stack[head].has_been_used =
-            Some(self.stack[head].has_been_used.map_or(usage_kind, |prev| prev.merge(usage_kind)));
-
-        // Subtle: when encountering a cyclic goal, we still first checked for overflow,
-        // so we have to update the reached depth.
-        let last_index = self.stack.last_index().unwrap();
-        let last = &mut self.stack[last_index];
-        last.required_depth = last.required_depth.max(1);
-
-        last.nested_goals.insert(input, step_kind_from_parent.into());
-        last.nested_goals.insert(last.input, PathsToNested::EMPTY);
-        if last_index != head {
-            last.heads.insert(head, step_kind_from_parent);
-        }
+        let path_kind = Self::cycle_path_kind(&self.stack, step_kind_from_parent, head_index);
+        debug!(?path_kind, "encountered cycle with depth {head_index:?}");
+        let head = CycleHead {
+            paths_to_head: step_kind_from_parent.into(),
+            usage_kind: UsageKind::Single(path_kind),
+        };
+        Self::update_parent_goal(
+            &mut self.stack,
+            step_kind_from_parent,
+            0,
+            iter::once((head_index, head)),
+            false,
+            UpdateParentGoalCtxt::CycleOnStack(input),
+        );
 
         // Return the provisional result or, if we're in the first iteration,
         // start with no constraints.
-        if let Some(result) = self.stack[head].provisional_result {
+        if let Some(result) = self.stack[head_index].provisional_result {
             Some(result)
         } else {
             Some(D::initial_provisional_result(cx, path_kind, input))
diff --git a/compiler/rustc_type_ir/src/search_graph/stack.rs b/compiler/rustc_type_ir/src/search_graph/stack.rs
index a58cd82b023..ea99dc6e7fd 100644
--- a/compiler/rustc_type_ir/src/search_graph/stack.rs
+++ b/compiler/rustc_type_ir/src/search_graph/stack.rs
@@ -1,4 +1,4 @@
-use std::ops::{Index, IndexMut};
+use std::ops::Index;
 
 use derive_where::derive_where;
 use rustc_index::IndexVec;
@@ -48,6 +48,12 @@ pub(super) struct StackEntry<X: Cx> {
     pub nested_goals: NestedGoals<X>,
 }
 
+/// The stack of goals currently being computed.
+///
+/// An element is *deeper* in the stack if its index is *lower*.
+///
+/// Only the last entry of the stack is mutable. All other entries get
+/// lazily updated in `update_parent_goal`.
 #[derive_where(Default; X: Cx)]
 pub(super) struct Stack<X: Cx> {
     entries: IndexVec<StackDepth, StackEntry<X>>,
@@ -62,10 +68,6 @@ impl<X: Cx> Stack<X> {
         self.entries.len()
     }
 
-    pub(super) fn last_index(&self) -> Option<StackDepth> {
-        self.entries.last_index()
-    }
-
     pub(super) fn last(&self) -> Option<&StackEntry<X>> {
         self.entries.raw.last()
     }
@@ -74,6 +76,10 @@ impl<X: Cx> Stack<X> {
         self.entries.raw.last_mut()
     }
 
+    pub(super) fn last_mut_with_index(&mut self) -> Option<(StackDepth, &mut StackEntry<X>)> {
+        self.entries.last_index().map(|idx| (idx, &mut self.entries[idx]))
+    }
+
     pub(super) fn next_index(&self) -> StackDepth {
         self.entries.next_index()
     }
@@ -108,9 +114,3 @@ impl<X: Cx> Index<StackDepth> for Stack<X> {
         &self.entries[index]
     }
 }
-
-impl<X: Cx> IndexMut<StackDepth> for Stack<X> {
-    fn index_mut(&mut self, index: StackDepth) -> &mut Self::Output {
-        &mut self.entries[index]
-    }
-}
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index c8feba48d84..2a236de0192 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -18,7 +18,7 @@ use serde_derive::Deserialize;
 #[cfg(feature = "tracing")]
 use tracing::{instrument, span};
 
-use crate::core::build_steps::gcc::{Gcc, add_cg_gcc_cargo_flags};
+use crate::core::build_steps::gcc::{Gcc, GccOutput, add_cg_gcc_cargo_flags};
 use crate::core::build_steps::tool::{RustcPrivateCompilers, SourceType, copy_lld_artifacts};
 use crate::core::build_steps::{dist, llvm};
 use crate::core::builder;
@@ -1543,13 +1543,22 @@ impl Step for RustcLink {
     }
 }
 
+/// Output of the `compile::GccCodegenBackend` step.
+/// It includes the path to the libgccjit library on which this backend depends.
+#[derive(Clone)]
+pub struct GccCodegenBackendOutput {
+    stamp: BuildStamp,
+    gcc: GccOutput,
+}
+
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct GccCodegenBackend {
     compilers: RustcPrivateCompilers,
 }
 
 impl Step for GccCodegenBackend {
-    type Output = BuildStamp;
+    type Output = GccCodegenBackendOutput;
+
     const ONLY_HOSTS: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
@@ -1584,6 +1593,8 @@ impl Step for GccCodegenBackend {
             &CodegenBackendKind::Gcc,
         );
 
+        let gcc = builder.ensure(Gcc { target });
+
         if builder.config.keep_stage.contains(&build_compiler.stage) {
             trace!("`keep-stage` requested");
             builder.info(
@@ -1592,7 +1603,7 @@ impl Step for GccCodegenBackend {
             );
             // Codegen backends are linked separately from this step today, so we don't do
             // anything here.
-            return stamp;
+            return GccCodegenBackendOutput { stamp, gcc };
         }
 
         let mut cargo = builder::Cargo::new(
@@ -1606,13 +1617,16 @@ impl Step for GccCodegenBackend {
         cargo.arg("--manifest-path").arg(builder.src.join("compiler/rustc_codegen_gcc/Cargo.toml"));
         rustc_cargo_env(builder, &mut cargo, target);
 
-        let gcc = builder.ensure(Gcc { target });
         add_cg_gcc_cargo_flags(&mut cargo, &gcc);
 
         let _guard =
             builder.msg(Kind::Build, "codegen backend gcc", Mode::Codegen, build_compiler, target);
         let files = run_cargo(builder, cargo, vec![], &stamp, vec![], false, false);
-        write_codegen_backend_stamp(stamp, files, builder.config.dry_run())
+
+        GccCodegenBackendOutput {
+            stamp: write_codegen_backend_stamp(stamp, files, builder.config.dry_run()),
+            gcc,
+        }
     }
 
     fn metadata(&self) -> Option<StepMetadata> {
@@ -2191,53 +2205,6 @@ impl Step for Assemble {
         );
         build_compiler.stage = actual_stage;
 
-        let mut codegen_backend_stamps = vec![];
-        {
-            #[cfg(feature = "tracing")]
-            let _codegen_backend_span =
-                span!(tracing::Level::DEBUG, "building requested codegen backends").entered();
-
-            for backend in builder.config.enabled_codegen_backends(target_compiler.host) {
-                // FIXME: this is a horrible hack used to make `x check` work when other codegen
-                // backends are enabled.
-                // `x check` will check stage 1 rustc, which copies its rmetas to the stage0 sysroot.
-                // Then it checks codegen backends, which correctly use these rmetas.
-                // Then it needs to check std, but for that it needs to build stage 1 rustc.
-                // This copies the build rmetas into the stage0 sysroot, effectively poisoning it,
-                // because we then have both check and build rmetas in the same sysroot.
-                // That would be fine on its own. However, when another codegen backend is enabled,
-                // then building stage 1 rustc implies also building stage 1 codegen backend (even if
-                // it isn't used for anything). And since that tries to use the poisoned
-                // rmetas, it fails to build.
-                // We don't actually need to build rustc-private codegen backends for checking std,
-                // so instead we skip that.
-                // Note: this would be also an issue for other rustc-private tools, but that is "solved"
-                // by check::Std being last in the list of checked things (see
-                // `Builder::get_step_descriptions`).
-                if builder.kind == Kind::Check && builder.top_stage == 1 {
-                    continue;
-                }
-
-                let prepare_compilers = || {
-                    RustcPrivateCompilers::from_build_and_target_compiler(
-                        build_compiler,
-                        target_compiler,
-                    )
-                };
-
-                let stamp = match backend {
-                    CodegenBackendKind::Cranelift => {
-                        builder.ensure(CraneliftCodegenBackend { compilers: prepare_compilers() })
-                    }
-                    CodegenBackendKind::Gcc => {
-                        builder.ensure(GccCodegenBackend { compilers: prepare_compilers() })
-                    }
-                    CodegenBackendKind::Llvm | CodegenBackendKind::Custom(_) => continue,
-                };
-                codegen_backend_stamps.push(stamp);
-            }
-        }
-
         let stage = target_compiler.stage;
         let host = target_compiler.host;
         let (host_info, dir_name) = if build_compiler.host == host {
@@ -2296,9 +2263,56 @@ impl Step for Assemble {
             }
         }
 
-        debug!("copying codegen backends to sysroot");
-        for stamp in codegen_backend_stamps {
-            copy_codegen_backends_to_sysroot(builder, stamp, target_compiler);
+        {
+            #[cfg(feature = "tracing")]
+            let _codegen_backend_span =
+                span!(tracing::Level::DEBUG, "building requested codegen backends").entered();
+
+            for backend in builder.config.enabled_codegen_backends(target_compiler.host) {
+                // FIXME: this is a horrible hack used to make `x check` work when other codegen
+                // backends are enabled.
+                // `x check` will check stage 1 rustc, which copies its rmetas to the stage0 sysroot.
+                // Then it checks codegen backends, which correctly use these rmetas.
+                // Then it needs to check std, but for that it needs to build stage 1 rustc.
+                // This copies the build rmetas into the stage0 sysroot, effectively poisoning it,
+                // because we then have both check and build rmetas in the same sysroot.
+                // That would be fine on its own. However, when another codegen backend is enabled,
+                // then building stage 1 rustc implies also building stage 1 codegen backend (even if
+                // it isn't used for anything). And since that tries to use the poisoned
+                // rmetas, it fails to build.
+                // We don't actually need to build rustc-private codegen backends for checking std,
+                // so instead we skip that.
+                // Note: this would be also an issue for other rustc-private tools, but that is "solved"
+                // by check::Std being last in the list of checked things (see
+                // `Builder::get_step_descriptions`).
+                if builder.kind == Kind::Check && builder.top_stage == 1 {
+                    continue;
+                }
+
+                let prepare_compilers = || {
+                    RustcPrivateCompilers::from_build_and_target_compiler(
+                        build_compiler,
+                        target_compiler,
+                    )
+                };
+
+                match backend {
+                    CodegenBackendKind::Cranelift => {
+                        let stamp = builder
+                            .ensure(CraneliftCodegenBackend { compilers: prepare_compilers() });
+                        copy_codegen_backends_to_sysroot(builder, stamp, target_compiler);
+                    }
+                    CodegenBackendKind::Gcc => {
+                        let output =
+                            builder.ensure(GccCodegenBackend { compilers: prepare_compilers() });
+                        copy_codegen_backends_to_sysroot(builder, output.stamp, target_compiler);
+                        // Also copy libgccjit to the library sysroot, so that it is available for
+                        // the codegen backend.
+                        output.gcc.install_to(builder, &rustc_libdir);
+                    }
+                    CodegenBackendKind::Llvm | CodegenBackendKind::Custom(_) => continue,
+                }
+            }
         }
 
         if builder.config.lld_enabled {
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index e63f9e14efe..7fdfeabe29d 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -478,7 +478,19 @@ impl Step for Rustc {
             if libdir_relative.to_str() != Some("bin") {
                 let libdir = builder.rustc_libdir(compiler);
                 for entry in builder.read_dir(&libdir) {
-                    if is_dylib(&entry.path()) {
+                    // A safeguard that we will not ship libgccjit.so from the libdir, in case the
+                    // GCC codegen backend is enabled by default.
+                    // Long-term we should probably split the config options for:
+                    // - Include cg_gcc in the rustc sysroot by default
+                    // - Run dist of a specific codegen backend in `x dist` by default
+                    if is_dylib(&entry.path())
+                        && !entry
+                            .path()
+                            .file_name()
+                            .and_then(|n| n.to_str())
+                            .map(|n| n.contains("libgccjit"))
+                            .unwrap_or(false)
+                    {
                         // Don't use custom libdir here because ^lib/ will be resolved again
                         // with installer
                         builder.install(&entry.path(), &image.join("lib"), FileType::NativeLibrary);
diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs
index d4cbbe60921..389afaecea3 100644
--- a/src/bootstrap/src/core/build_steps/gcc.rs
+++ b/src/bootstrap/src/core/build_steps/gcc.rs
@@ -12,6 +12,7 @@ use std::fs;
 use std::path::{Path, PathBuf};
 use std::sync::OnceLock;
 
+use crate::FileType;
 use crate::core::builder::{Builder, Cargo, Kind, RunConfig, ShouldRun, Step};
 use crate::core::config::TargetSelection;
 use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash};
@@ -28,6 +29,21 @@ pub struct GccOutput {
     pub libgccjit: PathBuf,
 }
 
+impl GccOutput {
+    /// Install the required libgccjit library file(s) to the specified `path`.
+    pub fn install_to(&self, builder: &Builder<'_>, directory: &Path) {
+        // At build time, cg_gcc has to link to libgccjit.so (the unversioned symbol).
+        // However, at runtime, it will by default look for libgccjit.so.0.
+        // So when we install the built libgccjit.so file to the target `directory`, we add it there
+        // with the `.0` suffix.
+        let mut target_filename = self.libgccjit.file_name().unwrap().to_str().unwrap().to_string();
+        target_filename.push_str(".0");
+
+        let dst = directory.join(target_filename);
+        builder.copy_link(&self.libgccjit, &dst, FileType::NativeLibrary);
+    }
+}
+
 impl Step for Gcc {
     type Output = GccOutput;
 
@@ -61,7 +77,6 @@ impl Step for Gcc {
         }
 
         build_gcc(&metadata, builder, target);
-        create_lib_alias(builder, &libgccjit_path);
 
         t!(metadata.stamp.write());
 
@@ -69,15 +84,6 @@ impl Step for Gcc {
     }
 }
 
-/// Creates a libgccjit.so.0 alias next to libgccjit.so if it does not
-/// already exist
-fn create_lib_alias(builder: &Builder<'_>, libgccjit: &PathBuf) {
-    let lib_alias = libgccjit.parent().unwrap().join("libgccjit.so.0");
-    if !lib_alias.exists() {
-        t!(builder.symlink_file(libgccjit, lib_alias));
-    }
-}
-
 pub struct Meta {
     stamp: BuildStamp,
     out_dir: PathBuf,
@@ -124,7 +130,6 @@ fn try_download_gcc(builder: &Builder<'_>, target: TargetSelection) -> Option<Pa
             }
 
             let libgccjit = root.join("lib").join("libgccjit.so");
-            create_lib_alias(builder, &libgccjit);
             Some(libgccjit)
         }
         PathFreshness::HasLocalModifications { .. } => {
diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs
index 3080e641b5b..bd02131b7fe 100644
--- a/src/bootstrap/src/core/sanity.rs
+++ b/src/bootstrap/src/core/sanity.rs
@@ -35,6 +35,7 @@ pub struct Finder {
 const STAGE0_MISSING_TARGETS: &[&str] = &[
     "armv7a-vex-v5",
     // just a dummy comment so the list doesn't get onelined
+    "aarch64_be-unknown-none-softfloat",
 ];
 
 /// Minimum version threshold for libstdc++ required when using prebuilt LLVM
diff --git a/src/ci/docker/scripts/freebsd-toolchain.sh b/src/ci/docker/scripts/freebsd-toolchain.sh
index 0d02636db91..56b3c5cd82e 100755
--- a/src/ci/docker/scripts/freebsd-toolchain.sh
+++ b/src/ci/docker/scripts/freebsd-toolchain.sh
@@ -28,7 +28,9 @@ exit 1
 # First up, build binutils
 mkdir binutils
 cd binutils
-curl https://ftp.gnu.org/gnu/binutils/binutils-${binutils_version}.tar.bz2 | tar xjf -
+# Originally downloaded from:
+# https://sourceware.org/pub/binutils/releases/binutils-${binutils_version}.tar.bz2
+curl https://ci-mirrors.rust-lang.org/rustc/binutils-${binutils_version}.tar.bz2 | tar xjf -
 mkdir binutils-build
 cd binutils-build
 hide_output ../binutils-${binutils_version}/configure \
diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md
index 25f154f1180..b942e4bfa61 100644
--- a/src/doc/rustc/src/SUMMARY.md
+++ b/src/doc/rustc/src/SUMMARY.md
@@ -47,6 +47,7 @@
     - [\*-apple-visionos](platform-support/apple-visionos.md)
     - [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md)
     - [aarch64-unknown-linux-musl](platform-support/aarch64-unknown-linux-musl.md)
+    - [aarch64_be-unknown-none-softfloat](platform-support/aarch64_be-unknown-none-softfloat.md)
     - [amdgcn-amd-amdhsa](platform-support/amdgcn-amd-amdhsa.md)
     - [armeb-unknown-linux-gnueabi](platform-support/armeb-unknown-linux-gnueabi.md)
     - [arm-none-eabi](platform-support/arm-none-eabi.md)
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index 8ebaa8dd874..89b43cda9b9 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -273,6 +273,7 @@ target | std | host | notes
 `aarch64_be-unknown-linux-gnu` | ✓ | ✓ | ARM64 Linux (big-endian)
 `aarch64_be-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (big-endian, ILP32 ABI)
 [`aarch64_be-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | ARM64 NetBSD (big-endian)
+[`aarch64_be-unknown-none-softfloat`](platform-support/aarch64_be-unknown-none-softfloat.md) | * |  | Bare big-endian ARM64, softfloat
 [`amdgcn-amd-amdhsa`](platform-support/amdgcn-amd-amdhsa.md) | * |  | `-Ctarget-cpu=gfx...` to specify [the AMD GPU] to compile for
 [`arm64_32-apple-watchos`](platform-support/apple-watchos.md) | ✓ |  | Arm Apple WatchOS 64-bit with 32-bit pointers
 [`arm64e-apple-darwin`](platform-support/arm64e-apple-darwin.md)  | ✓ | ✓ | ARM64e Apple Darwin
diff --git a/src/doc/rustc/src/platform-support/aarch64_be-unknown-none-softfloat.md b/src/doc/rustc/src/platform-support/aarch64_be-unknown-none-softfloat.md
new file mode 100644
index 00000000000..a28ddcdf7f2
--- /dev/null
+++ b/src/doc/rustc/src/platform-support/aarch64_be-unknown-none-softfloat.md
@@ -0,0 +1,74 @@
+# aarch64_be-unknown-none-softfloat
+
+**Tier: 3**
+
+Target for freestanding/bare-metal big-endian ARM64 binaries in ELF format:
+firmware, kernels, etc.
+
+## Target maintainers
+
+[@Gelbpunkt](https://github.com/Gelbpunkt)
+
+## Requirements
+
+This target is cross-compiled. There is no support for `std`. There is no
+default allocator, but it's possible to use `alloc` by supplying an allocator.
+
+The target does not assume existence of a FPU and does not make use of any
+non-GPR register. This allows the generated code to run in environments, such
+as kernels, which may need to avoid the use of such registers or which may have
+special considerations about the use of such registers (e.g. saving and
+restoring them to avoid breaking userspace code using the same registers). You
+can change code generation to use additional CPU features via the
+`-C target-feature=` codegen options to rustc, or via the `#[target_feature]`
+mechanism within Rust code.
+
+By default, code generated with the soft-float target should run on any
+big-endian ARM64 hardware, enabling additional target features may raise this
+baseline.
+
+`extern "C"` uses the [architecture's standard calling convention][aapcs64].
+
+[aapcs64]: https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst
+
+The targets generate binaries in the ELF format. Any alternate formats or
+special considerations for binary layout will require linker options or linker
+scripts.
+
+## Building the target
+
+You can build Rust with support for the target by adding it to the `target`
+list in `bootstrap.toml`:
+
+```toml
+[build]
+target = ["aarch64_be-unknown-none-softfloat"]
+```
+
+## Building Rust programs
+
+Rust does not yet ship pre-compiled artifacts for this target. To compile for
+this target, you will first need to build Rust with the target enabled (see
+"Building the target" above).
+
+## Cross-compilation
+
+For cross builds, you will need an appropriate ARM64 C/C++ toolchain for
+linking, or if you want to compile C code along with Rust (such as for Rust
+crates with C dependencies).
+
+Rust *may* be able to use an `aarch64_be-unknown-linux-{gnu,musl}-` toolchain
+with appropriate standalone flags to build for this target (depending on the
+assumptions of that toolchain, see below), or you may wish to use a separate
+`aarch64_be-unknown-none-softfloat` toolchain.
+
+On some ARM64 hosts that use ELF binaries, you *may* be able to use the host C
+toolchain, if it does not introduce assumptions about the host environment that
+don't match the expectations of a standalone environment. Otherwise, you may
+need a separate toolchain for standalone/freestanding development, just as when
+cross-compiling from a non-ARM64 platform.
+
+## Testing
+
+As the target supports a variety of different environments and does not support
+`std`, it does not support running the Rust test suite.
diff --git a/src/doc/rustdoc/src/write-documentation/documentation-tests.md b/src/doc/rustdoc/src/write-documentation/documentation-tests.md
index e6b15e0dbd3..4084c1d962a 100644
--- a/src/doc/rustdoc/src/write-documentation/documentation-tests.md
+++ b/src/doc/rustdoc/src/write-documentation/documentation-tests.md
@@ -191,6 +191,20 @@ We can document it by escaping the initial `#`:
 /// ## bar # baz";
 ```
 
+Here is an example with a macro rule which matches on tokens starting with `#`:
+
+`````rust,no_run
+/// ```
+/// macro_rules! ignore { (##tag) => {}; }
+/// ignore! {
+///     ###tag
+/// }
+/// ```
+# fn f() {}
+`````
+
+As you can see, the rule is expecting two `#`, so when calling it, we need to add an extra `#`
+because the first one is used as escape.
 
 ## Using `?` in doc tests
 
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 8c0f897c992..0d98c64bbde 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -6,7 +6,7 @@ use std::sync::Arc;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
 use rustc_hir::Mutability;
-use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def::{DefKind, MacroKinds, Res};
 use rustc_hir::def_id::{DefId, DefIdSet, LocalDefId, LocalModDefId};
 use rustc_metadata::creader::{CStore, LoadedMacro};
 use rustc_middle::ty::fast_reject::SimplifiedType;
@@ -137,13 +137,16 @@ pub(crate) fn try_inline(
                 clean::ConstantItem(Box::new(ct))
             })
         }
-        Res::Def(DefKind::Macro(kind), did) => {
-            let mac = build_macro(cx, did, name, kind);
-
-            let type_kind = match kind {
-                MacroKind::Bang => ItemType::Macro,
-                MacroKind::Attr => ItemType::ProcAttribute,
-                MacroKind::Derive => ItemType::ProcDerive,
+        Res::Def(DefKind::Macro(kinds), did) => {
+            let mac = build_macro(cx, did, name, kinds);
+
+            // FIXME: handle attributes and derives that aren't proc macros, and macros with
+            // multiple kinds
+            let type_kind = match kinds {
+                MacroKinds::BANG => ItemType::Macro,
+                MacroKinds::ATTR => ItemType::ProcAttribute,
+                MacroKinds::DERIVE => ItemType::ProcDerive,
+                _ => todo!("Handle macros with multiple kinds"),
             };
             record_extern_fqn(cx, did, type_kind);
             mac
@@ -749,22 +752,36 @@ fn build_macro(
     cx: &mut DocContext<'_>,
     def_id: DefId,
     name: Symbol,
-    macro_kind: MacroKind,
+    macro_kinds: MacroKinds,
 ) -> clean::ItemKind {
     match CStore::from_tcx(cx.tcx).load_macro_untracked(def_id, cx.tcx) {
-        LoadedMacro::MacroDef { def, .. } => match macro_kind {
-            MacroKind::Bang => clean::MacroItem(clean::Macro {
+        // FIXME: handle attributes and derives that aren't proc macros, and macros with multiple
+        // kinds
+        LoadedMacro::MacroDef { def, .. } => match macro_kinds {
+            MacroKinds::BANG => clean::MacroItem(clean::Macro {
                 source: utils::display_macro_source(cx, name, &def),
                 macro_rules: def.macro_rules,
             }),
-            MacroKind::Derive | MacroKind::Attr => {
-                clean::ProcMacroItem(clean::ProcMacro { kind: macro_kind, helpers: Vec::new() })
-            }
+            MacroKinds::DERIVE => clean::ProcMacroItem(clean::ProcMacro {
+                kind: MacroKind::Derive,
+                helpers: Vec::new(),
+            }),
+            MacroKinds::ATTR => clean::ProcMacroItem(clean::ProcMacro {
+                kind: MacroKind::Attr,
+                helpers: Vec::new(),
+            }),
+            _ => todo!("Handle macros with multiple kinds"),
         },
-        LoadedMacro::ProcMacro(ext) => clean::ProcMacroItem(clean::ProcMacro {
-            kind: ext.macro_kind(),
-            helpers: ext.helper_attrs,
-        }),
+        LoadedMacro::ProcMacro(ext) => {
+            // Proc macros can only have a single kind
+            let kind = match ext.macro_kinds() {
+                MacroKinds::BANG => MacroKind::Bang,
+                MacroKinds::ATTR => MacroKind::Attr,
+                MacroKinds::DERIVE => MacroKind::Derive,
+                _ => unreachable!(),
+            };
+            clean::ProcMacroItem(clean::ProcMacro { kind, helpers: ext.helper_attrs })
+        }
     }
 }
 
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 890bfaced6c..4ff94cc6f3b 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -40,7 +40,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet, In
 use rustc_errors::codes::*;
 use rustc_errors::{FatalError, struct_span_code_err};
 use rustc_hir::attrs::AttributeKind;
-use rustc_hir::def::{CtorKind, DefKind, Res};
+use rustc_hir::def::{CtorKind, DefKind, MacroKinds, Res};
 use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LOCAL_CRATE, LocalDefId};
 use rustc_hir::{LangItem, PredicateOrigin, find_attr};
 use rustc_hir_analysis::hir_ty_lowering::FeedConstTy;
@@ -2845,11 +2845,19 @@ fn clean_maybe_renamed_item<'tcx>(
                 generics: clean_generics(generics, cx),
                 fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(),
             }),
-            ItemKind::Macro(_, macro_def, MacroKind::Bang) => MacroItem(Macro {
+            // FIXME: handle attributes and derives that aren't proc macros, and macros with
+            // multiple kinds
+            ItemKind::Macro(_, macro_def, MacroKinds::BANG) => MacroItem(Macro {
                 source: display_macro_source(cx, name, macro_def),
                 macro_rules: macro_def.macro_rules,
             }),
-            ItemKind::Macro(_, _, macro_kind) => clean_proc_macro(item, &mut name, macro_kind, cx),
+            ItemKind::Macro(_, _, MacroKinds::ATTR) => {
+                clean_proc_macro(item, &mut name, MacroKind::Attr, cx)
+            }
+            ItemKind::Macro(_, _, MacroKinds::DERIVE) => {
+                clean_proc_macro(item, &mut name, MacroKind::Derive, cx)
+            }
+            ItemKind::Macro(_, _, _) => todo!("Handle macros with multiple kinds"),
             // proc macros can have a name set by attributes
             ItemKind::Fn { ref sig, generics, body: body_id, .. } => {
                 clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx)
diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs
index 3aba7a370ad..1dba84aa44c 100644
--- a/src/librustdoc/formats/item_type.rs
+++ b/src/librustdoc/formats/item_type.rs
@@ -2,7 +2,7 @@
 
 use std::fmt;
 
-use rustc_hir::def::{CtorOf, DefKind};
+use rustc_hir::def::{CtorOf, DefKind, MacroKinds};
 use rustc_span::hygiene::MacroKind;
 use serde::{Serialize, Serializer};
 
@@ -134,9 +134,10 @@ impl ItemType {
             DefKind::Trait => Self::Trait,
             DefKind::TyAlias => Self::TypeAlias,
             DefKind::TraitAlias => Self::TraitAlias,
-            DefKind::Macro(MacroKind::Bang) => ItemType::Macro,
-            DefKind::Macro(MacroKind::Attr) => ItemType::ProcAttribute,
-            DefKind::Macro(MacroKind::Derive) => ItemType::ProcDerive,
+            DefKind::Macro(MacroKinds::BANG) => ItemType::Macro,
+            DefKind::Macro(MacroKinds::ATTR) => ItemType::ProcAttribute,
+            DefKind::Macro(MacroKinds::DERIVE) => ItemType::ProcDerive,
+            DefKind::Macro(_) => todo!("Handle macros with multiple kinds"),
             DefKind::ForeignTy => Self::ForeignType,
             DefKind::Variant => Self::Variant,
             DefKind::Field => Self::StructField,
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 10e01b4e262..0011544d16e 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -2060,7 +2060,9 @@ class DocSearch {
             // Deprecated and unstable items and items with no description
             this.searchIndexDeprecated.set(crate, new RoaringBitmap(crateCorpus.c));
             this.searchIndexEmptyDesc.set(crate, new RoaringBitmap(crateCorpus.e));
-            this.searchIndexUnstable.set(crate, new RoaringBitmap(crateCorpus.u));
+            if (crateCorpus.u !== undefined && crateCorpus.u !== null) {
+                this.searchIndexUnstable.set(crate, new RoaringBitmap(crateCorpus.u));
+            }
             let descIndex = 0;
 
             /**
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 40191551e4f..bad51d7f5b2 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -13,7 +13,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_data_structures::intern::Interned;
 use rustc_errors::{Applicability, Diag, DiagMessage};
 use rustc_hir::def::Namespace::*;
-use rustc_hir::def::{DefKind, Namespace, PerNS};
+use rustc_hir::def::{DefKind, MacroKinds, Namespace, PerNS};
 use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE};
 use rustc_hir::{Mutability, Safety};
 use rustc_middle::ty::{Ty, TyCtxt};
@@ -25,7 +25,6 @@ use rustc_resolve::rustdoc::{
 use rustc_session::config::CrateType;
 use rustc_session::lint::Lint;
 use rustc_span::BytePos;
-use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{Ident, Symbol, sym};
 use smallvec::{SmallVec, smallvec};
 use tracing::{debug, info, instrument, trace};
@@ -115,9 +114,11 @@ impl Res {
 
         let prefix = match kind {
             DefKind::Fn | DefKind::AssocFn => return Suggestion::Function,
-            DefKind::Macro(MacroKind::Bang) => return Suggestion::Macro,
+            // FIXME: handle macros with multiple kinds, and attribute/derive macros that aren't
+            // proc macros
+            DefKind::Macro(MacroKinds::BANG) => return Suggestion::Macro,
 
-            DefKind::Macro(MacroKind::Derive) => "derive",
+            DefKind::Macro(MacroKinds::DERIVE) => "derive",
             DefKind::Struct => "struct",
             DefKind::Enum => "enum",
             DefKind::Trait => "trait",
@@ -881,9 +882,12 @@ fn trait_impls_for<'a>(
 fn is_derive_trait_collision<T>(ns: &PerNS<Result<Vec<(Res, T)>, ResolutionFailure<'_>>>) -> bool {
     if let (Ok(type_ns), Ok(macro_ns)) = (&ns.type_ns, &ns.macro_ns) {
         type_ns.iter().any(|(res, _)| matches!(res, Res::Def(DefKind::Trait, _)))
-            && macro_ns
-                .iter()
-                .any(|(res, _)| matches!(res, Res::Def(DefKind::Macro(MacroKind::Derive), _)))
+            && macro_ns.iter().any(|(res, _)| {
+                matches!(
+                    res,
+                    Res::Def(DefKind::Macro(kinds), _) if kinds.contains(MacroKinds::DERIVE)
+                )
+            })
     } else {
         false
     }
@@ -1674,11 +1678,11 @@ impl Disambiguator {
 
         let suffixes = [
             // If you update this list, please also update the relevant rustdoc book section!
-            ("!()", DefKind::Macro(MacroKind::Bang)),
-            ("!{}", DefKind::Macro(MacroKind::Bang)),
-            ("![]", DefKind::Macro(MacroKind::Bang)),
+            ("!()", DefKind::Macro(MacroKinds::BANG)),
+            ("!{}", DefKind::Macro(MacroKinds::BANG)),
+            ("![]", DefKind::Macro(MacroKinds::BANG)),
             ("()", DefKind::Fn),
-            ("!", DefKind::Macro(MacroKind::Bang)),
+            ("!", DefKind::Macro(MacroKinds::BANG)),
         ];
 
         if let Some(idx) = link.find('@') {
@@ -1697,7 +1701,7 @@ impl Disambiguator {
                     safety: Safety::Safe,
                 }),
                 "function" | "fn" | "method" => Kind(DefKind::Fn),
-                "derive" => Kind(DefKind::Macro(MacroKind::Derive)),
+                "derive" => Kind(DefKind::Macro(MacroKinds::DERIVE)),
                 "field" => Kind(DefKind::Field),
                 "variant" => Kind(DefKind::Variant),
                 "type" => NS(Namespace::TypeNS),
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index 9058277d72e..b2e4b594375 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -5,7 +5,7 @@ use std::mem;
 
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_hir as hir;
-use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def::{DefKind, MacroKinds, Res};
 use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LocalDefIdSet};
 use rustc_hir::intravisit::{Visitor, walk_body, walk_item};
 use rustc_hir::{CRATE_HIR_ID, Node};
@@ -13,7 +13,6 @@ use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::Span;
 use rustc_span::def_id::{CRATE_DEF_ID, LOCAL_CRATE};
-use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{Symbol, kw, sym};
 use tracing::debug;
 
@@ -325,7 +324,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
 
         let is_bang_macro = matches!(
             item,
-            Node::Item(&hir::Item { kind: hir::ItemKind::Macro(_, _, MacroKind::Bang), .. })
+            Node::Item(&hir::Item { kind: hir::ItemKind::Macro(_, _, kinds), .. }) if kinds.contains(MacroKinds::BANG)
         );
 
         if !self.view_item_stack.insert(res_did) && !is_bang_macro {
@@ -406,7 +405,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             // attribute can still be visible.
             || match item.kind {
                 hir::ItemKind::Impl(..) => true,
-                hir::ItemKind::Macro(_, _, MacroKind::Bang) => {
+                hir::ItemKind::Macro(_, _, _) => {
                     self.cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export)
                 }
                 _ => false,
diff --git a/src/llvm-project b/src/llvm-project
-Subproject d35840afa50d2615835d6a836f1967c57008188
+Subproject 9a1f898064f52269bc94675dcbd620b46d45d17
diff --git a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
index 95e16aae40f..945bb84708f 100644
--- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
+++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
@@ -8,7 +8,6 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, QPath, TyKind, Variant, VariantData};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
-use rustc_span::MacroKind;
 use rustc_span::symbol::Symbol;
 
 declare_clippy_lint! {
@@ -503,8 +502,8 @@ impl LateLintPass<'_> for ItemNameRepetitions {
                 );
             }
 
-            let is_macro_rule = matches!(item.kind, ItemKind::Macro(_, _, MacroKind::Bang));
-            if both_are_public && item_camel.len() > mod_camel.len() && !is_macro_rule {
+            let is_macro = matches!(item.kind, ItemKind::Macro(_, _, _));
+            if both_are_public && item_camel.len() > mod_camel.len() && !is_macro {
                 let matching = count_match_start(mod_camel, &item_camel);
                 let rmatching = count_match_end(mod_camel, &item_camel);
                 let nchars = mod_camel.chars().count();
diff --git a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
index 3828aff4164..902e8af7ec4 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
@@ -7,7 +7,6 @@ use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::impl_lint_pass;
 use rustc_span::def_id::CRATE_DEF_ID;
-use rustc_span::hygiene::MacroKind;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -89,7 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate {
 // We ignore macro exports. And `ListStem` uses, which aren't interesting.
 fn is_ignorable_export<'tcx>(item: &'tcx Item<'tcx>) -> bool {
     if let ItemKind::Use(path, kind) = item.kind {
-        let ignore = matches!(path.res.macro_ns, Some(Res::Def(DefKind::Macro(MacroKind::Bang), _)))
+        let ignore = matches!(path.res.macro_ns, Some(Res::Def(DefKind::Macro(_), _)))
             || kind == UseKind::ListStem;
         if ignore {
             return true;
diff --git a/tests/assembly-llvm/asm/aarch64-outline-atomics.rs b/tests/assembly-llvm/asm/aarch64-outline-atomics.rs
index 22599c18dcf..1177c1e68ed 100644
--- a/tests/assembly-llvm/asm/aarch64-outline-atomics.rs
+++ b/tests/assembly-llvm/asm/aarch64-outline-atomics.rs
@@ -8,6 +8,10 @@
 use std::sync::atomic::AtomicI32;
 use std::sync::atomic::Ordering::*;
 
+// Verify config on outline-atomics works (it is always enabled on aarch64-linux).
+#[cfg(not(target_feature = "outline-atomics"))]
+compile_error!("outline-atomics is not enabled");
+
 pub fn compare_exchange(a: &AtomicI32) {
     // On AArch64 LLVM should outline atomic operations.
     // CHECK: __aarch64_cas4_relax
diff --git a/tests/assembly-llvm/targets/targets-elf.rs b/tests/assembly-llvm/targets/targets-elf.rs
index ee63dffe9ea..a1d759ede2b 100644
--- a/tests/assembly-llvm/targets/targets-elf.rs
+++ b/tests/assembly-llvm/targets/targets-elf.rs
@@ -10,6 +10,9 @@
 //@ revisions: aarch64_be_unknown_netbsd
 //@ [aarch64_be_unknown_netbsd] compile-flags: --target aarch64_be-unknown-netbsd
 //@ [aarch64_be_unknown_netbsd] needs-llvm-components: aarch64
+//@ revisions: aarch64_be_unknown_none_softfloat
+//@ [aarch64_be_unknown_none_softfloat] compile-flags: --target aarch64_be-unknown-none-softfloat
+//@ [aarch64_be_unknown_none_softfloat] needs-llvm-components: aarch64
 //@ revisions: aarch64_kmc_solid_asp3
 //@ [aarch64_kmc_solid_asp3] compile-flags: --target aarch64-kmc-solid_asp3
 //@ [aarch64_kmc_solid_asp3] needs-llvm-components: aarch64
diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr
index 44fc23b6390..5dd81f486c8 100644
--- a/tests/ui/check-cfg/target_feature.stderr
+++ b/tests/ui/check-cfg/target_feature.stderr
@@ -183,6 +183,7 @@ LL |     cfg!(target_feature = "_UNEXPECTED_VALUE");
 `nnp-assist`
 `nontrapping-fptoint`
 `nvic`
+`outline-atomics`
 `paca`
 `pacg`
 `pan`
diff --git a/tests/ui/macros/macro-rules-attr-error.rs b/tests/ui/macros/macro-rules-attr-error.rs
index 1c8bb251e20..81eadb6692f 100644
--- a/tests/ui/macros/macro-rules-attr-error.rs
+++ b/tests/ui/macros/macro-rules-attr-error.rs
@@ -7,9 +7,46 @@ macro_rules! local_attr {
     //~^^ ERROR: local_attr
 }
 
+//~v NOTE: `fn_only` exists, but has no `attr` rules
+macro_rules! fn_only {
+    {} => {}
+}
+
+//~v NOTE: `attr_only` exists, but has no rules for function-like invocation
+macro_rules! attr_only {
+    attr() {} => {}
+}
+
 fn main() {
+    //~v NOTE: in this expansion of #[local_attr]
     #[local_attr]
     struct S;
 
-    local_attr!(arg); //~ ERROR: macro has no rules for function-like invocation
+    //~vv ERROR: cannot find macro `local_attr` in this scope
+    //~| NOTE: `local_attr` is in scope, but it is an attribute
+    local_attr!(arg);
+
+    //~v ERROR: cannot find attribute `fn_only` in this scope
+    #[fn_only]
+    struct S;
+
+    attr_only!(); //~ ERROR: cannot find macro `attr_only` in this scope
+}
+
+//~vv ERROR: cannot find attribute `forward_referenced_attr` in this scope
+//~| NOTE: consider moving the definition of `forward_referenced_attr` before this call
+#[forward_referenced_attr]
+struct S;
+
+//~v NOTE: a macro with the same name exists, but it appears later
+macro_rules! forward_referenced_attr {
+    attr() {} => {}
+}
+
+//~vv ERROR: cannot find attribute `cyclic_attr` in this scope
+//~| NOTE: consider moving the definition of `cyclic_attr` before this call
+#[cyclic_attr]
+//~v NOTE: a macro with the same name exists, but it appears later
+macro_rules! cyclic_attr {
+    attr() {} => {}
 }
diff --git a/tests/ui/macros/macro-rules-attr-error.stderr b/tests/ui/macros/macro-rules-attr-error.stderr
index 177b7009384..674d35091b6 100644
--- a/tests/ui/macros/macro-rules-attr-error.stderr
+++ b/tests/ui/macros/macro-rules-attr-error.stderr
@@ -9,14 +9,55 @@ LL |     #[local_attr]
    |
    = note: this error originates in the attribute macro `local_attr` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: macro has no rules for function-like invocation `local_attr!`
-  --> $DIR/macro-rules-attr-error.rs:14:5
+error: cannot find macro `local_attr` in this scope
+  --> $DIR/macro-rules-attr-error.rs:27:5
    |
-LL | macro_rules! local_attr {
-   | ----------------------- this macro has no rules for function-like invocation
-...
 LL |     local_attr!(arg);
-   |     ^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^
+   |
+   = note: `local_attr` is in scope, but it is an attribute: `#[local_attr]`
+
+error: cannot find attribute `fn_only` in this scope
+  --> $DIR/macro-rules-attr-error.rs:30:7
+   |
+LL | macro_rules! fn_only {
+   |              ------- `fn_only` exists, but has no `attr` rules
+...
+LL |     #[fn_only]
+   |       ^^^^^^^
+
+error: cannot find macro `attr_only` in this scope
+  --> $DIR/macro-rules-attr-error.rs:33:5
+   |
+LL | macro_rules! attr_only {
+   |              --------- `attr_only` exists, but has no rules for function-like invocation
+...
+LL |     attr_only!();
+   |     ^^^^^^^^^
+
+error: cannot find attribute `forward_referenced_attr` in this scope
+  --> $DIR/macro-rules-attr-error.rs:38:3
+   |
+LL | #[forward_referenced_attr]
+   |   ^^^^^^^^^^^^^^^^^^^^^^^ consider moving the definition of `forward_referenced_attr` before this call
+   |
+note: a macro with the same name exists, but it appears later
+  --> $DIR/macro-rules-attr-error.rs:42:14
+   |
+LL | macro_rules! forward_referenced_attr {
+   |              ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: cannot find attribute `cyclic_attr` in this scope
+  --> $DIR/macro-rules-attr-error.rs:48:3
+   |
+LL | #[cyclic_attr]
+   |   ^^^^^^^^^^^ consider moving the definition of `cyclic_attr` before this call
+   |
+note: a macro with the same name exists, but it appears later
+  --> $DIR/macro-rules-attr-error.rs:50:14
+   |
+LL | macro_rules! cyclic_attr {
+   |              ^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+error: aborting due to 6 previous errors
 
diff --git a/tests/ui/proc-macro/macro-namespace-reserved-2.stderr b/tests/ui/proc-macro/macro-namespace-reserved-2.stderr
index 0471124061e..c8a7cc3ba91 100644
--- a/tests/ui/proc-macro/macro-namespace-reserved-2.stderr
+++ b/tests/ui/proc-macro/macro-namespace-reserved-2.stderr
@@ -95,14 +95,6 @@ error: expected derive macro, found macro `crate::my_macro`
    |
 LL | #[derive(crate::my_macro)]
    |          ^^^^^^^^^^^^^^^ not a derive macro
-   |
-help: remove from the surrounding `derive()`
-  --> $DIR/macro-namespace-reserved-2.rs:50:10
-   |
-LL | #[derive(crate::my_macro)]
-   |          ^^^^^^^^^^^^^^^
-   = help: add as non-Derive macro
-           `#[crate::my_macro]`
 
 error: cannot find macro `my_macro_attr` in this scope
   --> $DIR/macro-namespace-reserved-2.rs:28:5