diff --git a/src/lib.rs b/src/lib.rs index 439044b625..b672578637 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; @@ -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!( @@ -1106,6 +1112,7 @@ impl_known_layout!( T => MaybeUninit, T: ?Sized => *const T, T: ?Sized => *mut T, + T => AtomicPtr ); impl_known_layout!(const N: usize, T => [T; N]); @@ -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]: diff --git a/src/macros.rs b/src/macros.rs index 4431e1fb23..317eebf536 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -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` 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`, + // 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. @@ -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` 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`, + // 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() { + is_transparent_wrapper::(); + } + } + + impl_for_transparent_wrapper!(@is_bit_valid $trait for $ty); + } + }; (@is_transparent_wrapper NoCell) => { // SAFETY: `W: TransparentWrapper` // requires that `W` has `UnsafeCell`s at the same byte offsets as @@ -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 @@ -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. }; @@ -553,15 +604,14 @@ macro_rules! unsafe_impl_known_layout { /// Note that `align_of` 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); + )* }; }