Skip to content

const_item_interior_mutations false-positive #150157

@JonasKruckenberg

Description

@JonasKruckenberg

Code

#![feature(thread_local)]

use std::cell::UnsafeCell;
use std::ops::Deref;
use std::ptr::NonNull;
use std::cell::Cell;

pub struct LocalKey<T> {
    inner: fn(Option<&mut Option<T>>) -> NonNull<T>,
}

impl<T: 'static> LocalKey<T> {
    #[doc(hidden)]
    pub const unsafe fn new(inner: fn(Option<&mut Option<T>>) -> NonNull<T>) -> Self {
        Self { inner }
    }
}

// stdlib's LocalKey does not implement Deref, this seems to be the difference
impl<T> Deref for LocalKey<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        unsafe { (self.inner)(None).as_ref() }
    }
}

// Inlined, cut-down version similar to the output of thread_local
const LOCAL_COUNT: LocalKey<Cell<usize>> = {
    #[inline]
    fn __init() -> Cell<usize> {
        Cell::new(0)
    }

    unsafe {
        LocalKey::new(const {
            |_| {
                #[thread_local]
                static VAL: UnsafeCell<Option<Cell<usize>>> = UnsafeCell::new(None);

                NonNull::from((*VAL.get()).get_or_insert_with(__init))
            }
        })
    }
};

fn main() {
    let count = LOCAL_COUNT.get();
    LOCAL_COUNT.set(count);
}

Current output

Compiling playground v0.0.1 (/playground)
warning: mutation of an interior mutable `const` item with call to `get`
  --> src/main.rs:48:17
   |
48 |     let count = LOCAL_COUNT.get();
   |                 -----------^^^^^^
   |                 |
   |                 `LOCAL_COUNT` is a interior mutable `const` item of type `LocalKey<Cell<usize>>`
   |
   = note: each usage of a `const` item creates a new temporary
   = note: only the temporaries and never the original `const LOCAL_COUNT` will be modified
   = help: for more details on interior mutability see <https://doc.rust-lang.org/reference/interior-mutability.html>
   = note: `#[warn(const_item_interior_mutations)]` on by default
help: for a shared instance of `LOCAL_COUNT`, consider making it a `static` item instead
   |
29 - const LOCAL_COUNT: LocalKey<Cell<usize>> = {
29 + static LOCAL_COUNT: LocalKey<Cell<usize>> = {
   |

warning: mutation of an interior mutable `const` item with call to `set`
  --> src/main.rs:49:5
   |
49 |     LOCAL_COUNT.set(count);
   |     -----------^^^^^^^^^^^
   |     |
   |     `LOCAL_COUNT` is a interior mutable `const` item of type `LocalKey<Cell<usize>>`
   |
   = note: each usage of a `const` item creates a new temporary
   = note: only the temporaries and never the original `const LOCAL_COUNT` will be modified
   = help: for more details on interior mutability see <https://doc.rust-lang.org/reference/interior-mutability.html>
help: for a shared instance of `LOCAL_COUNT`, consider making it a `static` item instead
   |
29 - const LOCAL_COUNT: LocalKey<Cell<usize>> = {
29 + static LOCAL_COUNT: LocalKey<Cell<usize>> = {
   |

warning: `playground` (bin "playground") generated 2 warnings
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.66s
     Running `target/debug/playground`

Desired output

Not that

Rationale and extra context

See a playground reproduction: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=ffb22b5b2cc4581b16463e46891b438e

Which is a cut down reproduction of a cpu-local storage primitive crate I'm maintaining (which is a simplified, no_std version of stdlibs thread_local).

The type is LocalKey<Cell<usize>> yes, but it is not actually interior-ly (?) mutable, it is just a function pointer to the actually interior mutable thread-local static (which is a static so all good). This seems to be a case where #148407 does not infer the type correctly?

Other cases

Rust Version

rustc 1.94.0-nightly (f52090008 2025-12-10)
binary: rustc
commit-hash: f5209000832c9d3bc29c91f4daef4ca9f28dc797
commit-date: 2025-12-10
host: aarch64-apple-darwin
release: 1.94.0-nightly
LLVM version: 21.1.5

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsA-lintsArea: Lints (warnings about flaws in source code) such as unused_mut.C-bugCategory: This is a bug.I-prioritizeIssue: Indicates that prioritization has been requested for this issue.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.regression-from-stable-to-betaPerformance or correctness regression from stable to beta.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions