diff --git a/src/atomics.rs b/src/atomics.rs new file mode 100644 index 0000000000..ad4cb21d19 --- /dev/null +++ b/src/atomics.rs @@ -0,0 +1,192 @@ +//! Conditionally implement traits for atomic types +//! +//! Note that we only implement traits for atomics types if the corresponding +//! [`#[cfg(target_has_atomic)]`](https://doc.rust-lang.org/reference/conditional-compilation.html#target_has_atomic) +//! configuration option is true. +//! +//! Note: On targets like `thumbv6m-none-eabi` (which has an [`AtomicU32`] +//! type but `#[cfg(target_has_atomic = "32")]` is false), we don't implement +//! the zerocopy traits. We will be able to handle this when Rust stabilizes +//! [`#[cfg(target_has_atomic_load_store)]`](https://www.github.com/rust-lang/rust/issues/94039). + +use core::{cell::UnsafeCell, sync::atomic::*}; + +use crate::{ + pointer::{invariant::Invariants, Maybe}, + util::{Covariant, Invariant, TransparentWrapper}, + FromBytes, FromZeros, IntoBytes, KnownLayout, TryFromBytes, Unaligned, +}; + +/// Implements `TransparentWrapper` for an atomic type. +/// +/// # Safety +/// +/// The caller promises that `$atomic` is an atomic type whose native equivalent +/// is `$native`. +macro_rules! unsafe_impl_transparent_wrapper_for_atomic { + ($atomic:ty [$native:ty]) => { + // We implement for `$atomic` and set `Inner = $native`. The caller has + // promised that `$atomic` and `$native` are an atomic type and its + // native counterpart, respectively. Per [1], `$atomic` and `$native` + // have the same size. + // + // [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943): + // Cite docs once they've landed. + type Inner = UnsafeCell<$native>; + + // SAFETY: It is "obvious" that each atomic type contains a single + // `UnsafeCell` that covers all bytes of the type, but we can also prove + // it: + // - Since `$atomic` provides an API which permits loading and storing + // values of type `$native` via a `&self` (shared) reference, *some* + // interior mutation must be happening, and interior mutation can only + // happen via `UnsafeCell`. Further, there must be enough bytes in + // `$atomic` covered by an `UnsafeCell` to hold every possible value + // of `$native`. + // - Per [1], `$atomic` has the same size as `$native`. This on its own + // isn't enough: it would still be possible for `$atomic` to store + // `$native` using a compact representation (for `$native` types for + // which some bit patterns are illegal). However, this is ruled out by + // the fact that `$atomic` has the same bit validity as `$native` [1]. + // Thus, we can conclude that every byte of `$atomic` must be covered + // by an `UnsafeCell`. + // + // Thus, every byte of `$atomic` is covered by an `UnsafeCell`, and we + // set `type Inner = UnsafeCell<$native>`. Thus, `Self` and + // `Self::Inner` have `UnsafeCell`s covering the same byte ranges. + // + // [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943): + // Cite docs once they've landed. + type UnsafeCellVariance = Covariant; + + // SAFETY: No safety justification is required for an invariant + // variance. + type AlignmentVariance = Invariant; + + // SAFETY: Per [1], all atomic types have the same bit validity as their + // native counterparts. The caller has promised that `$atomic` and + // `$native` are an atomic type and its native counterpart, + // respectively. + // + // [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943): + // Cite docs once they've landed. + type ValidityVariance = Covariant; + + fn cast_into_inner(ptr: *mut $atomic) -> *mut UnsafeCell<$native> { + // SAFETY: Per [1] (from comment on impl block), `$atomic` has the + // same size as `$native`. Thus, this cast preserves size. + // + // This cast trivially preserves provenance. + ptr.cast::>() + } + + fn cast_from_inner(ptr: *mut UnsafeCell<$native>) -> *mut $atomic { + // SAFETY: Per [1] (from comment on impl block), `$atomic` has the + // same size as `$native`. Thus, this cast preserves size. + // + // This cast trivially preserves provenance. + ptr.cast::<$atomic>() + } + }; +} + +macro_rules! impl_from_bytes_for_atomic { + (AtomicBool [$native:ty]) => {}; + ($atomic:ty [$native:ty]) => { + impl_for_transparent_wrapper!(FromBytes for $atomic [UnsafeCell<$native>]); + }; +} + +/// Implements the zerocopy traits for atomic integer types. +/// +/// # Safety +/// +/// The caller promises that `$atomic` is an atomic type whose native equivalent +/// is `$native`. +macro_rules! impl_traits_for_atomics { + ($($atomic:ident [$native:ty]),*) => {$( + /// SAFETY: All of these pass an atomic type and that type's native equivalent + unsafe impl TransparentWrapper for $atomic { + unsafe_impl_transparent_wrapper_for_atomic!($atomic [$native]); + } + impl_for_transparent_wrapper!(TryFromBytes for $atomic [UnsafeCell<$native>]); + impl_for_transparent_wrapper!(FromZeros for $atomic [UnsafeCell<$native>]); + impl_from_bytes_for_atomic!($atomic [$native]); + impl_for_transparent_wrapper!(IntoBytes for $atomic [UnsafeCell<$native>]); + + impl_known_layout!($atomic); + )*}; +} + +#[cfg(target_has_atomic = "8")] +#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "8")))] +mod atomic_8 { + use super::*; + impl_traits_for_atomics!(AtomicBool[bool], AtomicU8[u8], AtomicI8[i8]); + + 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); +} + +#[cfg(target_has_atomic = "16")] +#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "16")))] +mod atomic_16 { + use super::*; + impl_traits_for_atomics!(AtomicU16[u16], AtomicI16[i16]); +} + +#[cfg(target_has_atomic = "32")] +#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "32")))] +mod atomic_32 { + use super::*; + impl_traits_for_atomics!(AtomicU32[u32], AtomicI32[i32]); +} + +#[cfg(target_has_atomic = "64")] +#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "64")))] +mod atomic_64 { + use super::*; + impl_traits_for_atomics!(AtomicU64[u64], AtomicI64[i64]); +} + +#[cfg(target_has_atomic = "ptr")] +#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "ptr")))] +mod atomic_ptr { + use super::*; + impl_traits_for_atomics!(AtomicUsize[usize], AtomicIsize[isize]); + + /// SAFETY: All of these pass an atomic type and that type's native equivalent + /// For thin pointers (note that `T: Sized`) AtomicPtr is garunteed to + /// wrap a *mut T. Just like *mut T, we don't implement FromBytes/IntoBytes. + /// + /// See the comments on the *mut T impls for more information. + /// + /// This macro is just used to keep the impl attributes consistent. + unsafe impl TransparentWrapper for AtomicPtr { + unsafe_impl_transparent_wrapper_for_atomic!(AtomicPtr [*mut T]); + } + impl_for_transparent_wrapper!(T => TryFromBytes for AtomicPtr [UnsafeCell<*mut T>]); + impl_for_transparent_wrapper!(T => FromZeros for AtomicPtr [UnsafeCell<*mut T>]); + impl_known_layout!(T => AtomicPtr); +} diff --git a/src/lib.rs b/src/lib.rs index 15a8b87c62..0c54540c93 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -278,6 +278,7 @@ extern crate self as zerocopy; #[macro_use] mod macros; +mod atomics; pub mod byteorder; mod deprecated; #[doc(hidden)] @@ -305,10 +306,6 @@ 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; @@ -1101,9 +1098,7 @@ 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, - AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicU16, AtomicU32, - AtomicU8, AtomicUsize + NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize ); #[rustfmt::skip] impl_known_layout!( @@ -1112,8 +1107,7 @@ impl_known_layout!( T => Wrapping, T => MaybeUninit, T: ?Sized => *const T, - T: ?Sized => *mut T, - T => AtomicPtr + T: ?Sized => *mut T ); impl_known_layout!(const N: usize, T => [T; N]); @@ -3900,48 +3894,6 @@ 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/util.rs b/src/util.rs index ade2340c3a..64ee476b9a 100644 --- a/src/util.rs +++ b/src/util.rs @@ -11,10 +11,6 @@ use core::{ mem::{self, ManuallyDrop, MaybeUninit}, num::{NonZeroUsize, Wrapping}, ptr::NonNull, - sync::atomic::{ - AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicPtr, AtomicU16, AtomicU32, - AtomicU8, AtomicUsize, - }, }; use crate::{ @@ -330,112 +326,6 @@ unsafe impl TransparentWrapper for Unalign { } } -/// Implements `TransparentWrapper` for an atomic type. -/// -/// # Safety -/// -/// The caller promises that `$atomic` is an atomic type whose natie equivalent -/// is `$native`. -macro_rules! unsafe_impl_transparent_wrapper_for_atomic { - ($(#[$attr:meta])* $(,)?) => {}; - ($(#[$attr:meta])* $atomic:ty [$native:ty], $($atomics:ty [$natives:ty]),* $(,)?) => { - $(#[$attr])* - // SAFETY: See safety comment in next match arm. - unsafe impl TransparentWrapper for $atomic { - unsafe_impl_transparent_wrapper_for_atomic!(@inner $atomic [$native]); - } - unsafe_impl_transparent_wrapper_for_atomic!($(#[$attr])* $($atomics [$natives],)*); - }; - ($(#[$attr:meta])* $tyvar:ident => $atomic:ty [$native:ty]) => { - // We implement for `$atomic` and set `Inner = $native`. The caller has - // promised that `$atomic` and `$native` are an atomic type and its - // native counterpart, respectively. Per [1], `$atomic` and `$native` - // have the same size. - // - // [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943): - // Cite docs once they've landed. - $(#[$attr])* - unsafe impl<$tyvar, I: Invariants> TransparentWrapper for $atomic { - unsafe_impl_transparent_wrapper_for_atomic!(@inner $atomic [$native]); - } - }; - (@inner $atomic:ty [$native:ty]) => { - type Inner = UnsafeCell<$native>; - - // SAFETY: It is "obvious" that each atomic type contains a single - // `UnsafeCell` that covers all bytes of the type, but we can also prove - // it: - // - Since `$atomic` provides an API which permits loading and storing - // values of type `$native` via a `&self` (shared) reference, *some* - // interior mutation must be happening, and interior mutation can only - // happen via `UnsafeCell`. Further, there must be enough bytes in - // `$atomic` covered by an `UnsafeCell` to hold every possible value - // of `$native`. - // - Per [1], `$atomic` has the same size as `$native`. This on its own - // isn't enough: it would still be possible for `$atomic` to store - // `$native` using a compact representation (for `$native` types for - // which some bit patterns are illegal). However, this is ruled out by - // the fact that `$atomic` has the same bit validity as `$native` [1]. - // Thus, we can conclude that every byte of `$atomic` must be covered - // by an `UnsafeCell`. - // - // Thus, every byte of `$atomic` is covered by an `UnsafeCell`, and we - // set `type Inner = UnsafeCell<$native>`. Thus, `Self` and - // `Self::Inner` have `UnsafeCell`s covering the same byte ranges. - // - // [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943): - // Cite docs once they've landed. - type UnsafeCellVariance = Covariant; - - // SAFETY: No safety justification is required for an invariant - // variance. - type AlignmentVariance = Invariant; - - // SAFETY: Per [1], all atomic types have the same bit validity as their - // native counterparts. The caller has promised that `$atomic` and - // `$native` are an atomic type and its native counterpart, - // respectively. - // - // [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943): - // Cite docs once they've landed. - type ValidityVariance = Covariant; - - fn cast_into_inner(ptr: *mut $atomic) -> *mut UnsafeCell<$native> { - // SAFETY: Per [1] (from comment on impl block), `$atomic` has the - // same size as `$native`. Thus, this cast preserves size. - // - // This cast trivially preserves provenance. - ptr.cast::>() - } - - fn cast_from_inner(ptr: *mut UnsafeCell<$native>) -> *mut $atomic { - // SAFETY: Per [1] (from comment on impl block), `$atomic` has the - // same size as `$native`. Thus, this cast preserves size. - // - // This cast trivially preserves provenance. - ptr.cast::<$atomic>() - } - }; -} - -safety_comment! { - /// SAFETY: - /// All of these pass an atomic type and that type's native equivalent, as - /// required by the macro safety preconditions. - unsafe_impl_transparent_wrapper_for_atomic!(T => AtomicPtr [*mut T]); - unsafe_impl_transparent_wrapper_for_atomic!( - AtomicBool [bool], - AtomicI16 [i16], AtomicI32 [i32], AtomicI8 [i8], AtomicIsize [isize], - AtomicU16 [u16], AtomicU32 [u32], AtomicU8 [u8], AtomicUsize [usize], - ); - #[cfg(not(target_arch = "powerpc"))] - unsafe_impl_transparent_wrapper_for_atomic!( - #[cfg_attr(doc_cfg, doc(cfg(not(target_arch = "powerpc"))))] - core::sync::atomic::AtomicI64 [i64], - core::sync::atomic::AtomicU64 [u64], - ); -} - pub(crate) trait AsAddress { fn addr(self) -> usize; }