Skip to content

Commit 2d69836

Browse files
committed
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 <[email protected]>
1 parent 67fc903 commit 2d69836

File tree

6 files changed

+184
-0
lines changed

6 files changed

+184
-0
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Changed
11+
12+
- `#[pin_data]` now generates a `*Projection` struct similar to the `pin-project` crate.
13+
1014
## [0.0.10] - 2025-08-19
1115

1216
### Added

src/macros.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,17 @@ macro_rules! __pin_data {
831831
$($fields)*
832832
}
833833

834+
$crate::__pin_data!(make_pin_projections:
835+
@vis($vis),
836+
@name($name),
837+
@impl_generics($($impl_generics)*),
838+
@ty_generics($($ty_generics)*),
839+
@decl_generics($($decl_generics)*),
840+
@where($($whr)*),
841+
@pinned($($pinned)*),
842+
@not_pinned($($not_pinned)*),
843+
);
844+
834845
// We put the rest into this const item, because it then will not be accessible to anything
835846
// outside.
836847
const _: () = {
@@ -980,6 +991,55 @@ macro_rules! __pin_data {
980991
stringify!($($rest)*),
981992
);
982993
};
994+
(make_pin_projections:
995+
@vis($vis:vis),
996+
@name($name:ident),
997+
@impl_generics($($impl_generics:tt)*),
998+
@ty_generics($($ty_generics:tt)*),
999+
@decl_generics($($decl_generics:tt)*),
1000+
@where($($whr:tt)*),
1001+
@pinned($($(#[$($p_attr:tt)*])* $pvis:vis $p_field:ident : $p_type:ty),* $(,)?),
1002+
@not_pinned($($(#[$($attr:tt)*])* $fvis:vis $field:ident : $type:ty),* $(,)?),
1003+
) => {
1004+
$crate::macros::paste! {
1005+
#[doc(hidden)]
1006+
$vis struct [< $name Projection >] <'__pin, $($decl_generics)*> {
1007+
$($(#[$($p_attr)*])* $pvis $p_field : ::core::pin::Pin<&'__pin mut $p_type>,)*
1008+
$($(#[$($attr)*])* $fvis $field : &'__pin mut $type,)*
1009+
___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>,
1010+
}
1011+
1012+
impl<$($impl_generics)*> $name<$($ty_generics)*>
1013+
where $($whr)*
1014+
{
1015+
/// Pin-projects all fields of `Self`.
1016+
///
1017+
/// These fields are structurally pinned:
1018+
$(#[doc = ::core::concat!(" - `", ::core::stringify!($p_field), "`")])*
1019+
///
1020+
/// These fields are **not** structurally pinned:
1021+
$(#[doc = ::core::concat!(" - `", ::core::stringify!($field), "`")])*
1022+
$vis fn project<'__pin>(
1023+
self: ::core::pin::Pin<&'__pin mut Self>,
1024+
) -> [< $name Projection >] <'__pin, $($ty_generics)*> {
1025+
// SAFETY: we only give access to `&mut` for fields not structurally pinned.
1026+
let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) };
1027+
[< $name Projection >] {
1028+
$(
1029+
// SAFETY: `$p_field` is structurally pinned.
1030+
$(#[$($p_attr)*])*
1031+
$p_field : unsafe { ::core::pin::Pin::new_unchecked(&mut this.$p_field) },
1032+
)*
1033+
$(
1034+
$(#[$($attr)*])*
1035+
$field : &mut this.$field,
1036+
)*
1037+
___pin_phantom_data: ::core::marker::PhantomData,
1038+
}
1039+
}
1040+
}
1041+
}
1042+
};
9831043
(make_pin_data:
9841044
@pin_data($pin_data:ident),
9851045
@impl_generics($($impl_generics:tt)*),

tests/ui/compile-fail/pin_data/twice.stderr

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
error[E0428]: the name `FooProjection` is defined multiple times
2+
--> tests/ui/compile-fail/pin_data/twice.rs:3:1
3+
|
4+
3 | #[pin_data]
5+
| ^^^^^^^^^^^
6+
| |
7+
| `FooProjection` redefined here
8+
| previous definition of the type `FooProjection` here
9+
|
10+
= note: `FooProjection` must be defined only once in the type namespace of this module
11+
= 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)
12+
113
error[E0119]: conflicting implementations of trait `HasPinData` for type `Foo`
214
--> tests/ui/compile-fail/pin_data/twice.rs:3:1
315
|
@@ -19,3 +31,24 @@ error[E0119]: conflicting implementations of trait `Unpin` for type `Foo`
1931
| conflicting implementation for `Foo`
2032
|
2133
= 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)
34+
35+
error[E0592]: duplicate definitions with name `project`
36+
--> tests/ui/compile-fail/pin_data/twice.rs:3:1
37+
|
38+
3 | #[pin_data]
39+
| ^^^^^^^^^^^
40+
| |
41+
| duplicate definitions for `project`
42+
| other definition for `project`
43+
|
44+
= 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)
45+
46+
error[E0308]: mismatched types
47+
--> tests/ui/compile-fail/pin_data/twice.rs:3:1
48+
|
49+
3 | #[pin_data]
50+
| ^^^^^^^^^^^ expected `&mut usize`, found `Pin<&mut usize>`
51+
|
52+
= note: expected mutable reference `&mut usize`
53+
found struct `Pin<&mut usize>`
54+
= 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)

tests/ui/expand/many_generics.expanded.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,43 @@ where
1212
r: &'b mut [&'a mut T; SIZE],
1313
_pin: PhantomPinned,
1414
}
15+
#[doc(hidden)]
16+
struct FooProjection<
17+
'__pin,
18+
'a,
19+
'b: 'a,
20+
T: Bar<'b> + ?Sized + 'a,
21+
const SIZE: usize = 0,
22+
> {
23+
_pin: ::core::pin::Pin<&'__pin mut PhantomPinned>,
24+
array: &'__pin mut [u8; 1024 * 1024],
25+
r: &'__pin mut &'b mut [&'a mut T; SIZE],
26+
___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>,
27+
}
28+
impl<'a, 'b: 'a, T: Bar<'b> + ?Sized + 'a, const SIZE: usize> Foo<'a, 'b, T, SIZE>
29+
where
30+
T: Bar<'a, 1>,
31+
{
32+
/// Pin-projects all fields of `Self`.
33+
///
34+
/// These fields are structurally pinned:
35+
/// - `_pin`
36+
///
37+
/// These fields are **not** structurally pinned:
38+
/// - `array`
39+
/// - `r`
40+
fn project<'__pin>(
41+
self: ::core::pin::Pin<&'__pin mut Self>,
42+
) -> FooProjection<'__pin, 'a, 'b, T, SIZE> {
43+
let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) };
44+
FooProjection {
45+
_pin: unsafe { ::core::pin::Pin::new_unchecked(&mut this._pin) },
46+
array: &mut this.array,
47+
r: &mut this.r,
48+
___pin_phantom_data: ::core::marker::PhantomData,
49+
}
50+
}
51+
}
1552
const _: () = {
1653
struct __ThePinData<'a, 'b: 'a, T: Bar<'b> + ?Sized + 'a, const SIZE: usize>
1754
where

tests/ui/expand/pin-data.expanded.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,31 @@ struct Foo {
44
array: [u8; 1024 * 1024],
55
_pin: PhantomPinned,
66
}
7+
#[doc(hidden)]
8+
struct FooProjection<'__pin> {
9+
_pin: ::core::pin::Pin<&'__pin mut PhantomPinned>,
10+
array: &'__pin mut [u8; 1024 * 1024],
11+
___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>,
12+
}
13+
impl Foo {
14+
/// Pin-projects all fields of `Self`.
15+
///
16+
/// These fields are structurally pinned:
17+
/// - `_pin`
18+
///
19+
/// These fields are **not** structurally pinned:
20+
/// - `array`
21+
fn project<'__pin>(
22+
self: ::core::pin::Pin<&'__pin mut Self>,
23+
) -> FooProjection<'__pin> {
24+
let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) };
25+
FooProjection {
26+
_pin: unsafe { ::core::pin::Pin::new_unchecked(&mut this._pin) },
27+
array: &mut this.array,
28+
___pin_phantom_data: ::core::marker::PhantomData,
29+
}
30+
}
31+
}
732
const _: () = {
833
struct __ThePinData {
934
__phantom: ::core::marker::PhantomData<fn(Foo) -> Foo>,

tests/ui/expand/pinned_drop.expanded.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,31 @@ struct Foo {
44
array: [u8; 1024 * 1024],
55
_pin: PhantomPinned,
66
}
7+
#[doc(hidden)]
8+
struct FooProjection<'__pin> {
9+
_pin: ::core::pin::Pin<&'__pin mut PhantomPinned>,
10+
array: &'__pin mut [u8; 1024 * 1024],
11+
___pin_phantom_data: ::core::marker::PhantomData<&'__pin mut ()>,
12+
}
13+
impl Foo {
14+
/// Pin-projects all fields of `Self`.
15+
///
16+
/// These fields are structurally pinned:
17+
/// - `_pin`
18+
///
19+
/// These fields are **not** structurally pinned:
20+
/// - `array`
21+
fn project<'__pin>(
22+
self: ::core::pin::Pin<&'__pin mut Self>,
23+
) -> FooProjection<'__pin> {
24+
let this = unsafe { ::core::pin::Pin::get_unchecked_mut(self) };
25+
FooProjection {
26+
_pin: unsafe { ::core::pin::Pin::new_unchecked(&mut this._pin) },
27+
array: &mut this.array,
28+
___pin_phantom_data: ::core::marker::PhantomData,
29+
}
30+
}
31+
}
732
const _: () = {
833
struct __ThePinData {
934
__phantom: ::core::marker::PhantomData<fn(Foo) -> Foo>,

0 commit comments

Comments
 (0)