Skip to content

Commit

Permalink
Support custom Drop implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
taiki-e committed Jan 10, 2021
1 parent 05e9c0d commit 6158662
Show file tree
Hide file tree
Showing 8 changed files with 366 additions and 11 deletions.
5 changes: 0 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,6 @@ be useful in most cases. If you do need useful error messages, then upon
error you can pass the same input to [pin-project] to receive a helpful
description of the compile error.

### Different: No support for custom Drop implementation

pin-project supports this by [`#[pinned_drop]`][pinned-drop].

### Different: No support for custom Unpin implementation

pin-project supports this by [`UnsafeUnpin`][unsafe-unpin] and [`!Unpin`][not-unpin].
Expand All @@ -115,7 +111,6 @@ pin-project supports this.
[`pin_project!`]: https://docs.rs/pin-project-lite/0.2/pin_project_lite/macro.pin_project.html
[not-unpin]: https://docs.rs/pin-project/1/pin_project/attr.pin_project.html#unpin
[pin-project]: https://github.com/taiki-e/pin-project
[pinned-drop]: https://docs.rs/pin-project/1/pin_project/attr.pin_project.html#pinned_drop
[unsafe-unpin]: https://docs.rs/pin-project/1/pin_project/attr.pin_project.html#unsafeunpin

## License
Expand Down
103 changes: 97 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,6 @@
//! error you can pass the same input to [pin-project] to receive a helpful
//! description of the compile error.
//!
//! ## Different: No support for custom Drop implementation
//!
//! pin-project supports this by [`#[pinned_drop]`][pinned-drop].
//!
//! ## Different: No support for custom Unpin implementation
//!
//! pin-project supports this by [`UnsafeUnpin`][unsafe-unpin] and [`!Unpin`][not-unpin].
Expand All @@ -94,7 +90,6 @@
//!
//! [not-unpin]: https://docs.rs/pin-project/1/pin_project/attr.pin_project.html#unpin
//! [pin-project]: https://github.com/taiki-e/pin-project
//! [pinned-drop]: https://docs.rs/pin-project/1/pin_project/attr.pin_project.html#pinned_drop
//! [unsafe-unpin]: https://docs.rs/pin-project/1/pin_project/attr.pin_project.html#unsafeunpin

#![no_std]
Expand All @@ -105,7 +100,6 @@
allow(dead_code, unused_variables)
)
))]
#![warn(unsafe_code)]
#![warn(future_incompatible, rust_2018_idioms, single_use_lifetimes, unreachable_pub)]
#![warn(clippy::all, clippy::default_trait_access)]

