Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gate implementations for Atomic* types behind #[cfg(target_has_atomic)] and Rust 1.60 #1091

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,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 @@ -70,6 +70,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 @@ -88,6 +90,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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# From 1.60.0, Rust supports detecting if a target supports atomics, so we can
# From 1.60.0, Rust supports detecting whether 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
122 changes: 87 additions & 35 deletions src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
// those terms.

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

safety_comment! {
/// SAFETY:
Expand Down Expand Up @@ -440,46 +442,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.
///
/// [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.
/// `AtomicPtr<T>`` is garunteed to wrap a `*mut T`. Just like *mut T, we
/// don't implement FromBytes/IntoBytes.
///
/// [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
11 changes: 2 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,10 +323,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, BecauseExclusive, BecauseImmutable};
Expand Down Expand Up @@ -805,9 +801,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 @@ -816,8 +810,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
Loading
Loading