Skip to content

Commit 866b645

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 e29fb6a commit 866b645

34 files changed

+247
-137
lines changed

.github/workflows/ci.yml

+37
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ jobs:
5555
# `build.rs`. Each of these represents the minimum Rust version for
5656
# which a particular feature is supported.
5757
"zerocopy-generic-bounds-in-const-fn",
58+
"zerocopy-target-has-atomics",
5859
"zerocopy-aarch64-simd",
5960
"zerocopy-panic-in-const"
6061
]
@@ -68,6 +69,7 @@ jobs:
6869
"riscv64gc-unknown-linux-gnu",
6970
"s390x-unknown-linux-gnu",
7071
"x86_64-pc-windows-msvc",
72+
"thumbv6m-none-eabi",
7173
"wasm32-wasi"
7274
]
7375
features: [
@@ -87,6 +89,8 @@ jobs:
8789
features: "--all-features"
8890
- toolchain: "zerocopy-generic-bounds-in-const-fn"
8991
features: "--all-features"
92+
- toolchain: "zerocopy-target-has-atomics"
93+
features: "--all-features"
9094
- toolchain: "zerocopy-aarch64-simd"
9195
features: "--all-features"
9296
- toolchain: "zerocopy-panic-in-const"
@@ -105,6 +109,8 @@ jobs:
105109
# zerocopy-derive doesn't behave different on these toolchains.
106110
- crate: "zerocopy-derive"
107111
toolchain: "zerocopy-generic-bounds-in-const-fn"
112+
- crate: "zerocopy-derive"
113+
toolchain: "zerocopy-target-has-atomics"
108114
- crate: "zerocopy-derive"
109115
toolchain: "zerocopy-aarch64-simd"
110116
- crate: "zerocopy-derive"
@@ -127,6 +133,8 @@ jobs:
127133
target: "s390x-unknown-linux-gnu"
128134
- toolchain: "zerocopy-aarch64-simd"
129135
target: "x86_64-pc-windows-msvc"
136+
- toolchain: "zerocopy-aarch64-simd"
137+
target: "thumbv6m-none-eabi"
130138
- toolchain: "zerocopy-aarch64-simd"
131139
target: "wasm32-wasi"
132140
# Exclude most targets targets from the
@@ -147,6 +155,8 @@ jobs:
147155
target: "s390x-unknown-linux-gnu"
148156
- toolchain: "zerocopy-generic-bounds-in-const-fn"
149157
target: "x86_64-pc-windows-msvc"
158+
- toolchain: "zerocopy-generic-bounds-in-const-fn"
159+
target: "thumbv6m-none-eabi"
150160
- toolchain: "zerocopy-generic-bounds-in-const-fn"
151161
target: "wasm32-wasi"
152162
# Exclude most targets during PR development, but allow them in the
@@ -165,6 +175,9 @@ jobs:
165175
event_name: "pull_request"
166176
- target: "s390x-unknown-linux-gnu"
167177
event_name: "pull_request"
178+
# TODO(#1586): Uncomment this before merging.
179+
# - target: "thumbv6m-none-eabi"
180+
# event_name: "pull_request"
168181
- target: "wasm32-wasi"
169182
event_name: "pull_request"
170183

@@ -176,6 +189,19 @@ jobs:
176189
- name: Populate cache
177190
uses: ./.github/actions/cache
178191

192+
# Ensure that Cargo resolves the minimum possible syn version so that if we
193+
# accidentally make a change which depends upon features added in more
194+
# recent versions of syn, we'll catch it in CI.
195+
#
196+
# TODO(#1595): Debug why this step is still necessary after #1564 and maybe
197+
# remove it.
198+
- name: Pin syn dependency
199+
run: |
200+
set -eo pipefail
201+
# Override the exising `syn` dependency with one which requires an exact
202+
# version.
203+
cargo add -p zerocopy-derive 'syn@=2.0.46'
204+
179205
- name: Configure environment variables
180206
run: |
181207
set -eo pipefail
@@ -240,11 +266,19 @@ jobs:
240266
with:
241267
key: "${{ matrix.target }}"
242268

269+
# On the `thumbv6m-none-eabi` target, we can't run `cargo check --tests` due
270+
# to the `memchr` crate, so we just do `cargo check` instead.
271+
- name: Check
272+
run: ./cargo.sh +${{ matrix.toolchain }} check --package ${{ matrix.crate }} --target ${{ matrix.target }} ${{ matrix.features }} --verbose
273+
if: matrix.target == 'thumbv6m-none-eabi'
274+
243275
- name: Check tests
244276
run: ./cargo.sh +${{ matrix.toolchain }} check --tests --package ${{ matrix.crate }} --target ${{ matrix.target }} ${{ matrix.features }} --verbose
277+
if: matrix.target != 'thumbv6m-none-eabi'
245278

246279
- name: Build
247280
run: ./cargo.sh +${{ matrix.toolchain }} build --package ${{ matrix.crate }} --target ${{ matrix.target }} ${{ matrix.features }} --verbose
281+
if: matrix.target != 'thumbv6m-none-eabi'
248282

249283
# When building tests for the i686 target, we need certain libraries which
250284
# are not installed by default; `gcc-multilib` includes these libraries.
@@ -544,6 +578,9 @@ jobs:
544578
# See comment on "Pin syn dependency" job for why we do this. It needs
545579
# to happen before the subsequent `cargo check`, so we don't
546580
# background it.
581+
#
582+
# TODO(#1595): Debug why this step is still necessary after #1564 and
583+
# maybe remove it.
547584
cargo add -p zerocopy-derive 'syn@=2.0.46' &> /dev/null
548585
549586
cargo check --workspace --tests &> /dev/null &

Cargo.toml

+4
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

+133-40
Original file line numberDiff line numberDiff line change
@@ -440,49 +440,142 @@ 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-
)*
451-
};
452-
}
443+
#[cfg(zerocopy_target_has_atomics)]
444+
mod atomics {
445+
use super::*;
453446

454-
#[rustfmt::skip]
455-
impl_traits_for_atomics!(
456-
AtomicI16, AtomicI32, AtomicI8, AtomicIsize,
457-
AtomicU16, AtomicU32, AtomicU8, AtomicUsize,
458-
);
447+
macro_rules! impl_traits_for_atomics {
448+
($($atomics:ident),* $(,)?) => {
449+
$(
450+
impl_known_layout!($atomics);
451+
impl_for_transparent_wrapper!(=> TryFromBytes for $atomics);
452+
impl_for_transparent_wrapper!(=> FromZeros for $atomics);
453+
impl_for_transparent_wrapper!(=> FromBytes for $atomics);
454+
impl_for_transparent_wrapper!(=> IntoBytes for $atomics);
455+
)*
456+
};
457+
}
459458

460-
impl_for_transparent_wrapper!(=> TryFromBytes for AtomicBool);
461-
impl_for_transparent_wrapper!(=> FromZeros for AtomicBool);
462-
impl_for_transparent_wrapper!(=> IntoBytes for AtomicBool);
459+
#[cfg(target_has_atomic = "8")]
460+
#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "8")))]
461+
mod atomic_8 {
462+
use core::sync::atomic::{AtomicBool, AtomicI8, AtomicU8};
463+
464+
use super::*;
465+
466+
impl_traits_for_atomics!(AtomicU8, AtomicI8);
467+
468+
impl_known_layout!(AtomicBool);
469+
470+
impl_for_transparent_wrapper!(=> TryFromBytes for AtomicBool);
471+
impl_for_transparent_wrapper!(=> FromZeros for AtomicBool);
472+
impl_for_transparent_wrapper!(=> IntoBytes for AtomicBool);
473+
474+
safety_comment! {
475+
/// SAFETY:
476+
/// Per [1], `AtomicBool`, `AtomicU8`, and `AtomicI8` have the same
477+
/// size as `bool`, `u8`, and `i8` respectively. Since a type's
478+
/// alignment cannot be smaller than 1 [2], and since its alignment
479+
/// cannot be greater than its size [3], the only possible value for
480+
/// the alignment is 1. Thus, it is sound to implement `Unaligned`.
481+
///
482+
/// [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943):
483+
/// Cite docs once they've landed.
484+
///
485+
/// [2] Per https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment:
486+
///
487+
/// Alignment is measured in bytes, and must be at least 1.
488+
///
489+
/// [3] Per https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment:
490+
///
491+
/// The size of a value is always a multiple of its alignment.
492+
unsafe_impl!(AtomicBool: Unaligned);
493+
unsafe_impl!(AtomicU8: Unaligned);
494+
unsafe_impl!(AtomicI8: Unaligned);
495+
assert_unaligned!(AtomicBool, AtomicU8, AtomicI8);
496+
497+
/// SAFETY:
498+
/// All of these pass an atomic type and that type's native equivalent, as
499+
/// required by the macro safety preconditions.
500+
unsafe_impl_transparent_wrapper_for_atomic!(AtomicU8 [u8], AtomicI8 [i8], AtomicBool [bool]);
501+
}
502+
}
463503

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);
504+
#[cfg(target_has_atomic = "16")]
505+
#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "16")))]
506+
mod atomic_16 {
507+
use core::sync::atomic::{AtomicI16, AtomicU16};
508+
509+
use super::*;
510+
511+
impl_traits_for_atomics!(AtomicU16, AtomicI16);
512+
513+
safety_comment! {
514+
/// SAFETY:
515+
/// All of these pass an atomic type and that type's native equivalent, as
516+
/// required by the macro safety preconditions.
517+
unsafe_impl_transparent_wrapper_for_atomic!(AtomicU16 [u16], AtomicI16 [i16]);
518+
}
519+
}
520+
521+
#[cfg(target_has_atomic = "32")]
522+
#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "32")))]
523+
mod atomic_32 {
524+
use core::sync::atomic::{AtomicI32, AtomicU32};
525+
526+
use super::*;
527+
528+
impl_traits_for_atomics!(AtomicU32, AtomicI32);
529+
530+
safety_comment! {
531+
/// SAFETY:
532+
/// All of these pass an atomic type and that type's native equivalent, as
533+
/// required by the macro safety preconditions.
534+
unsafe_impl_transparent_wrapper_for_atomic!(AtomicU32 [u32], AtomicI32 [i32]);
535+
}
536+
}
537+
538+
#[cfg(target_has_atomic = "64")]
539+
#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "64")))]
540+
mod atomic_64 {
541+
use core::sync::atomic::{AtomicI64, AtomicU64};
542+
543+
use super::*;
544+
545+
impl_traits_for_atomics!(AtomicU64, AtomicI64);
546+
547+
safety_comment! {
548+
/// SAFETY:
549+
/// All of these pass an atomic type and that type's native equivalent, as
550+
/// required by the macro safety preconditions.
551+
unsafe_impl_transparent_wrapper_for_atomic!(AtomicU64 [u64], AtomicI64 [i64]);
552+
}
553+
}
554+
555+
#[cfg(target_has_atomic = "ptr")]
556+
#[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "ptr")))]
557+
mod atomic_ptr {
558+
use core::sync::atomic::{AtomicIsize, AtomicPtr, AtomicUsize};
559+
560+
use super::*;
561+
562+
impl_traits_for_atomics!(AtomicUsize, AtomicIsize);
563+
564+
impl_known_layout!(T => AtomicPtr<T>);
565+
566+
// TODO(#170): Implement `FromBytes` and `IntoBytes` once we implement
567+
// those traits for `*mut T`.
568+
impl_for_transparent_wrapper!(T => TryFromBytes for AtomicPtr<T>);
569+
impl_for_transparent_wrapper!(T => FromZeros for AtomicPtr<T>);
570+
571+
safety_comment! {
572+
/// SAFETY:
573+
/// This passes an atomic type and that type's native equivalent, as
574+
/// required by the macro safety preconditions.
575+
unsafe_impl_transparent_wrapper_for_atomic!(AtomicUsize [usize], AtomicIsize [isize]);
576+
unsafe_impl_transparent_wrapper_for_atomic!(T => AtomicPtr<T> [*mut T]);
577+
}
578+
}
486579
}
487580

488581
safety_comment! {

src/lib.rs

+4-10
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

0 commit comments

Comments
 (0)