Skip to content

Commit 50f3cb6

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 <joerichey@google.com>
1 parent e29fb6a commit 50f3cb6

34 files changed

+280
-139
lines changed

.github/workflows/ci.yml

+52-2
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,8 +155,17 @@ 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"
162+
# Exclude `thumbv6m-none-eabi` combined with any feature that implies
163+
# the `std` feature since `thumbv6m-none-eabi` does not include a
164+
# pre-compiled std.
165+
- target: "thumbv6m-none-eabi"
166+
features: "--features __internal_use_only_features_that_work_on_stable"
167+
- target: "thumbv6m-none-eabi"
168+
features: "--all-features"
152169
# Exclude most targets during PR development, but allow them in the
153170
# merge queue. This speeds up our development flow, while still
154171
# ensuring that errors on these targets are caught before a PR is
@@ -165,6 +182,9 @@ jobs:
165182
event_name: "pull_request"
166183
- target: "s390x-unknown-linux-gnu"
167184
event_name: "pull_request"
185+
# TODO(#1586): Uncomment this before merging.
186+
# - target: "thumbv6m-none-eabi"
187+
# event_name: "pull_request"
168188
- target: "wasm32-wasi"
169189
event_name: "pull_request"
170190

@@ -176,6 +196,19 @@ jobs:
176196
- name: Populate cache
177197
uses: ./.github/actions/cache
178198

199+
# Ensure that Cargo resolves the minimum possible syn version so that if we
200+
# accidentally make a change which depends upon features added in more
201+
# recent versions of syn, we'll catch it in CI.
202+
#
203+
# TODO(#1595): Debug why this step is still necessary after #1564 and maybe
204+
# remove it.
205+
- name: Pin syn dependency
206+
run: |
207+
set -eo pipefail
208+
# Override the exising `syn` dependency with one which requires an exact
209+
# version.
210+
cargo add -p zerocopy-derive 'syn@=2.0.46'
211+
179212
- name: Configure environment variables
180213
run: |
181214
set -eo pipefail
@@ -240,11 +273,19 @@ jobs:
240273
with:
241274
key: "${{ matrix.target }}"
242275

276+
# On the `thumbv6m-none-eabi` target, we can't run `cargo check --tests` due
277+
# to the `memchr` crate, so we just do `cargo check` instead.
278+
- name: Check
279+
run: ./cargo.sh +${{ matrix.toolchain }} check --package ${{ matrix.crate }} --target ${{ matrix.target }} ${{ matrix.features }} --verbose
280+
if: matrix.target == 'thumbv6m-none-eabi'
281+
243282
- name: Check tests
244283
run: ./cargo.sh +${{ matrix.toolchain }} check --tests --package ${{ matrix.crate }} --target ${{ matrix.target }} ${{ matrix.features }} --verbose
284+
if: matrix.target != 'thumbv6m-none-eabi'
245285

246286
- name: Build
247287
run: ./cargo.sh +${{ matrix.toolchain }} build --package ${{ matrix.crate }} --target ${{ matrix.target }} ${{ matrix.features }} --verbose
288+
if: matrix.target != 'thumbv6m-none-eabi'
248289

249290
# When building tests for the i686 target, we need certain libraries which
250291
# are not installed by default; `gcc-multilib` includes these libraries.
@@ -359,13 +400,19 @@ jobs:
359400
matrix.target != 'wasm32-wasi' &&
360401
github.event_name != 'pull_request'
361402
362-
- name: Clippy check
403+
# On the `thumbv6m-none-eabi` target, we can't run `cargo clippy --tests`
404+
# due to the `memchr` crate, so we just do `cargo clippy` instead.
405+
- name: Clippy
406+
run: ./cargo.sh +${{ matrix.toolchain }} clippy --package ${{ matrix.crate }} --target ${{ matrix.target }} ${{ matrix.features }} --verbose
407+
if: matrix.toolchain == 'nightly' && matrix.target == 'thumbv6m-none-eabi'
408+
409+
- name: Clippy tests
363410
run: ./cargo.sh +${{ matrix.toolchain }} clippy --package ${{ matrix.crate }} --target ${{ matrix.target }} ${{ matrix.features }} --tests --verbose
364411
# Clippy improves the accuracy of lints over time, and fixes bugs. Only
365412
# running Clippy on nightly allows us to avoid having to write code which
366413
# is compatible with older versions of Clippy, which sometimes requires
367414
# hacks to work around limitations that are fixed in more recent versions.
368-
if: matrix.toolchain == 'nightly'
415+
if: matrix.toolchain == 'nightly' && matrix.target != 'thumbv6m-none-eabi'
369416

370417
- name: Cargo doc
371418
# We pass --document-private-items and --document-hidden items to ensure that
@@ -544,6 +591,9 @@ jobs:
544591
# See comment on "Pin syn dependency" job for why we do this. It needs
545592
# to happen before the subsequent `cargo check`, so we don't
546593
# background it.
594+
#
595+
# TODO(#1595): Debug why this step is still necessary after #1564 and
596+
# maybe remove it.
547597
cargo add -p zerocopy-derive 'syn@=2.0.46' &> /dev/null
548598
549599
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

+142-40
Original file line numberDiff line numberDiff line change
@@ -440,49 +440,151 @@ 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(all(
444+
zerocopy_target_has_atomics,
445+
any(
446+
target_has_atomic = "8",
447+
target_has_atomic = "16",
448+
target_has_atomic = "32",
449+
target_has_atomic = "64",
450+
target_has_atomic = "ptr"
451+
)
452+
))]
453+
mod atomics {
454+
use super::*;
453455

454-
#[rustfmt::skip]
455-
impl_traits_for_atomics!(
456-
AtomicI16, AtomicI32, AtomicI8, AtomicIsize,
457-
AtomicU16, AtomicU32, AtomicU8, AtomicUsize,
458-
);
456+
macro_rules! impl_traits_for_atomics {
457+
($($atomics:ident),* $(,)?) => {
458+
$(
459+
impl_known_layout!($atomics);
460+
impl_for_transparent_wrapper!(=> TryFromBytes for $atomics);
461+
impl_for_transparent_wrapper!(=> FromZeros for $atomics);
462+
impl_for_transparent_wrapper!(=> FromBytes for $atomics);
463+
impl_for_transparent_wrapper!(=> IntoBytes for $atomics);
464+
)*
465+
};
466+
}
459467

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

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

488590
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)