From 3e9c6b59fd9a57fdd6da7bf1dd7e7f4c89553a64 Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Sat, 23 Aug 2025 23:21:02 +0200 Subject: [PATCH 1/2] rename `project` -> `project_this` in doctest The next commit makes the `#[pin_data]` attribute generate a `project` function that would collide with any existing ones. Signed-off-by: Benno Lossin --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 62e013a..2d0d9fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -994,7 +994,7 @@ macro_rules! try_init { /// } /// /// impl Foo { -/// fn project(self: Pin<&mut Self>) -> Pin<&mut T> { +/// fn project_this(self: Pin<&mut Self>) -> Pin<&mut T> { /// assert_pinned!(Foo, elem, T, inline); /// /// // SAFETY: The field is structurally pinned. From b502754aab0342323d39ecd8551956db50f0f8da Mon Sep 17 00:00:00 2001 From: Benno Lossin Date: Wed, 23 Jul 2025 21:57:49 +0200 Subject: [PATCH 2/2] add pin projections to `#[pin_data]` Make the `#[pin_data]` macro generate a `*Projection` struct that holds either `Pin<&mut Field>` or `&mut Field` for every field of the original struct. Which version is chosen depends on weather there is a `#[pin]` or not respectively. Access to this projected version is enabled through generating `fn project(self: Pin<&mut Self>) -> SelfProjection<'_>`. Signed-off-by: Benno Lossin --- CHANGELOG.md | 4 ++ src/macros.rs | 60 +++++++++++++++++++++ tests/ui/compile-fail/pin_data/twice.stderr | 33 ++++++++++++ tests/ui/expand/many_generics.expanded.rs | 37 +++++++++++++ tests/ui/expand/pin-data.expanded.rs | 25 +++++++++ tests/ui/expand/pinned_drop.expanded.rs | 25 +++++++++ 6 files changed, 184 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e3ceb9..135e5bf 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] +### Changed + +- `#[pin_data]` now generates a `*Projection` struct similar to the `pin-project` crate. + ## [0.0.10] - 2025-08-19 ### Added diff --git a/src/macros.rs b/src/macros.rs index 9ced630..d225cc1 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -831,6 +831,17 @@ macro_rules! __pin_data { $($fields)* } + $crate::__pin_data!(make_pin_projections: + @vis($vis), + @name($name), + @impl_generics($($impl_generics)*), + @ty_generics($($ty_generics)*), + @decl_generics($($decl_generics)*), + @where($($whr)*), + @pinned($($pinned)*), + @not_pinned($($not_pinned)*), + ); + // We put the rest into this const item, because it then will not be accessible to anything // outside. const _: () = { @@ -980,6 +991,55 @@ macro_rules! __pin_data { stringify!($($rest)*), ); }; + (make_pin_projections: + @vis($vis:vis), + @name($name:ident), + @impl_generics($($impl_generics:tt)*), + @ty_generics($($ty_generics:tt)*), + @decl_generics($($decl_generics:tt)*), + @where($($whr:tt)*), + @pinned($($(#[$($p_attr:tt)*])* $pvis:vis $p_field:ident : $p_type:ty),* $(,)?), + @not_pinned($($(#[$($attr:tt)*])* $fvis:vis $field:ident : $type:ty),* $(,)?), + ) => { + $crate::macros::paste! { + #[doc(hidden)] + $vis struct [< $name Projection >] <'__pin, $($decl_generics)*> { + $($(#[$($p_attr)*])* $pvis $p_field : ::core::pin::Pin<&'__pin mut $p_type>,)* + $($(#[$($attr)*])* $fvis $field : &'__pin mut $type,)* + ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>, + } + + impl<$($impl_generics)*> $name<$($ty_generics)*> + where $($whr)* + { + /// Pin-projects all fields of `Self`. + /// + /// These fields are structurally pinned: + $(#[doc = ::core::concat!(" - `", ::core::stringify!($p_field), "`")])* + /// + /// These fields are **not** structurally pinned: + $(#[doc = ::core::concat!(" - `", ::core::stringify!($field), "`")])* + $vis fn project<'__pin>( + self: ::core::pin::Pin<&'__pin mut Self>, + ) -> [< $name Projection >] <'__pin, $($ty_generics)*> { + // SAFETY: we only give access to `&mut` for fields not structurally pinned. + let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; + [< $name Projection >] { + $( + // SAFETY: `$p_field` is structurally pinned. + $(#[$($p_attr)*])* + $p_field : unsafe { ::core::pin::Pin::new_unchecked(&mut this.$p_field) }, + )* + $( + $(#[$($attr)*])* + $field : &mut this.$field, + )* + ___pin_phantom_data: ::core::marker::PhantomData, + } + } + } + } + }; (make_pin_data: @pin_data($pin_data:ident), @impl_generics($($impl_generics:tt)*), diff --git a/tests/ui/compile-fail/pin_data/twice.stderr b/tests/ui/compile-fail/pin_data/twice.stderr index 2a89d95..c09a6ce 100644 --- a/tests/ui/compile-fail/pin_data/twice.stderr +++ b/tests/ui/compile-fail/pin_data/twice.stderr @@ -1,3 +1,15 @@ +error[E0428]: the name `FooProjection` is defined multiple times + --> tests/ui/compile-fail/pin_data/twice.rs:3:1 + | +3 | #[pin_data] + | ^^^^^^^^^^^ + | | + | `FooProjection` redefined here + | previous definition of the type `FooProjection` here + | + = note: `FooProjection` must be defined only once in the type namespace of this module + = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0119]: conflicting implementations of trait `HasPinData` for type `Foo` --> tests/ui/compile-fail/pin_data/twice.rs:3:1 | @@ -19,3 +31,24 @@ error[E0119]: conflicting implementations of trait `Unpin` for type `Foo` | conflicting implementation for `Foo` | = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0592]: duplicate definitions with name `project` + --> tests/ui/compile-fail/pin_data/twice.rs:3:1 + | +3 | #[pin_data] + | ^^^^^^^^^^^ + | | + | duplicate definitions for `project` + | other definition for `project` + | + = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0308]: mismatched types + --> tests/ui/compile-fail/pin_data/twice.rs:3:1 + | +3 | #[pin_data] + | ^^^^^^^^^^^ expected `&mut usize`, found `Pin<&mut usize>` + | + = note: expected mutable reference `&mut usize` + found struct `Pin<&mut usize>` + = note: this error originates in the macro `$crate::__pin_data` which comes from the expansion of the attribute macro `pin_data` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/expand/many_generics.expanded.rs b/tests/ui/expand/many_generics.expanded.rs index f22ef12..2c46952 100644 --- a/tests/ui/expand/many_generics.expanded.rs +++ b/tests/ui/expand/many_generics.expanded.rs @@ -12,6 +12,43 @@ where r: &'b mut [&'a mut T; SIZE], _pin: PhantomPinned, } +#[doc(hidden)] +struct FooProjection< + '__pin, + 'a, + 'b: 'a, + T: Bar<'b> + ?Sized + 'a, + const SIZE: usize = 0, +> { + _pin: ::core::pin::Pin<&'__pin mut PhantomPinned>, + array: &'__pin mut [u8; 1024 * 1024], + r: &'__pin mut &'b mut [&'a mut T; SIZE], + ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>, +} +impl<'a, 'b: 'a, T: Bar<'b> + ?Sized + 'a, const SIZE: usize> Foo<'a, 'b, T, SIZE> +where + T: Bar<'a, 1>, +{ + /// Pin-projects all fields of `Self`. + /// + /// These fields are structurally pinned: + /// - `_pin` + /// + /// These fields are **not** structurally pinned: + /// - `array` + /// - `r` + fn project<'__pin>( + self: ::core::pin::Pin<&'__pin mut Self>, + ) -> FooProjection<'__pin, 'a, 'b, T, SIZE> { + let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; + FooProjection { + _pin: unsafe { ::core::pin::Pin::new_unchecked(&mut this._pin) }, + array: &mut this.array, + r: &mut this.r, + ___pin_phantom_data: ::core::marker::PhantomData, + } + } +} const _: () = { struct __ThePinData<'a, 'b: 'a, T: Bar<'b> + ?Sized + 'a, const SIZE: usize> where diff --git a/tests/ui/expand/pin-data.expanded.rs b/tests/ui/expand/pin-data.expanded.rs index 58b8f53..f47194c 100644 --- a/tests/ui/expand/pin-data.expanded.rs +++ b/tests/ui/expand/pin-data.expanded.rs @@ -4,6 +4,31 @@ struct Foo { array: [u8; 1024 * 1024], _pin: PhantomPinned, } +#[doc(hidden)] +struct FooProjection<'__pin> { + _pin: ::core::pin::Pin<&'__pin mut PhantomPinned>, + array: &'__pin mut [u8; 1024 * 1024], + ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>, +} +impl Foo { + /// Pin-projects all fields of `Self`. + /// + /// These fields are structurally pinned: + /// - `_pin` + /// + /// These fields are **not** structurally pinned: + /// - `array` + fn project<'__pin>( + self: ::core::pin::Pin<&'__pin mut Self>, + ) -> FooProjection<'__pin> { + let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; + FooProjection { + _pin: unsafe { ::core::pin::Pin::new_unchecked(&mut this._pin) }, + array: &mut this.array, + ___pin_phantom_data: ::core::marker::PhantomData, + } + } +} const _: () = { struct __ThePinData { __phantom: ::core::marker::PhantomData Foo>, diff --git a/tests/ui/expand/pinned_drop.expanded.rs b/tests/ui/expand/pinned_drop.expanded.rs index 107a885..a2c3a2f 100644 --- a/tests/ui/expand/pinned_drop.expanded.rs +++ b/tests/ui/expand/pinned_drop.expanded.rs @@ -4,6 +4,31 @@ struct Foo { array: [u8; 1024 * 1024], _pin: PhantomPinned, } +#[doc(hidden)] +struct FooProjection<'__pin> { + _pin: ::core::pin::Pin<&'__pin mut PhantomPinned>, + array: &'__pin mut [u8; 1024 * 1024], + ___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>, +} +impl Foo { + /// Pin-projects all fields of `Self`. + /// + /// These fields are structurally pinned: + /// - `_pin` + /// + /// These fields are **not** structurally pinned: + /// - `array` + fn project<'__pin>( + self: ::core::pin::Pin<&'__pin mut Self>, + ) -> FooProjection<'__pin> { + let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) }; + FooProjection { + _pin: unsafe { ::core::pin::Pin::new_unchecked(&mut this._pin) }, + array: &mut this.array, + ___pin_phantom_data: ::core::marker::PhantomData, + } + } +} const _: () = { struct __ThePinData { __phantom: ::core::marker::PhantomData Foo>,