Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -994,7 +994,7 @@ macro_rules! try_init {
/// }
///
/// impl<T> Foo<T> {
/// fn project(self: Pin<&mut Self>) -> Pin<&mut T> {
/// fn project_this(self: Pin<&mut Self>) -> Pin<&mut T> {
/// assert_pinned!(Foo<T>, elem, T, inline);
///
/// // SAFETY: The field is structurally pinned.
Expand Down
60 changes: 60 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 _: () = {
Expand Down Expand Up @@ -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)*),
Expand Down
33 changes: 33 additions & 0 deletions tests/ui/compile-fail/pin_data/twice.stderr
Original file line number Diff line number Diff line change
@@ -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
|
Expand All @@ -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)
37 changes: 37 additions & 0 deletions tests/ui/expand/many_generics.expanded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 25 additions & 0 deletions tests/ui/expand/pin-data.expanded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<fn(Foo) -> Foo>,
Expand Down
25 changes: 25 additions & 0 deletions tests/ui/expand/pinned_drop.expanded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<fn(Foo) -> Foo>,
Expand Down
Loading