about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-05-11 15:46:50 +0000
committerbors <bors@rust-lang.org>2025-05-11 15:46:50 +0000
commit3528a5b76db3501d8835d7a154accf77528b96c9 (patch)
tree0503eb9c9fe4ff37480cb1a2ec5400ddf4b7357f
parent16c1c54a2921d5ace22e4a71c0ba7d4ef4b8aec7 (diff)
parent4d3c4726d2ee89745c1fe7ee7a8dee1db7157776 (diff)
downloadrust-3528a5b76db3501d8835d7a154accf77528b96c9.tar.gz
rust-3528a5b76db3501d8835d7a154accf77528b96c9.zip
Auto merge of #140915 - matthiaskrgr:rollup-lxce4zr, r=matthiaskrgr
Rollup of 3 pull requests

Successful merges:

 - #140397 (Add T-compiler backports Zulip notifications)
 - #140851 (Warn when `#[export_name]` is used with generic functions)
 - #140862 (Enable non-leaf Frame Pointers for Arm64EC Windows)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_lint/src/builtin.rs27
-rw-r--r--compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs8
-rw-r--r--tests/assembly/asm/aarch64-types.rs6
-rw-r--r--tests/ui/generics/export-name-on-generics.fixed157
-rw-r--r--tests/ui/generics/export-name-on-generics.rs159
-rw-r--r--tests/ui/generics/export-name-on-generics.stderr144
-rw-r--r--triagebot.toml47
7 files changed, 523 insertions, 25 deletions
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 41b43f64798..95e31e4af1e 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -976,6 +976,9 @@ declare_lint! {
     /// ```rust
     /// #[unsafe(no_mangle)]
     /// fn foo<T>(t: T) {}
+    ///
+    /// #[unsafe(export_name = "bar")]
+    /// fn bar<T>(t: T) {}
     /// ```
     ///
     /// {{produces}}
@@ -983,10 +986,11 @@ declare_lint! {
     /// ### Explanation
     ///
     /// A function with generics must have its symbol mangled to accommodate
-    /// the generic parameter. The [`no_mangle` attribute] has no effect in
-    /// this situation, and should be removed.
+    /// the generic parameter. The [`no_mangle`] and [`export_name`] attributes
+    /// have no effect in this situation, and should be removed.
     ///
-    /// [`no_mangle` attribute]: https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute
+    /// [`no_mangle`]: https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute
+    /// [`export_name`]: https://doc.rust-lang.org/reference/abi.html#the-export_name-attribute
     NO_MANGLE_GENERIC_ITEMS,
     Warn,
     "generic items must be mangled"
@@ -997,7 +1001,7 @@ declare_lint_pass!(InvalidNoMangleItems => [NO_MANGLE_CONST_ITEMS, NO_MANGLE_GEN
 impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
     fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
         let attrs = cx.tcx.hir_attrs(it.hir_id());
-        let check_no_mangle_on_generic_fn = |no_mangle_attr: &hir::Attribute,
+        let check_no_mangle_on_generic_fn = |attr: &hir::Attribute,
                                              impl_generics: Option<&hir::Generics<'_>>,
                                              generics: &hir::Generics<'_>,
                                              span| {
@@ -1010,7 +1014,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
                         cx.emit_span_lint(
                             NO_MANGLE_GENERIC_ITEMS,
                             span,
-                            BuiltinNoMangleGeneric { suggestion: no_mangle_attr.span() },
+                            BuiltinNoMangleGeneric { suggestion: attr.span() },
                         );
                         break;
                     }
@@ -1019,8 +1023,10 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
         };
         match it.kind {
             hir::ItemKind::Fn { generics, .. } => {
-                if let Some(no_mangle_attr) = attr::find_by_name(attrs, sym::no_mangle) {
-                    check_no_mangle_on_generic_fn(no_mangle_attr, None, generics, it.span);
+                if let Some(attr) = attr::find_by_name(attrs, sym::export_name)
+                    .or_else(|| attr::find_by_name(attrs, sym::no_mangle))
+                {
+                    check_no_mangle_on_generic_fn(attr, None, generics, it.span);
                 }
             }
             hir::ItemKind::Const(..) => {
@@ -1048,11 +1054,12 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
             hir::ItemKind::Impl(hir::Impl { generics, items, .. }) => {
                 for it in *items {
                     if let hir::AssocItemKind::Fn { .. } = it.kind {
-                        if let Some(no_mangle_attr) =
-                            attr::find_by_name(cx.tcx.hir_attrs(it.id.hir_id()), sym::no_mangle)
+                        let attrs = cx.tcx.hir_attrs(it.id.hir_id());
+                        if let Some(attr) = attr::find_by_name(attrs, sym::export_name)
+                            .or_else(|| attr::find_by_name(attrs, sym::no_mangle))
                         {
                             check_no_mangle_on_generic_fn(
-                                no_mangle_attr,
+                                attr,
                                 Some(generics),
                                 cx.tcx.hir_get_generics(it.id.owner_id.def_id).unwrap(),
                                 it.span,
diff --git a/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs
index bb3e3e544cb..8f93523909e 100644
--- a/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs
+++ b/compiler/rustc_target/src/spec/targets/arm64ec_pc_windows_msvc.rs
@@ -1,4 +1,4 @@
-use crate::spec::{LinkerFlavor, Lld, Target, TargetMetadata, add_link_args, base};
+use crate::spec::{FramePointer, LinkerFlavor, Lld, Target, TargetMetadata, add_link_args, base};
 
 pub(crate) fn target() -> Target {
     let mut base = base::windows_msvc::opts();
@@ -10,6 +10,12 @@ pub(crate) fn target() -> Target {
         &["/machine:arm64ec", "softintrin.lib"],
     );
 
+    // Microsoft recommends enabling frame pointers on Arm64 Windows.
+    // From https://learn.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=msvc-170#integer-registers
+    // "The frame pointer (x29) is required for compatibility with fast stack walking used by ETW
+    // and other services. It must point to the previous {x29, x30} pair on the stack."
+    base.frame_pointer = FramePointer::NonLeaf;
+
     Target {
         llvm_target: "arm64ec-pc-windows-msvc".into(),
         metadata: TargetMetadata {
diff --git a/tests/assembly/asm/aarch64-types.rs b/tests/assembly/asm/aarch64-types.rs
index 8e4e0850325..ad2770d43e3 100644
--- a/tests/assembly/asm/aarch64-types.rs
+++ b/tests/assembly/asm/aarch64-types.rs
@@ -86,10 +86,12 @@ pub unsafe fn sym_static() {
 
 // Regression test for #75761
 // CHECK-LABEL: {{("#)?}}issue_75761{{"?}}
-// CHECK: str {{.*}}x30
+// aarch64: str {{.*}}x30
+// arm64ec: stp {{.*}}x30
 // CHECK: //APP
 // CHECK: //NO_APP
-// CHECK: ldr {{.*}}x30
+// aarch64: ldr {{.*}}x30
+// arm64ec: ldp {{.*}}x30
 #[no_mangle]
 pub unsafe fn issue_75761() {
     asm!("", out("v0") _, out("x30") _);
diff --git a/tests/ui/generics/export-name-on-generics.fixed b/tests/ui/generics/export-name-on-generics.fixed
new file mode 100644
index 00000000000..4430cd9a299
--- /dev/null
+++ b/tests/ui/generics/export-name-on-generics.fixed
@@ -0,0 +1,157 @@
+//@ run-rustfix
+#![allow(dead_code, elided_named_lifetimes)]
+#![deny(no_mangle_generic_items)]
+
+pub fn foo<T>() {} //~ ERROR functions generic over types or consts must be mangled
+
+pub extern "C" fn bar<T>() {} //~ ERROR functions generic over types or consts must be mangled
+
+#[export_name = "baz"]
+pub fn baz(x: &i32) -> &i32 { x }
+
+#[export_name = "qux"]
+pub fn qux<'a>(x: &'a i32) -> &i32 { x }
+
+pub struct Foo;
+
+impl Foo {
+    
+    pub fn foo<T>() {} //~ ERROR functions generic over types or consts must be mangled
+
+    
+    pub extern "C" fn bar<T>() {} //~ ERROR functions generic over types or consts must be mangled
+
+    #[export_name = "baz"]
+    pub fn baz(x: &i32) -> &i32 { x }
+
+    #[export_name = "qux"]
+    pub fn qux<'a>(x: &'a i32) -> &i32 { x }
+}
+
+trait Trait1 {
+    fn foo<T>();
+    extern "C" fn bar<T>();
+    fn baz(x: &i32) -> &i32;
+    fn qux<'a>(x: &'a i32) -> &i32;
+}
+
+impl Trait1 for Foo {
+    
+    fn foo<T>() {} //~ ERROR functions generic over types or consts must be mangled
+
+    
+    extern "C" fn bar<T>() {} //~ ERROR functions generic over types or consts must be mangled
+
+    #[export_name = "baz"]
+    fn baz(x: &i32) -> &i32 { x }
+
+    #[export_name = "qux"]
+    fn qux<'a>(x: &'a i32) -> &i32 { x }
+}
+
+trait Trait2<T> {
+    fn foo();
+    fn foo2<U>();
+    extern "C" fn bar();
+    fn baz(x: &i32) -> &i32;
+    fn qux<'a>(x: &'a i32) -> &i32;
+}
+
+impl<T> Trait2<T> for Foo {
+    
+    fn foo() {} //~ ERROR functions generic over types or consts must be mangled
+
+    
+    fn foo2<U>() {} //~ ERROR functions generic over types or consts must be mangled
+
+    
+    extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled
+
+    
+    fn baz(x: &i32) -> &i32 { x } //~ ERROR functions generic over types or consts must be mangled
+
+    
+    fn qux<'a>(x: &'a i32) -> &i32 { x } //~ ERROR functions generic over types or consts must be mangled
+}
+
+pub struct Bar<T>(#[allow(dead_code)] T);
+
+impl<T> Bar<T> {
+    
+    pub fn foo() {} //~ ERROR functions generic over types or consts must be mangled
+
+    
+    pub extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled
+
+    
+    pub fn baz<U>() {} //~ ERROR functions generic over types or consts must be mangled
+}
+
+impl Bar<i32> {
+    #[export_name = "qux"]
+    pub fn qux() {}
+}
+
+trait Trait3 {
+    fn foo();
+    extern "C" fn bar();
+    fn baz<U>();
+}
+
+impl<T> Trait3 for Bar<T> {
+    
+    fn foo() {} //~ ERROR functions generic over types or consts must be mangled
+
+    
+    extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled
+
+    
+    fn baz<U>() {} //~ ERROR functions generic over types or consts must be mangled
+}
+
+pub struct Baz<'a>(#[allow(dead_code)] &'a i32);
+
+impl<'a> Baz<'a> {
+    #[export_name = "foo"]
+    pub fn foo() {}
+
+    #[export_name = "bar"]
+    pub fn bar<'b>(x: &'b i32) -> &i32 { x }
+}
+
+trait Trait4 {
+    fn foo();
+    fn bar<'a>(x: &'a i32) -> &i32;
+}
+
+impl Trait4 for Bar<i32> {
+    #[export_name = "foo"]
+    fn foo() {}
+
+    #[export_name = "bar"]
+    fn bar<'b>(x: &'b i32) -> &i32 { x }
+}
+
+impl<'a> Trait4 for Baz<'a> {
+    #[export_name = "foo"]
+    fn foo() {}
+
+    #[export_name = "bar"]
+    fn bar<'b>(x: &'b i32) -> &i32 { x }
+}
+
+trait Trait5<T> {
+    fn foo();
+}
+
+impl Trait5<i32> for Foo {
+    #[export_name = "foo"]
+    fn foo() {}
+}
+
+impl Trait5<i32> for Bar<i32> {
+    #[export_name = "foo"]
+    fn foo() {}
+}
+
+fn main() {}
diff --git a/tests/ui/generics/export-name-on-generics.rs b/tests/ui/generics/export-name-on-generics.rs
new file mode 100644
index 00000000000..cbf11021960
--- /dev/null
+++ b/tests/ui/generics/export-name-on-generics.rs
@@ -0,0 +1,159 @@
+//@ run-rustfix
+#![allow(dead_code, elided_named_lifetimes)]
+#![deny(no_mangle_generic_items)]
+
+#[export_name = "foo"]
+pub fn foo<T>() {} //~ ERROR functions generic over types or consts must be mangled
+
+#[export_name = "bar"]
+pub extern "C" fn bar<T>() {} //~ ERROR functions generic over types or consts must be mangled
+
+#[export_name = "baz"]
+pub fn baz(x: &i32) -> &i32 { x }
+
+#[export_name = "qux"]
+pub fn qux<'a>(x: &'a i32) -> &i32 { x }
+
+pub struct Foo;
+
+impl Foo {
+    #[export_name = "foo"]
+    pub fn foo<T>() {} //~ ERROR functions generic over types or consts must be mangled
+
+    #[export_name = "bar"]
+    pub extern "C" fn bar<T>() {} //~ ERROR functions generic over types or consts must be mangled
+
+    #[export_name = "baz"]
+    pub fn baz(x: &i32) -> &i32 { x }
+
+    #[export_name = "qux"]
+    pub fn qux<'a>(x: &'a i32) -> &i32 { x }
+}
+
+trait Trait1 {
+    fn foo<T>();
+    extern "C" fn bar<T>();
+    fn baz(x: &i32) -> &i32;
+    fn qux<'a>(x: &'a i32) -> &i32;
+}
+
+impl Trait1 for Foo {
+    #[export_name = "foo"]
+    fn foo<T>() {} //~ ERROR functions generic over types or consts must be mangled
+
+    #[export_name = "bar"]
+    extern "C" fn bar<T>() {} //~ ERROR functions generic over types or consts must be mangled
+
+    #[export_name = "baz"]
+    fn baz(x: &i32) -> &i32 { x }
+
+    #[export_name = "qux"]
+    fn qux<'a>(x: &'a i32) -> &i32 { x }
+}
+
+trait Trait2<T> {
+    fn foo();
+    fn foo2<U>();
+    extern "C" fn bar();
+    fn baz(x: &i32) -> &i32;
+    fn qux<'a>(x: &'a i32) -> &i32;
+}
+
+impl<T> Trait2<T> for Foo {
+    #[export_name = "foo"]
+    fn foo() {} //~ ERROR functions generic over types or consts must be mangled
+
+    #[export_name = "foo2"]
+    fn foo2<U>() {} //~ ERROR functions generic over types or consts must be mangled
+
+    #[export_name = "baz"]
+    extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled
+
+    #[export_name = "baz"]
+    fn baz(x: &i32) -> &i32 { x } //~ ERROR functions generic over types or consts must be mangled
+
+    #[export_name = "qux"]
+    fn qux<'a>(x: &'a i32) -> &i32 { x } //~ ERROR functions generic over types or consts must be mangled
+}
+
+pub struct Bar<T>(#[allow(dead_code)] T);
+
+impl<T> Bar<T> {
+    #[export_name = "foo"]
+    pub fn foo() {} //~ ERROR functions generic over types or consts must be mangled
+
+    #[export_name = "bar"]
+    pub extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled
+
+    #[export_name = "baz"]
+    pub fn baz<U>() {} //~ ERROR functions generic over types or consts must be mangled
+}
+
+impl Bar<i32> {
+    #[export_name = "qux"]
+    pub fn qux() {}
+}
+
+trait Trait3 {
+    fn foo();
+    extern "C" fn bar();
+    fn baz<U>();
+}
+
+impl<T> Trait3 for Bar<T> {
+    #[export_name = "foo"]
+    fn foo() {} //~ ERROR functions generic over types or consts must be mangled
+
+    #[export_name = "bar"]
+    extern "C" fn bar() {} //~ ERROR functions generic over types or consts must be mangled
+
+    #[export_name = "baz"]
+    fn baz<U>() {} //~ ERROR functions generic over types or consts must be mangled
+}
+
+pub struct Baz<'a>(#[allow(dead_code)] &'a i32);
+
+impl<'a> Baz<'a> {
+    #[export_name = "foo"]
+    pub fn foo() {}
+
+    #[export_name = "bar"]
+    pub fn bar<'b>(x: &'b i32) -> &i32 { x }
+}
+
+trait Trait4 {
+    fn foo();
+    fn bar<'a>(x: &'a i32) -> &i32;
+}
+
+impl Trait4 for Bar<i32> {
+    #[export_name = "foo"]
+    fn foo() {}
+
+    #[export_name = "bar"]
+    fn bar<'b>(x: &'b i32) -> &i32 { x }
+}
+
+impl<'a> Trait4 for Baz<'a> {
+    #[export_name = "foo"]
+    fn foo() {}
+
+    #[export_name = "bar"]
+    fn bar<'b>(x: &'b i32) -> &i32 { x }
+}
+
+trait Trait5<T> {
+    fn foo();
+}
+
+impl Trait5<i32> for Foo {
+    #[export_name = "foo"]
+    fn foo() {}
+}
+
+impl Trait5<i32> for Bar<i32> {
+    #[export_name = "foo"]
+    fn foo() {}
+}
+
+fn main() {}
diff --git a/tests/ui/generics/export-name-on-generics.stderr b/tests/ui/generics/export-name-on-generics.stderr
new file mode 100644
index 00000000000..7bc7b8ca559
--- /dev/null
+++ b/tests/ui/generics/export-name-on-generics.stderr
@@ -0,0 +1,144 @@
+error: functions generic over types or consts must be mangled
+  --> $DIR/export-name-on-generics.rs:6:1
+   |
+LL | #[export_name = "foo"]
+   | ---------------------- help: remove this attribute
+LL | pub fn foo<T>() {}
+   | ^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> $DIR/export-name-on-generics.rs:3:9
+   |
+LL | #![deny(no_mangle_generic_items)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: functions generic over types or consts must be mangled
+  --> $DIR/export-name-on-generics.rs:9:1
+   |
+LL | #[export_name = "bar"]
+   | ---------------------- help: remove this attribute
+LL | pub extern "C" fn bar<T>() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: functions generic over types or consts must be mangled
+  --> $DIR/export-name-on-generics.rs:21:5
+   |
+LL |     #[export_name = "foo"]
+   |     ---------------------- help: remove this attribute
+LL |     pub fn foo<T>() {}
+   |     ^^^^^^^^^^^^^^^^^^
+
+error: functions generic over types or consts must be mangled
+  --> $DIR/export-name-on-generics.rs:24:5
+   |
+LL |     #[export_name = "bar"]
+   |     ---------------------- help: remove this attribute
+LL |     pub extern "C" fn bar<T>() {}
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: functions generic over types or consts must be mangled
+  --> $DIR/export-name-on-generics.rs:42:5
+   |
+LL |     #[export_name = "foo"]
+   |     ---------------------- help: remove this attribute
+LL |     fn foo<T>() {}
+   |     ^^^^^^^^^^^^^^
+
+error: functions generic over types or consts must be mangled
+  --> $DIR/export-name-on-generics.rs:45:5
+   |
+LL |     #[export_name = "bar"]
+   |     ---------------------- help: remove this attribute
+LL |     extern "C" fn bar<T>() {}
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: functions generic over types or consts must be mangled
+  --> $DIR/export-name-on-generics.rs:64:5
+   |
+LL |     #[export_name = "foo"]
+   |     ---------------------- help: remove this attribute
+LL |     fn foo() {}
+   |     ^^^^^^^^^^^
+
+error: functions generic over types or consts must be mangled
+  --> $DIR/export-name-on-generics.rs:67:5
+   |
+LL |     #[export_name = "foo2"]
+   |     ----------------------- help: remove this attribute
+LL |     fn foo2<U>() {}
+   |     ^^^^^^^^^^^^^^^
+
+error: functions generic over types or consts must be mangled
+  --> $DIR/export-name-on-generics.rs:70:5
+   |
+LL |     #[export_name = "baz"]
+   |     ---------------------- help: remove this attribute
+LL |     extern "C" fn bar() {}
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+
+error: functions generic over types or consts must be mangled
+  --> $DIR/export-name-on-generics.rs:73:5
+   |
+LL |     #[export_name = "baz"]
+   |     ---------------------- help: remove this attribute
+LL |     fn baz(x: &i32) -> &i32 { x }
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: functions generic over types or consts must be mangled
+  --> $DIR/export-name-on-generics.rs:76:5
+   |
+LL |     #[export_name = "qux"]
+   |     ---------------------- help: remove this attribute
+LL |     fn qux<'a>(x: &'a i32) -> &i32 { x }
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: functions generic over types or consts must be mangled
+  --> $DIR/export-name-on-generics.rs:83:5
+   |
+LL |     #[export_name = "foo"]
+   |     ---------------------- help: remove this attribute
+LL |     pub fn foo() {}
+   |     ^^^^^^^^^^^^^^^
+
+error: functions generic over types or consts must be mangled
+  --> $DIR/export-name-on-generics.rs:86:5
+   |
+LL |     #[export_name = "bar"]
+   |     ---------------------- help: remove this attribute
+LL |     pub extern "C" fn bar() {}
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: functions generic over types or consts must be mangled
+  --> $DIR/export-name-on-generics.rs:89:5
+   |
+LL |     #[export_name = "baz"]
+   |     ---------------------- help: remove this attribute
+LL |     pub fn baz<U>() {}
+   |     ^^^^^^^^^^^^^^^^^^
+
+error: functions generic over types or consts must be mangled
+  --> $DIR/export-name-on-generics.rs:105:5
+   |
+LL |     #[export_name = "foo"]
+   |     ---------------------- help: remove this attribute
+LL |     fn foo() {}
+   |     ^^^^^^^^^^^
+
+error: functions generic over types or consts must be mangled
+  --> $DIR/export-name-on-generics.rs:108:5
+   |
+LL |     #[export_name = "bar"]
+   |     ---------------------- help: remove this attribute
+LL |     extern "C" fn bar() {}
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+
+error: functions generic over types or consts must be mangled
+  --> $DIR/export-name-on-generics.rs:111:5
+   |
+LL |     #[export_name = "baz"]
+   |     ---------------------- help: remove this attribute
+LL |     fn baz<U>() {}
+   |     ^^^^^^^^^^^^^^
+
+error: aborting due to 17 previous errors
+
diff --git a/triagebot.toml b/triagebot.toml
index 1f0afdc4442..6f3ca493335 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -611,9 +611,6 @@ message_on_remove = "Issue #{number}'s prioritization request has been removed."
 message_on_close = "Issue #{number} has been closed while requested for prioritization."
 message_on_reopen = "Issue #{number} has been reopened."
 
