From 90d81aa067277d03657dbc32748b97728b70bad5 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 22 Sep 2020 01:55:31 +0900 Subject: [PATCH] Enum support --- README.md | 9 +- src/lib.rs | 333 +++++++++++++++++- .../tests/expand/default-enum.expanded.rs | 79 +++++ tests/expand/tests/expand/default-enum.rs | 16 + .../tests/expand/default-struct.expanded.rs | 1 + .../expand/tests/expand/pub-enum.expanded.rs | 79 +++++ tests/expand/tests/expand/pub-enum.rs | 16 + .../tests/expand/pub-struct.expanded.rs | 1 + tests/include/basic.rs | 15 + tests/lint.rs | 92 +++++ tests/test.rs | 172 +++++++++ 11 files changed, 797 insertions(+), 16 deletions(-) create mode 100644 tests/expand/tests/expand/default-enum.expanded.rs create mode 100644 tests/expand/tests/expand/default-enum.rs create mode 100644 tests/expand/tests/expand/pub-enum.expanded.rs create mode 100644 tests/expand/tests/expand/pub-enum.rs diff --git a/README.md b/README.md index faec117..23f7b51 100644 --- a/README.md +++ b/README.md @@ -72,10 +72,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: Structs only - -pin-project-lite will refuse anything other than a braced struct with named fields. Enums and tuple structs are not supported. - ### Different: No support for custom Drop implementation pin-project supports this by [`#[pinned_drop]`][pinned-drop]. @@ -84,12 +80,11 @@ pin-project supports this by [`#[pinned_drop]`][pinned-drop]. pin-project supports this by [`UnsafeUnpin`][unsafe-unpin] and [`!Unpin`][not-unpin]. -### Different: No support for pattern matching and destructing +### Different: No support for tuple structs and tuple variants -[pin-project supports this.][naming] +pin-project supports this. [`pin_project!`]: https://docs.rs/pin-project-lite/0.1/pin_project_lite/macro.pin_project.html -[naming]: https://docs.rs/pin-project/0.4/pin_project/attr.pin_project.html [not-unpin]: https://docs.rs/pin-project/0.4/pin_project/attr.pin_project.html#unpin [pin-project]: https://github.com/taiki-e/pin-project [pinned-drop]: https://docs.rs/pin-project/0.4/pin_project/attr.pin_project.html#pinned_drop diff --git a/src/lib.rs b/src/lib.rs index b66999a..3f1dabd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,10 +45,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: Structs only -//! -//! pin-project-lite will refuse anything other than a braced struct with named fields. Enums and tuple structs are not supported. -//! //! ## Different: No support for custom Drop implementation //! //! pin-project supports this by [`#[pinned_drop]`][pinned-drop]. @@ -57,12 +53,11 @@ //! //! pin-project supports this by [`UnsafeUnpin`][unsafe-unpin] and [`!Unpin`][not-unpin]. //! -//! ## Different: No support for pattern matching and destructing +//! ## Different: No support for tuple structs and tuple variants //! -//! [pin-project supports this.][naming] +//! pin-project supports this. //! //! [`pin_project!`]: https://docs.rs/pin-project-lite/0.1/pin_project_lite/macro.pin_project.html -//! [naming]: https://docs.rs/pin-project/0.4/pin_project/attr.pin_project.html //! [not-unpin]: https://docs.rs/pin-project/0.4/pin_project/attr.pin_project.html#unpin //! [pin-project]: https://github.com/taiki-e/pin-project //! [pinned-drop]: https://docs.rs/pin-project/0.4/pin_project/attr.pin_project.html#pinned_drop @@ -171,8 +166,7 @@ macro_rules! pin_project { } // limitations: -// * no support for tuple structs and enums. -// * no support for naming the projection types. +// * no support for tuple structs and tuple variant (wontfix). // * no support for multiple trait/lifetime bounds. // * no support for `Self` in where clauses. (wontfix) // * no support for overlapping lifetime names. (wontfix) @@ -297,6 +291,132 @@ macro_rules! __pin_project_internal { }; }; + // ============================================================================================= + // main: enum + (@enum_internal; + [$proj_vis:vis] + [$proj_mut_ident:ident] + [$proj_ref_ident:ident] + [$(#[$attrs:meta])* $vis:vis enum $ident:ident] + [$($def_generics:tt)*] + [$($impl_generics:tt)*] [$($ty_generics:tt)*] [$(where $($where_clause:tt)*)?] + { + $( + $variant:ident $({ + $( + $(#[$pin:ident])? + $field:ident: $field_ty:ty + ),+ + })? + ),+ + } + ) => { + $(#[$attrs])* + $vis enum $ident $($def_generics)* + $(where + $($where_clause)*)? + { + $( + $variant $({ + $( + $field: $field_ty + ),+ + })? + ),+ + } + + $crate::__pin_project_internal! { @make_proj_ty_enum; + [$proj_vis] + [$proj_mut_ident] + [$proj_ref_ident] + [$vis enum $ident] + [$($impl_generics)*] [$($ty_generics)*] [$(where $($where_clause)*)?] + { + $( + $variant $({ + $( + $(#[$pin])? + $field: $field_ty + ),+ + })? + ),+ + } + } + + #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 + #[allow(clippy::used_underscore_binding)] + const _: () = { + impl <$($impl_generics)*> $ident <$($ty_generics)*> + $(where + $($where_clause)*)? + { + $proj_vis fn project<'__pin>( + self: $crate::__private::Pin<&'__pin mut Self>, + ) -> $proj_mut_ident <'__pin, $($ty_generics)*> { + unsafe { + match self.get_unchecked_mut() { + $( + $ident::$variant $({ + $($field),+ + })? => { + $proj_mut_ident::$variant $({ + $( + $field: $crate::__pin_project_internal!( + @make_unsafe_field_proj; + $(#[$pin])? $field + ) + ),+ + })? + } + ),+ + } + } + } + $proj_vis fn project_ref<'__pin>( + self: $crate::__private::Pin<&'__pin Self>, + ) -> $proj_ref_ident <'__pin, $($ty_generics)*> { + unsafe { + match self.get_ref() { $( + $ident::$variant $({ + $($field),+ + })? => { + $proj_ref_ident::$variant $({ + $( + $field: $crate::__pin_project_internal!( + @make_unsafe_field_proj; + $(#[$pin])? $field) + ),+ + })? + } + ),+ } + } + } + } + + $crate::__pin_project_internal! { @make_unpin_impl; + [$vis $ident] + [$($impl_generics)*] [$($ty_generics)*] [$(where $($where_clause)*)?] + $( + $variant: ($( + $( + $crate::__pin_project_internal!(@make_unpin_bound; + $(#[$pin])? $field_ty + ) + ),+ + )?) + ),+ + } + + $crate::__pin_project_internal! { @make_drop_impl; + [$ident] + [$($impl_generics)*] [$($ty_generics)*] [$(where $($where_clause)*)?] + } + + // We don't need to check for '#[repr(packed)]', + // since it does not apply to enums. + }; + }; + // ============================================================================================= // make_proj_ty: struct (@make_proj_ty_struct; @@ -339,6 +459,64 @@ macro_rules! __pin_project_internal { } }; + // ============================================================================================= + // make_proj_ty: enum + (@make_proj_ty_enum; + [$proj_vis:vis] + [$proj_mut_ident:ident] + [$proj_ref_ident:ident] + [$vis:vis enum $ident:ident] + [$($impl_generics:tt)*] [$($ty_generics:tt)*] [$(where $($where_clause:tt)* )?] + { + $( + $variant:ident $({ + $( + $(#[$pin:ident])? + $field:ident: $field_ty:ty + ),+ + })? + ),+ + } + ) => { + #[allow(dead_code)] // This lint warns unused fields/variants. + #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 + #[allow(clippy::mut_mut)] // This lint warns `&mut &mut `. + #[allow(clippy::type_repetition_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/4326 + $proj_vis enum $proj_mut_ident <'__pin, $($impl_generics)*> + where + $ident <$($ty_generics)*>: '__pin + $(, $($where_clause)*)? + { + $( + $variant $({ + $( + $field: $crate::__pin_project_internal!(@make_proj_field; + $(#[$pin])? $field_ty; mut + ) + ),+ + })? + ),+ + } + #[allow(dead_code)] // This lint warns unused fields/variants. + #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 + #[allow(clippy::type_repetition_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/4326 + $proj_vis enum $proj_ref_ident <'__pin, $($impl_generics)*> + where + $ident <$($ty_generics)*>: '__pin + $(, $($where_clause)*)? + { + $( + $variant $({ + $( + $field: $crate::__pin_project_internal!(@make_proj_field; + $(#[$pin])? $field_ty; + ) + ),+ + })? + ),+ + } + }; + // ============================================================================================= // make_unpin_impl (@make_unpin_impl; @@ -371,6 +549,7 @@ macro_rules! __pin_project_internal { // regardless of the privacy of the types of their fields. // // See also https://github.com/taiki-e/pin-project/pull/53. + #[allow(non_snake_case)] $vis struct __Origin <'__pin, $($impl_generics)*> $(where $($where_clause)*)? @@ -457,6 +636,7 @@ macro_rules! __pin_project_internal { // ============================================================================================= // Parses input and determines visibility + // struct ( $(#[$attrs:meta])* pub struct $ident:ident $(< @@ -561,6 +741,141 @@ macro_rules! __pin_project_internal { } } }; + // enum + ( + // FIXME: decide syntax + project = $proj_mut_ident:ident; + // FIXME: make optional + project_ref = $proj_ref_ident:ident; + + $(#[$attrs:meta])* + pub enum $ident:ident $(< + $( $generics:tt + $(: $generics_bound:path)? + $(: ?$generics_unsized_bound:path)? + $(: $generics_lifetime_bound:lifetime)? + $(= $generics_default:ty)? + ),* $(,)? + >)? + $(where + $( $where_clause_ty:ty + $(: $where_clause_bound:path)? + $(: ?$where_clause_unsized_bound:path)? + $(: $where_clause_lifetime_bound:lifetime)? + ),* $(,)? + )? + { + $( + $variant:ident $({ + $( + $(#[$pin:ident])? + $field:ident: $field_ty:ty + ),+ $(,)? + })? + ),+ $(,)? + } + ) => { + $crate::__pin_project_internal! { @enum_internal; + [pub(crate)] + [$proj_mut_ident] + [$proj_ref_ident] + [$(#[$attrs])* pub enum $ident] + [$(< $( $generics + $(: $generics_bound)? + $(: ?$generics_unsized_bound)? + $(: $generics_lifetime_bound)? + $(= $generics_default)? + ),* >)?] + [$( $( $generics + $(: $generics_bound)? + $(: ?$generics_unsized_bound)? + $(: $generics_lifetime_bound)? + ),* )?] + [$( $( $generics ),* )?] + [$(where $( $where_clause_ty + $(: $where_clause_bound)? + $(: ?$where_clause_unsized_bound)? + $(: $where_clause_lifetime_bound)? + ),* )?] + { + $( + $variant $({ + $( + $(#[$pin])? + $field: $field_ty + ),+ + })? + ),+ + } + } + }; + ( + // FIXME: decide syntax + project = $proj_mut_ident:ident; + // FIXME: make optional + project_ref = $proj_ref_ident:ident; + + $(#[$attrs:meta])* + $vis:vis enum $ident:ident $(< + $( $generics:tt + $(: $generics_bound:path)? + $(: ?$generics_unsized_bound:path)? + $(: $generics_lifetime_bound:lifetime)? + $(= $generics_default:ty)? + ),* $(,)? + >)? + $(where + $( $where_clause_ty:ty + $(: $where_clause_bound:path)? + $(: ?$where_clause_unsized_bound:path)? + $(: $where_clause_lifetime_bound:lifetime)? + ),* $(,)? + )? + { + $( + $variant:ident $({ + $( + $(#[$pin:ident])? + $field:ident: $field_ty:ty + ),+ $(,)? + })? + ),+ $(,)? + } + ) => { + $crate::__pin_project_internal! { @enum_internal; + [$vis] + [$proj_mut_ident] + [$proj_ref_ident] + [$(#[$attrs])* $vis enum $ident] + [$(< $( $generics + $(: $generics_bound)? + $(: ?$generics_unsized_bound)? + $(: $generics_lifetime_bound)? + $(= $generics_default)? + ),* >)?] + [$( $( $generics + $(: $generics_bound)? + $(: ?$generics_unsized_bound)? + $(: $generics_lifetime_bound)? + ),* )?] + [$( $( $generics ),* )?] + [$(where $( $where_clause_ty + $(: $where_clause_bound)? + $(: ?$where_clause_unsized_bound)? + $(: $where_clause_lifetime_bound)? + ),* )?] + { + $( + $variant $({ + $( + $(#[$pin])? + $field: $field_ty + ),+ + })? + ),+ + } + } + }; } // Not public API. diff --git a/tests/expand/tests/expand/default-enum.expanded.rs b/tests/expand/tests/expand/default-enum.expanded.rs new file mode 100644 index 0000000..cc3d7ed --- /dev/null +++ b/tests/expand/tests/expand/default-enum.expanded.rs @@ -0,0 +1,79 @@ +use pin_project_lite::pin_project; +enum Enum { + Struct { pinned: T, unpinned: U }, + Unit, +} +#[allow(dead_code)] +#[allow(single_use_lifetimes)] +#[allow(clippy::mut_mut)] +#[allow(clippy::type_repetition_in_bounds)] +enum EnumProj<'__pin, T, U> +where + Enum: '__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::type_repetition_in_bounds)] +enum EnumProjRef<'__pin, T, U> +where + Enum: '__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 Enum { + 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), + Unit: (), + } + impl<'__pin, T, U> ::pin_project_lite::__private::Unpin for Enum where + __Origin<'__pin, T, U>: ::pin_project_lite::__private::Unpin + { + } + trait MustNotImplDrop {} + #[allow(clippy::drop_bounds)] + impl MustNotImplDrop for T {} + impl MustNotImplDrop for Enum {} +}; +fn main() {} diff --git a/tests/expand/tests/expand/default-enum.rs b/tests/expand/tests/expand/default-enum.rs new file mode 100644 index 0000000..54e4a34 --- /dev/null +++ b/tests/expand/tests/expand/default-enum.rs @@ -0,0 +1,16 @@ +use pin_project_lite::pin_project; + +pin_project! { + project = EnumProj; + project_ref = EnumProjRef; + enum Enum { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Unit, + } +} + +fn main() {} diff --git a/tests/expand/tests/expand/default-struct.expanded.rs b/tests/expand/tests/expand/default-struct.expanded.rs index 9ce9282..7bbf367 100644 --- a/tests/expand/tests/expand/default-struct.expanded.rs +++ b/tests/expand/tests/expand/default-struct.expanded.rs @@ -49,6 +49,7 @@ const _: () = { } } } + #[allow(non_snake_case)] struct __Origin<'__pin, T, U> { __dummy_lifetime: ::pin_project_lite::__private::PhantomData<&'__pin ()>, pinned: T, diff --git a/tests/expand/tests/expand/pub-enum.expanded.rs b/tests/expand/tests/expand/pub-enum.expanded.rs new file mode 100644 index 0000000..801c448 --- /dev/null +++ b/tests/expand/tests/expand/pub-enum.expanded.rs @@ -0,0 +1,79 @@ +use pin_project_lite::pin_project; +pub enum Enum { + Struct { pinned: T, unpinned: U }, + Unit, +} +#[allow(dead_code)] +#[allow(single_use_lifetimes)] +#[allow(clippy::mut_mut)] +#[allow(clippy::type_repetition_in_bounds)] +pub(crate) enum EnumProj<'__pin, T, U> +where + Enum: '__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::type_repetition_in_bounds)] +pub(crate) enum EnumProjRef<'__pin, T, U> +where + Enum: '__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 Enum { + pub(crate) 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, + } + } + } + pub(crate) 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)] + pub struct __Origin<'__pin, T, U> { + __dummy_lifetime: ::pin_project_lite::__private::PhantomData<&'__pin ()>, + Struct: (T, ::pin_project_lite::__private::AlwaysUnpin), + Unit: (), + } + impl<'__pin, T, U> ::pin_project_lite::__private::Unpin for Enum where + __Origin<'__pin, T, U>: ::pin_project_lite::__private::Unpin + { + } + trait MustNotImplDrop {} + #[allow(clippy::drop_bounds)] + impl MustNotImplDrop for T {} + impl MustNotImplDrop for Enum {} +}; +fn main() {} diff --git a/tests/expand/tests/expand/pub-enum.rs b/tests/expand/tests/expand/pub-enum.rs new file mode 100644 index 0000000..542f984 --- /dev/null +++ b/tests/expand/tests/expand/pub-enum.rs @@ -0,0 +1,16 @@ +use pin_project_lite::pin_project; + +pin_project! { + project = EnumProj; + project_ref = EnumProjRef; + pub enum Enum { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Unit, + } +} + +fn main() {} diff --git a/tests/expand/tests/expand/pub-struct.expanded.rs b/tests/expand/tests/expand/pub-struct.expanded.rs index 39cb20b..7ed433d 100644 --- a/tests/expand/tests/expand/pub-struct.expanded.rs +++ b/tests/expand/tests/expand/pub-struct.expanded.rs @@ -49,6 +49,7 @@ const _: () = { } } } + #[allow(non_snake_case)] pub struct __Origin<'__pin, T, U> { __dummy_lifetime: ::pin_project_lite::__private::PhantomData<&'__pin ()>, pinned: T, diff --git a/tests/include/basic.rs b/tests/include/basic.rs index 967cf81..4a79dd6 100644 --- a/tests/include/basic.rs +++ b/tests/include/basic.rs @@ -8,3 +8,18 @@ pub unpinned: U, } } + +::pin_project_lite::pin_project! { + project = DefaultEnumProj; + project_ref = DefaultEnumProjRef; + + #[derive(Debug)] + pub enum DefaultEnum { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Unit, + } +} diff --git a/tests/lint.rs b/tests/lint.rs index d564d7a..31e538b 100644 --- a/tests/lint.rs +++ b/tests/lint.rs @@ -56,6 +56,20 @@ pub mod box_pointers { pub u: Box, } } + + pin_project! { + project = EnumProj; + project_ref = EnumProjRef; + #[derive(Debug)] + pub enum Enum { + Struct { + #[pin] + p: Box, + u: Box, + }, + Unit, + } + } } pub mod explicit_outlives_requirements { @@ -73,6 +87,40 @@ pub mod explicit_outlives_requirements { pub unpinned: &'a mut U, } } + + pin_project! { + project = EnumProj; + project_ref = EnumProjRef; + #[derive(Debug)] + pub enum Enum<'a, T, U> + where + T: ?Sized, + U: ?Sized, + { + Struct { + #[pin] + pinned: &'a mut T, + unpinned: &'a mut U, + }, + Unit, + } + } +} + +pub mod variant_size_differences { + use pin_project_lite::pin_project; + + pin_project! { + project = EnumProj; + project_ref = EnumProjRef; + #[allow(missing_debug_implementations, missing_copy_implementations)] // https://github.com/rust-lang/rust/pull/74060 + #[allow(variant_size_differences)] // for the type itself + #[allow(clippy::large_enum_variant)] // for the type itself + pub enum Enum { + V1 { f: u8 }, + V2 { f: [u8; 1024] }, + } + } } pub mod clippy_mut_mut { @@ -86,6 +134,20 @@ pub mod clippy_mut_mut { pub unpinned: &'a mut U, } } + + pin_project! { + project = EnumProj; + project_ref = EnumProjRef; + #[derive(Debug)] + pub enum Enum<'a, T, U> { + Struct { + #[pin] + pinned: &'a mut T, + unpinned: &'a mut U, + }, + Unit, + } + } } pub mod clippy_type_repetition_in_bounds { @@ -102,6 +164,23 @@ pub mod clippy_type_repetition_in_bounds { pub unpinned: U, } } + + pin_project! { + project = EnumProj; + project_ref = EnumProjRef; + #[derive(Debug)] + pub enum Enum + where + Enum: Sized, + { + Struct { + #[pin] + pinned: T, + unpinned: U, + }, + Unit, + } + } } pub mod clippy_used_underscore_binding { @@ -115,6 +194,19 @@ pub mod clippy_used_underscore_binding { pub _unpinned: U, } } + + pin_project! { + project = EnumProj; + project_ref = EnumProjRef; + #[derive(Debug)] + pub enum Enum { + Struct { + #[pin] + _pinned: T, + _unpinned: U, + }, + } + } } #[allow(box_pointers)] diff --git a/tests/test.rs b/tests/test.rs index afbef0b..b593f32 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -34,6 +34,83 @@ fn projection() { let _: Pin<&mut i32> = s.f1; let _: &mut i32 = s.f2; + + pin_project! { + project = EnumProj; + project_ref = EnumProjRef; + #[derive(Eq, PartialEq, Debug)] + enum Enum { + Struct { + #[pin] + f1: C, + f2: D, + }, + Unit, + } + } + + let mut e = Enum::Struct { f1: 1, f2: 2 }; + let mut e_orig = Pin::new(&mut e); + let e = e_orig.as_mut().project(); + + match e { + EnumProj::Struct { f1, f2 } => { + let x: Pin<&mut i32> = f1; + assert_eq!(*x, 1); + let y: &mut i32 = f2; + assert_eq!(*y, 2); + } + EnumProj::Unit => {} + } + + assert_eq!(Pin::into_ref(e_orig).get_ref(), &Enum::Struct { f1: 1, f2: 2 }); + + let mut e = Enum::Struct { f1: 3, f2: 4 }; + let mut e = Pin::new(&mut e).project(); + + match &mut e { + EnumProj::Struct { f1, f2 } => { + let x: &mut Pin<&mut i32> = f1; + assert_eq!(**x, 3); + let y: &mut &mut i32 = f2; + assert_eq!(**y, 4); + } + EnumProj::Unit => {} + } + + if let EnumProj::Struct { f1, f2 } = e { + let x: Pin<&mut i32> = f1; + assert_eq!(*x, 3); + let y: &mut i32 = f2; + assert_eq!(*y, 4); + } +} + +#[test] +fn enum_project_set() { + pin_project! { + project = EnumProj; + project_ref = EnumProjRef; + #[derive(Eq, PartialEq, Debug)] + enum Enum { + V1 { #[pin] f: u8 }, + V2 { f: bool }, + } + } + + let mut e = Enum::V1 { f: 25 }; + let mut e_orig = Pin::new(&mut e); + let e_proj = e_orig.as_mut().project(); + + match e_proj { + EnumProj::V1 { f } => { + let new_e = Enum::V2 { f: f.as_ref().get_ref() == &25 }; + e_orig.set(new_e); + } + _ => unreachable!(), + } + + assert_eq!(e, Enum::V2 { f: true }); } #[test] @@ -46,6 +123,17 @@ fn where_clause() { f: T, } } + + pin_project! { + project = EnumProj; + project_ref = EnumProjRef; + enum Enum + where + T: Copy, + { + V { f: T }, + } + } } #[test] @@ -84,6 +172,18 @@ fn where_clause_and_associated_type_field() { trait Static: 'static {} impl Static for Struct3 {} + + pin_project! { + project = EnumProj; + project_ref = EnumProjRef; + enum Enum + where + I: Iterator, + { + V1 { #[pin] f: I }, + V2 { f: I::Item }, + } + } } #[test] @@ -112,6 +212,20 @@ fn move_out() { let x = Struct { f: NotCopy }; let _val: NotCopy = x.f; + + pin_project! { + project = EnumProj; + project_ref = EnumProjRef; + enum Enum { + V { f: NotCopy }, + } + } + + let x = Enum::V { f: NotCopy }; + #[allow(clippy::infallible_destructuring_match)] + let _val: NotCopy = match x { + Enum::V { f } => f, + }; } #[test] @@ -170,6 +284,14 @@ fn trait_bounds_on_type_generics() { f2: &'b u8, } } + + pin_project! { + project = EnumProj; + project_ref = EnumProjRef; + enum Enum<'a, T: ?Sized> { + V { f: &'a mut T }, + } + } } #[test] @@ -202,6 +324,18 @@ fn lifetime_project() { } } + pin_project! { + project = EnumProj; + project_ref = EnumProjRef; + enum Enum { + V { + #[pin] + pinned: T, + unpinned: U, + }, + } + } + impl Struct1 { fn get_pin_ref<'a>(self: Pin<&'a Self>) -> Pin<&'a T> { self.project_ref().pinned @@ -219,6 +353,19 @@ fn lifetime_project() { self.project().pinned } } + + impl Enum { + fn get_pin_ref<'a>(self: Pin<&'a Self>) -> Pin<&'a T> { + match self.project_ref() { + EnumProjRef::V { pinned, .. } => pinned, + } + } + fn get_pin_mut<'a>(self: Pin<&'a mut Self>) -> Pin<&'a mut T> { + match self.project() { + EnumProj::V { pinned, .. } => pinned, + } + } + } } #[test] @@ -239,6 +386,18 @@ fn lifetime_project_elided() { } } + pin_project! { + project = EnumProj; + project_ref = EnumProjRef; + enum Enum { + V { + #[pin] + pinned: T, + unpinned: U, + }, + } + } + impl Struct1 { fn get_pin_ref(self: Pin<&Self>) -> Pin<&T> { self.project_ref().pinned @@ -256,6 +415,19 @@ fn lifetime_project_elided() { self.project().pinned } } + + impl Enum { + fn get_pin_ref(self: Pin<&Self>) -> Pin<&T> { + match self.project_ref() { + EnumProjRef::V { pinned, .. } => pinned, + } + } + fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> { + match self.project() { + EnumProj::V { pinned, .. } => pinned, + } + } + } } mod visibility {