Skip to content

Commit

Permalink
Gate Atomic type behind the corresponding #[cfg(target_has_atomic)]
Browse files Browse the repository at this point in the history
Signed-off-by: Joe Richey <[email protected]>
  • Loading branch information
josephlr committed May 8, 2024
1 parent f405033 commit cd818a1
Show file tree
Hide file tree
Showing 17 changed files with 201 additions and 259 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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"
Expand All @@ -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"
Expand Down
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
135 changes: 91 additions & 44 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -314,12 +315,11 @@ use core::{
ops::{Deref, DerefMut},
ptr::{self, NonNull},
slice,
sync::atomic::{
AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicPtr, AtomicU16, AtomicU32,
AtomicU8, AtomicUsize,
},
};

#[cfg(zerocopy_atomics)]
use core::sync::atomic::*;

use crate::pointer::invariant;

#[cfg(any(feature = "alloc", test))]
Expand Down Expand Up @@ -665,9 +665,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!(
Expand All @@ -676,8 +674,7 @@ impl_known_layout!(
T => Wrapping<T>,
T => MaybeUninit<T>,
T: ?Sized => *const T,
T: ?Sized => *mut T,
T => AtomicPtr<T>
T: ?Sized => *mut T
);
impl_known_layout!(const N: usize, T => [T; N]);

Expand Down Expand Up @@ -3797,46 +3794,96 @@ 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`.
#[cfg(zerocopy_atomics)]
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`.
///
/// [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943):
/// Cite docs once they've landed.
/// `AtomicPtr<T>`` is garunteed to wrap a `*mut T`. Just like *mut T, we
/// don't implement FromBytes/IntoBytes.
///
/// [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<T> util::Atomic for AtomicPtr<T> {
type Native = *mut T;
}
impl_for_transparent_wrapper!(T => TryFromBytes for AtomicPtr<T>);
impl_for_transparent_wrapper!(T => FromZeros for AtomicPtr<T>);
impl_known_layout!(T => AtomicPtr<T>);
}

safety_comment! {
Expand Down
Loading

0 comments on commit cd818a1

Please sign in to comment.