Skip to content

Commit ee02ee8

Browse files
committed
Implement traits for atomic types
Makes progress on #1009. This commit doesn't implement all traits for `AtomicPtr<T>`, so there's more work to do.
1 parent 2f32f0e commit ee02ee8

File tree

2 files changed

+123
-24
lines changed

2 files changed

+123
-24
lines changed

src/lib.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,10 @@ use core::{
304304
ops::{Deref, DerefMut},
305305
ptr::{self, NonNull},
306306
slice,
307+
sync::atomic::{
308+
AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicPtr, AtomicU16, AtomicU32,
309+
AtomicU8, AtomicUsize,
310+
},
307311
};
308312

309313
use crate::pointer::invariant;
@@ -1096,7 +1100,9 @@ impl_known_layout!(
10961100
u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize, f32, f64,
10971101
bool, char,
10981102
NonZeroU8, NonZeroI8, NonZeroU16, NonZeroI16, NonZeroU32, NonZeroI32,
1099-
NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize
1103+
NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize,
1104+
AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicU16, AtomicU32,
1105+
AtomicU8, AtomicUsize
11001106
);
11011107
#[rustfmt::skip]
11021108
impl_known_layout!(
@@ -1106,6 +1112,7 @@ impl_known_layout!(
11061112
T => MaybeUninit<T>,
11071113
T: ?Sized => *const T,
11081114
T: ?Sized => *mut T,
1115+
T => AtomicPtr<T>
11091116
);
11101117
impl_known_layout!(const N: usize, T => [T; N]);
11111118

@@ -3892,6 +3899,48 @@ safety_comment! {
38923899
unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => NoCell for opt_extern_c_fn!(...));
38933900
}
38943901

