From 7376e9e9388a309398cc72d077bf2b10793127ad Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Wed, 8 May 2024 04:53:58 -0700 Subject: [PATCH] Gate Atomic type behind the corresponding #[cfg(target_has_atomic)] Signed-off-by: Joe Richey --- .github/workflows/ci.yml | 6 +- Cargo.toml | 4 + src/lib.rs | 132 ++++++++++----- src/macros.rs | 105 +++--------- src/util.rs | 158 ++++++------------ .../include_value_not_from_bytes.stderr | 2 +- .../transmute-dst-not-frombytes.stderr | 2 +- .../transmute-mut-dst-not-frombytes.stderr | 2 +- .../transmute-mut-dst-not-intobytes.stderr | 2 +- .../transmute-mut-src-not-frombytes.stderr | 4 +- .../transmute-mut-src-not-intobytes.stderr | 4 +- .../transmute-ref-dst-not-frombytes.stderr | 2 +- .../transmute-ref-src-not-intobytes.stderr | 4 +- .../transmute-src-not-intobytes.stderr | 4 +- .../ui-nightly/derive_transparent.stderr | 8 +- .../tests/ui-nightly/late_compile_pass.stderr | 14 +- .../tests/ui-nightly/struct.stderr | 4 +- 17 files changed, 198 insertions(+), 259 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 98b20453df..5c3b8b9e80 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: matrix: # See `INTERNAL.md` for an explanation of these pinned toolchain # versions. - toolchain: [ "msrv", "stable", "nightly", "zerocopy-generic-bounds-in-const-fn", "zerocopy-aarch64-simd", "zerocopy-panic-in-const", ] + toolchain: [ "msrv", "stable", "nightly", "zerocopy-generic-bounds-in-const-fn", "zerocopy-atomics", "zerocopy-aarch64-simd", "zerocopy-panic-in-const", ] target: [ "i686-unknown-linux-gnu", "x86_64-unknown-linux-gnu", @@ -61,6 +61,8 @@ jobs: features: "--all-features" - toolchain: "zerocopy-generic-bounds-in-const-fn" features: "--all-features" + - toolchain: "zerocopy-atomics" + features: "--all-features" - toolchain: "zerocopy-aarch64-simd" features: "--all-features" - toolchain: "zerocopy-panic-in-const" @@ -80,6 +82,8 @@ jobs: # zerocopy-derive doesn't behave different on these toolchains. - crate: "zerocopy-derive" toolchain: "zerocopy-generic-bounds-in-const-fn" + - crate: "zerocopy-derive" + toolchain: "zerocopy-atomics" - crate: "zerocopy-derive" toolchain: "zerocopy-aarch64-simd" - crate: "zerocopy-derive" diff --git a/Cargo.toml b/Cargo.toml index d6340710e8..9d883697b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,10 @@ exclude = [".*"] # From 1.61.0, Rust supports generic types with trait bounds in `const fn`. zerocopy-generic-bounds-in-const-fn = "1.61.0" +# From 1.60.0, Rust supports detecting if a target supports atomics, so we can +# reliably implement traits on the Atomic* types. +zerocopy-atomics = "1.60.0" + # When the "simd" feature is enabled, include SIMD types from the # `core::arch::aarch64` module, which was stabilized in 1.59.0. On earlier Rust # versions, these types require the "simd-nightly" feature. diff --git a/src/lib.rs b/src/lib.rs index b5a154fcbc..24691107ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -292,6 +292,7 @@ pub mod layout; pub mod macro_util; #[doc(hidden)] pub mod pointer; +#[macro_use] mod util; // TODO(#252): If we make this pub, come up with a better name. mod wrappers; @@ -314,10 +315,7 @@ use core::{ ops::{Deref, DerefMut}, ptr::{self, NonNull}, slice, - sync::atomic::{ - AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicPtr, AtomicU16, AtomicU32, - AtomicU8, AtomicUsize, - }, + sync::atomic::*, }; use crate::pointer::invariant; @@ -665,9 +663,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!( @@ -676,8 +672,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]); @@ -3797,46 +3792,95 @@ safety_comment! { unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => Immutable 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>]); - )* - }; +/// Implements the zerocopy traits for atomic integer types. +/// +/// Note that we only implement traits for atomics types if the corresponding +/// [`#[cfg(target_has_atomic)]`][target_has_atomic] configuration 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)]`][target_has_atomic_load_store]. +/// +/// [target_has_atomic]: https://doc.rust-lang.org/reference/conditional-compilation.html#target_has_atomic +/// [target_has_atomic_load_store]: https://www.github.com/rust-lang/rust/issues/94039 +/// +/// # Safety +/// +/// `$atomic` must be an atomic type with corresponding native type `$native`. +macro_rules! unsafe_impl_traits_for_atomics { + ($($atomic:ident [$native:ident]),*) => {$( + // SAFETY: `$atomic` is an atomic type with native type `$native`. + unsafe impl util::Atomic for $atomic { type Native = $native; } + + impl_for_transparent_wrapper!(TryFromBytes for $atomic); + impl_for_transparent_wrapper!(FromZeros for $atomic); + unsafe_impl_traits_for_atomics!(@from_bytes $atomic); + impl_for_transparent_wrapper!(IntoBytes for $atomic); + impl_known_layout!($atomic); + )*}; + (@from_bytes AtomicBool) => {}; + (@from_bytes $atomic:ty) => { impl_for_transparent_wrapper!(FromBytes for $atomic); }; } -#[rustfmt::skip] -impl_traits_for_atomics!( - AtomicBool [bool], - AtomicI16 [i16], AtomicI32 [i32], AtomicI8 [i8], AtomicIsize [isize], - AtomicU16 [u16], AtomicU32 [u32], AtomicU8 [u8], AtomicUsize [usize], -); +#[cfg(zerocopy_atomics)] +#[cfg(target_has_atomic = "8")] +#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "8")))] +mod atomic_8 { + use super::*; + unsafe_impl_traits_for_atomics!(AtomicBool[bool], AtomicU8[u8], AtomicI8[i8]); + safety_comment! { + /// SAFETY: + /// Per [1], `AtomicBool`, `AtomicU8`, and `AtomicI8` have an alignment of 1. + /// + /// [1] Per https://doc.rust-lang.org/nightly/core/sync/atomic/struct.AtomicBool.html: + /// + /// This type has the same size, alignment, and bit validity as [the native type]. + unsafe_impl!(AtomicBool: Unaligned); + unsafe_impl!(AtomicU8: Unaligned); + unsafe_impl!(AtomicI8: Unaligned); + } + assert_unaligned!(AtomicBool, AtomicU8, AtomicI8); +} +#[cfg(zerocopy_atomics)] +#[cfg(target_has_atomic = "16")] +#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "16")))] +mod atomic_16 { + use super::*; + unsafe_impl_traits_for_atomics!(AtomicU16[u16], AtomicI16[i16]); +} +#[cfg(zerocopy_atomics)] +#[cfg(target_has_atomic = "32")] +#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "32")))] +mod atomic_32 { + use super::*; + unsafe_impl_traits_for_atomics!(AtomicU32[u32], AtomicI32[i32]); +} +#[cfg(zerocopy_atomics)] +#[cfg(target_has_atomic = "64")] +#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "64")))] +mod atomic_64 { + use super::*; + unsafe_impl_traits_for_atomics!(AtomicU64[u64], AtomicI64[i64]); +} +#[cfg(zerocopy_atomics)] +#[cfg(target_has_atomic = "ptr")] +#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "ptr")))] +mod atomic_ptr { + use super::*; + unsafe_impl_traits_for_atomics!(AtomicUsize[usize], AtomicIsize[isize]); -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`. + /// `AtomicPtr`` is garunteed to wrap a `*mut T`. Just like *mut T, we + /// don't implement FromBytes/IntoBytes. /// - /// [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); + /// See the comments on the `*mut T` impls for more information. + unsafe impl util::Atomic for AtomicPtr { + type Native = *mut T; + } + impl_for_transparent_wrapper!(T => TryFromBytes for AtomicPtr); + impl_for_transparent_wrapper!(T => FromZeros for AtomicPtr); + impl_known_layout!(T => AtomicPtr); } safety_comment! { diff --git a/src/macros.rs b/src/macros.rs index 062cb250d5..279d08ce00 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -153,7 +153,7 @@ macro_rules! unsafe_impl { // SAFETY: The caller has promised that the referenced memory region // will contain a valid `$repr`. - let $candidate = unsafe { candidate.assume_validity::() }; + let $candidate = unsafe { candidate.assume_validity::() }; $is_bit_valid } }; @@ -176,7 +176,7 @@ macro_rules! unsafe_impl { // Restore the invariant that the referent bytes are initialized. // SAFETY: The above cast does not uninitialize any referent bytes; // they remain initialized. - let $candidate = unsafe { $candidate.assume_validity::() }; + let $candidate = unsafe { $candidate.assume_validity::() }; $is_bit_valid } @@ -201,14 +201,12 @@ macro_rules! unsafe_impl { /// trait bounds which ensure that the given impl is sound. macro_rules! impl_for_transparent_wrapper { ( - $(#[$attr:meta])* - $tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )? - => $trait:ident for $ty:ty $(; |$candidate:ident $(: MaybeAligned<$ref_repr:ty>)? $(: Maybe<$ptr_repr:ty>)?| $is_bit_valid:expr)? + $($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?)? => + $trait:ident for $ty:ty ) => { - $(#[$attr])* #[allow(non_local_definitions)] // SAFETY: - // - We explicitly add a `$tyvar: $trait` bound (regardless of what + // - We explicitly add a `$inner: $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`, and is generic over the same @@ -228,82 +226,28 @@ macro_rules! impl_for_transparent_wrapper { // // Each `@is_transparent_wrapper` arm contains its own safety comment // which explains why its bounds are sound. - unsafe impl<$tyvar: $($(? $optbound +)* $($bound +)*)? $trait> $trait for $ty { + unsafe impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?)?> $trait for $ty where { #[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::(); + fn f() { + is_transparent_wrapper::() } } - impl_for_transparent_wrapper!( - @is_bit_valid - <$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); + impl_for_transparent_wrapper!(@is_bit_valid $trait); } }; + ($trait:ident for $ty:ty) => { impl_for_transparent_wrapper!(=> $trait for $ty); }; (@is_transparent_wrapper Immutable) => { // SAFETY: `W: TransparentWrapper` // requires that `W` has `UnsafeCell`s at the same byte offsets as // `W::Inner = T`. `T: Immutable` implies that `T` does not contain any // `UnsafeCell`s, and so `W` does not contain any `UnsafeCell`s. Thus, // `W` can soundly implement `Immutable`. - fn is_transparent_wrapper + ?Sized>() {} + fn is_transparent_wrapper + ?Sized>() where W::Inner: Immutable {} }; (@is_transparent_wrapper FromZeros) => { // SAFETY: `W: TransparentWrapper` requires @@ -311,7 +255,7 @@ macro_rules! impl_for_transparent_wrapper { // implies that the all-zeros bit pattern is a bit-valid instance of // `T`, and so the all-zeros bit pattern is a bit-valid instance of `W`. // Thus, `W` can soundly implement `FromZeros`. - fn is_transparent_wrapper + ?Sized>() {} + fn is_transparent_wrapper + ?Sized>() where W::Inner: FromZeros {} }; (@is_transparent_wrapper FromBytes) => { // SAFETY: `W: TransparentWrapper` requires @@ -319,7 +263,7 @@ macro_rules! impl_for_transparent_wrapper { // implies that any initialized bit pattern is a bit-valid instance of // `T`, and so any initialized bit pattern is a bit-valid instance of // `W`. Thus, `W` can soundly implement `FromBytes`. - fn is_transparent_wrapper + ?Sized>() {} + fn is_transparent_wrapper + ?Sized>() where W::Inner: FromBytes {} }; (@is_transparent_wrapper IntoBytes) => { // SAFETY: `W: TransparentWrapper` requires @@ -327,14 +271,14 @@ macro_rules! impl_for_transparent_wrapper { // implies that no bit-valid instance of `T` contains uninitialized // bytes, and so no bit-valid instance of `W` contains uninitialized // bytes. Thus, `W` can soundly implement `IntoBytes`. - fn is_transparent_wrapper + ?Sized>() {} + fn is_transparent_wrapper + ?Sized>() where W::Inner: IntoBytes {} }; (@is_transparent_wrapper Unaligned) => { // SAFETY: `W: TransparentWrapper` requires // that `W` has the same alignment as `W::Inner = T`. `T: Unaligned` // implies `T`'s alignment is 1, and so `W`'s alignment is 1. Thus, `W` // can soundly implement `Unaligned`. - fn is_transparent_wrapper + ?Sized>() {} + fn is_transparent_wrapper + ?Sized>() where W::Inner: Unaligned {} }; (@is_transparent_wrapper TryFromBytes) => { // SAFETY: `W: TransparentWrapper` requires @@ -346,28 +290,19 @@ macro_rules! impl_for_transparent_wrapper { // as TryFromBytes>::is_bit_valid` by deferring to `::is_bit_valid`. Thus, it is sound for `W` to implement // `TryFromBytes` with this implementation of `is_bit_valid`. - fn is_transparent_wrapper + ?Sized>() {} + fn is_transparent_wrapper + ?Sized>() where W::Inner: TryFromBytes {} }; - ( - @is_bit_valid - $(<$tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?>)? - TryFromBytes for $ty:ty - ) => { + (@is_bit_valid TryFromBytes) => { // SAFETY: See safety comment in `(@is_transparent_wrapper // TryFromBytes)` macro arm for an explanation of why this is a sound // implementation of `is_bit_valid`. #[inline] - fn is_bit_valid>(candidate: Maybe<'_, Self, A>) -> bool { + fn is_bit_valid>(candidate: Maybe<'_, Self, A>) -> bool { TryFromBytes::is_bit_valid(candidate.transparent_wrapper_into_inner()) } }; - ( - @is_bit_valid - $(<$tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?>)? - $trait:ident for $ty:ty - ) => { - // Trait other than `TryFromBytes`; no `is_bit_valid` impl. - }; + // Trait other than `TryFromBytes`; no `is_bit_valid` impl. + (@is_bit_valid $trait:ident) => {} } /// Implements a trait for a type, bounding on each memeber of the power set of diff --git a/src/util.rs b/src/util.rs index 6a6d88adae..f093336954 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,110 +326,66 @@ unsafe impl TransparentWrapper for Unalign { } } -/// Implements `TransparentWrapper` for an atomic type. +/// Trait implemented by all `core::sync::atomic::Atomic*` types. /// /// # 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>() - } - }; +/// `Self` must be an atomic type whose native equivalent is `Self::Native`. +pub unsafe trait Atomic { + type Native; } -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], - ); +// SAFETY: the implementation of `Atomic` promises that `A` is atomic type and +// `A::Native` is its native counterpart. Per [1], `A` and `A::Native` have the +// same size and bit validatity (but `A::Native` might have greater alignment). +// +// [1] https://doc.rust-lang.org/nightly/core/sync/atomic/struct.AtomicU32.html +unsafe impl TransparentWrapper for A { + type Inner = UnsafeCell; + + // 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 `A` provides an API which permits loading and storing values of + // type `A::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 `A` covered by an + // `UnsafeCell` to hold every possible value of `A::Native`. + // - Per the above libcore docs, `A` has the same size as `A::Native`. This + // on its own isn't enough: it would still be possible for `A` to store + // `A::Native` using a compact representation (for `A::Native` types for + // which some bit patterns are illegal). However, this is ruled out by + // the fact that `A` has the same bit validity as `A::Native`. Thus, we + // conclude that every byte of `A` must be covered by an `UnsafeCell`. + // + // Thus, every byte of `A` is covered by an `UnsafeCell`, and we + // set `type Inner = UnsafeCell`. Thus, `Self` and + // `Self::Inner` have `UnsafeCell`s covering the same byte ranges. + type UnsafeCellVariance = Covariant; + + // SAFETY: No safety justification is required for an invariant + // variance. + type AlignmentVariance = Invariant; + + // SAFETY: Per the above libcore docs, all atomic types (`A`) have the same + // bit validity as their native counterparts (`A::Native`). + type ValidityVariance = Covariant; + + fn cast_into_inner(ptr: *mut A) -> *mut Self::Inner { + // SAFETY: Per [1] (from comment on impl block), `A` has the + // same size as `Self::Inner`. Thus, this cast preserves size. + // + // This cast trivially preserves provenance. + ptr.cast::() + } + + fn cast_from_inner(ptr: *mut Self::Inner) -> *mut A { + // SAFETY: Per [1] (from comment on impl block), `A` has the + // same size as `Self::Inner`. Thus, this cast preserves size. + // + // This cast trivially preserves provenance. + ptr.cast::() + } } pub(crate) trait AsAddress { diff --git a/tests/ui-nightly/include_value_not_from_bytes.stderr b/tests/ui-nightly/include_value_not_from_bytes.stderr index 2f96227a64..7974607ecb 100644 --- a/tests/ui-nightly/include_value_not_from_bytes.stderr +++ b/tests/ui-nightly/include_value_not_from_bytes.stderr @@ -10,9 +10,9 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::FromBytes` is not sat = help: the following other types implement trait `zerocopy::FromBytes`: () AU16 - AtomicBool AtomicI16 AtomicI32 + AtomicI64 AtomicI8 AtomicIsize AtomicU16 diff --git a/tests/ui-nightly/transmute-dst-not-frombytes.stderr b/tests/ui-nightly/transmute-dst-not-frombytes.stderr index 0567fd3ba8..8c90c2bd45 100644 --- a/tests/ui-nightly/transmute-dst-not-frombytes.stderr +++ b/tests/ui-nightly/transmute-dst-not-frombytes.stderr @@ -10,9 +10,9 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::FromBytes` is not satisfie = help: the following other types implement trait `zerocopy::FromBytes`: () AU16 - AtomicBool AtomicI16 AtomicI32 + AtomicI64 AtomicI8 AtomicIsize AtomicU16 diff --git a/tests/ui-nightly/transmute-mut-dst-not-frombytes.stderr b/tests/ui-nightly/transmute-mut-dst-not-frombytes.stderr index 6c2ba0d290..b92f2eadc2 100644 --- a/tests/ui-nightly/transmute-mut-dst-not-frombytes.stderr +++ b/tests/ui-nightly/transmute-mut-dst-not-frombytes.stderr @@ -9,9 +9,9 @@ error[E0277]: the trait bound `Dst: FromBytes` is not satisfied | = help: the following other types implement trait `FromBytes`: () - AtomicBool AtomicI16 AtomicI32 + AtomicI64 AtomicI8 AtomicIsize AtomicU16 diff --git a/tests/ui-nightly/transmute-mut-dst-not-intobytes.stderr b/tests/ui-nightly/transmute-mut-dst-not-intobytes.stderr index 49991c14ff..9702273272 100644 --- a/tests/ui-nightly/transmute-mut-dst-not-intobytes.stderr +++ b/tests/ui-nightly/transmute-mut-dst-not-intobytes.stderr @@ -12,10 +12,10 @@ error[E0277]: the trait bound `Dst: IntoBytes` is not satisfied AtomicBool AtomicI16 AtomicI32 + AtomicI64 AtomicI8 AtomicIsize AtomicU16 - AtomicU32 and $N others note: required by a bound in `AssertDstIsIntoBytes` --> tests/ui-nightly/transmute-mut-dst-not-intobytes.rs:24:36 diff --git a/tests/ui-nightly/transmute-mut-src-not-frombytes.stderr b/tests/ui-nightly/transmute-mut-src-not-frombytes.stderr index 860cc180e4..88258f36d8 100644 --- a/tests/ui-nightly/transmute-mut-src-not-frombytes.stderr +++ b/tests/ui-nightly/transmute-mut-src-not-frombytes.stderr @@ -9,9 +9,9 @@ error[E0277]: the trait bound `Src: FromBytes` is not satisfied | = help: the following other types implement trait `FromBytes`: () - AtomicBool AtomicI16 AtomicI32 + AtomicI64 AtomicI8 AtomicIsize AtomicU16 @@ -32,9 +32,9 @@ error[E0277]: the trait bound `Src: FromBytes` is not satisfied | = help: the following other types implement trait `FromBytes`: () - AtomicBool AtomicI16 AtomicI32 + AtomicI64 AtomicI8 AtomicIsize AtomicU16 diff --git a/tests/ui-nightly/transmute-mut-src-not-intobytes.stderr b/tests/ui-nightly/transmute-mut-src-not-intobytes.stderr index 5907bad499..c47004d8c0 100644 --- a/tests/ui-nightly/transmute-mut-src-not-intobytes.stderr +++ b/tests/ui-nightly/transmute-mut-src-not-intobytes.stderr @@ -12,10 +12,10 @@ error[E0277]: the trait bound `Src: IntoBytes` is not satisfied AtomicBool AtomicI16 AtomicI32 + AtomicI64 AtomicI8 AtomicIsize AtomicU16 - AtomicU32 and $N others note: required by a bound in `AssertSrcIsIntoBytes` --> tests/ui-nightly/transmute-mut-src-not-intobytes.rs:24:36 @@ -35,10 +35,10 @@ error[E0277]: the trait bound `Src: IntoBytes` is not satisfied AtomicBool AtomicI16 AtomicI32 + AtomicI64 AtomicI8 AtomicIsize AtomicU16 - AtomicU32 and $N others note: required by a bound in `AssertSrcIsIntoBytes` --> tests/ui-nightly/transmute-mut-src-not-intobytes.rs:24:36 diff --git a/tests/ui-nightly/transmute-ref-dst-not-frombytes.stderr b/tests/ui-nightly/transmute-ref-dst-not-frombytes.stderr index 5b89403f1e..c1a638f299 100644 --- a/tests/ui-nightly/transmute-ref-dst-not-frombytes.stderr +++ b/tests/ui-nightly/transmute-ref-dst-not-frombytes.stderr @@ -10,9 +10,9 @@ error[E0277]: the trait bound `Dst: zerocopy::FromBytes` is not satisfied = help: the following other types implement trait `zerocopy::FromBytes`: () AU16 - AtomicBool AtomicI16 AtomicI32 + AtomicI64 AtomicI8 AtomicIsize AtomicU16 diff --git a/tests/ui-nightly/transmute-ref-src-not-intobytes.stderr b/tests/ui-nightly/transmute-ref-src-not-intobytes.stderr index 8cac27acc3..7085623417 100644 --- a/tests/ui-nightly/transmute-ref-src-not-intobytes.stderr +++ b/tests/ui-nightly/transmute-ref-src-not-intobytes.stderr @@ -13,9 +13,9 @@ error[E0277]: the trait bound `Src: zerocopy::IntoBytes` is not satisfied AtomicBool AtomicI16 AtomicI32 + AtomicI64 AtomicI8 AtomicIsize - AtomicU16 and $N others note: required by a bound in `AssertSrcIsIntoBytes` --> tests/ui-nightly/transmute-ref-src-not-intobytes.rs:23:33 @@ -36,9 +36,9 @@ error[E0277]: the trait bound `Src: zerocopy::IntoBytes` is not satisfied AtomicBool AtomicI16 AtomicI32 + AtomicI64 AtomicI8 AtomicIsize - AtomicU16 and $N others note: required by a bound in `AssertSrcIsIntoBytes` --> tests/ui-nightly/transmute-ref-src-not-intobytes.rs:23:33 diff --git a/tests/ui-nightly/transmute-src-not-intobytes.stderr b/tests/ui-nightly/transmute-src-not-intobytes.stderr index 110a8e7583..54d16e1e49 100644 --- a/tests/ui-nightly/transmute-src-not-intobytes.stderr +++ b/tests/ui-nightly/transmute-src-not-intobytes.stderr @@ -13,9 +13,9 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::IntoBytes` is not sa AtomicBool AtomicI16 AtomicI32 + AtomicI64 AtomicI8 AtomicIsize - AtomicU16 and $N others note: required by a bound in `AssertIsIntoBytes` --> tests/ui-nightly/transmute-src-not-intobytes.rs:19:32 @@ -36,9 +36,9 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::IntoBytes` is not sa AtomicBool AtomicI16 AtomicI32 + AtomicI64 AtomicI8 AtomicIsize - AtomicU16 and $N others note: required by a bound in `AssertIsIntoBytes` --> tests/ui-nightly/transmute-src-not-intobytes.rs:19:32 diff --git a/zerocopy-derive/tests/ui-nightly/derive_transparent.stderr b/zerocopy-derive/tests/ui-nightly/derive_transparent.stderr index 4db7cf96c3..822cc3860b 100644 --- a/zerocopy-derive/tests/ui-nightly/derive_transparent.stderr +++ b/zerocopy-derive/tests/ui-nightly/derive_transparent.stderr @@ -12,7 +12,7 @@ error[E0277]: the trait bound `NotZerocopy: TryFromBytes` is not satisfied AtomicBool AtomicI16 AtomicI32 - AtomicI8 + AtomicI64 and $N others note: required for `TransparentStruct` to implement `TryFromBytes` --> tests/ui-nightly/derive_transparent.rs:24:21 @@ -40,7 +40,7 @@ error[E0277]: the trait bound `NotZerocopy: FromZeros` is not satisfied AtomicBool AtomicI16 AtomicI32 - AtomicI8 + AtomicI64 and $N others note: required for `TransparentStruct` to implement `FromZeros` --> tests/ui-nightly/derive_transparent.rs:24:21 @@ -63,9 +63,9 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::FromBytes` is not satisfie = help: the following other types implement trait `zerocopy::FromBytes`: () AU16 - AtomicBool AtomicI16 AtomicI32 + AtomicI64 AtomicI8 AtomicIsize AtomicU16 @@ -94,9 +94,9 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::IntoBytes` is not satisfie AtomicBool AtomicI16 AtomicI32 + AtomicI64 AtomicI8 AtomicIsize - AtomicU16 and $N others note: required for `TransparentStruct` to implement `zerocopy::IntoBytes` --> tests/ui-nightly/derive_transparent.rs:24:10 diff --git a/zerocopy-derive/tests/ui-nightly/late_compile_pass.stderr b/zerocopy-derive/tests/ui-nightly/late_compile_pass.stderr index ef1d79ce5b..fbfc567600 100644 --- a/zerocopy-derive/tests/ui-nightly/late_compile_pass.stderr +++ b/zerocopy-derive/tests/ui-nightly/late_compile_pass.stderr @@ -20,7 +20,7 @@ error[E0277]: the trait bound `NotZerocopy: TryFromBytes` is not satisfied AtomicBool AtomicI16 AtomicI32 - AtomicI8 + AtomicI64 and $N others = help: see issue #48214 = note: this error originates in the derive macro `TryFromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -43,7 +43,7 @@ error[E0277]: the trait bound `NotZerocopy: TryFromBytes` is not satisfied AtomicBool AtomicI16 AtomicI32 - AtomicI8 + AtomicI64 and $N others = help: see issue #48214 = note: this error originates in the derive macro `FromZeros` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -66,7 +66,7 @@ error[E0277]: the trait bound `NotZerocopy: FromZeros` is not satisfied AtomicBool AtomicI16 AtomicI32 - AtomicI8 + AtomicI64 and $N others = help: see issue #48214 = note: this error originates in the derive macro `FromZeros` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -89,7 +89,7 @@ error[E0277]: the trait bound `NotZerocopy: TryFromBytes` is not satisfied AtomicBool AtomicI16 AtomicI32 - AtomicI8 + AtomicI64 and $N others = help: see issue #48214 = note: this error originates in the derive macro `FromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -112,7 +112,7 @@ error[E0277]: the trait bound `NotZerocopy: FromZeros` is not satisfied AtomicBool AtomicI16 AtomicI32 - AtomicI8 + AtomicI64 and $N others = help: see issue #48214 = note: this error originates in the derive macro `FromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -130,9 +130,9 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::FromBytes` is not satisfie = help: the following other types implement trait `zerocopy::FromBytes`: () AU16 - AtomicBool AtomicI16 AtomicI32 + AtomicI64 AtomicI8 AtomicIsize AtomicU16 @@ -156,9 +156,9 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::IntoBytes` is not satisfie AtomicBool AtomicI16 AtomicI32 + AtomicI64 AtomicI8 AtomicIsize - AtomicU16 and $N others = help: see issue #48214 = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/zerocopy-derive/tests/ui-nightly/struct.stderr b/zerocopy-derive/tests/ui-nightly/struct.stderr index 59b855a5c4..93c802c273 100644 --- a/zerocopy-derive/tests/ui-nightly/struct.stderr +++ b/zerocopy-derive/tests/ui-nightly/struct.stderr @@ -86,7 +86,7 @@ error[E0277]: the trait bound `NotKnownLayoutDst: zerocopy::KnownLayout` is not AtomicBool AtomicI16 AtomicI32 - AtomicI8 + AtomicI64 and $N others = help: see issue #48214 = note: this error originates in the derive macro `KnownLayout` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -109,7 +109,7 @@ error[E0277]: the trait bound `NotKnownLayout: zerocopy::KnownLayout` is not sat AtomicBool AtomicI16 AtomicI32 - AtomicI8 + AtomicI64 and $N others = help: see issue #48214 = note: this error originates in the derive macro `KnownLayout` (in Nightly builds, run with -Z macro-backtrace for more info)