-# FIXME: Patch triagebot to support `notify-zulip.<label>` getting mapped to an array of actions.
-#        At the moment, the beta-nominated+T-rustdoc action fully occupies the beta-nominated slot
-#        preventing others from adding more beta-nominated actions.
 [notify-zulip."beta-nominated"]
 required_labels = ["T-rustdoc"]
 zulip_stream = 266220 # #t-rustdoc
@@ -635,9 +632,6 @@ message_on_remove = "PR #{number}'s beta-nomination has been removed."
 message_on_close = "PR #{number} has been closed. Thanks for participating!"
 message_on_reopen = "PR #{number} has been reopened. Pinging @*T-rustdoc*."
 
-# FIXME: Patch triagebot to support `notify-zulip.<label>` getting mapped to an array of actions.
-#        At the moment, the beta-accepted+T-rustdoc action fully occupies the beta-accepted slot
-#        preventing others from adding more beta-accepted actions.
 [notify-zulip."beta-accepted"]
 required_labels = ["T-rustdoc"]
 zulip_stream = 266220 # #t-rustdoc
@@ -648,9 +642,6 @@ message_on_remove = "PR #{number}'s beta-acceptance has been **removed**."
 message_on_close = "PR #{number} has been closed. Thanks for participating!"
 message_on_reopen = "PR #{number} has been reopened. Pinging @*T-rustdoc*."
 
