Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eagerly expand bang macros within stability attributes #118406

Closed
wants to merge 5 commits into from

Conversation

dtolnay
Copy link
Member

@dtolnay dtolnay commented Nov 28, 2023

This PR implements support for macro invocations in the values of builtin stability attributes:

- #[stable(feature = "string literal")]
+ #[stable(feature = get_feature!($thing))]

This is analogous to how macro expansion is already supported in the value part of doc attributes since Rust 1.54 (https://blog.rust-lang.org/2021/07/29/Rust-1.54.0.html#attributes-can-invoke-function-like-macros).

#[doc = include_str!("README.md")]

All such macro calls must expand to a literal, like for doc and TokenStream::expand_expr.

I have limited this feature to just the following 4 builtin attributes, none of which are available in stable Rust: #[stable(...)], #[unstable(...)], #[rustc_const_stable(...)], #[rustc_const_unstable(...)]. Other builtin attributes, for example #[deprecated(...)], will continue to reject the above syntax.

Context

I began looking into refactoring the macros which generate our NonZero* integer types (https://github.com/rust-lang/rust/blob/1.74.0/library/core/src/num/nonzero.rs) in preparation for landing the refactor to NonZero<T> (#82363 (comment)).

Today there are 7 separate impl blocks per NonZero integer type. You can see this by counting the impl sections under "Implementations" in https://doc.rust-lang.org/1.74.0/std/num/struct.NonZeroU32.html. There isn't anything wrong with that today, but after the 12 different NonZero* types all get melded into a NonZero<T>, there end up being 67 impl blocks in #100428 on that type.

This is a usability regression compared to today's separate pages per type. With all those blocks expanded, Ctrl+F is obnoxious because you need to skip 12× past every match you don't care about. With all the blocks collapsed, Ctrl+F is useless. Getting to a state in which exactly one type's (e.g. NonZero<u32>) impl blocks are expanded while the rest are collapsed is annoying.

Ideally there would be one impl block per type.

Today's implementation in the standard library is arranged like this:

macro_rules! nonzero_leading_trailing_zeros {
    (...) => {
        $(
            impl $Ty { ... }
        )*
    };
}

nonzero_leading_trailing_zeros!(...);

macro_rules! nonzero_bits {
    (...) => {
        $(
            impl $Ty { ... }
        )*
    };
}

nonzero_bits!(...);
...

The thing that makes it difficult to transpose this into something that produces 1 impl block per type, while remaining maintainable, is that composing stability attributes using a macro is impossible.

nonzero_integers! {
#[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU8(u8);
#[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU16(u16);
#[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU32(u32);
#[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU64(u64);
#[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroU128(u128);
#[stable(feature = "nonzero", since = "1.28.0")] #[rustc_const_stable(feature = "nonzero", since = "1.28.0")] NonZeroUsize(usize);
#[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI8(i8);
#[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI16(i16);
#[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI32(i32);
#[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI64(i64);
#[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroI128(i128);
#[stable(feature = "signed_nonzero", since = "1.34.0")] #[rustc_const_stable(feature = "signed_nonzero", since = "1.34.0")] NonZeroIsize(isize);
}

The UI test added in this PR (tests/ui/stability-attribute/eager-expansion.rs) is illustrative of the direction I was headed before realizing this was not supported.

@rustbot
Copy link
Collaborator

rustbot commented Nov 28, 2023

r? @TaKO8Ki

(rustbot has picked a reviewer for you, use r? to override)

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Nov 28, 2023
@dtolnay dtolnay added the A-attributes Area: Attributes (`#[…]`, `#![…]`) label Nov 28, 2023
@petrochenkov
Copy link
Contributor

I'm quite strongly against doing this.
Assigning to myself to look at this in more detail some time later.

@petrochenkov petrochenkov self-assigned this Nov 28, 2023
@Mark-Simulacrum
Copy link
Member

The thing that makes it difficult to transpose this into something that produces 1 impl block per type, while remaining maintainable, is that composing stability attributes using a macro is impossible.

It seems like an alternative might be some doc(merge) or whatever annotation to teach rustdoc to not preserve the impl block boundaries, if we don't want that at all for these types?

@TaKO8Ki
Copy link
Member

TaKO8Ki commented Nov 30, 2023

r? @petrochenkov

@rustbot
Copy link
Collaborator

rustbot commented Nov 30, 2023

Could not assign reviewer from: petrochenkov.
User(s) petrochenkov are either the PR author, already assigned, or on vacation, and there are no other candidates.
Use r? to specify someone else to assign.

@dtolnay dtolnay added the A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..) label Dec 15, 2023
@petrochenkov
Copy link
Contributor

(I should get to this on new year holidays.)

@bors
Copy link
Contributor

bors commented Dec 26, 2023

☔ The latest upstream changes (presumably #119146) made this pull request unmergeable. Please resolve the merge conflicts.

@dtolnay
Copy link
Member Author

dtolnay commented Dec 26, 2023

@bors
Copy link
Contributor

bors commented Jan 13, 2024

☔ The latest upstream changes (presumably #119088) made this pull request unmergeable. Please resolve the merge conflicts.

    error: expected unsuffixed literal or identifier, found `stable_feature`
      --> tests/ui/stability-attribute/eager-expansion.rs:37:32
       |
    LL |               #[stable(feature = stable_feature!($signedness), since = stable_since!($signedness))]
       |                                  ^^^^^^^^^^^^^^
    ...
    LL | / nonzero_integers! {
    LL | |     unsigned NonZeroU8(u8)
    LL | |     unsigned NonZeroU16(u16)
    LL | |     unsigned NonZeroU32(u32)
    ...  |
    LL | |     signed NonZeroI64(u64)
    LL | | }
       | |_- in this macro invocation
       |
       = note: this error originates in the macro `nonzero_integers` (in Nightly builds, run with -Z macro-backtrace for more info)
@dtolnay
Copy link
Member Author

dtolnay commented Jan 14, 2024

@dtolnay
Copy link
Member Author

dtolnay commented Jan 21, 2024

In #118665 I discovered that tidy requires being able to see #[stable(feature = "…", since = "…")] written out with string literals; #[stable(feature = $feature, since = $since)] or #[stable(feature = feature!(…), since = since!(…))] will never cut it.

tidy error: library/core/src/num/nonzero.rs:67: malformed stability attribute: missing `feature` key
tidy error: library/core/src/num/nonzero.rs:82: malformed stability attribute: missing `feature` key
tidy error: library/core/src/num/nonzero.rs:98: malformed stability attribute: missing the `since` key
tidy error: library/core/src/num/nonzero.rs:112: malformed stability attribute: missing `feature` key
tidy error: library/core/src/num/nonzero.rs:450: malformed stability attribute: missing `feature` key
some tidy checks failed

I am still interesting in seeing something like this PR made to work, but specifically for stability this may not be worth doing now, and #118406 (comment) tells me there isn't a path to extend this to other kinds of inert attrs (like deprecated).

@dtolnay dtolnay closed this Jan 21, 2024
@dtolnay dtolnay deleted the eager branch January 21, 2024 04:31
@dtolnay dtolnay added A-stability Area: `#[stable]`, `#[unstable]` etc. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jan 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-attributes Area: Attributes (`#[…]`, `#![…]`) A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..) A-stability Area: `#[stable]`, `#[unstable]` etc. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants