-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
New Lint: [thread_local_initializer_can_be_made_const
]
#12026
Conversation
r? @llogiq (rustbot has picked a reviewer for you, use r? to override) |
I wonder whether we should take mutability into account – some thread locals might be there to be mutated & making those const leads to compile errors. At least we should have a test against this, also if the lint catches those, we should move it to nursery until the false positive no longer appears. |
Edit: I will add some more tests and accommodate mutability 🤔 |
@llogiq I checked, and it's impossible to have a
because the patterns are: ($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const { $init:expr }; $($rest:tt)*) =>
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = const { $init:expr }) =>
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr; $($rest:tt)*) =>
($(#[$attr:meta])* $vis:vis static $name:ident: $t:ty = $init:expr) => |
I love the idea, but is there any way that the implementation could avoid depending on internal std details? There is some work currently being done on thread locals (see rust-lang/rust#110897 and e.g. rust-lang/rust#116123), and I really want to avoid accidentally breaking clippy when changing the expansion. |
I am uncertain. This has to be done as a late-lint so that we can check whether it can be const evaluated. Perhaps if we find a way to go back to pre-expanded code, and then do a kind of pattern match? The issue is that we must know what to lint, in a I am out of my element element I think. If anyone has any suggestions i'd be happy to give it a shot. |
// this is the `__init` function emitted by the `thread_local!` macro | ||
// when the `const` keyword is not used. We avoid checking the `__init` directly | ||
// as that is not a public API. | ||
// we know that the function is const-qualifiable, so now we need only to get the | ||
// initializer expression to span-lint it. | ||
&& let ExprKind::Block(block, _) = body.value.kind | ||
&& let Some(ret_expr) = block.expr | ||
&& let initializer_snippet = snippet(cx, ret_expr.span, "thread_local! { ... }") | ||
&& initializer_snippet != "thread_local! { ... }" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@joboet I have reduced the dependency on internal details to "just" knowing that the last expression in the initializer is the original snippet and that an initializer function exists. Name doesn't matter anymore.
I have also reviewed the storage pr you listed, and it looks fine from the perspective of this, but having a comment there indicating that the initializer is checked would most likely avoid breaking it buuut I really don't want to tie this to particular impl.
☔ The latest upstream changes (presumably #10283) made this pull request unmergeable. Please resolve the merge conflicts. |
There are merge commits (commits with multiple parents) in your changes. We have a no merge policy so these commits will need to be removed for this pull request to be merged. You can start a rebase with the following commands:
The following commits are merge commits: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a final tiny nit pick, otherwise this looks good to merge.
Adds a new lint to suggest using `const` on `thread_local!` initializers that can be evaluated at compile time. Impl details: The lint relies on the expansion of `thread_local!`. For non const-labelled initializers, `thread_local!` produces a function called `__init` that lazily initializes the value. We check the function and decide whether the body can be const. The body of the function is exactly the initializer. If so, we lint the body. changelog: new lint [`thread_local_initializer_can_be_made_const`]
Thank you. I think the dependency on the @bors r+ |
☀️ Test successful - checks-action_dev_test, checks-action_remark_test, checks-action_test |
@partiallytyped |
(I noticed this because full rust-lang/rust test suite, which includes clippy, fails on windows-gnu now.) |
I have opened a new PR for that #12186 |
Adds a new lint to suggest using
const
onthread_local!
initializers that can be evaluated at compile time.Impl details:
The lint relies on the expansion of
thread_local!
. For non const-labelled initializers,thread_local!
produces a function called__init
that lazily initializes the value. We check the function and decide whether the body can be const. If so, we lint that the initializer value can be made const.changelog: new lint [
thread_local_initializer_can_be_made_const
]fixes: #12015