Skip to content

Commit 0bc9d36

Browse files
joshlfjosephlr
andcommitted
Detect atomic support using target_has_atomic
Implements `TryFromBytes` and `FromZeros` for `AtomicPtr`; `FromBytes` and `IntoBytes` are blocked by #170. This is adapted from @josephlr's similar implementation in #1092. Fixes #1086 Co-authored-by: Joe Richey <[email protected]>
1 parent 739c3d1 commit 0bc9d36

34 files changed

+236
-137
lines changed

.github/workflows/ci.yml

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,19 @@ jobs:
4646
matrix:
4747
# See `INTERNAL.md` for an explanation of these pinned toolchain
4848
# versions.
49-
toolchain: [ "msrv", "stable", "nightly", "zerocopy-generic-bounds-in-const-fn", "zerocopy-aarch64-simd", "zerocopy-panic-in-const", ]
49+
toolchain: [
50+
"msrv",
51+
"stable",
52+
"nightly",
53+
54+
# These are the names of specific Rust versions detected in
55+
# `build.rs`. Each of these represents the minimum Rust version for
56+
# which a particular feature is supported.
57+
"zerocopy-generic-bounds-in-const-fn",
58+
"zerocopy-target-has-atomics",
59+
"zerocopy-aarch64-simd",
60+
"zerocopy-panic-in-const"
61+
]
5062
target: [
5163
"i686-unknown-linux-gnu",
5264
"x86_64-unknown-linux-gnu",
@@ -57,6 +69,7 @@ jobs:
5769
"riscv64gc-unknown-linux-gnu",
5870
"s390x-unknown-linux-gnu",
5971
"x86_64-pc-windows-msvc",
72+
"thumbv6m-none-eabi",
6073
"wasm32-wasi"
6174
]
6275
features: [ "--no-default-features", "", "--features __internal_use_only_features_that_work_on_stable", "--all-features" ]
@@ -109,6 +122,8 @@ jobs:
109122
event_name: "pull_request"
110123
- target: "s390x-unknown-linux-gnu"
111124
event_name: "pull_request"
125+
- target: "thumbv6m-none-eabi"
126+
event_name: "pull_request"
112127
- target: "wasm32-wasi"
113128
event_name: "pull_request"
114129

@@ -120,6 +135,19 @@ jobs:
120135
- name: Populate cache
121136
uses: ./.github/actions/cache
122137

138+
# Ensure that Cargo resolves the minimum possible syn version so that if we
139+
# accidentally make a change which depends upon features added in more
140+
# recent versions of syn, we'll catch it in CI.
141+
#
142+
# TODO(#1595): Debug why this step is still necessary after #1564 and maybe
143+
# remove it.
144+
- name: Pin syn dependency
145+
run: |
146+
set -eo pipefail
147+
# Override the exising `syn` dependency with one which requires an exact
148+
# version.
149+
cargo add -p zerocopy-derive 'syn@=2.0.46'
150+
123151
- name: Configure environment variables
124152
run: |
125153
set -eo pipefail
@@ -488,6 +516,9 @@ jobs:
488516
# See comment on "Pin syn dependency" job for why we do this. It needs
489517
# to happen before the subsequent `cargo check`, so we don't
490518
# background it.
519+
#
520+
# TODO(#1595): Debug why this step is still necessary after #1564 and
521+
# maybe remove it.
491522
cargo add -p zerocopy-derive 'syn@=2.0.46' &> /dev/null
492523
493524
cargo check --workspace --tests &> /dev/null &

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 `cfg(target_has_atomics)`, which allows us to
39+
# detect whether a target supports particular sets of atomics.
40+
zerocopy-target-has-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/impls.rs

Lines changed: 127 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -440,49 +440,137 @@ safety_comment! {
440440
unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => Immutable for opt_extern_c_fn!(...));
441441
}
442442