Expand Down Expand Up @@ -367,6 +361,7 @@ macro_rules! __pin_project_internal {
$field_vis:vis $field:ident: $field_ty:ty
),+
}
$(impl $($pinned_drop:tt)*)?
) => {
$(#[$attrs])*
$vis struct $ident $($def_generics)*
Expand Down Expand Up @@ -483,6 +478,7 @@ macro_rules! __pin_project_internal {
$crate::__pin_project_internal! { @make_drop_impl;
[$ident]
[$($impl_generics)*] [$($ty_generics)*] [$(where $($where_clause)*)?]
$(impl $($pinned_drop)*)?
}

// Ensure that it's impossible to use pin projections on a #[repr(packed)] struct.
Expand Down Expand Up @@ -529,6 +525,7 @@ macro_rules! __pin_project_internal {
})?
),+
}
$(impl $($pinned_drop:tt)*)?
) => {
$(#[$attrs])*
$vis enum $ident $($def_generics)*
Expand Down Expand Up @@ -641,6 +638,7 @@ macro_rules! __pin_project_internal {
$crate::__pin_project_internal! { @make_drop_impl;
[$ident]
[$($impl_generics)*] [$($ty_generics)*] [$(where $($where_clause)*)?]
$(impl $($pinned_drop)*)?
}

// We don't need to check for '#[repr(packed)]',
Expand Down Expand Up @@ -913,6 +911,91 @@ macro_rules! __pin_project_internal {

// =============================================================================================
// make_drop_impl
(@make_drop_impl;
// FIXME: check $_ident + $_ty_generics == $self_ty
[$_ident:ident]
[$($_impl_generics:tt)*] [$($_ty_generics:tt)*] [$(where $($_where_clause:tt)* )?]
impl $(<
$( $lifetime:lifetime $(: $lifetime_bound:lifetime)? ),* $(,)?
$( $generics:ident
$(: $generics_bound:path)?
$(: ?$generics_unsized_bound:path)?
$(: $generics_lifetime_bound:lifetime)?
),*
>)? PinnedDrop for $self_ty:ty
$(where
$( $where_clause_ty:ty
$(: $where_clause_bound:path)?
$(: ?$where_clause_unsized_bound:path)?
$(: $where_clause_lifetime_bound:lifetime)?
),*
)?
{
fn drop($(mut $self1:ident)? $($self2:ident)?: Pin<&mut Self>) {
$($tt:tt)*
}
}
) => {
impl $(<
$( $lifetime $(: $lifetime_bound)? ,)*
$( $generics
$(: $generics_bound)?
$(: ?$generics_unsized_bound)?
$(: $generics_lifetime_bound)?
),*
>)? $crate::__private::Drop for $self_ty
$(where
$( $where_clause_ty
$(: $where_clause_bound)?
$(: ?$where_clause_unsized_bound)?
$(: $where_clause_lifetime_bound)?
),*
)?
{
fn drop(&mut self) {
// Implementing `__DropInner::__drop_inner` is safe, but calling it is not safe.
// This is because destructors can be called multiple times in safe code and
// [double dropping is unsound](https://github.com/rust-lang/rust/pull/62360).
//
// `__drop_inner` is defined as a safe method, but this is fine since
// `__drop_inner` is not accessible by the users and we call `__drop_inner` only
// once.
//
// Users can implement [`Drop`] safely using `pin_project!` and can drop a
// type that implements `PinnedDrop` using the [`drop`] function safely.
trait __DropInner {
fn __drop_inner(self: $crate::__private::Pin<&mut Self>);
}
impl $(<
$( $lifetime $(: $lifetime_bound)? ,)*
$( $generics
$(: $generics_bound)?
$(: ?$generics_unsized_bound)?
$(: $generics_lifetime_bound)?
),*
>)? __DropInner for $self_ty
$(where
$( $where_clause_ty
$(: $where_clause_bound)?
$(: ?$where_clause_unsized_bound)?
$(: $where_clause_lifetime_bound)?
),*
)?
{
fn __drop_inner($(mut $self1)? $($self2)?: $crate::__private::Pin<&mut Self>) {
$($tt)*
}
}

// Safety - we're in 'drop', so we know that 'self' will
// never move again.
let pinned_self = unsafe { $crate::__private::Pin::new_unchecked(self) };
// We call `__drop_inner` only once. Since `__DropInner::__drop_inner`
// is not accessible by the users, it is never called again.
__DropInner::__drop_inner(pinned_self);
}
}
};
(@make_drop_impl;
[$ident:ident]
[$($impl_generics:tt)*] [$($ty_generics:tt)*] [$(where $($where_clause:tt)* )?]
Expand Down Expand Up @@ -1019,6 +1102,7 @@ macro_rules! __pin_project_internal {
$field_vis:vis $field:ident: $field_ty:ty
),+ $(,)?
}
$(impl $($pinned_drop:tt)*)?
) => {
$crate::__pin_project_internal! { @struct=>internal;
[$($proj_mut_ident)?]
Expand Down Expand Up @@ -1054,6 +1138,7 @@ macro_rules! __pin_project_internal {
$field_vis $field: $field_ty
),+
}
$(impl $($pinned_drop)*)?
}
};
(
Expand Down Expand Up @@ -1083,6 +1168,7 @@ macro_rules! __pin_project_internal {
$field_vis:vis $field:ident: $field_ty:ty
),+ $(,)?
}
$(impl $($pinned_drop:tt)*)?
) => {
$crate::__pin_project_internal! { @struct=>internal;
[$($proj_mut_ident)?]
Expand Down Expand Up @@ -1118,6 +1204,7 @@ macro_rules! __pin_project_internal {
$field_vis $field: $field_ty
),+
}
$(impl $($pinned_drop)*)?
}
};
// enum
Expand Down Expand Up @@ -1153,6 +1240,7 @@ macro_rules! __pin_project_internal {
})?
),+ $(,)?
}
$(impl $($pinned_drop:tt)*)?
) => {
$crate::__pin_project_internal! { @enum=>internal;
[$($proj_mut_ident)?]
Expand Down Expand Up @@ -1193,6 +1281,7 @@ macro_rules! __pin_project_internal {
})?
),+
}
$(impl $($pinned_drop)*)?
}
};
(
Expand Down Expand Up @@ -1227,6 +1316,7 @@ macro_rules! __pin_project_internal {
})?
),+ $(,)?
}
$(impl $($pinned_drop:tt)*)?
) => {
$crate::__pin_project_internal! { @enum=>internal;
[$($proj_mut_ident)?]
Expand Down Expand Up @@ -1267,6 +1357,7 @@ macro_rules! __pin_project_internal {
})?
),+
}
$(impl $($pinned_drop)*)?
}
};
}
Expand Down
98 changes: 98 additions & 0 deletions tests/expand/tests/expand/pinned_drop-enum.expanded.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
use pin_project_lite::pin_project;
use std::pin::Pin;
enum Enum<T, U> {
Struct { pinned: T, unpinned: U },
Unit,
}
#[allow(dead_code)]
#[allow(single_use_lifetimes)]
#[allow(clippy::unknown_clippy_lints)]
#[allow(clippy::mut_mut)]
#[allow(clippy::redundant_pub_crate)]
#[allow(clippy::ref_option_ref)]
#[allow(clippy::type_repetition_in_bounds)]
enum EnumProj<'__pin, T, U>
where
Enum<T, U>: '__pin,
{
Struct {
pinned: ::pin_project_lite::__private::Pin<&'__pin mut (T)>,
unpinned: &'__pin mut (U),
},
Unit,
}
#[allow(dead_code)]
#[allow(single_use_lifetimes)]
#[allow(clippy::unknown_clippy_lints)]
#[allow(clippy::mut_mut)]
#[allow(clippy::redundant_pub_crate)]
#[allow(clippy::ref_option_ref)]
#[allow(clippy::type_repetition_in_bounds)]
enum EnumProjRef<'__pin, T, U>
where
Enum<T, U>: '__pin,
{
Struct {
pinned: ::pin_project_lite::__private::Pin<&'__pin (T)>,
unpinned: &'__pin (U),
},
Unit,
}
#[allow(single_use_lifetimes)]
#[allow(clippy::unknown_clippy_lints)]
#[allow(clippy::used_underscore_binding)]
const _: () = {
impl<T, U> Enum<T, U> {
fn project<'__pin>(
self: ::pin_project_lite::__private::Pin<&'__pin mut Self>,
) -> EnumProj<'__pin, T, U> {
unsafe {
match self.get_unchecked_mut() {
Self::Struct { pinned, unpinned } => EnumProj::Struct {
pinned: ::pin_project_lite::__private::Pin::new_unchecked(pinned),
unpinned: unpinned,
},
Self::Unit => EnumProj::Unit,
}
}
}
fn project_ref<'__pin>(
self: ::pin_project_lite::__private::Pin<&'__pin Self>,
) -> EnumProjRef<'__pin, T, U> {
unsafe {
match self.get_ref() {
Self::Struct { pinned, unpinned } => EnumProjRef::Struct {
pinned: ::pin_project_lite::__private::Pin::new_unchecked(pinned),
unpinned: unpinned,
},
Self::Unit => EnumProjRef::Unit,
}
}
}
}
#[allow(non_snake_case)]
struct __Origin<'__pin, T, U> {
__dummy_lifetime: ::pin_project_lite::__private::PhantomData<&'__pin ()>,
Struct: (T, ::pin_project_lite::__private::AlwaysUnpin<U>),
Unit: (),
}
impl<'__pin, T, U> ::pin_project_lite::__private::Unpin for Enum<T, U> where
__Origin<'__pin, T, U>: ::pin_project_lite::__private::Unpin
{
}
impl<T, U> ::pin_project_lite::__private::Drop for Enum<T, U> {
fn drop(&mut self) {
trait __DropInner {
fn __drop_inner(self: ::pin_project_lite::__private::Pin<&mut Self>);
}
impl<T, U> __DropInner for Enum<T, U> {
fn __drop_inner(self: ::pin_project_lite::__private::Pin<&mut Self>) {
let _ = self;
}
}
let pinned_self = unsafe { ::pin_project_lite::__private::Pin::new_unchecked(self) };
__DropInner::__drop_inner(pinned_self);
}
}
};
fn main() {}
22 changes: 22 additions & 0 deletions tests/expand/tests/expand/pinned_drop-enum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use pin_project_lite::pin_project;
use std::pin::Pin;

pin_project! {
#[project = EnumProj]
#[project_ref = EnumProjRef]
enum Enum<T, U> {
Struct {
#[pin]
pinned: T,
unpinned: U,
},
Unit,
}
impl<T, U> PinnedDrop for Enum<T, U> {
fn drop(self: Pin<&mut Self>) {
let _ = self;
}
}
}

fn main() {}
Loading

0 comments on commit 6158662

Please sign in to comment.