Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement traits for atomic types #1028

Merged
merged 1 commit into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading