Call to `needs_drop` doesn't optimize properly in combination with unrelated mutation of static variable
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
I tried this code (on Godbolt):
use std::sync::atomic::{AtomicBool, Ordering};
#[no_mangle]
pub fn f1() -> u32 {
let slot = Slot::new(|| 6);
slot.get() + 12
}
#[no_mangle]
pub fn f2() -> u32 {
static S: Slot<u32> = Slot::new(|| 5);
S.get() + 10
}
struct Slot<T>(fn() -> T, AtomicBool);
impl<T> Slot<T> {
const fn new(f: fn() -> T) -> Self {
Self(f, AtomicBool::new(false))
}
fn get(&self) -> T {
if std::mem::needs_drop::<T>() {
self.1.store(true, Ordering::SeqCst);
}
(self.0)()
}
}
I expected to see this happen: With opt-level 3, both f1 and f2 produce optimized assembly that simply returns 18 and 15, respectively. For u32, the whole branch with std::mem::needs_drop::<T>() should completely optimize away.
Instead, this happened: f1 optimizes perfectly while f2 contains an unnecessary call which is not inlined. This only seems to happen if the branch guarded by the drop check could have a side effect on a global variable. The side effect is optimized away, but for some reason the call is not inlined anymore:
core::ops::function::FnOnce::call_once::h572ede77f557e91f:
mov eax, 5
ret
f1:
mov eax, 18
ret
f2:
push rax
call core::ops::function::FnOnce::call_once::h572ede77f557e91f
add eax, 10
pop rcx
ret
Meta
rustc --version --verbose:
rustc 1.88.0 (6b00bc388 2025-06-23)
binary: rustc
commit-hash: 6b00bc3880198600130e1cf62b8f8a93494488cc
commit-date: 2025-06-23
host: x86_64-unknown-linux-gnu
release: 1.88.0
LLVM version: 20.1.5
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the issue with the provided Slot example using rustc 1.88.0 and -C opt-level=3, then compare the generated assembly for f1 and f2. Investigate why the static case retains the call to FnOnce::call_once even though the needs_drop branch is eliminated. Done means the unnecessary call is inlined or removed while preserving the expected results 18 and 15.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100