Skip to content

Commit 7376e9e

Browse files
committed
Gate Atomic type behind the corresponding #[cfg(target_has_atomic)]
Signed-off-by: Joe Richey <[email protected]>
1 parent f405033 commit 7376e9e

17 files changed

+198
-259
lines changed

.github/workflows/ci.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
matrix:
3939
# See `INTERNAL.md` for an explanation of these pinned toolchain
4040
# versions.
41-
toolchain: [ "msrv", "stable", "nightly", "zerocopy-generic-bounds-in-const-fn", "zerocopy-aarch64-simd", "zerocopy-panic-in-const", ]
41+
toolchain: [ "msrv", "stable", "nightly", "zerocopy-generic-bounds-in-const-fn", "zerocopy-atomics", "zerocopy-aarch64-simd", "zerocopy-panic-in-const", ]
4242
target: [
4343
"i686-unknown-linux-gnu",
4444
"x86_64-unknown-linux-gnu",
@@ -61,6 +61,8 @@ jobs:
6161
features: "--all-features"
6262
- toolchain: "zerocopy-generic-bounds-in-const-fn"
6363
features: "--all-features"
64+
- toolchain: "zerocopy-atomics"
65+
features: "--all-features"
6466
- toolchain: "zerocopy-aarch64-simd"
6567
features: "--all-features"
6668
- toolchain: "zerocopy-panic-in-const"
@@ -80,6 +82,8 @@ jobs:
8082
# zerocopy-derive doesn't behave different on these toolchains.
8183
- crate: "zerocopy-derive"
8284
toolchain: "zerocopy-generic-bounds-in-const-fn"
85+
- crate: "zerocopy-derive"
86+
toolchain: "zerocopy-atomics"
8387
- crate: "zerocopy-derive"
8488
toolchain: "zerocopy-aarch64-simd"
8589
- crate: "zerocopy-derive"

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ exclude = [".*"]
3535
# From 1.61.0, Rust supports generic types with trait bounds in `const fn`.
3636
zerocopy-generic-bounds-in-const-fn = "1.61.0"
3737

38+
# From 1.60.0, Rust supports detecting if a target supports atomics, so we can
39+
# reliably implement traits on the Atomic* types.
40+
zerocopy-atomics = "1.60.0"
41+
3842
# When the "simd" feature is enabled, include SIMD types from the
3943
# `core::arch::aarch64` module, which was stabilized in 1.59.0. On earlier Rust
4044
# versions, these types require the "simd-nightly" feature.

src/lib.rs

Lines changed: 88 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ pub mod layout;
292292
pub mod macro_util;
293293
#[doc(hidden)]
294294
pub mod pointer;
295+
#[macro_use]
295296
mod util;
296297
// TODO(#252): If we make this pub, come up with a better name.
297298
mod wrappers;
@@ -314,10 +315,7 @@ use core::{
314315
ops::{Deref, DerefMut},
315316
ptr::{self, NonNull},
316317
slice,
317-
sync::atomic::{
318-
AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicPtr, AtomicU16, AtomicU32,
319-
AtomicU8, AtomicUsize,
320-
},
318+
sync::atomic::*,
321319
};
322320

323321
use crate::pointer::invariant;
@@ -665,9 +663,7 @@ impl_known_layout!(
665663
u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize, f32, f64,
666664
bool, char,
667665
NonZeroU8, NonZeroI8, NonZeroU16, NonZeroI16, NonZeroU32, NonZeroI32,
668-
NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize,
669-
AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicU16, AtomicU32,
670-
AtomicU8, AtomicUsize
666+
NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize
671667
);
672668
#[rustfmt::skip]
673669
impl_known_layout!(
@@ -676,8 +672,7 @@ impl_known_layout!(
676672
T => Wrapping<T>,
677673
T => MaybeUninit<T>,
678674
T: ?Sized => *const T,
679-
T: ?Sized => *mut T,
680-
T => AtomicPtr<T>
675+
T: ?Sized => *mut T
681676
);
682677
impl_known_layout!(const N: usize, T => [T; N]);
683678

@@ -3797,46 +3792,95 @@ safety_comment! {
37973792
unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => Immutable for opt_extern_c_fn!(...));
37983793
}
37993794

3800-
macro_rules! impl_traits_for_atomics {
3801-
($($atomics:ident [$inners:ident]),* $(,)?) => {
3802-
$(
3803-
impl_for_transparent_wrapper!(TryFromBytes for $atomics [UnsafeCell<$inners>]);
3804-
impl_for_transparent_wrapper!(FromZeros for $atomics [UnsafeCell<$inners>]);
3805-
impl_for_transparent_wrapper!(FromBytes for $atomics [UnsafeCell<$inners>]);
3806-
impl_for_transparent_wrapper!(IntoBytes for $atomics [UnsafeCell<$inners>]);
3807-
)*
3808-
};
3795+
/// Implements the zerocopy traits for atomic integer types.
3796+
///
3797+
/// Note that we only implement traits for atomics types if the corresponding
3798+
/// [`#[cfg(target_has_atomic)]`][target_has_atomic] configuration is true.
3799+
///
3800+
/// Note: On targets like `thumbv6m-none-eabi` (which has an [`AtomicU32`]
3801+
/// type but `#[cfg(target_has_atomic = "32")]` is false), we don't implement
3802+
/// the zerocopy traits. We will be able to handle this when Rust stabilizes
3803+
/// [`#[cfg(target_has_atomic_load_store)]`][target_has_atomic_load_store].
3804+
///
3805+
/// [target_has_atomic]: https://doc.rust-lang.org/reference/conditional-compilation.html#target_has_atomic
3806+
/// [target_has_atomic_load_store]: https://www.github.com/rust-lang/rust/issues/94039
3807+
///
3808+
/// # Safety
3809+
///
3810+
/// `$atomic` must be an atomic type with corresponding native type `$native`.
3811+
macro_rules! unsafe_impl_traits_for_atomics {
3812+
($($atomic:ident [$native:ident]),*) => {$(
3813+
// SAFETY: `$atomic` is an atomic type with native type `$native`.
3814+
unsafe impl util::Atomic for $atomic { type Native = $native; }
3815+
3816+
impl_for_transparent_wrapper!(TryFromBytes for $atomic);
3817+
impl_for_transparent_wrapper!(FromZeros for $atomic);
3818+
unsafe_impl_traits_for_atomics!(@from_bytes $atomic);
3819+
impl_for_transparent_wrapper!(IntoBytes for $atomic);
3820+
impl_known_layout!($atomic);
3821+
)*};
3822+
(@from_bytes AtomicBool) => {};
3823+
(@from_bytes $atomic:ty) => { impl_for_transparent_wrapper!(FromBytes for $atomic); };
38093824
}
38103825

3811-
#[rustfmt::skip]
3812-
impl_traits_for_atomics!(
3813-
AtomicBool [bool],
3814-
AtomicI16 [i16], AtomicI32 [i32], AtomicI8 [i8], AtomicIsize [isize],
3815-
AtomicU16 [u16], AtomicU32 [u32], AtomicU8 [u8], AtomicUsize [usize],
3816-
);
3826+
#[cfg(zerocopy_atomics)]
3827+
#[cfg(target_has_atomic = "8")]
3828+
#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "8")))]
3829+
mod atomic_8 {
3830+
use super::*;
3831+
unsafe_impl_traits_for_atomics!(AtomicBool[bool], AtomicU8[u8], AtomicI8[i8]);
3832+
safety_comment! {
3833+
/// SAFETY:
3834+
/// Per [1], `AtomicBool`, `AtomicU8`, and `AtomicI8` have an alignment of 1.
3835+
///
3836+
/// [1] Per https://doc.rust-lang.org/nightly/core/sync/atomic/struct.AtomicBool.html:
3837+
///
3838+
/// This type has the same size, alignment, and bit validity as [the native type].
3839+
unsafe_impl!(AtomicBool: Unaligned);
3840+
unsafe_impl!(AtomicU8: Unaligned);
3841+
unsafe_impl!(AtomicI8: Unaligned);
3842+
}
3843+
assert_unaligned!(AtomicBool, AtomicU8, AtomicI8);
3844+
}
3845+
#[cfg(zerocopy_atomics)]
3846+
#[cfg(target_has_atomic = "16")]
3847+
#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "16")))]
3848+
mod atomic_16 {
3849+
use super::*;
3850+
unsafe_impl_traits_for_atomics!(AtomicU16[u16], AtomicI16[i16]);
3851+
}
3852+
#[cfg(zerocopy_atomics)]
3853+
#[cfg(target_has_atomic = "32")]
3854+
#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "32")))]
3855+
mod atomic_32 {
3856+
use super::*;
3857+
unsafe_impl_traits_for_atomics!(AtomicU32[u32], AtomicI32[i32]);
3858+
}
3859+
#[cfg(zerocopy_atomics)]
3860+
#[cfg(target_has_atomic = "64")]
3861+
#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "64")))]
3862+
mod atomic_64 {
3863+
use super::*;
3864+
unsafe_impl_traits_for_atomics!(AtomicU64[u64], AtomicI64[i64]);
3865+
}
3866+
#[cfg(zerocopy_atomics)]
3867+
#[cfg(target_has_atomic = "ptr")]
3868+
#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "ptr")))]
3869+
mod atomic_ptr {
3870+
use super::*;
3871+
unsafe_impl_traits_for_atomics!(AtomicUsize[usize], AtomicIsize[isize]);
38173872

3818-
safety_comment! {
38193873
/// SAFETY:
3820-
/// Per [1], `AtomicBool`, `AtomicU8`, and `AtomicI8` have the same size as
3821-
/// `bool`, `u8`, and `i8` respectively. Since a type's alignment cannot be
3822-
/// smaller than 1 [2], and since its alignment cannot be greater than its
3823-
/// size [3], the only possible value for the alignment is 1. Thus, it is
3824-
/// sound to implement `Unaligned`.
3874+
/// `AtomicPtr<T>`` is garunteed to wrap a `*mut T`. Just like *mut T, we
3875+
/// don't implement FromBytes/IntoBytes.
38253876
///
3826-
/// [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943):
3827-
/// Cite docs once they've landed.
3828-
///
3829-
/// [2] Per https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment:
3830-
///
3831-
/// Alignment is measured in bytes, and must be at least 1.
3832-
///
3833-
/// [3] Per https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment:
3834-
///
3835-
/// The size of a value is always a multiple of its alignment.
3836-
unsafe_impl!(AtomicBool: Unaligned);
3837-
unsafe_impl!(AtomicU8: Unaligned);
3838-
unsafe_impl!(AtomicI8: Unaligned);
3839-
assert_unaligned!(AtomicBool, AtomicU8, AtomicI8);
3877+
/// See the comments on the `*mut T` impls for more information.
3878+
unsafe impl<T> util::Atomic for AtomicPtr<T> {
3879+
type Native = *mut T;
3880+
}
3881+
impl_for_transparent_wrapper!(T => TryFromBytes for AtomicPtr<T>);
3882+
impl_for_transparent_wrapper!(T => FromZeros for AtomicPtr<T>);
3883+
impl_known_layout!(T => AtomicPtr<T>);
38403884
}
38413885

38423886
safety_comment! {

0 commit comments

Comments
 (0)