diff --git a/CHANGELOG.md b/CHANGELOG.md index 4528c2f2..ddcbd529 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- `Wrapper` trait added for creating wrapper structs with a structurally pinned value. + ### Changed - `InPlaceInit` now only exists when the `alloc` or `std` features are enabled diff --git a/Cargo.toml b/Cargo.toml index 857d0738..619adac6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ pin-init-internal = { path = "./internal", version = "=0.0.5" } default = ["std", "alloc"] std = [] alloc = [] +unsafe-pinned = [] [build-dependencies] rustc_version = "0.4" @@ -34,7 +35,11 @@ prettyplease = { version = "0.2", features = ["verbatim"] } [lints.rust] non_ascii_idents = "deny" -unexpected_cfgs = { level = "warn", check-cfg = ['cfg(NO_UI_TESTS)', 'cfg(NO_ALLOC_FAIL_TESTS)', 'cfg(kernel)'] } +unexpected_cfgs = { level = "warn", check-cfg = [ + 'cfg(NO_UI_TESTS)', + 'cfg(NO_ALLOC_FAIL_TESTS)', + 'cfg(kernel)', +] } unsafe_op_in_unsafe_fn = "deny" unused_attributes = "deny" warnings = "deny" diff --git a/README.md b/README.md index 24d0f0a3..2d0cda96 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,12 @@ However, using the crate on stable compilers is possible by disabling `alloc`. I will require the `std` feature, because stable compilers have neither `Box` nor `Arc` in no-std mode. +### Nightly needed for `unsafe-pinned` feature + +This feature enables the `Wrapper` implementation on the unstable `core::pin::UnsafePinned` type. +This requires the [`unsafe_pinned` unstable feature](https://github.com/rust-lang/rust/issues/125735) +and therefore a nightly compiler. Note that this feature is not enabled by default. + ## Overview To initialize a `struct` with an in-place constructor you will need two things: @@ -216,7 +222,7 @@ the `kernel` crate. The [`sync`] module is a good starting point. [`sync`]: https://rust.docs.kernel.org/kernel/sync/index.html [pinning]: https://doc.rust-lang.org/std/pin/index.html -[structurally pinned fields]: https://doc.rust-lang.org/std/pin/index.html#pinning-is-structural-for-field +[structurally pinned fields]: https://doc.rust-lang.org/std/pin/index.html#projections-and-structural-pinning [stack]: https://docs.rs/pin-init/latest/pin_init/macro.stack_pin_init.html [`impl PinInit`]: https://docs.rs/pin-init/latest/pin_init/trait.PinInit.html [`impl PinInit`]: https://docs.rs/pin-init/latest/pin_init/trait.PinInit.html diff --git a/build.rs b/build.rs index 68257fa3..cd6a9eb5 100644 --- a/build.rs +++ b/build.rs @@ -3,6 +3,7 @@ use rustc_version::{version, Version}; fn main() { println!("cargo::rustc-check-cfg=cfg(RUSTC_LINT_REASONS_IS_STABLE)"); println!("cargo::rustc-check-cfg=cfg(RUSTC_NEW_UNINIT_IS_STABLE)"); + println!("cargo::rustc-check-cfg=cfg(CONFIG_RUSTC_HAS_UNSAFE_PINNED)"); if version().unwrap() >= Version::parse("1.81.0").unwrap() || version().unwrap() >= Version::parse("1.81.0-nightly").unwrap() { @@ -11,4 +12,7 @@ fn main() { if version().unwrap() >= Version::parse("1.82.0").unwrap() { println!("cargo:rustc-cfg=RUSTC_NEW_UNINIT_IS_STABLE"); } + if version().unwrap() >= Version::parse("1.88.0-nightly").unwrap() { + println!("cargo:rustc-cfg=CONFIG_RUSTC_HAS_UNSAFE_PINNED"); + } } diff --git a/src/lib.rs b/src/lib.rs index a880c21d..774f8ca0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,12 @@ //! will require the `std` feature, because stable compilers have neither `Box` nor `Arc` in no-std //! mode. //! +//! ## Nightly needed for `unsafe-pinned` feature +//! +//! This feature enables the `Wrapper` implementation on the unstable `core::pin::UnsafePinned` type. +//! This requires the [`unsafe_pinned` unstable feature](https://github.com/rust-lang/rust/issues/125735) +//! and therefore a nightly compiler. Note that this feature is not enabled by default. +//! //! # Overview //! //! To initialize a `struct` with an in-place constructor you will need two things: @@ -241,7 +247,7 @@ //! [`sync`]: https://rust.docs.kernel.org/kernel/sync/index.html //! [pinning]: https://doc.rust-lang.org/std/pin/index.html //! [structurally pinned fields]: -//! https://doc.rust-lang.org/std/pin/index.html#pinning-is-structural-for-field +//! https://doc.rust-lang.org/std/pin/index.html#projections-and-structural-pinning //! [stack]: crate::stack_pin_init #![cfg_attr( kernel, @@ -269,6 +275,10 @@ #![forbid(missing_docs, unsafe_op_in_unsafe_fn)] #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(feature = "alloc", feature(allocator_api))] +#![cfg_attr( + all(feature = "unsafe-pinned", CONFIG_RUSTC_HAS_UNSAFE_PINNED), + feature(unsafe_pinned) +)] use core::{ cell::UnsafeCell, @@ -1513,3 +1523,55 @@ macro_rules! impl_tuple_zeroable { } impl_tuple_zeroable!(A, B, C, D, E, F, G, H, I, J); + +/// This trait allows creating an instance of `Self` which contains exactly one +/// [structurally pinned value](https://doc.rust-lang.org/std/pin/index.html#projections-and-structural-pinning). +/// +/// This is useful when using wrapper `struct`s like [`UnsafeCell`] or with new-type `struct`s. +/// +/// # Examples +/// +/// ``` +/// # use core::cell::UnsafeCell; +/// # use pin_init::{pin_data, pin_init, Wrapper}; +/// +/// #[pin_data] +/// struct Foo {} +/// +/// #[pin_data] +/// struct Bar { +/// #[pin] +/// content: UnsafeCell +/// }; +/// +/// let foo_initializer = pin_init!(Foo{}); +/// let initializer = pin_init!(Bar { +/// content <- UnsafeCell::pin_init(foo_initializer) +/// }); +/// ``` +pub trait Wrapper { + /// Create an pin-initializer for a [`Self`] containing `T` form the `value_init` initializer. + fn pin_init(value_init: impl PinInit) -> impl PinInit; +} + +impl Wrapper for UnsafeCell { + fn pin_init(value_init: impl PinInit) -> impl PinInit { + // SAFETY: `UnsafeCell` has a compatible layout to `T`. + unsafe { cast_pin_init(value_init) } + } +} + +impl Wrapper for MaybeUninit { + fn pin_init(value_init: impl PinInit) -> impl PinInit { + // SAFETY: `MaybeUninit` has a compatible layout to `T`. + unsafe { cast_pin_init(value_init) } + } +} + +#[cfg(all(feature = "unsafe-pinned", CONFIG_RUSTC_HAS_UNSAFE_PINNED))] +impl Wrapper for core::pin::UnsafePinned { + fn pin_init(init: impl PinInit) -> impl PinInit { + // SAFETY: `UnsafePinned` has a compatible layout to `T`. + unsafe { cast_pin_init(init) } + } +}