From 0a0f6df1d5258c7de0a4f19702cd00f01f301ac5 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Thu, 6 Aug 2026 09:23:24 +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 72b51ad204b9d..bc6f87a2a7f17 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::rustc_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 530483e87329c..94241e6a31eb0 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 ff1d4253c4414..a346a5216128b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1872,6 +1872,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, rustdoc, rustdoc_internals, rustdoc_missing_doc_code_examples, @@ -2254,7 +2256,6 @@ symbols! { unreachable_display, unreachable_macro, unrestricted_attribute_tokens, - unroll, unsafe_attributes, unsafe_binders, unsafe_block_in_unsafe_fn, From 44d291e788e84e53ab6afef2ca074183865e6a0c 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 91ee1f3a9b72a19d6c21bbcd34872fa24da08617 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 945d2f32788c3698b820201319955a4764ac2efb 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 d1265b7cfb2f221c80701bddf09d9614d076dcd3 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.