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

util: add portable_atomic_unstable_coerce_unsized cfg option #195

Merged
merged 6 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions .github/.cspell/project-dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ uart
umax
umin
unclonable
unsize
usart
uscat
uwrite
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,10 @@ jobs:
if: matrix.rust == 'nightly-2024-02-13'

- run: tools/test.sh -vv ${TARGET:-} ${DOCTEST_XCOMPILE:-} ${BUILD_STD:-} ${RELEASE:-}
- run: tools/test.sh -vv --tests ${TARGET:-} ${BUILD_STD:-} ${RELEASE:-}
env:
RUSTFLAGS: ${{ env.RUSTFLAGS }} --cfg portable_atomic_unstable_coerce_unsized
if: startsWith(matrix.rust, 'nightly')
# We test doctest only once with the default build conditions because doctest is slow. Both api-test
# and src/tests have extended copies of doctest, so this will not reduce test coverage.
# portable_atomic_no_outline_atomics only affects x86_64, AArch64, Arm, powerpc64, and RISC-V Linux.
Expand All @@ -368,7 +372,8 @@ jobs:
RUSTDOCFLAGS: ${{ env.RUSTDOCFLAGS }} --cfg portable_atomic_test_outline_atomics_detect_false
RUSTFLAGS: ${{ env.RUSTFLAGS }} --cfg portable_atomic_test_outline_atomics_detect_false
# powerpc64 is skipped because tested below.
if: (matrix.target == '' && !contains(matrix.rust, 'i686') || startsWith(matrix.target, 'x86_64')) || startsWith(matrix.target, 'aarch64') && !(contains(matrix.target, '-musl') && matrix.flags == '') || startsWith(matrix.target, 'armv5te') || matrix.target == 'arm-linux-androideabi'
# TODO: arm-linux-androideabi is removed due to an issue between Rust nightly & QEMU in CI testing - needs investigation
if: (matrix.target == '' && !contains(matrix.rust, 'i686') || startsWith(matrix.target, 'x86_64')) || startsWith(matrix.target, 'aarch64') && !(contains(matrix.target, '-musl') && matrix.flags == '') || startsWith(matrix.target, 'armv5te')
- run: tools/test.sh -vv --tests ${TARGET:-} ${BUILD_STD:-} ${RELEASE:-}
env:
QEMU_CPU: power7 # no quadword-atomics
Expand Down
2 changes: 2 additions & 0 deletions portable-atomic-util/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Note: In this file, do not use the hard wrap in the middle of a sentence for com

## [Unreleased]

- Add `portable_atomic_unstable_coerce_unsized` cfg option (requires Rust nightly)

## [0.2.3] - 2024-10-17

