diff --git a/clippy_lints/src/missing_const_for_thread_local.rs b/clippy_lints/src/missing_const_for_thread_local.rs
index 0c0548029f5e..08056ae5b2ff 100644
--- a/clippy_lints/src/missing_const_for_thread_local.rs
+++ b/clippy_lints/src/missing_const_for_thread_local.rs
@@ -122,6 +122,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForThreadLocal {
&& let ExprKind::Block(block, _) = body.value.kind
&& let Some(unpeeled) = block.expr
&& let ret_expr = peel_blocks(unpeeled)
+ // The initializer is already a `const` block: there is nothing to suggest.
+ // On targets without `#[thread_local]` (e.g. x86_64-pc-windows-gnu), the
+ // generated init fn is not a `const fn` even for `const` initializers,
+ // so the `is_const_fn` check above does not cover this case.
+ && !matches!(ret_expr.kind, ExprKind::ConstBlock(_))
// A common pattern around threadlocal! is to make the value unreachable
// to force an initialization before usage
// https://github.com/rust-lang/rust-clippy/issues/12637
diff --git a/tests/ui/missing_const_for_thread_local.fixed b/tests/ui/missing_const_for_thread_local.fixed
index 2efe10c153da..8480d81dccd8 100644
--- a/tests/ui/missing_const_for_thread_local.fixed
+++ b/tests/ui/missing_const_for_thread_local.fixed
@@ -69,6 +69,14 @@ fn issue_12637() {
println!("{}", STATE_12637_UNREACHABLE.get());
}
+fn issue_17566() {
+ // On targets without native `#[thread_local]`, a `const` initializer expands to a
+ // non-`const` init fn, which used to slip past the `is_const_fn` guard and lint here.
+ thread_local! {
+ static STATE_17566: Cell