Skip to content

Commit 7f5ddf3

Browse files
committed
AtomicCell: Make into_inner const
1 parent 936fa69 commit 7f5ddf3

File tree

2 files changed

+42
-5
lines changed

2 files changed

+42
-5
lines changed

crossbeam-utils/src/atomic/atomic_cell.rs

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,46 @@ impl<T> AtomicCell<T> {
8484
///
8585
/// assert_eq!(v, 7);
8686
/// ```
87-
pub fn into_inner(self) -> T {
88-
let this = ManuallyDrop::new(self);
87+
pub const fn into_inner(self) -> T {
88+
// HACK: This is equivalent to transmute_copy by value, but available in const
89+
// context even on older rustc (const transmute_copy requires Rust 1.74), and
90+
// can work around "cannot borrow here, since the borrowed element may contain
91+
// interior mutability" error occurs (until const_refs_to_cell stabilized, i.e.,
92+
// Rust 1.83) when using transmute_copy with generic type in const context
93+
// (because this is a by-value transmutation that doesn't create a reference to
94+
// the source value).
95+
/// # Safety
96+
///
97+
/// This function has the same safety requirements as [`core::mem::transmute_copy`].
98+
///
99+
/// Since this is a by-value transmutation, it copies the bits from the source value
100+
/// into the destination value, then forgets the original, as with the [`core::mem::transmute`].
101+
#[inline]
102+
#[must_use]
103+
const unsafe fn transmute_copy_by_val<Src, Dst>(src: Src) -> Dst {
104+
#[repr(C)]
105+
union ConstHack<Src, Dst> {
106+
src: ManuallyDrop<Src>,
107+
dst: ManuallyDrop<Dst>,
108+
}
109+
assert!(mem::size_of::<Src>() >= mem::size_of::<Dst>()); // assertion copied from transmute_copy
110+
// SAFETY: ConstHack is #[repr(C)] union, and the caller must guarantee that
111+
// transmuting Src to Dst is safe.
112+
ManuallyDrop::into_inner(unsafe {
113+
ConstHack::<Src, Dst> {
114+
src: ManuallyDrop::new(src),
115+
}
116+
.dst
117+
})
118+
}
119+
89120
// SAFETY:
121+
// - Self is repr(transparent) over `UnsafeCell<MaybeUninit<T>>` and
122+
// `UnsafeCell<MaybeUninit<T>>` and `T` has the same layout.
90123
// - passing `self` by value guarantees that no other threads are concurrently
91124
// accessing the atomic data
92-
// - the raw pointer passed in is valid because we got it from an owned value.
93-
// - `ManuallyDrop` prevents double dropping `T`
94-
unsafe { this.as_ptr().read() }
125+
// (Equivalent to UnsafeCell::into_inner which is unstable in const context.)
126+
unsafe { transmute_copy_by_val(self) }
95127
}
96128

97129
/// Returns `true` if operations on values of this type are lock-free.

crossbeam-utils/tests/atomic_cell.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,10 @@ fn garbage_padding() {
276276
fn const_atomic_cell() {
277277
const _IS_LOCK_FREE_U: bool = AtomicCell::<usize>::is_lock_free();
278278
const _IS_LOCK_FREE_I: bool = AtomicCell::<isize>::is_lock_free();
279+
static INTO_INNER: usize = {
280+
let v = AtomicCell::new(!0);
281+
v.into_inner()
282+
};
279283
#[rustversion::since(1.83)]
280284
static _AS_PTR: AtomicCell<usize> = {
281285
let v = AtomicCell::new(!0);
@@ -286,6 +290,7 @@ fn const_atomic_cell() {
286290

287291
CELL.store(1);
288292
assert_eq!(CELL.load(), 1);
293+
assert_eq!(INTO_INNER, !0);
289294
}
290295

291296
// https://github.com/crossbeam-rs/crossbeam/pull/767

0 commit comments

Comments
 (0)