Skip to content

Commit

Permalink
util: add portable_atomic_unstable_coerce_unsized cfg option (#195)
Browse files Browse the repository at this point in the history
Co-authored-by: Taiki Endo <[email protected]>
  • Loading branch information
brodycj and taiki-e authored Nov 22, 2024
1 parent 13e9580 commit d8e6bbf
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 15 deletions.
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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ unexpected_cfgs = { level = "warn", check-cfg = [
'cfg(portable_atomic_test_outline_atomics_detect_false,qemu,valgrind)',
# Public APIs, considered unstable unless documented in readme.
'cfg(portable_atomic_no_outline_atomics,portable_atomic_outline_atomics)',
# Public unstable API(s) - portable-atomic-util
'cfg(portable_atomic_unstable_coerce_unsized)',
] }
unreachable_pub = "warn"
# unsafe_op_in_unsafe_fn = "warn" # Set at crate-level instead since https://github.com/rust-lang/rust/pull/100081 is not available on MSRV
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 (and optionally support some unstable features).
//
// 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

0 comments on commit d8e6bbf

Please sign in to comment.