From 95f831c237dca53155e60262c84cc4c523126c47 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Thu, 30 Jul 2026 19:08:54 +0800 Subject: [PATCH 1/5] Rename `#[unroll]` => `#[rustc_unroll]` to mitigate nameres ambiguity Mitigation for [RUST-159429]. The recurring problem is that built-in attributes are treated differently compared to ordinary prelude attributes, built-in attributes, even while unstable, can name-collide with stable macro re-exports of the same name (and proc-macro helper attributes of the same name), which can break stable code. See [RUST-134964]. [RUST-159429]: https://github.com/rust-lang/rust/issues/159429 [RUST-134963]: https://github.com/rust-lang/rust/issues/134963 --- compiler/rustc_attr_parsing/src/attributes/unroll.rs | 3 ++- compiler/rustc_feature/src/builtin_attrs.rs | 6 ++++-- compiler/rustc_hir/src/attrs/data_structures.rs | 3 ++- compiler/rustc_span/src/symbol.rs | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/unroll.rs b/compiler/rustc_attr_parsing/src/attributes/unroll.rs index 3438fc044ec55..5a49feca2a1ea 100644 --- a/compiler/rustc_attr_parsing/src/attributes/unroll.rs +++ b/compiler/rustc_attr_parsing/src/attributes/unroll.rs @@ -6,7 +6,8 @@ use super::prelude::*; pub(crate) struct UnrollParser; impl SingleAttributeParser for UnrollParser { - const PATH: &[Symbol] = &[sym::unroll]; + // FIXME(#159429): temporarily renamed to mitigate `#[unroll]` nameres ambiguity. + const PATH: &[Symbol] = &[sym::rustc_unroll]; const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Loop), Allow(Target::ForLoop), diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index f4b3696a9d56e..7439838b11b9b 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -217,10 +217,12 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[ // - https://github.com/rust-lang/rust/issues/153629 sym::splat, - // The `#[unroll]` attribute. + // The `#[rustc_unroll]` attribute. // // - https://github.com/rust-lang/rust/pull/156816 - sym::unroll, + // + // FIXME(#159429): temporarily renamed to mitigate `#[unroll]` nameres ambiguity + sym::rustc_unroll, // `#[instrument_fn = "on|off"]` to insert or inhibit instrumentation function // calls inside a function, usually around the prologue. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 8747d5aced73f..20417d76cf4f5 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1706,7 +1706,8 @@ pub enum AttributeKind { limit: Limit, }, - /// Represents `#[unroll]` + /// Represents `#[rustc_unroll]` + // FIXME(#159429): temporarily renamed from `#[unroll]` to mitigate nameres ambiguity Unroll(UnrollAttr), /// Represents `#[unstable_feature_bound]`. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 8eb597fccad63..ecf4f6ef8e24c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1867,6 +1867,8 @@ symbols! { rustc_test_marker, rustc_then_this_would_need, rustc_trivial_field_reads, + // FIXME(#159429): temporary rename to avoid `#[unroll]` nameres ambiguity + rustc_unroll, rustc_unsafe_specialization_marker, rustdoc, rustdoc_internals, @@ -2248,7 +2250,6 @@ symbols! { unreachable_display, unreachable_macro, unrestricted_attribute_tokens, - unroll, unsafe_attributes, unsafe_binders, unsafe_block_in_unsafe_fn, From 4be7c82fb59825fee5c383ff612c753f92d48762 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Thu, 30 Jul 2026 19:21:21 +0800 Subject: [PATCH 2/5] Update attr name in `#[unroll]` codegen-llvm tests --- tests/codegen-llvm/loop-attrs/unroll-for-metadata.rs | 8 ++++---- tests/codegen-llvm/loop-attrs/unroll-for-works.rs | 6 +++--- tests/codegen-llvm/loop-attrs/unroll-loop-metadata.rs | 8 ++++---- tests/codegen-llvm/loop-attrs/unroll-while-metadata.rs | 8 ++++---- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/codegen-llvm/loop-attrs/unroll-for-metadata.rs b/tests/codegen-llvm/loop-attrs/unroll-for-metadata.rs index 64113fbeb3247..60f9b6da6c9fe 100644 --- a/tests/codegen-llvm/loop-attrs/unroll-for-metadata.rs +++ b/tests/codegen-llvm/loop-attrs/unroll-for-metadata.rs @@ -15,7 +15,7 @@ unsafe extern "C" { pub fn unroll_hint() { // CHECK-LABEL: @unroll_hint // CHECK: !llvm.loop ![[HINT:[0-9]+]] - #[unroll] + #[rustc_unroll] for _ in 0..10 { unsafe { maybe_has_side_effect() } } @@ -25,7 +25,7 @@ pub fn unroll_hint() { pub fn unroll_full() { // CHECK-LABEL: @unroll_full // CHECK: !llvm.loop ![[FULL:[0-9]+]] - #[unroll(full)] + #[rustc_unroll(full)] for _ in 0..10 { unsafe { maybe_has_side_effect() } } @@ -35,7 +35,7 @@ pub fn unroll_full() { pub fn unroll_never() { // CHECK-LABEL: @unroll_never // CHECK: !llvm.loop ![[DISABLE:[0-9]+]] - #[unroll(never)] + #[rustc_unroll(never)] for _ in 0..10 { unsafe { maybe_has_side_effect() } } @@ -45,7 +45,7 @@ pub fn unroll_never() { pub fn unroll_count() { // CHECK-LABEL: @unroll_count // CHECK: !llvm.loop ![[COUNT:[0-9]+]] - #[unroll(5)] + #[rustc_unroll(5)] for _ in 0..10 { unsafe { maybe_has_side_effect() } } diff --git a/tests/codegen-llvm/loop-attrs/unroll-for-works.rs b/tests/codegen-llvm/loop-attrs/unroll-for-works.rs index b2f8b58c93573..0aa8d805c4f68 100644 --- a/tests/codegen-llvm/loop-attrs/unroll-for-works.rs +++ b/tests/codegen-llvm/loop-attrs/unroll-for-works.rs @@ -11,7 +11,7 @@ unsafe extern "C" { pub fn unroll_full() { // CHECK-LABEL: @unroll_full // CHECK-COUNT-512: tail call void @maybe_has_side_effect() - #[unroll(full)] + #[rustc_unroll(full)] for _ in 0..512 { unsafe { maybe_has_side_effect() } } @@ -22,7 +22,7 @@ pub fn unroll_never() { // CHECK-LABEL: @unroll_never // CHECK: tail call void @maybe_has_side_effect() // CHECK-NOT: tail call void @maybe_has_side_effect() - #[unroll(never)] + #[rustc_unroll(never)] for _ in 0..3 { unsafe { maybe_has_side_effect() } } @@ -32,7 +32,7 @@ pub fn unroll_never() { pub fn unroll_count() { // CHECK-LABEL: @unroll_count // CHECK-COUNT-5: tail call void @maybe_has_side_effect() - #[unroll(5)] + #[rustc_unroll(5)] for _ in 0..10 { unsafe { maybe_has_side_effect() } } diff --git a/tests/codegen-llvm/loop-attrs/unroll-loop-metadata.rs b/tests/codegen-llvm/loop-attrs/unroll-loop-metadata.rs index 2b2b0779cf49e..7b715d1ac1e32 100644 --- a/tests/codegen-llvm/loop-attrs/unroll-loop-metadata.rs +++ b/tests/codegen-llvm/loop-attrs/unroll-loop-metadata.rs @@ -17,7 +17,7 @@ pub fn unroll_hint() { // CHECK-LABEL: @unroll_hint // CHECK: !llvm.loop ![[HINT:[0-9]+]] let mut i = 0; - #[unroll] + #[rustc_unroll] loop { unsafe { maybe_has_side_effect() } i += 1; @@ -35,7 +35,7 @@ pub fn unroll_full() { // CHECK-LABEL: @unroll_full // CHECK: !llvm.loop ![[FULL:[0-9]+]] let mut i = 0; - let _return = (#[unroll(full)] + let _return = (#[rustc_unroll(full)] loop { unsafe { maybe_has_side_effect() } i += 1; @@ -50,7 +50,7 @@ pub fn unroll_never() { // CHECK-LABEL: @unroll_never // CHECK: !llvm.loop ![[DISABLE:[0-9]+]] let mut i = 0; - let _return = (1 + #[unroll(never)] + let _return = (1 + #[rustc_unroll(never)] loop { unsafe { maybe_has_side_effect() } i += 1; @@ -65,7 +65,7 @@ pub fn unroll_count() { // CHECK-LABEL: @unroll_count // CHECK: !llvm.loop ![[COUNT:[0-9]+]] let mut i = 0; - #[unroll(5)] + #[rustc_unroll(5)] loop { unsafe { maybe_has_side_effect() } i += 1; diff --git a/tests/codegen-llvm/loop-attrs/unroll-while-metadata.rs b/tests/codegen-llvm/loop-attrs/unroll-while-metadata.rs index c40a4188334e8..1a100aae1e717 100644 --- a/tests/codegen-llvm/loop-attrs/unroll-while-metadata.rs +++ b/tests/codegen-llvm/loop-attrs/unroll-while-metadata.rs @@ -16,7 +16,7 @@ pub fn unroll_hint() { // CHECK-LABEL: @unroll_hint // CHECK: !llvm.loop ![[HINT:[0-9]+]] let mut i = 0; - #[unroll] + #[rustc_unroll] while i < 10 { unsafe { maybe_has_side_effect() } i += 1; @@ -28,7 +28,7 @@ pub fn unroll_full() { // CHECK-LABEL: @unroll_full // CHECK: !llvm.loop ![[FULL:[0-9]+]] let mut i = 0; - #[unroll(full)] + #[rustc_unroll(full)] while i < 10 { unsafe { maybe_has_side_effect() } i += 1; @@ -40,7 +40,7 @@ pub fn unroll_never() { // CHECK-LABEL: @unroll_never // CHECK: !llvm.loop ![[DISABLE:[0-9]+]] let mut i = 0; - #[unroll(never)] + #[rustc_unroll(never)] while i < 10 { unsafe { maybe_has_side_effect() } i += 1; @@ -52,7 +52,7 @@ pub fn unroll_count() { // CHECK-LABEL: @unroll_count // CHECK: !llvm.loop ![[COUNT:[0-9]+]] let mut i = 0; - #[unroll(5)] + #[rustc_unroll(5)] while i < 10 { unsafe { maybe_has_side_effect() } i += 1; From 9db7e2858016518e0f44ab93a13ea05839c1d4d8 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Thu, 30 Jul 2026 19:21:53 +0800 Subject: [PATCH 3/5] Update attr name in `#[unroll]` ui tests To account for the renamed `#[rustc_unroll]` attribute. --- tests/ui/attributes/unroll/invalid-unroll.rs | 10 ++-- .../attributes/unroll/invalid-unroll.stderr | 50 +++++++++---------- .../feature-gates/feature-gate-loop-hints.rs | 2 +- .../feature-gate-loop-hints.stderr | 6 +-- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/tests/ui/attributes/unroll/invalid-unroll.rs b/tests/ui/attributes/unroll/invalid-unroll.rs index 8696cefe818f7..13a14c2713fc1 100644 --- a/tests/ui/attributes/unroll/invalid-unroll.rs +++ b/tests/ui/attributes/unroll/invalid-unroll.rs @@ -2,18 +2,18 @@ #![crate_type = "lib"] pub fn main() { - #[unroll(please)] //~ ERROR malformed `unroll` attribute input + #[rustc_unroll(please)] //~ ERROR malformed `rustc_unroll` attribute input for _ in 0..10 {} - #[unroll("never")] //~ ERROR malformed `unroll` attribute input + #[rustc_unroll("never")] //~ ERROR malformed `rustc_unroll` attribute input for _ in 0..10 {} - #[unroll()] //~ ERROR malformed `unroll` attribute input + #[rustc_unroll()] //~ ERROR malformed `rustc_unroll` attribute input for _ in 0..10 {} - #[unroll(-1)] //~ ERROR expected a literal + #[rustc_unroll(-1)] //~ ERROR expected a literal for _ in 0..10 {} - #[unroll(1.5)] //~ ERROR malformed `unroll` attribute input + #[rustc_unroll(1.5)] //~ ERROR malformed `rustc_unroll` attribute input for _ in 0..10 {} } diff --git a/tests/ui/attributes/unroll/invalid-unroll.stderr b/tests/ui/attributes/unroll/invalid-unroll.stderr index 9d25fa2c42d66..ced0523bf99ea 100644 --- a/tests/ui/attributes/unroll/invalid-unroll.stderr +++ b/tests/ui/attributes/unroll/invalid-unroll.stderr @@ -1,46 +1,46 @@ -error[E0539]: malformed `unroll` attribute input +error[E0539]: malformed `rustc_unroll` attribute input --> $DIR/invalid-unroll.rs:5:7 | -LL | #[unroll(please)] - | ^^^^^^^------^ - | | - | valid arguments are `full` or `never` +LL | #[rustc_unroll(please)] + | ^^^^^^^^^^^^^------^ + | | + | valid arguments are `full` or `never` -error[E0539]: malformed `unroll` attribute input +error[E0539]: malformed `rustc_unroll` attribute input --> $DIR/invalid-unroll.rs:8:7 | -LL | #[unroll("never")] - | ^^^^^^^-------^ - | | - | valid arguments are `full` or `never` +LL | #[rustc_unroll("never")] + | ^^^^^^^^^^^^^-------^ + | | + | valid arguments are `full` or `never` -error[E0805]: malformed `unroll` attribute input +error[E0805]: malformed `rustc_unroll` attribute input --> $DIR/invalid-unroll.rs:11:7 | -LL | #[unroll()] - | ^^^^^^-- - | | - | expected an argument here +LL | #[rustc_unroll()] + | ^^^^^^^^^^^^-- + | | + | expected an argument here error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found expression - --> $DIR/invalid-unroll.rs:14:14 + --> $DIR/invalid-unroll.rs:14:20 | -LL | #[unroll(-1)] - | ^^ expressions are not allowed here +LL | #[rustc_unroll(-1)] + | ^^ expressions are not allowed here | help: negative numbers are not literals, try removing the `-` sign | -LL - #[unroll(-1)] -LL + #[unroll(1)] +LL - #[rustc_unroll(-1)] +LL + #[rustc_unroll(1)] | -error[E0539]: malformed `unroll` attribute input +error[E0539]: malformed `rustc_unroll` attribute input --> $DIR/invalid-unroll.rs:17:7 | -LL | #[unroll(1.5)] - | ^^^^^^^---^ - | | - | valid arguments are `full` or `never` +LL | #[rustc_unroll(1.5)] + | ^^^^^^^^^^^^^---^ + | | + | valid arguments are `full` or `never` error: aborting due to 5 previous errors diff --git a/tests/ui/feature-gates/feature-gate-loop-hints.rs b/tests/ui/feature-gates/feature-gate-loop-hints.rs index 85a1f10ab0a63..480d9a95f08a9 100644 --- a/tests/ui/feature-gates/feature-gate-loop-hints.rs +++ b/tests/ui/feature-gates/feature-gate-loop-hints.rs @@ -1,4 +1,4 @@ fn main() { - #[unroll] //~ ERROR the `unroll` attribute is an experimental feature + #[rustc_unroll] //~ ERROR the `rustc_unroll` attribute is an experimental feature for _ in 0..10 {} } diff --git a/tests/ui/feature-gates/feature-gate-loop-hints.stderr b/tests/ui/feature-gates/feature-gate-loop-hints.stderr index 98279fe144126..56c3ec6812c9c 100644 --- a/tests/ui/feature-gates/feature-gate-loop-hints.stderr +++ b/tests/ui/feature-gates/feature-gate-loop-hints.stderr @@ -1,8 +1,8 @@ -error[E0658]: the `unroll` attribute is an experimental feature +error[E0658]: the `rustc_unroll` attribute is an experimental feature --> $DIR/feature-gate-loop-hints.rs:2:7 | -LL | #[unroll] - | ^^^^^^ +LL | #[rustc_unroll] + | ^^^^^^^^^^^^ | = note: see issue #156874 for more information = help: add `#![feature(loop_hints)]` to the crate attributes to enable From ab6d79bc8d6787b30cfa74a553d5134a9f040290 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Thu, 30 Jul 2026 19:22:16 +0800 Subject: [PATCH 4/5] Rebless `rustc-attrs` feature gate test This stderr diff is a funny side-effect of renaming `#[unroll]` => `#[rustc_unroll]`, where the `#[rustc_unknown]` attribute name is just similar enough edit distance wise to `#[rustc_unroll]` that `#[rustc_unroll]` shows up as a plausible suggestion candidate, lol. --- tests/ui/feature-gates/feature-gate-rustc-attrs.stderr | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/ui/feature-gates/feature-gate-rustc-attrs.stderr b/tests/ui/feature-gates/feature-gate-rustc-attrs.stderr index 629d25ec4f01c..884a02c5ec25d 100644 --- a/tests/ui/feature-gates/feature-gate-rustc-attrs.stderr +++ b/tests/ui/feature-gates/feature-gate-rustc-attrs.stderr @@ -33,6 +33,12 @@ error: cannot find attribute `rustc_unknown` in this scope | LL | #[rustc_unknown] | ^^^^^^^^^^^^^ + | +help: a built-in attribute with a similar name exists + | +LL - #[rustc_unknown] +LL + #[rustc_unroll] + | error[E0658]: use of an internal attribute --> $DIR/feature-gate-rustc-attrs.rs:20:3 From 3defc966ecf56f8de000cbd09446e7b669fa8241 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Thu, 30 Jul 2026 19:31:02 +0800 Subject: [PATCH 5/5] Update attr name for `#[unroll]` in Unstable Book --- .../src/language-features/loop-hints.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/doc/unstable-book/src/language-features/loop-hints.md b/src/doc/unstable-book/src/language-features/loop-hints.md index c02411d30c668..b82a7b367095a 100644 --- a/src/doc/unstable-book/src/language-features/loop-hints.md +++ b/src/doc/unstable-book/src/language-features/loop-hints.md @@ -6,18 +6,22 @@ The tracking issue for this feature is: [#156874] ------ + + Loop unrolling can be a powerful optimization but like inlining, it is sometimes useful to manually provide hints to optimizations. -`#[unroll]` will encourage unrolling of a loop. +`#[rustc_unroll]` will encourage unrolling of a loop. -`#[unroll(full)]` is a stronger hint and can cause optimizations to completely ignore the code +`#[rustc_unroll(full)]` is a stronger hint and can cause optimizations to completely ignore the code side growth from repeating a loop body. -`#[unroll(never)]` is a strong hint to not unroll the loop at all. Note that other loop +`#[rustc_unroll(never)]` is a strong hint to not unroll the loop at all. Note that other loop optimizations may still be applied. -`#[unroll(N)]` is a hint to unroll `N` iterations of the loop. +`#[rustc_unroll(N)]` is a hint to unroll `N` iterations of the loop. In all cases these are just hints and may be ignored. But unlike function inlining hints, loops tend to be heavily modified during compilation, which can make obeying hints challenging.