- Add `new_uninit`/`new_uninit_slice`/`assume_init` to `Arc` at Rust 1.36+. (align to the [std `Arc` change in Rust 1.82](https://github.com/rust-lang/rust/pull/129401)) ([362dc9a](https://github.com/taiki-e/portable-atomic/commit/362dc9af2779c81aa346e89c4d3f3eef71cf29ed))
Expand Down
24 changes: 24 additions & 0 deletions portable-atomic-util/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,30 @@ See [#1] for other primitives being considered for addition to this crate.
[portable-atomic]: https://github.com/taiki-e/portable-atomic
[#1]: https://github.com/taiki-e/portable-atomic/issues/1

## Optional cfg

One of the ways to enable cfg is to set [rustflags in the cargo config](https://doc.rust-lang.org/cargo/reference/config.html#targettriplerustflags):

```toml
# .cargo/config.toml
[target.<target>]
rustflags = ["--cfg", "portable_atomic_unstable_coerce_unsized"]
```

Or set environment variable:

```sh
RUSTFLAGS="--cfg portable_atomic_unstable_coerce_unsized" cargo ...
```

- <a name="portable-atomic-unstable-coerce-unsized"></a>**`--cfg portable_atomic_unstable_coerce_unsized`**<br> support standard coercing of `Arc<T>` to `Arc<U>`

<!-- TODO: add standard coercing of `Weak<T>` to `Weak<U>` as well, with testing & documentation updates -->

This coercing requires Rust nightly to compile (with help from [unstable `CoerceUnsized` trait](https://doc.rust-lang.org/nightly/core/ops/trait.CoerceUnsized.html)).

See [this issue comment](https://github.com/taiki-e/portable-atomic/issues/143#issuecomment-1866488569) for another known workaround.

<!-- tidy:crate-doc:end -->

## License
Expand Down
25 changes: 18 additions & 7 deletions portable-atomic-util/src/arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

// This module is based on alloc::sync::Arc.
//
// The code has been adjusted to work with stable Rust.
// The code has been adjusted to work with stable Rust (with unstable cfg option).
brody4hire marked this conversation as resolved.
Show resolved Hide resolved
//
// Source: https://github.com/rust-lang/rust/blob/a0c2aba29aa9ea50a7c45c3391dd446f856bef7b/library/alloc/src/sync.rs.
//
Expand Down Expand Up @@ -39,6 +39,8 @@ use core::{
ptr::{self, NonNull},
usize,
};
#[cfg(portable_atomic_unstable_coerce_unsized)]
use core::{marker::Unsize, ops::CoerceUnsized};

/// A soft limit on the amount of references that may be made to an `Arc`.
///
Expand Down Expand Up @@ -77,11 +79,13 @@ macro_rules! acquire {
/// This is an equivalent to [`std::sync::Arc`], but using [portable-atomic] for synchronization.
/// See the documentation for [`std::sync::Arc`] for more details.
///
/// **Note:** Unlike `std::sync::Arc`, coercing `Arc<T>` to `Arc<U>` is not supported at all.
/// **Note:** Unlike `std::sync::Arc`, coercing `Arc<T>` to `Arc<U>` is only possible if
/// the optional cfg `portable_atomic_unstable_coerce_unsized` is enabled, as documented at the crate-level documentation,
/// and this optional cfg item is only supported with Rust nightly version.
/// This is because coercing the pointee requires the
/// [unstable `CoerceUnsized` trait](https://doc.rust-lang.org/nightly/core/ops/trait.CoerceUnsized.html).
/// See [this issue comment](https://github.com/taiki-e/portable-atomic/issues/143#issuecomment-1866488569)
/// for the known workaround.
/// for a workaround that works without depending on unstable features.
///
/// [portable-atomic]: https://crates.io/crates/portable-atomic
///
Expand Down Expand Up @@ -115,6 +119,9 @@ impl<T: ?Sized + core::panic::RefUnwindSafe> core::panic::UnwindSafe for Arc<T>
#[cfg(all(portable_atomic_no_core_unwind_safe, feature = "std"))]
impl<T: ?Sized + std::panic::RefUnwindSafe> std::panic::UnwindSafe for Arc<T> {}

#[cfg(portable_atomic_unstable_coerce_unsized)]
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Arc<U>> for Arc<T> {}

impl<T: ?Sized> Arc<T> {
#[inline]
fn into_inner_non_null(this: Self) -> NonNull<ArcInner<T>> {
Expand Down Expand Up @@ -142,6 +149,10 @@ impl<T: ?Sized> Arc<T> {
/// This is an equivalent to [`std::sync::Weak`], but using [portable-atomic] for synchronization.
/// See the documentation for [`std::sync::Weak`] for more details.
///
/// <!-- TODO: support coercing `Weak<T>` to `Weak<U>` with testing, if optional cfg `portable_atomic_unstable_coerce_unsized` is enabled -->
/// **Note:** Unlike `std::sync::Weak`, coercing `Weak<T>` to `Weak<U>` is not possible, not even if
/// the optional cfg `portable_atomic_unstable_coerce_unsized` is enabled.
///
/// [`upgrade`]: Weak::upgrade
/// [portable-atomic]: https://crates.io/crates/portable-atomic
///
Expand Down Expand Up @@ -1508,11 +1519,11 @@ impl Arc<dyn Any + Send + Sync> {
/// }
///
/// let my_string = "Hello World".to_string();
// TODO: CoerceUnsized is needed to cast Arc<String> -> Arc<dyn Any + Send + Sync> directly.
// /// print_if_string(Arc::new(my_string));
// /// print_if_string(Arc::new(0i8));
/// print_if_string(Arc::from(Box::new(my_string) as Box<dyn Any + Send + Sync>));
/// print_if_string(Arc::from(Box::new(0i8) as Box<dyn Any + Send + Sync>));
/// // or with "--cfg portable_atomic_unstable_coerce_unsized" in RUSTFLAGS (requires Rust nightly):
/// // print_if_string(Arc::new(my_string));
/// // print_if_string(Arc::new(0i8));
/// ```
#[inline]
pub fn downcast<T>(self) -> Result<Arc<T>, Self>
Expand Down Expand Up @@ -2234,7 +2245,7 @@ impl<T> Default for Arc<[T]> {
#[inline]
fn default() -> Self {
// TODO: we cannot use non-allocation optimization (https://github.com/rust-lang/rust/blob/1.80.0/library/alloc/src/sync.rs#L3449)
// for now due to casting Arc<[T; N]> -> Arc<[T]> requires unstable CoerceUnsized.
// for now since casting Arc<[T; N]> -> Arc<[T]> requires unstable CoerceUnsized.
let arr: [T; 0] = [];
Arc::from(arr)
}
Expand Down
26 changes: 26 additions & 0 deletions portable-atomic-util/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,30 @@ See [#1] for other primitives being considered for addition to this crate.
[portable-atomic]: https://github.com/taiki-e/portable-atomic
[#1]: https://github.com/taiki-e/portable-atomic/issues/1

## Optional cfg

One of the ways to enable cfg is to set [rustflags in the cargo config](https://doc.rust-lang.org/cargo/reference/config.html#targettriplerustflags):

```toml
# .cargo/config.toml
[target.<target>]
rustflags = ["--cfg", "portable_atomic_unstable_coerce_unsized"]
```

Or set environment variable:

```sh
RUSTFLAGS="--cfg portable_atomic_unstable_coerce_unsized" cargo ...
```

- <a name="portable-atomic-unstable-coerce-unsized"></a>**`--cfg portable_atomic_unstable_coerce_unsized`**<br> support standard coercing of `Arc<T>` to `Arc<U>`

<!-- TODO: add standard coercing of `Weak<T>` to `Weak<U>` as well, with testing & documentation updates -->

This coercing requires Rust nightly to compile (with help from [unstable `CoerceUnsized` trait](https://doc.rust-lang.org/nightly/core/ops/trait.CoerceUnsized.html)).

See [this issue comment](https://github.com/taiki-e/portable-atomic/issues/143#issuecomment-1866488569) for another known workaround.

<!-- tidy:crate-doc:end -->
*/

Expand Down Expand Up @@ -60,6 +84,8 @@ See [#1] for other primitives being considered for addition to this crate.
#![allow(clippy::inline_always)]
// docs.rs only (cfg is enabled by docs.rs, not build script)
#![cfg_attr(docsrs, feature(doc_cfg))]
// Enable custom unsized coercions if the user explicitly opts-in to unstable cfg
#![cfg_attr(portable_atomic_unstable_coerce_unsized, feature(coerce_unsized, unsize))]

#[cfg(all(feature = "alloc", not(portable_atomic_no_alloc)))]
extern crate alloc;
Expand Down
25 changes: 18 additions & 7 deletions portable-atomic-util/tests/arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,10 @@ mod alloc_tests {
assert_eq!(unsafe { &*ptr }, "foo");
assert_eq!(arc, arc2);

// TODO: CoerceUnsized is needed to cast to Arc<dyn ..>
// let arc: Arc<dyn Display> = Arc::new(123);
#[cfg(portable_atomic_unstable_coerce_unsized)]
let arc: Arc<dyn Display> = Arc::new(123);
// TODO: This is a workaround in case CoerceUnsized is not available - remove this once it is no longer needed
#[cfg(not(portable_atomic_unstable_coerce_unsized))]
let arc: Arc<dyn Display> = Arc::from(Box::new(123) as Box<dyn Display>);

let ptr = Arc::into_raw(arc.clone());
Expand Down Expand Up @@ -301,6 +303,7 @@ mod alloc_tests {
// assert!(weak.ptr_eq(&weak2));

// // TODO: CoerceUnsized is needed to cast to Arc<dyn ..>
// // (may be possible to support this with portable_atomic_unstable_coerce_unsized cfg option)
// // let arc: Arc<dyn Display> = Arc::new(123);
// let arc: Arc<dyn Display> = Arc::from(Box::new(123) as Box<dyn Display>);
// let weak: Weak<dyn Display> = Arc::downgrade(&arc);
Expand Down Expand Up @@ -477,8 +480,10 @@ mod alloc_tests {

#[test]
fn test_unsized() {
// TODO: CoerceUnsized is needed to cast to Arc<[..]>
// let x: Arc<[i32]> = Arc::new([1, 2, 3]);
#[cfg(portable_atomic_unstable_coerce_unsized)]
let x: Arc<[i32]> = Arc::new([1, 2, 3]);
// TODO: This is a workaround in case CoerceUnsized is not available - remove this once it is no longer needed
#[cfg(not(portable_atomic_unstable_coerce_unsized))]
let x: Arc<[i32]> = Arc::from(Box::new([1, 2, 3]) as Box<[i32]>);
assert_eq!(format!("{:?}", x), "[1, 2, 3]");
let y = Arc::downgrade(&x.clone());
Expand Down Expand Up @@ -652,11 +657,17 @@ mod alloc_tests {
fn test_downcast() {
use std::any::Any;

// TODO: CoerceUnsized is needed to cast to Arc<dyn ..>
// let r1: Arc<dyn Any + Send + Sync> = Arc::new(i32::MAX);
// let r2: Arc<dyn Any + Send + Sync> = Arc::new("abc");
#[cfg(portable_atomic_unstable_coerce_unsized)]
let r1: Arc<dyn Any + Send + Sync> = Arc::new(i32::MAX);
// TODO: This is a workaround in case CoerceUnsized is not available - remove this once it is no longer needed
#[cfg(not(portable_atomic_unstable_coerce_unsized))]
let r1: Arc<dyn Any + Send + Sync> =
Arc::from(Box::new(i32::MAX) as Box<dyn Any + Send + Sync>);

#[cfg(portable_atomic_unstable_coerce_unsized)]
let r2: Arc<dyn Any + Send + Sync> = Arc::new("abc");
// TODO: This is a workaround in case CoerceUnsized is not available - remove this once it is no longer needed
#[cfg(not(portable_atomic_unstable_coerce_unsized))]
let r2: Arc<dyn Any + Send + Sync> =
Arc::from(Box::new("abc") as Box<dyn Any + Send + Sync>);

Expand Down
Loading