Skip to content

Commit db382f0

Browse files
committed
Implement TransparentWrapper for atomic types
Makes progress on #1009
1 parent 86ee1fc commit db382f0

File tree

1 file changed

+144
-41
lines changed

1 file changed

+144
-41
lines changed

src/util.rs

Lines changed: 144 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,14 @@
77
// those terms.
88

99
use core::{
10+
cell::UnsafeCell,
1011
mem::{self, ManuallyDrop, MaybeUninit},
1112
num::{NonZeroUsize, Wrapping},
1213
ptr::NonNull,
14+
sync::atomic::{
15+
AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicPtr, AtomicU16, AtomicU32,
16+
AtomicU8, AtomicUsize,
17+
},
1318
};
1419

1520
use crate::{
@@ -21,11 +26,11 @@ use crate::{
2126
///
2227
/// # Safety
2328
///
24-
/// `T: TransparentWrapper` implies that `T` has the same size and field offsets
25-
/// as [`T::Inner`]. Note that this implies that `T` has [`UnsafeCell`]s
26-
/// covering the same byte ranges as `T::Inner`. This further implies that `T`
27-
/// has zero-sized `UnsafeCell`s (e.g., `UnsafeCell<()>`, `[UnsafeCell<u8>; 0]`,
28-
/// etc) at the same byte offsets as `T::Inner`.
29+
/// `T: TransparentWrapper` implies that:
30+
/// - `T` has the same size as [`T::Inner`]
31+
/// - `T` has `UnsafeCell`s covering the same byte ranges as `T::Inner`
32+
/// - `T` has zero-sized `UnsafeCell`s (e.g., `UnsafeCell<()>`,
33+
/// `[UnsafeCell<u8>; 0]`, etc) at the same byte offsets as `T::Inner`
2934
///
3035
/// Further, `T: TransparentWrapper<I>` implies that:
3136
/// - If a `T` pointer satisfies the alignment invariant `I::Alignment`, then
@@ -100,22 +105,21 @@ impl<I: invariant::Validity> ValidityVariance<I> for Invariant {
100105
}
101106

102107
// SAFETY:
103-
// - Per [1], `MaybeUninit<T>` has the same layout as `Inner = T`.
104-
// - Per [2], `MaybeUninit<T>` has `UnsafeCell`s at the same byte ranges as
108+
// - Per [1], `MaybeUninit<T>` has `UnsafeCell`s at the same byte ranges as
105109
// `Inner = T`, and `UnsafeCell`s at the same byte offsets as `T`.
106110
// - See inline comments for other safety justifications.
107111
//
108-
// [1] Per https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#layout-1:
109-
//
110-
// `MaybeUninit<T>` is guaranteed to have the same size, alignment, and ABI as
111-
// `T`.
112-
//
113-
// [2] TODO(#896): Write a safety proof before the next stable release.
112+
// [1] TODO(#896): Write a safety proof before the next stable release.
114113
unsafe impl<T, I: Invariants> TransparentWrapper<I> for MaybeUninit<T> {
115114
type Inner = T;
116115

117-
// SAFETY: Per [1] (from comment above), `MaybeUninit<T>` has the same
118-
// layout as `T`, and thus has the same alignment as `T`.
116+
// SAFETY: Per [1], `MaybeUninit<T>` has the same layout as `T`, and thus
117+
// has the same alignment as `T`.
118+
//
119+
// [1] Per https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#layout-1:
120+
//
121+
// `MaybeUninit<T>` is guaranteed to have the same size, alignment, and
122+
// ABI as `T`.
119123
type AlignmentVariance = Covariant;
120124
// SAFETY: `MaybeUninit` has no validity invariants. Thus, a valid
121125
// `MaybeUninit<T>` is not necessarily a valid `T`.
@@ -139,22 +143,21 @@ unsafe impl<T, I: Invariants> TransparentWrapper<I> for MaybeUninit<T> {
139143
}
140144

141145
// SAFETY:
142-
// - Per [1], `ManuallyDrop<T>` has the same layout as `Inner = T`.
143-
// - Per [2], `ManuallyDrop<T>` has `UnsafeCell`s at the same byte ranges as
146+
// - Per [1], `ManuallyDrop<T>` has `UnsafeCell`s at the same byte ranges as
144147
// `Inner = T`, and `UnsafeCell`s at the same byte offsets as `T`.
145148
// - See inline comments for other safety justifications.
146149
//
147-
// [1] Per https://doc.rust-lang.org/nightly/core/mem/struct.ManuallyDrop.html:
148-
//
149-
// `ManuallyDrop<T>` is guaranteed to have the same layout and bit validity as
150-
// `T`
151-
//
152-
// [2] TODO(#896): Write a safety proof before the next stable release.
150+
// [1] TODO(#896): Write a safety proof before the next stable release.
153151
unsafe impl<T: ?Sized, I: Invariants> TransparentWrapper<I> for ManuallyDrop<T> {
154152
type Inner = T;
155153

156-
// SAFETY: Per [1] (from comment above), `ManuallyDrop<T>` has the same
157-
// layout as `T`, and thus has the same alignment as `T`.
154+
// SAFETY: Per [1], `ManuallyDrop<T>` has the same layout as `T`, and thus
155+
// has the same alignment as `T`.
156+
//
157+
// [1] Per https://doc.rust-lang.org/nightly/core/mem/struct.ManuallyDrop.html:
158+
//
159+
// `ManuallyDrop<T>` is guaranteed to have the same layout and bit
160+
// validity as `T`
158161
type AlignmentVariance = Covariant;
159162

160163
// SAFETY: Per [1] (from comment above), `ManuallyDrop<T>` has the same bit
@@ -181,21 +184,20 @@ unsafe impl<T: ?Sized, I: Invariants> TransparentWrapper<I> for ManuallyDrop<T>
181184
}
182185

183186
// SAFETY:
184-
// - Per [1], `Wrapping<T>` has the same layout as `Inner = T`.
185-
// - Per [2], `Wrapping<T>` has `UnsafeCell`s at the same byte ranges as `Inner
187+
// - Per [1], `Wrapping<T>` has `UnsafeCell`s at the same byte ranges as `Inner
186188
// = T`, and `UnsafeCell`s at the same byte offsets as `T`.
187189
// - See inline comments for other safety justifications.
188190
//
189-
// [1] Per https://doc.rust-lang.org/core/num/struct.Wrapping.html#layout-1:
190-
//
191-
// `Wrapping<T>` is guaranteed to have the same layout and ABI as `T`.
192-
//
193-
// [2] TODO(#896): Write a safety proof before the next stable release.
191+
// [1] TODO(#896): Write a safety proof before the next stable release.
194192
unsafe impl<T, I: Invariants> TransparentWrapper<I> for Wrapping<T> {
195193
type Inner = T;
196194

197-
// SAFETY: Per [1] (from comment above), `Wrapping<T>` has the same layout
198-
// as `T`, and thus has the same alignment as `T`.
195+
// SAFETY: Per [1], `Wrapping<T>` has the same layout as `T`, and thus has
196+
// the same alignment as `T`.
197+
//
198+
// [1] Per https://doc.rust-lang.org/core/num/struct.Wrapping.html#layout-1:
199+
//
200+
// `Wrapping<T>` is guaranteed to have the same layout and ABI as `T`.
199201
type AlignmentVariance = Covariant;
200202

201203
// SAFETY: `Wrapping<T>` has only one field, which is `pub` [2]. We are also
@@ -230,9 +232,8 @@ unsafe impl<T, I: Invariants> TransparentWrapper<I> for Wrapping<T> {
230232
}
231233

232234
// SAFETY: We define `Unalign<T>` to be a `#[repr(C, packed)]` type wrapping a
233-
// single `T` field. Thus, `Unalign<T>` has the same size and field offsets as
234-
// `T`, has `UnsafeCell`s at the same byte ranges as `T`, and has `UnsafeCell`s
235-
// at the same byte offsets as `T`.
235+
// single `T` field. Thus, `Unalign<T>` has `UnsafeCell`s at the same byte
236+
// ranges as `T` and has `UnsafeCell`s at the same byte offsets as `T`.
236237
//
237238
// See inline comments for other safety justifications.
238239
unsafe impl<T, I: Invariants> TransparentWrapper<I> for Unalign<T> {
@@ -248,22 +249,124 @@ unsafe impl<T, I: Invariants> TransparentWrapper<I> for Unalign<T> {
248249
type ValidityVariance = Covariant;
249250

250251
fn cast_into_inner(ptr: *mut Unalign<T>) -> *mut T {
251-
// SAFETY: Per the comment above, `Unalign<T>` has the same layout as
252-
// `T`. Thus, this cast preserves size.
252+
// SAFETY: We define `Unalign<T>` to be a `#[repr(C, packed)]` wrapper
253+
// around a single `T` field. Thus, `Unalign<T>` has the size as `T`.
254+
// Thus, this cast preserves size.
253255
//
254256
// This cast trivially preserves provenance.
255257
ptr.cast::<T>()
256258
}
257259

258260
fn cast_from_inner(ptr: *mut T) -> *mut Unalign<T> {
259-
// SAFETY: Per the comment above, `Unalign<T>` has the same layout as
260-
// `T`. Thus, this cast preserves size.
261+
// SAFETY: Per the comment in `cast_into_inner`, `Unalign<T>` has the
262+
// same size as `T`. Thus, this cast preserves size.
261263
//
262264
// This cast trivially preserves provenance.
263265
ptr.cast::<Unalign<T>>()
264266
}
265267
}
266268

269+
/// Implements `TransparentWrapper` for an atomic type.
270+
///
271+
/// # Safety
272+
///
273+
/// The caller promises that `$atomic` is an atomic type whose natie equivalent
274+
/// is `$native`.
275+
macro_rules! unsafe_impl_transparent_wrapper_for_atomic {
276+
($(#[$attr:meta])* $(,)?) => {};
277+
($(#[$attr:meta])* $atomic:ty [$native:ty], $($atomics:ty [$natives:ty]),* $(,)?) => {
278+
$(#[$attr])*
279+
// SAFETY: See safety comment in next match arm.
280+
unsafe impl<I: Invariants> TransparentWrapper<I> for $atomic {
281+
unsafe_impl_transparent_wrapper_for_atomic!(@inner $atomic [$native]);
282+
}
283+
unsafe_impl_transparent_wrapper_for_atomic!($(#[$attr])* $($atomics [$natives],)*);
284+
};
285+
($(#[$attr:meta])* $tyvar:ident => $atomic:ty [$native:ty]) => {
286+
// The caller has promised that `$atomic` and `$native` are an atomic
287+
// type and its native counterpart, respectively. It is "obvious" that
288+
// each atomic type contains a single `UnsafeCell` that covers all bytes
289+
// of the type, but we can also prove it:
290+
// - Since `$atomic` provides an API which permits loading and storing
291+
// values of type `$native` via a `&self` (shared) reference, *some*
292+
// interior mutation must be happening, and interior mutation can only
293+
// happen via `UnsafeCell`. Further, there must be enough bytes in
294+
// `$atomic` covered by an `UnsafeCell` to hold every possible value
295+
// of `$native`.
296+
// - Per [1], `$atomic` has the same size as `$native`. This on its own
297+
// isn't enough: it would still be possible for `$atomic` to store
298+
// `$native` using a compact representation (for `$native` types for
299+
// which some bit patterns are illegal). However, this is ruled out by
300+
// the fact that `$atomic` has the same bit validity as `$native` [1].
301+
// Thus, we can conclude that every byte of `$atomic` must be covered
302+
// by an `UnsafeCell`.
303+
//
304+
// Thus, every byte of `$atomic` is covered by an `UnsafeCell`, and we
305+
// set `type Inner = UnsafeCell<$native>`. Thus, `Self` and
306+
// `Self::Inner` have `UnsafeCell`s covering the same byte ranges.
307+
//
308+
// See inline comments for safety requirements regarding invariant
309+
// variance.
310+
//
311+
// [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943):
312+
// Cite docs once they've landed.
313+
$(#[$attr])*
314+
unsafe impl<$tyvar, I: Invariants> TransparentWrapper<I> for $atomic {
315+
unsafe_impl_transparent_wrapper_for_atomic!(@inner $atomic [$native]);
316+
}
317+
};
318+
(@inner $atomic:ty [$native:ty]) => {
319+
type Inner = UnsafeCell<$native>;
320+
321+
// SAFETY: No safety justification is required for an invariant
322+
// variance.
323+
type AlignmentVariance = Invariant;
324+
325+
// SAFETY: Per [1], all atomic types have the same bit validity as their
326+
// native counterparts. The caller has promised that `$atomic` and
327+
// `$native` are an atomic type and its native counterpart,
328+
// respectively.
329+
//
330+
// [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943):
331+
// Cite docs once they've landed.
332+
type ValidityVariance = Covariant;
333+
334+
fn cast_into_inner(ptr: *mut $atomic) -> *mut UnsafeCell<$native> {
335+
// SAFETY: Per [1] (from comment on impl block), `$atomic` has the
336+
// same size as `$native`. Thus, this cast preserves size.
337+
//
338+
// This cast trivially preserves provenance.
339+
ptr.cast::<UnsafeCell<$native>>()
340+
}
341+
342+
fn cast_from_inner(ptr: *mut UnsafeCell<$native>) -> *mut $atomic {
343+
// SAFETY: Per [1] (from comment on impl block), `$atomic` has the
344+
// same size as `$native`. Thus, this cast preserves size.
345+
//
346+
// This cast trivially preserves provenance.
347+
ptr.cast::<$atomic>()
348+
}
349+
};
350+
}
351+
352+
safety_comment! {
353+
/// SAFETY:
354+
/// All of these pass an atomic type and that type's native equivalent, as
355+
/// required by the macro safety preconditions.
356+
unsafe_impl_transparent_wrapper_for_atomic!(T => AtomicPtr<T> [*mut T]);
357+
unsafe_impl_transparent_wrapper_for_atomic!(
358+
AtomicBool [bool],
359+
AtomicI16 [i16], AtomicI32 [i32], AtomicI8 [i8], AtomicIsize [isize],
360+
AtomicU16 [u16], AtomicU32 [u32], AtomicU8 [u8], AtomicUsize [usize],
361+
);
362+
#[cfg(not(target_arch = "powerpc"))]
363+
unsafe_impl_transparent_wrapper_for_atomic!(
364+
#[cfg_attr(doc_cfg, doc(cfg(not(target_arch = "powerpc"))))]
365+
core::sync::atomic::AtomicI64 [i64],
366+
core::sync::atomic::AtomicU64 [u64],
367+
);
368+
}
369+
267370
pub(crate) trait AsAddress {
268371
fn addr(self) -> usize;
269372
}

0 commit comments

Comments
 (0)