-# FIXME: Patch triagebot to support `notify-zulip.<label>` getting mapped to an array of actions.
-#        At the moment, the stable-nominated+T-rustdoc action fully occupies the stable-nominated slot
-#        preventing others from adding more stable-nominated actions.
 [notify-zulip."stable-nominated"]
 required_labels = ["T-rustdoc"]
 zulip_stream = 266220 # #t-rustdoc
@@ -673,9 +664,6 @@ message_on_remove = "PR #{number}'s stable-nomination has been removed."
 message_on_close = "PR #{number} has been closed. Thanks for participating!"
 message_on_reopen = "PR #{number} has been reopened. Pinging @*T-rustdoc*."
 
-# FIXME: Patch triagebot to support `notify-zulip.<label>` getting mapped to an array of actions.
-#        At the moment, the stable-accepted+T-rustdoc action fully occupies the stable-accepted slot
-#        preventing others from adding more stable-accepted actions.
 [notify-zulip."stable-accepted"]
 required_labels = ["T-rustdoc"]
 zulip_stream = 266220 # #t-rustdoc
@@ -696,6 +684,41 @@ message_on_remove = "Issue #{number}'s nomination has been removed. Thanks all f
 message_on_close = "Issue #{number} has been closed. Thanks for participating!"
 message_on_reopen = "Issue #{number} has been reopened. Pinging @*T-types*."
 
+[notify-zulip."beta-nominated".compiler]
+required_labels = ["T-compiler"]
+zulip_stream = 474880 # #t-compiler/backports
+topic = "#{number}: beta-nominated"
+message_on_add = [
+    """\
+@**channel** PR #{number} "{title}" has been nominated for beta backport.
+""",
+    """\
+/poll Approve beta backport of #{number}?
+approve
+decline
+don't know
+""",
+]
+message_on_remove = "PR #{number}'s beta-nomination has been removed."
+
+[notify-zulip."stable-nominated".compiler]
+required_labels = ["T-compiler"]
+zulip_stream = 474880 # #t-compiler/backports
+topic = "#{number}: stable-nominated"
+message_on_add = [
+    """\
+@**channel** PR #{number} "{title}" has been nominated for stable backport.
+""",
+    """\
+/poll Approve stable backport of #{number}?
+approve
+approve (but does not justify new dot release on its own)
+decline
+don't know
+""",
+]
+message_on_remove = "PR #{number}'s stable-nomination has been removed."
+
 [notify-zulip."A-edition-2021"]
 required_labels = ["C-bug"]
 zulip_stream = 268952 # #edition