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 Nov 18, 2020
1 parent 12d5623 commit 78ea009
Show file tree
Hide file tree
Showing 8 changed files with 356 additions and 11 deletions.
5 changes: 0 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,6 @@ This is the **only** reason to use this crate. However, **if you already have pr

This macro does not handle any invalid input. So error messages are not to 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 @@ -113,7 +109,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 @@ -71,10 +71,6 @@
//!
//! This macro does not handle any invalid input. So error messages are not to 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 @@ -85,7 +81,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 @@ -94,7 +89,6 @@
no_crate_inject,
attr(deny(warnings, rust_2018_idioms, single_use_lifetimes), allow(dead_code))
))]
#![warn(unsafe_code)]
#![warn(future_incompatible, rust_2018_idioms, single_use_lifetimes, unreachable_pub)]
#![warn(clippy::all, clippy::default_trait_access)]
// mem::take, #[non_exhaustive], and Option::{as_deref, as_deref_mut} require Rust 1.40,
Expand Down Expand Up @@ -365,6 +359,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 @@ -478,6 +473,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 @@ -524,6 +520,7 @@ macro_rules! __pin_project_internal {
})?
),+
}
$(impl $($pinned_drop:tt)*)?
) => {
$(#[$attrs])*
$vis enum $ident $($def_generics)*
Expand Down Expand Up @@ -633,6 +630,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 @@ -897,6 +895,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 @@ -1003,6 +1086,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 @@ -1038,6 +1122,7 @@ macro_rules! __pin_project_internal {
$field_vis $field: $field_ty
),+
}
$(impl $($pinned_drop)*)?
}
};
(
Expand Down Expand Up @@ -1067,6 +1152,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 @@ -1102,6 +1188,7 @@ macro_rules! __pin_project_internal {
$field_vis $field: $field_ty
),+
}
$(impl $($pinned_drop)*)?
}
};
// enum
Expand Down Expand Up @@ -1137,6 +1224,7 @@ macro_rules! __pin_project_internal {
})?
),+ $(,)?
}
$(impl $($pinned_drop:tt)*)?
) => {
$crate::__pin_project_internal! { @enum=>internal;
[$($proj_mut_ident)?]
Expand Down Expand Up @@ -1177,6 +1265,7 @@ macro_rules! __pin_project_internal {
})?
),+
}
$(impl $($pinned_drop)*)?
}
};
(
Expand Down Expand Up @@ -1211,6 +1300,7 @@ macro_rules! __pin_project_internal {
})?
),+ $(,)?
}
$(impl $($pinned_drop:tt)*)?
) => {
$crate::__pin_project_internal! { @enum=>internal;
[$($proj_mut_ident)?]
Expand Down Expand Up @@ -1251,6 +1341,7 @@ macro_rules! __pin_project_internal {
})?
),+
}
$(impl $($pinned_drop)*)?
}
};
}
Expand Down
93 changes: 93 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,93 @@
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::mut_mut)]
#[allow(clippy::redundant_pub_crate)]
#[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::mut_mut)]
#[allow(clippy::redundant_pub_crate)]
#[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::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() {
Enum::Struct { pinned, unpinned } => EnumProj::Struct {
pinned: ::pin_project_lite::__private::Pin::new_unchecked(pinned),
unpinned: unpinned,
},
Enum::Unit => EnumProj::Unit,
}
}
}
fn project_ref<'__pin>(
self: ::pin_project_lite::__private::Pin<&'__pin Self>,
) -> EnumProjRef<'__pin, T, U> {
unsafe {
match self.get_ref() {
Enum::Struct { pinned, unpinned } => EnumProjRef::Struct {
pinned: ::pin_project_lite::__private::Pin::new_unchecked(pinned),
unpinned: unpinned,
},
Enum::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 78ea009

Please sign in to comment.