rust-lang / rust-lang/rust-clippy

`missing_const_for_thread_local` false positive on targets without `#[thread_local]`: fires on already-`const` initializers

Open Beginner friendly
#17,566 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

C-bug I-false-positive
Dominant language
Rust
Stars
13.5k
Forks
2.2k
Avg merge
2d 10h
Merged PRs (30d)
32

Description

Summary

On targets without native #[thread_local] support (no target_thread_local cfg — e.g. x86_64-pc-windows-gnu), missing_const_for_thread_local fires on thread_local! statics whose initializer is already a const block, spanning the whole macro invocation.

The trigger is a std expansion change between stable 1.97 and current nightlies: on the os-based TLS path, a const initializer now expands to a plain (non-const) init fn (-Zunpretty=expanded on the reproducer below):

#[inline]
fn __rust_std_internal_init_fn() -> Cell<Option<u8>> {
    const { Cell::new(None) }
}

The lint already anticipates init-fn-generating backends and skips when the generated fn is const (the !cx.tcx.is_const_fn(defid) guard from #12276) — but this generated fn is no longer const, so the guard doesn't catch it, is_min_const_fn approves the body, and the lint fires with the whole macro invocation as the span.

Observed consequences on x86_64-pc-windows-gnu with nightly-2026-08-07 (stable 1.97.1 is not affected, so this reaches stable with 1.99 if it stays):

  • the reduced example below warns spuriously, with the lint on by default;
  • cargo test --test dogfood fails on a master checkout: the lint fires on clippy_utils/src/macros.rs:205, whose initializer is already const { Cell::new(None) };
  • tests/ui/missing_const_for_thread_local.rs produces 8 errors instead of the blessed 6 (extra whole-block-span errors for the two already-const statics).

CI cannot see this: the Windows job tests x86_64-pc-windows-msvc only, and every current CI host has target_thread_local.

Lint Name

missing_const_for_thread_local

Reproducer

I tried this code:

use std::cell::Cell;
thread_local! {
    static ALREADY_CONST: Cell<Option<u8>> = const { Cell::new(None) };
}
fn main() { ALREADY_CONST.with(|c| c.get()); }

I saw this happen (cargo clippy, host x86_64-pc-windows-gnu):

warning: initializer for `thread_local` value can be made `const`
 --> src\main.rs:2:1
  |
2 | / thread_local! {
3 | |     static ALREADY_CONST: Cell<Option<u8>> = const { Cell::new(None) };
4 | | }
  | |_^
  |
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_thread_local
  = note: `#[warn(clippy::missing_const_for_thread_local)]` on by default

I expected to see this happen: no warning — the initializer is already const, there is nothing to suggest. On x86_64-pc-windows-msvc with the same nightly there is no warning.

Version
rustc 1.99.0-nightly (84b36a78a 2026-08-06)
binary: rustc
commit-hash: 84b36a78a28a63f134171c670be1932ffa2485f8
commit-date: 2026-08-06
host: x86_64-pc-windows-gnu
release: 1.99.0-nightly
LLVM version: 23.1.0
Additional Labels

@rustbot label +C-bug +I-false-positive

A small fix (skip ExprKind::ConstBlock initializers before the MIR check) removes the two extra ui-test errors and un-breaks dogfood on this host; PR to follow.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with tests/ui/missing_const_for_thread_local.rs and the missing_const_for_thread_local lint's initializer handling, then inspect the existing dogfood case at clippy_utils/src/macros.rs:205. Verify the change removes the two spurious diagnostics, restores the expected six UI-test errors, and leaves already-const initializers without warnings.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
tooling
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.