Skip to content

Commit

Permalink
Implement traits for atomic types (#1028)
Browse files Browse the repository at this point in the history
Makes progress on #1009. This commit doesn't implement all traits for
`AtomicPtr<T>`, so there's more work to do.
  • Loading branch information
joshlf authored Mar 7, 2024
1 parent 2f32f0e commit fe7f030
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 24 deletions.
51 changes: 50 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,10 @@ use core::{
ops::{Deref, DerefMut},
ptr::{self, NonNull},
slice,
sync::atomic::{
AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicPtr, AtomicU16, AtomicU32,
AtomicU8, AtomicUsize,
},
};

use crate::pointer::invariant;
Expand Down Expand Up @@ -1096,7 +1100,9 @@ impl_known_layout!(
u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize, f32, f64,
bool, char,
NonZeroU8, NonZeroI8, NonZeroU16, NonZeroI16, NonZeroU32, NonZeroI32,
NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize
NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize,
AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicU16, AtomicU32,
AtomicU8, AtomicUsize
);
#[rustfmt::skip]
impl_known_layout!(
Expand All @@ -1106,6 +1112,7 @@ impl_known_layout!(
T => MaybeUninit<T>,
T: ?Sized => *const T,
T: ?Sized => *mut T,
T => AtomicPtr<T>
);
impl_known_layout!(const N: usize, T => [T; N]);

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

macro_rules! impl_traits_for_atomics {
($($atomics:ident [$inners:ident]),* $(,)?) => {
$(
impl_for_transparent_wrapper!(TryFromBytes for $atomics [UnsafeCell<$inners>]);
impl_for_transparent_wrapper!(FromZeros for $atomics [UnsafeCell<$inners>]);
impl_for_transparent_wrapper!(FromBytes for $atomics [UnsafeCell<$inners>]);
impl_for_transparent_wrapper!(IntoBytes for $atomics [UnsafeCell<$inners>]);
)*
};
}

#[rustfmt::skip]
impl_traits_for_atomics!(
AtomicBool [bool],
AtomicI16 [i16], AtomicI32 [i32], AtomicI8 [i8], AtomicIsize [isize],
AtomicU16 [u16], AtomicU32 [u32], AtomicU8 [u8], AtomicUsize [usize],
);

safety_comment! {
/// SAFETY:
/// Per [1], `AtomicBool`, `AtomicU8`, and `AtomicI8` have the same size as
/// `bool`, `u8`, and `i8` respectively. Since a type's alignment cannot be
/// smaller than 1 [2], and since its alignment cannot be greater than its
/// size [3], the only possible value for the alignment is 1. Thus, it is
/// sound to implement `Unaligned`.
///
/// [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943):
/// Cite docs once they've landed.
///
/// [2] Per https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment:
///
/// Alignment is measured in bytes, and must be at least 1.
///
/// [3] Per https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment:
///
/// The size of a value is always a multiple of its alignment.
unsafe_impl!(AtomicBool: Unaligned);
unsafe_impl!(AtomicU8: Unaligned);
unsafe_impl!(AtomicI8: Unaligned);
assert_unaligned!(AtomicBool, AtomicU8, AtomicI8);
}

safety_comment! {
/// SAFETY:
/// Per reference [1]:
Expand Down
96 changes: 73 additions & 23 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,14 +216,15 @@ macro_rules! impl_for_transparent_wrapper {
// - `f` can only compile if its internal call to
// `is_transparent_wrapper` compiles.
//
// The definition of `is_transparent_wrapper` is generated by each
// `@is_transparent_wrapper` macro arm. Each arm is parameterized by
// `$trait`, and emits bounds which are sufficient to ensure that this
// is a sound implementation of `$trait for $ty` *so long as* `$tyvar:
// $trait`. Note that we require `$tyvar: $trait` in the `impl` block
// itself. Thus, so long as the bounds emitted by the
// `@is_transparent_wrapper` arm are sound, then this entire impl is
// sound.
// The definition of `is_transparent_wrapper<I, T, W>` is generated by
// each `@is_transparent_wrapper` macro arm. Each arm is parameterized
// by `$trait`, and emits bounds which are sufficient to ensure that
// this is a sound implementation of `$trait for W` *so long as* `T:
// $trait`. In `f`, we call `is_transparent_wrapper<I, $tyvar, $ty>`,
// and so if this code compiles, that guarantees that `$ty` satisfies
// the same bounds for all `$tyvar: $trait`. Thus, so long as the bounds
// emitted by the `@is_transparent_wrapper` arm are sound, then this
// entire impl is sound.
//
// Each `@is_transparent_wrapper` arm contains its own safety comment
// which explains why its bounds are sound.
Expand All @@ -241,11 +242,61 @@ macro_rules! impl_for_transparent_wrapper {

impl_for_transparent_wrapper!(
@is_bit_valid
$tyvar: $($(? $optbound +)* $($bound +)*)?
=> $trait for $ty
<$tyvar: $($(? $optbound +)* $($bound +)*)?>
$trait for $ty
);
}
};
(
$(#[$attr:meta])*
for $ty:ty [$inner:ty] $(; |$candidate:ident $(: MaybeAligned<$ref_repr:ty>)? $(: Maybe<$ptr_repr:ty>)?| $is_bit_valid:expr)?
) => {};
(
$(#[$attr:meta])*
$trait:ident $(, $traits:ident)* for $ty:ty [$inner:ty] $(; |$candidate:ident $(: MaybeAligned<$ref_repr:ty>)? $(: Maybe<$ptr_repr:ty>)?| $is_bit_valid:expr)?
) => {
impl_for_transparent_wrapper!(
$(#[$attr])*
$($traits),* for $ty [$inner] $(; |$candidate $(: MaybeAligned<$ref_repr>)? $(: Maybe<$ptr_repr>)?| $is_bit_valid:expr)?
);

$(#[$attr])*
#[allow(non_local_definitions)]
// SAFETY:
// - We explicitly add a `$tyvar: $trait` bound (regardless of what
// bounds the caller has provided).
// - Inside of `only_derive_is_allowed_to_implement_this_trait`, `f` is
// generic over the any `I: Invariants`.
// - `f` can only compile if its internal call to
// `is_transparent_wrapper` compiles.
//
// The definition of `is_transparent_wrapper<I, T, W>` is generated by
// each `@is_transparent_wrapper` macro arm. Each arm is parameterized
// by `$trait`, and emits bounds which are sufficient to ensure that
// this is a sound implementation of `$trait for W` *so long as* `T:
// $trait`. In `f`, we call `is_transparent_wrapper<I, $inner, $ty>`,
// and so if this code compiles, that guarantees that `$ty` satisfies
// the same bounds so long as `$inner: $trait`. Thus, so long as the
// bounds emitted by the `@is_transparent_wrapper` arm are sound, then
// this entire impl is sound.
//
// Each `@is_transparent_wrapper` arm contains its own safety comment
// which explains why its bounds are sound.
unsafe impl $trait for $ty {
#[allow(dead_code, clippy::missing_inline_in_public_items)]
fn only_derive_is_allowed_to_implement_this_trait() {
use crate::{pointer::invariant::Invariants, util::*};

impl_for_transparent_wrapper!(@is_transparent_wrapper $trait);

fn f<I: Invariants>() {
is_transparent_wrapper::<I, $inner, $ty>();
}
}

impl_for_transparent_wrapper!(@is_bit_valid $trait for $ty);
}
};
(@is_transparent_wrapper NoCell) => {
// SAFETY: `W: TransparentWrapper<UnsafeCellVariance=Covariant>`
// requires that `W` has `UnsafeCell`s at the same byte offsets as
Expand Down Expand Up @@ -299,8 +350,8 @@ macro_rules! impl_for_transparent_wrapper {
};
(
@is_bit_valid
$tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?
=> TryFromBytes for $ty:ty
$(<$tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?>)?
TryFromBytes for $ty:ty
) => {
// SAFETY: See safety comment in `(@is_transparent_wrapper
// TryFromBytes)` macro arm for an explanation of why this is a sound
Expand All @@ -312,8 +363,8 @@ macro_rules! impl_for_transparent_wrapper {
};
(
@is_bit_valid
$tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?
=> $trait:ident for $ty:ty
$(<$tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?>)?
$trait:ident for $ty:ty
) => {
// Trait other than `TryFromBytes`; no `is_bit_valid` impl.
};
Expand Down Expand Up @@ -553,15 +604,14 @@ macro_rules! unsafe_impl_known_layout {
/// Note that `align_of<T>` requires `T: Sized`, so this macro doesn't work for
/// unsized types.
macro_rules! assert_unaligned {
($ty:ty) => {
// We only compile this assertion under `cfg(test)` to avoid taking an
// extra non-dev dependency (and making this crate more expensive to
// compile for our dependents).
#[cfg(test)]
static_assertions::const_assert_eq!(core::mem::align_of::<$ty>(), 1);
};
($($ty:ty),*) => {
$(assert_unaligned!($ty);)*
($($tys:ty),*) => {
$(
// We only compile this assertion under `cfg(test)` to avoid taking
// an extra non-dev dependency (and making this crate more expensive
// to compile for our dependents).
#[cfg(test)]
static_assertions::const_assert_eq!(core::mem::align_of::<$tys>(), 1);
)*
};
}

Expand Down

0 comments on commit fe7f030

Please sign in to comment.