443-
macro_rules! impl_traits_for_atomics {
444-
($($atomics:ident),* $(,)?) => {
445-
$(
446-
impl_for_transparent_wrapper!(=> TryFromBytes for $atomics);
447-
impl_for_transparent_wrapper!(=> FromZeros for $atomics);
448-
impl_for_transparent_wrapper!(=> FromBytes for $atomics);
449-
impl_for_transparent_wrapper!(=> IntoBytes for $atomics);
450-
)*
443+
#[cfg(zerocopy_target_has_atomics)]
444+
mod atomics {
445+
use core::sync::atomic::{
446+
AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicPtr, AtomicU16,
447+
AtomicU32, AtomicU64, AtomicU8, AtomicUsize,
451448
};
452-
}
453449

454-
#[rustfmt::skip]
455-
impl_traits_for_atomics!(
456-
AtomicI16, AtomicI32, AtomicI8, AtomicIsize,
457-
AtomicU16, AtomicU32, AtomicU8, AtomicUsize,
458-
);
450+
use super::*;
459451

460-
impl_for_transparent_wrapper!(=> TryFromBytes for AtomicBool);
461-
impl_for_transparent_wrapper!(=> FromZeros for AtomicBool);
462-
impl_for_transparent_wrapper!(=> IntoBytes for AtomicBool);
452+
macro_rules! impl_traits_for_atomics {
453+
($($atomics:ident),* $(,)?) => {
454+
$(
455+
impl_known_layout!($atomics);
456+
impl_for_transparent_wrapper!(=> TryFromBytes for $atomics);
457+
impl_for_transparent_wrapper!(=> FromZeros for $atomics);
458+
impl_for_transparent_wrapper!(=> FromBytes for $atomics);
459+
impl_for_transparent_wrapper!(=> IntoBytes for $atomics);
460+
)*
461+
};
462+
}
463463

464-
safety_comment! {
465-
/// SAFETY:
466-
/// Per [1], `AtomicBool`, `AtomicU8`, and `AtomicI8` have the same size as
467-
/// `bool`, `u8`, and `i8` respectively. Since a type's alignment cannot be
468-
/// smaller than 1 [2], and since its alignment cannot be greater than its
469-
/// size [3], the only possible value for the alignment is 1. Thus, it is
470-
/// sound to implement `Unaligned`.
471-
///
472-
/// [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943):
473-
/// Cite docs once they've landed.
474-
///
475-
/// [2] Per https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment:
476-
///
477-
/// Alignment is measured in bytes, and must be at least 1.
478-
///
479-
/// [3] Per https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment:
480-
///
481-
/// The size of a value is always a multiple of its alignment.
482-
unsafe_impl!(AtomicBool: Unaligned);
483-
unsafe_impl!(AtomicU8: Unaligned);
484-
unsafe_impl!(AtomicI8: Unaligned);
485-
assert_unaligned!(AtomicBool, AtomicU8, AtomicI8);
464+
#[cfg(target_has_atomic = "8")]
465+
#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "8")))]
466+
mod atomic_8 {
467+
use super::*;
468+
469+
impl_traits_for_atomics!(AtomicU8, AtomicI8);
470+
471+
impl_known_layout!(AtomicBool);
472+
473+
impl_for_transparent_wrapper!(=> TryFromBytes for AtomicBool);
474+
impl_for_transparent_wrapper!(=> FromZeros for AtomicBool);
475+
impl_for_transparent_wrapper!(=> IntoBytes for AtomicBool);
476+
477+
safety_comment! {
478+
/// SAFETY:
479+
/// Per [1], `AtomicBool`, `AtomicU8`, and `AtomicI8` have the same
480+
/// size as `bool`, `u8`, and `i8` respectively. Since a type's
481+
/// alignment cannot be smaller than 1 [2], and since its alignment
482+
/// cannot be greater than its size [3], the only possible value for
483+
/// the alignment is 1. Thus, it is sound to implement `Unaligned`.
484+
///
485+
/// [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943):
486+
/// Cite docs once they've landed.
487+
///
488+
/// [2] Per https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment:
489+
///
490+
/// Alignment is measured in bytes, and must be at least 1.
491+
///
492+
/// [3] Per https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment:
493+
///
494+
/// The size of a value is always a multiple of its alignment.
495+
unsafe_impl!(AtomicBool: Unaligned);
496+
unsafe_impl!(AtomicU8: Unaligned);
497+
unsafe_impl!(AtomicI8: Unaligned);
498+
assert_unaligned!(AtomicBool, AtomicU8, AtomicI8);
499+
500+
/// SAFETY:
501+
/// All of these pass an atomic type and that type's native equivalent, as
502+
/// required by the macro safety preconditions.
503+
unsafe_impl_transparent_wrapper_for_atomic!(AtomicU8 [u8], AtomicI8 [i8], AtomicBool [bool]);
504+
}
505+
}
506+
507+
#[cfg(target_has_atomic = "16")]
508+
#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "16")))]
509+
mod atomic_16 {
510+
use super::*;
511+
512+
impl_traits_for_atomics!(AtomicU16, AtomicI16);
513+
514+
safety_comment! {
515+
/// SAFETY:
516+
/// All of these pass an atomic type and that type's native equivalent, as
517+
/// required by the macro safety preconditions.
518+
unsafe_impl_transparent_wrapper_for_atomic!(AtomicU16 [u16], AtomicI16 [i16]);
519+
}
520+
}
521+
522+
#[cfg(target_has_atomic = "32")]
523+
#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "32")))]
524+
mod atomic_32 {
525+
use super::*;
526+
527+
impl_traits_for_atomics!(AtomicU32, AtomicI32);
528+
529+
safety_comment! {
530+
/// SAFETY:
531+
/// All of these pass an atomic type and that type's native equivalent, as
532+
/// required by the macro safety preconditions.
533+
unsafe_impl_transparent_wrapper_for_atomic!(AtomicU32 [u32], AtomicI32 [i32]);
534+
}
535+
}
536+
537+
#[cfg(target_has_atomic = "64")]
538+
#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "64")))]
539+
mod atomic_64 {
540+
use super::*;
541+
542+
impl_traits_for_atomics!(AtomicU64, AtomicI64);
543+
544+
safety_comment! {
545+
/// SAFETY:
546+
/// All of these pass an atomic type and that type's native equivalent, as
547+
/// required by the macro safety preconditions.
548+
unsafe_impl_transparent_wrapper_for_atomic!(AtomicU64 [u64], AtomicI64 [i64]);
549+
}
550+
}
551+
552+
#[cfg(target_has_atomic = "ptr")]
553+
#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "ptr")))]
554+
mod atomic_ptr {
555+
use super::*;
556+
557+
impl_traits_for_atomics!(AtomicUsize, AtomicIsize);
558+
559+
impl_known_layout!(T => AtomicPtr<T>);
560+
561+
// TODO(#170): Implement `FromBytes` and `IntoBytes` once we implement
562+
// those traits for `*mut T`.
563+
impl_for_transparent_wrapper!(T => TryFromBytes for AtomicPtr<T>);
564+
impl_for_transparent_wrapper!(T => FromZeros for AtomicPtr<T>);
565+
566+
safety_comment! {
567+
/// SAFETY:
568+
/// This passes an atomic type and that type's native equivalent, as
569+
/// required by the macro safety preconditions.
570+
unsafe_impl_transparent_wrapper_for_atomic!(AtomicUsize [usize], AtomicIsize [isize]);
571+
unsafe_impl_transparent_wrapper_for_atomic!(T => AtomicPtr<T> [*mut T]);
572+
}
573+
}
486574
}
487575

488576
safety_comment! {

src/lib.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,8 @@ extern crate self as zerocopy;
296296

297297
#[macro_use]
298298
mod macros;
299+
#[macro_use]
300+
mod util;
299301

300302
pub mod byte_slice;
301303
pub mod byteorder;
@@ -313,7 +315,6 @@ pub mod macro_util;
313315
#[doc(hidden)]
314316
pub mod pointer;
315317
mod r#ref;
316-
mod util;
317318
// TODO(#252): If we make this pub, come up with a better name.
318319
mod wrappers;
319320

@@ -337,10 +338,6 @@ use core::{
337338
ops::{Deref, DerefMut},
338339
ptr::{self, NonNull},
339340
slice,
340-
sync::atomic::{
341-
AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicPtr, AtomicU16, AtomicU32,
342-
AtomicU8, AtomicUsize,
343-
},
344341
};
345342

346343
use crate::pointer::{invariant, BecauseExclusive, BecauseImmutable};
@@ -819,9 +816,7 @@ impl_known_layout!(
819816
u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize, f32, f64,
820817
bool, char,
821818
NonZeroU8, NonZeroI8, NonZeroU16, NonZeroI16, NonZeroU32, NonZeroI32,
822-
NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize,
823-
AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicU16, AtomicU32,
824-
AtomicU8, AtomicUsize
819+
NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize
825820
);
826821
#[rustfmt::skip]
827822
impl_known_layout!(
@@ -830,8 +825,7 @@ impl_known_layout!(
830825
T => Wrapping<T>,
831826
T => MaybeUninit<T>,
832827
T: ?Sized => *const T,
833-
T: ?Sized => *mut T,
834-
T => AtomicPtr<T>
828+
T: ?Sized => *mut T
835829
);
836830
impl_known_layout!(const N: usize, T => [T; N]);
837831

src/util.rs

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@ use core::{
1212
mem::{self, ManuallyDrop, MaybeUninit},
1313
num::{NonZeroUsize, Wrapping},
1414
ptr::NonNull,
15-
sync::atomic::{
16-
AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicPtr, AtomicU16, AtomicU32,
17-
AtomicU8, AtomicUsize,
18-
},
1915
};
2016

2117
use crate::{
@@ -332,12 +328,13 @@ unsafe impl<T, I: Invariants> TransparentWrapper<I> for Unalign<T> {
332328
///
333329
/// The caller promises that `$atomic` is an atomic type whose natie equivalent
334330
/// is `$native`.
331+
#[cfg(zerocopy_target_has_atomics)]
335332
macro_rules! unsafe_impl_transparent_wrapper_for_atomic {
336333
($(#[$attr:meta])* $(,)?) => {};
337334
($(#[$attr:meta])* $atomic:ty [$native:ty], $($atomics:ty [$natives:ty]),* $(,)?) => {
338335
$(#[$attr])*
339336
// SAFETY: See safety comment in next match arm.
340-
unsafe impl<I: Invariants> TransparentWrapper<I> for $atomic {
337+
unsafe impl<I: crate::invariant::Invariants> crate::util::TransparentWrapper<I> for $atomic {
341338
unsafe_impl_transparent_wrapper_for_atomic!(@inner $atomic [$native]);
342339
}
343340
unsafe_impl_transparent_wrapper_for_atomic!($(#[$attr])* $($atomics [$natives],)*);
@@ -351,7 +348,7 @@ macro_rules! unsafe_impl_transparent_wrapper_for_atomic {
351348
// [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943):
352349
// Cite docs once they've landed.
353350
$(#[$attr])*
354-
unsafe impl<$tyvar, I: Invariants> TransparentWrapper<I> for $atomic {
351+
unsafe impl<$tyvar, I: crate::invariant::Invariants> crate::util::TransparentWrapper<I> for $atomic {
355352
unsafe_impl_transparent_wrapper_for_atomic!(@inner $atomic [$native]);
356353
}
357354
};
@@ -381,11 +378,11 @@ macro_rules! unsafe_impl_transparent_wrapper_for_atomic {
381378
//
382379
// [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943):
383380
// Cite docs once they've landed.
384-
type UnsafeCellVariance = Covariant;
381+
type UnsafeCellVariance = crate::util::Covariant;
385382

386383
// SAFETY: No safety justification is required for an invariant
387384
// variance.
388-
type AlignmentVariance = Invariant;
385+
type AlignmentVariance = crate::util::Invariant;
389386

390387
// SAFETY: Per [1], all atomic types have the same bit validity as their
391388
// native counterparts. The caller has promised that `$atomic` and
@@ -394,7 +391,7 @@ macro_rules! unsafe_impl_transparent_wrapper_for_atomic {
394391
//
395392
// [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943):
396393
// Cite docs once they've landed.
397-
type ValidityVariance = Covariant;
394+
type ValidityVariance = crate::util::Covariant;
398395

399396
fn cast_into_inner(ptr: *mut $atomic) -> *mut UnsafeCell<$native> {
400397
// SAFETY: Per [1] (from comment on impl block), `$atomic` has the
@@ -414,24 +411,6 @@ macro_rules! unsafe_impl_transparent_wrapper_for_atomic {
414411
};
415412
}
416413

417-
safety_comment! {
418-
/// SAFETY:
419-
/// All of these pass an atomic type and that type's native equivalent, as
420-
/// required by the macro safety preconditions.
421-
unsafe_impl_transparent_wrapper_for_atomic!(T => AtomicPtr<T> [*mut T]);
422-
unsafe_impl_transparent_wrapper_for_atomic!(
423-
AtomicBool [bool],
424-
AtomicI16 [i16], AtomicI32 [i32], AtomicI8 [i8], AtomicIsize [isize],
425-
AtomicU16 [u16], AtomicU32 [u32], AtomicU8 [u8], AtomicUsize [usize],
426-
);
427-
#[cfg(not(target_arch = "powerpc"))]
428-
unsafe_impl_transparent_wrapper_for_atomic!(
429-
#[cfg_attr(doc_cfg, doc(cfg(not(target_arch = "powerpc"))))]
430-
core::sync::atomic::AtomicI64 [i64],
431-
core::sync::atomic::AtomicU64 [u64],
432-
);
433-
}
434-
435414
/// Like [`PhantomData`], but [`Send`] and [`Sync`] regardless of whether the
436415
/// wrapped `T` is.
437416
pub(crate) struct SendSyncPhantomData<T: ?Sized>(PhantomData<T>);

tests/ui-nightly/include_value_not_from_bytes.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ error[E0277]: the trait bound `NotZerocopy<u32>: zerocopy::FromBytes` is not sat
1212
AU16
1313
AtomicI16
1414
AtomicI32
15+
AtomicI64
1516
AtomicI8
1617
AtomicIsize
1718
AtomicU16
18-
AtomicU32
1919
and $N others
2020
note: required by a bound in `AssertIsFromBytes`
2121
--> tests/ui-nightly/include_value_not_from_bytes.rs:15:42

0 commit comments

Comments
 (0)