Open
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