Skip to content

Commit 2a787c1

Browse files
authored
Implement TransparentWrapper for atomic types (#1011)
Makes progress on #1009
1 parent 86ee1fc commit 2a787c1

File tree

1 file changed

+150
-41
lines changed

1 file changed

+150
-41
lines changed

src/util.rs

+150-41
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,22 @@ 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 the same size as `T`.
109+
// - Per [1], `MaybeUninit<T>` has `UnsafeCell`s at the same byte ranges as
105110
// `Inner = T`, and `UnsafeCell`s at the same byte offsets as `T`.
106111
// - See inline comments for other safety justifications.
107112
//
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.
113+
// [1] TODO(#896): Write a safety proof before the next stable release.
114114
unsafe impl<T, I: Invariants> TransparentWrapper<I> for MaybeUninit<T> {
115115
type Inner = T;
116116

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

141146
// 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
147+
// - Per [1], `ManuallyDrop<T>` has the same size as `T`.
148+
// - Per [1], `ManuallyDrop<T>` has `UnsafeCell`s at the same byte ranges as
144149
// `Inner = T`, and `UnsafeCell`s at the same byte offsets as `T`.
145150
// - See inline comments for other safety justifications.
146151
//
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.
152+
// [1] TODO(#896): Write a safety proof before the next stable release.
153153
unsafe impl<T: ?Sized, I: Invariants> TransparentWrapper<I> for ManuallyDrop<T> {
154154
type Inner = T;
155155

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

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

183188
// 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
189+
// - Per [1], `Wrapping<T>` has the same size as `T`.
190+
// - Per [1], `Wrapping<T>` has `UnsafeCell`s at the same byte ranges as `Inner
186191
// = T`, and `UnsafeCell`s at the same byte offsets as `T`.
187192
// - See inline comments for other safety justifications.
188193
//
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.
194+
// [1] TODO(#896): Write a safety proof before the next stable release.
194195
unsafe impl<T, I: Invariants> TransparentWrapper<I> for Wrapping<T> {
195196
type Inner = T;
196197

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

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

232237
// 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`.
238+
// single `T` field. Thus, `Unalign<T>` has the same size as `T`, has
239+
// `UnsafeCell`s at the same byte ranges as `T`, and has `UnsafeCell`s at the
240+
// same byte offsets as `T`.
236241
//
237242
// See inline comments for other safety justifications.
238243
unsafe impl<T, I: Invariants> TransparentWrapper<I> for Unalign<T> {
@@ -248,22 +253,126 @@ unsafe impl<T, I: Invariants> TransparentWrapper<I> for Unalign<T> {
248253
type ValidityVariance = Covariant;
249254

250255
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.
256+
// SAFETY: Per the safety comment on the impl block, `Unalign<T>` has
257+
// the size as `T`. Thus, this cast preserves size.
253258
//
254259
// This cast trivially preserves provenance.
255260
ptr.cast::<T>()
256261
}
257262

258263
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.
264+
// SAFETY: Per the safety comment on the impl block, `Unalign<T>` has
265+
// the size as `T`. Thus, this cast preserves size.
261266
//
262267
// This cast trivially preserves provenance.
263268
ptr.cast::<Unalign<T>>()
264269
}
265270
}
266271

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

0 commit comments

Comments
 (0)