3902+
macro_rules! impl_traits_for_atomics {
3903+
($($atomics:ident [$inners:ident]),* $(,)?) => {
3904+
$(
3905+
impl_for_transparent_wrapper!(TryFromBytes for $atomics [UnsafeCell<$inners>]);
3906+
impl_for_transparent_wrapper!(FromZeros for $atomics [UnsafeCell<$inners>]);
3907+
impl_for_transparent_wrapper!(FromBytes for $atomics [UnsafeCell<$inners>]);
3908+
impl_for_transparent_wrapper!(IntoBytes for $atomics [UnsafeCell<$inners>]);
3909+
)*
3910+
};
3911+
}
3912+
3913+
#[rustfmt::skip]
3914+
impl_traits_for_atomics!(
3915+
AtomicBool [bool],
3916+
AtomicI16 [i16], AtomicI32 [i32], AtomicI8 [i8], AtomicIsize [isize],
3917+
AtomicU16 [u16], AtomicU32 [u32], AtomicU8 [u8], AtomicUsize [usize],
3918+
);
3919+
3920+
safety_comment! {
3921+
/// SAFETY:
3922+
/// Per [1], `AtomicBool`, `AtomicU8`, and `AtomicI8` have the same size as
3923+
/// `bool`, `u8`, and `i8` respectively. Since a type's alignment cannot be
3924+
/// smaller than 1 [2], and since its alignment cannot be greater than its
3925+
/// size [3], the only possible value for the alignment is 1. Thus, it is
3926+
/// sound to implement `Unaligned`.
3927+
///
3928+
/// [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943):
3929+
/// Cite docs once they've landed.
3930+
///
3931+
/// [2] Per https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment:
3932+
///
3933+
/// Alignment is measured in bytes, and must be at least 1.
3934+
///
3935+
/// [3] Per https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment:
3936+
///
3937+
/// The size of a value is always a multiple of its alignment.
3938+
unsafe_impl!(AtomicBool: Unaligned);
3939+
unsafe_impl!(AtomicU8: Unaligned);
3940+
unsafe_impl!(AtomicI8: Unaligned);
3941+
assert_unaligned!(AtomicBool, AtomicU8, AtomicI8);
3942+
}
3943+
38953944
safety_comment! {
38963945
/// SAFETY:
38973946
/// Per reference [1]:

src/macros.rs

Lines changed: 73 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -216,14 +216,15 @@ macro_rules! impl_for_transparent_wrapper {
216216
// - `f` can only compile if its internal call to
217217
// `is_transparent_wrapper` compiles.
218218
//
219-
// The definition of `is_transparent_wrapper` is generated by each
220-
// `@is_transparent_wrapper` macro arm. Each arm is parameterized by
221-
// `$trait`, and emits bounds which are sufficient to ensure that this
222-
// is a sound implementation of `$trait for $ty` *so long as* `$tyvar:
223-
// $trait`. Note that we require `$tyvar: $trait` in the `impl` block
224-
// itself. Thus, so long as the bounds emitted by the
225-
// `@is_transparent_wrapper` arm are sound, then this entire impl is
226-
// sound.
219+
// The definition of `is_transparent_wrapper<I, T, W>` is generated by
220+
// each `@is_transparent_wrapper` macro arm. Each arm is parameterized
221+
// by `$trait`, and emits bounds which are sufficient to ensure that
222+
// this is a sound implementation of `$trait for W` *so long as* `T:
223+
// $trait`. In `f`, we call `is_transparent_wrapper<I, $tyvar, $ty>`,
224+
// and so if this code compiles, that guarantees that `$ty` satisfies
225+
// the same bounds for all `$tyvar: $trait`. Thus, so long as the bounds
226+
// emitted by the `@is_transparent_wrapper` arm are sound, then this
227+
// entire impl is sound.
227228
//
228229
// Each `@is_transparent_wrapper` arm contains its own safety comment
229230
// which explains why its bounds are sound.
@@ -241,11 +242,61 @@ macro_rules! impl_for_transparent_wrapper {
241242

242243
impl_for_transparent_wrapper!(
243244
@is_bit_valid
244-
$tyvar: $($(? $optbound +)* $($bound +)*)?
245-
=> $trait for $ty
245+
<$tyvar: $($(? $optbound +)* $($bound +)*)?>
246+
$trait for $ty
246247
);
247248
}
248249
};
250+
(
251+
$(#[$attr:meta])*
252+
for $ty:ty [$inner:ty] $(; |$candidate:ident $(: MaybeAligned<$ref_repr:ty>)? $(: Maybe<$ptr_repr:ty>)?| $is_bit_valid:expr)?
253+
) => {};
254+
(
255+
$(#[$attr:meta])*
256+
$trait:ident $(, $traits:ident)* for $ty:ty [$inner:ty] $(; |$candidate:ident $(: MaybeAligned<$ref_repr:ty>)? $(: Maybe<$ptr_repr:ty>)?| $is_bit_valid:expr)?
257+
) => {
258+
impl_for_transparent_wrapper!(
259+
$(#[$attr])*
260+
$($traits),* for $ty [$inner] $(; |$candidate $(: MaybeAligned<$ref_repr>)? $(: Maybe<$ptr_repr>)?| $is_bit_valid:expr)?
261+
);
262+
263+
$(#[$attr])*
264+
#[allow(non_local_definitions)]
265+
// SAFETY:
266+
// - We explicitly add a `$tyvar: $trait` bound (regardless of what
267+
// bounds the caller has provided).
268+
// - Inside of `only_derive_is_allowed_to_implement_this_trait`, `f` is
269+
// generic over the any `I: Invariants`.
270+
// - `f` can only compile if its internal call to
271+
// `is_transparent_wrapper` compiles.
272+
//
273+
// The definition of `is_transparent_wrapper<I, T, W>` is generated by
274+
// each `@is_transparent_wrapper` macro arm. Each arm is parameterized
275+
// by `$trait`, and emits bounds which are sufficient to ensure that
276+
// this is a sound implementation of `$trait for W` *so long as* `T:
277+
// $trait`. In `f`, we call `is_transparent_wrapper<I, $inner, $ty>`,
278+
// and so if this code compiles, that guarantees that `$ty` satisfies
279+
// the same bounds so long as `$inner: $trait`. Thus, so long as the
280+
// bounds emitted by the `@is_transparent_wrapper` arm are sound, then
281+
// this entire impl is sound.
282+
//
283+
// Each `@is_transparent_wrapper` arm contains its own safety comment
284+
// which explains why its bounds are sound.
285+
unsafe impl $trait for $ty {
286+
#[allow(dead_code, clippy::missing_inline_in_public_items)]
287+
fn only_derive_is_allowed_to_implement_this_trait() {
288+
use crate::{pointer::invariant::Invariants, util::*};
289+
290+
impl_for_transparent_wrapper!(@is_transparent_wrapper $trait);
291+
292+
fn f<I: Invariants>() {
293+
is_transparent_wrapper::<I, $inner, $ty>();
294+
}
295+
}
296+
297+
impl_for_transparent_wrapper!(@is_bit_valid $trait for $ty);
298+
}
299+
};
249300
(@is_transparent_wrapper NoCell) => {
250301
// SAFETY: `W: TransparentWrapper<UnsafeCellVariance=Covariant>`
251302
// requires that `W` has `UnsafeCell`s at the same byte offsets as
@@ -299,8 +350,8 @@ macro_rules! impl_for_transparent_wrapper {
299350
};
300351
(
301352
@is_bit_valid
302-
$tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?
303-
=> TryFromBytes for $ty:ty
353+
$(<$tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?>)?
354+
TryFromBytes for $ty:ty
304355
) => {
305356
// SAFETY: See safety comment in `(@is_transparent_wrapper
306357
// TryFromBytes)` macro arm for an explanation of why this is a sound
@@ -312,8 +363,8 @@ macro_rules! impl_for_transparent_wrapper {
312363
};
313364
(
314365
@is_bit_valid
315-
$tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?
316-
=> $trait:ident for $ty:ty
366+
$(<$tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?>)?
367+
$trait:ident for $ty:ty
317368
) => {
318369
// Trait other than `TryFromBytes`; no `is_bit_valid` impl.
319370
};
@@ -553,15 +604,14 @@ macro_rules! unsafe_impl_known_layout {
553604
/// Note that `align_of<T>` requires `T: Sized`, so this macro doesn't work for
554605
/// unsized types.
555606
macro_rules! assert_unaligned {
556-
($ty:ty) => {
557-
// We only compile this assertion under `cfg(test)` to avoid taking an
558-
// extra non-dev dependency (and making this crate more expensive to
559-
// compile for our dependents).
560-
#[cfg(test)]
561-
static_assertions::const_assert_eq!(core::mem::align_of::<$ty>(), 1);
562-
};
563-
($($ty:ty),*) => {
564-
$(assert_unaligned!($ty);)*
607+
($($tys:ty),*) => {
608+
$(
609+
// We only compile this assertion under `cfg(test)` to avoid taking
610+
// an extra non-dev dependency (and making this crate more expensive
611+
// to compile for our dependents).
612+
#[cfg(test)]
613+
static_assertions::const_assert_eq!(core::mem::align_of::<$tys>(), 1);
614+
)*
565615
};
566616
}
567617

0 commit comments

Comments
 (0)